java和python中函数式编程
本篇文章將基于java和python分別介紹Lambda表達式,包括定義,使用等
java函數式編程
自jdk1.8開始,java中引入了函數式編程,使編程更加簡潔靈活。接下來通過詳細的例子闡述
函數式接口
Lambda語法
Function
方法引用
引用靜態(tài)方法
引用指定對象的實例方法
引用任意對象的實例方法
引用構造方法
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的人員,另一個需求需要篩選性別為男性的人員。我們可能會寫如下兩個實現(xiàn)方法供使用方調用:
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之間的人員。。。
顯然以上設計是不合理的,如果繼續(xù)增加方法,方法數量呈直線增加,我們可以考慮設計接口,并在print方法中傳入接口,用戶只需要傳入實現(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();}}}?--調用時傳入匿名類Test.printPersons(list,new CheckPerson() {public boolean test(Person p) {return p.getAge() >= 18 && p.getAge() <= 25;}});通過申明CheckPerson接口和實現(xiàn)printPersons一個方法,我們就可以篩選滿足個人條件的數據進行打印。
函數式接口
上述CheckPerson接口為函數式接口。java中將只有一個抽象方法的接口稱為函數式接口(不包括和Object中同名的方法,如Comparator中同時含有compare和equals抽象方法,Object中有equals方法)。在api文檔中包含@FunctionalInterface注解的均為函數式接口
Lambda語法
以上通過匿名類方式較復雜,針對函數式接口,java有其特定的語法,稱為Lambda表達式,你可以通過如下方式調用
Test.printPersons(list,p -> p.getAge() >= 18 && p.getAge() <= 25);lambda語法分三部分
參數列表
上述用法完整參數形式為(Person p) ,可以省略掉類型申明和括號
箭頭
參數和方法體之間用 ->指向
方法體
方法體可以是一個表達式,也可以是語句塊。還是以上面為例子,你也可以這樣寫
-- 完整形式Test.printPersons(list,(Person p) -> {return p.getAge() >= 18 && p.getAge() <= 25;});-- 簡寫形式Test.printPersons(list,(Person p) -> p.getAge() >= 18 && p.getAge() <= 25);
Lambda表達式可以看成一個方法申明;你可以認為它是一個沒用名稱的匿名方法
Function
通過上面實例我們可以看到,為了使用lambda表達式我們需要申明一個函數式接口,這在一定程度上增加了復雜度,有沒有可能不需要申明接口也能使用lambda表達式,答案是肯定的。接下來該Function登場了,在java.util.function包下申明了很多函數式接口。其中具有代表性的為如下:
-- 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 表示有輸入參數,沒有返回類型的方法
Predicate 表示有輸入參數,返回類型為boolean的方法
Function 表示有輸入參數且有返回類型的方法
Supplier 表示沒有輸入參數有返回類型的方法
有了以上的接口申明,針對此類問題再也不需要進行另外接口申明,于是我們的方法可以改寫為如下:
-- 方法public static void print(List<Person> persons,Predicate<Person> func) {for(Person p:persons) {if(func.test(p)) {p.printPerson();}}}-- 調用Test.print(list,(Person p) -> p.getAge() >= 18 && p.getAge() <= 25);方法引用
針對lambda還有更簡潔的使用方式
| 引用靜態(tài)方法 | ContainingClass::staticMethodName |
| 引用指定對象的實例方法 | ContainingObject::instanceMethodName |
| 引用任意對象的實例方法 | ContainingType::methodName |
| 引用構造方法 | 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)引用指定對象的實例方法
List<String> list1 = new ArrayList<>(Arrays.asList("xxx","yyy","zzz",null));?--原始寫法list1.stream().map(item -> item.toUpperCase());?-- 方法引用list1.stream().map(String::toUpperCase);引用任意對象的實例方法
String[] stringArray = { "Barbara", "James", "Mary", "John","Patricia", "Robert", "Michael", "Linda" };Arrays.sort(stringArray, String::compareToIgnoreCase);引用構造方法
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進行一系列聚合操作(filter、map等),需要用到Stream和管道
一個管道是順序的聚合操作。接下來的操作給出了stream的使用方法
-- roster為listroster.stream().filter(e -> e.getGender() == Person.Sex.MALE).forEach(e -> System.out.println(e.getName()));一個管道包括如下部分:
一個源:源可以是集合,數組,泛型函數。上例中為collection roster
0個或者多個中間操作,如filter,此操作產生一個新的stream(流)
流是一系列元素。 與集合不同,它不是存儲元素的數據結構。 相反,流通過管道從源傳輸值。 此示例通過stream()方法從集合roster創(chuàng)建流。
過濾器操作返回與predicate匹配元素(此操作的參數)新流。 在此示例中,predicate是lambda表達式e - > e.getGender()== Person.Sex.MALE。 如果對象e的gender字段的值為Person.Sex.MALE,則返回布爾值true。 因此,此示例中的過濾器操作返回包含集合名單中所有男性成員的流。
終端操作。 終結操作(例如forEach)產生非流結果,例如原始值,集合,或者對于forEach,根本沒有值。 在此示例中,forEach操作的參數是lambda表達式e - > System.out.println(e.getName()),它在對象e上調用方法getName。 (Java運行時和編譯器推斷對象e的類型是Person。)
以下示例計算集合名單中包含的所有男性成員的平均年齡,其中管道包含聚合操作過濾器,mapToInt和average
double average = roster.stream().filter(p -> p.getGender() == Person.Sex.MALE).mapToInt(Person::getAge).average().getAsDouble();python匿名函數
python中l(wèi)ambda又叫匿名函數,其語法為
lambda [arg1 [,art2,…artn]]:expression?# [arg1 [,art2,…artn]] 為參數# expression 為計算表達式匿名函數有個限制,就是只能有一個表達式,不用寫return,返回值就是該表達式的結果
實戰(zhàn)
有l(wèi)ist為[1,2,3,4,5,6,7],需要先過濾掉其中偶數,然后對每個元素進行,然后對每個元素求和,此處要求我們用filter、map、reduce等函數進行操作
# 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表達式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))))總結
以上是生活随笔為你收集整理的java和python中函数式编程的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 使用批处理文件(.bat)批量在文件名前
- 下一篇: 惠普总裁口述的职业规划(3)