java和python中函数式编程
本篇文章將基于java和python分別介紹Lambda表達(dá)式,包括定義,使用等
java函數(shù)式編程
自jdk1.8開始,java中引入了函數(shù)式編程,使編程更加簡潔靈活。接下來通過詳細(xì)的例子闡述
函數(shù)式接口
Lambda語法
Function
方法引用
引用靜態(tài)方法
引用指定對象的實(shí)例方法
引用任意對象的實(shí)例方法
引用構(gòu)造方法
stream
有如下的person類,屬性為name、sex、age
package lambda.pojo;?/*** @author liufeifei* @date 2018/06/26*/public class Person {?public enum Sex{MALE,FEMALE;}?private String name;?private Sex sex;?private int age;?public Person(String name, Sex sex, int age) {this.name = name;this.sex = sex;this.age = age;}?public Person() {}?public String getName() {return name;}?public void setName(String name) {this.name = name;}?public Sex getSex() {return sex;}?public void setSex(Sex sex) {this.sex = sex;}?public int getAge() {return age;}?public void setAge(int age) {this.age = age;}?public void printPerson() {System.out.println("name:" + this.name + " age:"+ this.age + " sex:" + this.sex);}}現(xiàn)在需要篩選List<Person>中年齡大于age的人員,另一個需求需要篩選性別為男性的人員。我們可能會寫如下兩個實(shí)現(xiàn)方法供使用方調(diào)用:
public static void printPersonsOlderThan(List<Person> persons,int age) {for(Person p:persons) {if(p.getAge() >= age) {p.printPerson();}}}?public static void printPersonsSex(List<Person> persons,Sex sex) {for(Person p:persons) {if(p.getSex() == sex) {p.printPerson();}}}以上的場景得到了解決。但是麻煩來了,如果還需要篩選“zhang”姓的人員,還要篩選年齡段在low和hight之間的人員。。。
顯然以上設(shè)計(jì)是不合理的,如果繼續(xù)增加方法,方法數(shù)量呈直線增加,我們可以考慮設(shè)計(jì)接口,并在print方法中傳入接口,用戶只需要傳入實(shí)現(xiàn)接口的匿名類
--接口public interface CheckPerson {boolean test(Person p);}?--打印方法printPersons(List<Person> persons, CheckPerson checkPerson) {for(Person p:persons) {if(checkPerson.test(p)) {p.printPerson();}}}?--調(diào)用時傳入匿名類Test.printPersons(list,new CheckPerson() {public boolean test(Person p) {return p.getAge() >= 18 && p.getAge() <= 25;}});通過申明CheckPerson接口和實(shí)現(xiàn)printPersons一個方法,我們就可以篩選滿足個人條件的數(shù)據(jù)進(jìn)行打印。
函數(shù)式接口
上述CheckPerson接口為函數(shù)式接口。java中將只有一個抽象方法的接口稱為函數(shù)式接口(不包括和Object中同名的方法,如Comparator中同時含有compare和equals抽象方法,Object中有equals方法)。在api文檔中包含@FunctionalInterface注解的均為函數(shù)式接口
Lambda語法
以上通過匿名類方式較復(fù)雜,針對函數(shù)式接口,java有其特定的語法,稱為Lambda表達(dá)式,你可以通過如下方式調(diào)用
Test.printPersons(list,p -> p.getAge() >= 18 && p.getAge() <= 25);lambda語法分三部分
參數(shù)列表
上述用法完整參數(shù)形式為(Person p) ,可以省略掉類型申明和括號
箭頭
參數(shù)和方法體之間用 ->指向
方法體
方法體可以是一個表達(dá)式,也可以是語句塊。還是以上面為例子,你也可以這樣寫
-- 完整形式Test.printPersons(list,(Person p) -> {return p.getAge() >= 18 && p.getAge() <= 25;});-- 簡寫形式Test.printPersons(list,(Person p) -> p.getAge() >= 18 && p.getAge() <= 25);
Lambda表達(dá)式可以看成一個方法申明;你可以認(rèn)為它是一個沒用名稱的匿名方法
Function
通過上面實(shí)例我們可以看到,為了使用lambda表達(dá)式我們需要申明一個函數(shù)式接口,這在一定程度上增加了復(fù)雜度,有沒有可能不需要申明接口也能使用lambda表達(dá)式,答案是肯定的。接下來該Function登場了,在java.util.function包下申明了很多函數(shù)式接口。其中具有代表性的為如下:
-- Consumer 接口public interface Consumer<T> {void accept(T t);}?-- Predicate 接口public interface Predicate<T> {boolean test(T t);}?-- Function 接口public interface Function<T, R> {R apply(T t);}?-- Supplier 接口public interface Supplier<T> {T get();}Consumer 表示有輸入?yún)?shù),沒有返回類型的方法
Predicate 表示有輸入?yún)?shù),返回類型為boolean的方法
Function 表示有輸入?yún)?shù)且有返回類型的方法
Supplier 表示沒有輸入?yún)?shù)有返回類型的方法
有了以上的接口申明,針對此類問題再也不需要進(jìn)行另外接口申明,于是我們的方法可以改寫為如下:
-- 方法public static void print(List<Person> persons,Predicate<Person> func) {for(Person p:persons) {if(func.test(p)) {p.printPerson();}}}-- 調(diào)用Test.print(list,(Person p) -> p.getAge() >= 18 && p.getAge() <= 25);方法引用
針對lambda還有更簡潔的使用方式
| 引用靜態(tài)方法 | ContainingClass::staticMethodName |
| 引用指定對象的實(shí)例方法 | ContainingObject::instanceMethodName |
| 引用任意對象的實(shí)例方法 | ContainingType::methodName |
| 引用構(gòu)造方法 | ClassName::new |
引用靜態(tài)方法
List<String> list1 = new ArrayList<>(Arrays.asList("xxx","yyy","zzz",null));?-- 靜態(tài)方法 原始使用list1.stream().filter(item -> Strings.isNotEmpty(item));?-- 靜態(tài)方法 方法引用list1.stream().filter(Strings::isNotEmpty)引用指定對象的實(shí)例方法
List<String> list1 = new ArrayList<>(Arrays.asList("xxx","yyy","zzz",null));?--原始寫法list1.stream().map(item -> item.toUpperCase());?-- 方法引用list1.stream().map(String::toUpperCase);引用任意對象的實(shí)例方法
String[] stringArray = { "Barbara", "James", "Mary", "John","Patricia", "Robert", "Michael", "Linda" };Arrays.sort(stringArray, String::compareToIgnoreCase);引用構(gòu)造方法
public static <T, SOURCE extends Collection<T>, DEST extends Collection<T>>DEST transferElements(SOURCE sourceCollection,Supplier<DEST> collectionFactory) {DEST result = collectionFactory.get();for (T t : sourceCollection) {result.add(t);}return result;}?Set<Person> rosterSetLambda = transferElements(roster, () -> { return new HashSet<>(); });Set<Person> rosterSet = transferElements(roster, HashSet::new);Set<Person> rosterSet = transferElements(roster, HashSet<Person>::new);
Stream
如果要對Collection進(jìn)行一系列聚合操作(filter、map等),需要用到Stream和管道
一個管道是順序的聚合操作。接下來的操作給出了stream的使用方法
-- roster為listroster.stream().filter(e -> e.getGender() == Person.Sex.MALE).forEach(e -> System.out.println(e.getName()));一個管道包括如下部分:
一個源:源可以是集合,數(shù)組,泛型函數(shù)。上例中為collection roster
0個或者多個中間操作,如filter,此操作產(chǎn)生一個新的stream(流)
流是一系列元素。 與集合不同,它不是存儲元素的數(shù)據(jù)結(jié)構(gòu)。 相反,流通過管道從源傳輸值。 此示例通過stream()方法從集合roster創(chuàng)建流。
過濾器操作返回與predicate匹配元素(此操作的參數(shù))新流。 在此示例中,predicate是lambda表達(dá)式e - > e.getGender()== Person.Sex.MALE。 如果對象e的gender字段的值為Person.Sex.MALE,則返回布爾值true。 因此,此示例中的過濾器操作返回包含集合名單中所有男性成員的流。
終端操作。 終結(jié)操作(例如forEach)產(chǎn)生非流結(jié)果,例如原始值,集合,或者對于forEach,根本沒有值。 在此示例中,forEach操作的參數(shù)是lambda表達(dá)式e - > System.out.println(e.getName()),它在對象e上調(diào)用方法getName。 (Java運(yùn)行時和編譯器推斷對象e的類型是Person。)
以下示例計(jì)算集合名單中包含的所有男性成員的平均年齡,其中管道包含聚合操作過濾器,mapToInt和average
double average = roster.stream().filter(p -> p.getGender() == Person.Sex.MALE).mapToInt(Person::getAge).average().getAsDouble();python匿名函數(shù)
python中l(wèi)ambda又叫匿名函數(shù),其語法為
lambda [arg1 [,art2,…artn]]:expression?# [arg1 [,art2,…artn]] 為參數(shù)# expression 為計(jì)算表達(dá)式匿名函數(shù)有個限制,就是只能有一個表達(dá)式,不用寫return,返回值就是該表達(dá)式的結(jié)果
實(shí)戰(zhàn)
有l(wèi)ist為[1,2,3,4,5,6,7],需要先過濾掉其中偶數(shù),然后對每個元素進(jìn)行,然后對每個元素求和,此處要求我們用filter、map、reduce等函數(shù)進(jìn)行操作
# python版本為3.5from functools import reduce??li = [1, 2, 3, 4, 5, 6, 7]??def filter_func(x):return x % 2 == 1??def map_func(x):return x * x??def reduce_func(x, y):return x + y??print(reduce(reduce_func,map(map_func,filter(filter_func, li))))?# lambda表達(dá)式print(reduce(lambda x, y: x + y, map(lambda x: x * x, filter(lambda x: x % 2 == 1, li))))?# 使用sum簡潔寫法print(sum(map(lambda x: x * x, filter(lambda x: x % 2 == 1, li))))總結(jié)
以上是生活随笔為你收集整理的java和python中函数式编程的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 使用批处理文件(.bat)批量在文件名前
- 下一篇: python导出数据到excel文件_P