lambda 函数式编程_Java 8 Lambda表达式的函数式编程– Monads
lambda 函數式編程
什么是monad ?: monad是一種設計模式概念,用于大多數功能編程語言(如lisp)或現代世界的Clojure或Scala中。 (實際上,我會從scala復制一些內容。)現在,為什么它在Java中變得很重要? 因為java從版本8獲得了新的lambda功能。Lambda或閉包是一種功能編程功能。 它使您可以將代碼塊用作變量,并像這樣傳遞代碼塊。 我在上一篇文章Java 8中的烹飪-項目Lambda中討論了Java的“項目Lambda”。 現在,您可以在此處提供的JDK 8預覽版中進行嘗試。 現在我們可以在Java 8之前做monad嗎? 當然,畢竟Java的lambda在語義上只是實現接口的另一種方式(實際上并不是因為編譯器知道它的使用位置),而是太多混亂的代碼,幾乎可以殺死其實用程序。
現在,讓我在Java中建立一個用例,就像沒有monad一樣,而不是向您描述一個抽象的,看似毫無意義的想法。
討厭的null檢查:如果您編寫了任何非平凡的(例如Hello-World)java程序,則可能已經做了一些null檢查。 它們就像編程必不可少的弊端,您不能沒有它們,但是它們會使您的程序雜亂無章。 讓我們以帶有一組Java數據對象的以下示例為例。 注意,無論如何,我沒有使用過反模式的getter或setter。
public static class Userdetails{public Address address;public Name name;}public static class Name{public String firstName;public String lastName; }public static class Address{public String houseNumber;public Street street;public City city;}public static class Street{public String name; }public static class City{public String name; }現在說您想從UserDetails用戶訪問街道名稱,并且任何屬性都可能為null。 沒有monad,您可能會編寫如下代碼。
if(user == null )return null; else if(user.address == null)return null; else if(user.address.street == null)return null; elsereturn user.address.street.name;理想情況下,它應該是單線的。 我們真正關心的代碼周圍雜亂無章。 現在讓我們看看如何解決該問題。 讓我們創建一個代表可選值的Option類。 然后讓我們有一個map方法,該方法將在其包裝后的值上運行lambda并返回另一個選項。 如果包裝的值為null,它將返回一個包含null的Option而不處理lambda,從而避免使用null指針異常。 請注意,map方法實際上需要使用lambda作為參數,但是我們將需要創建一個接口SingleArgExpression來支持該方法。
SingleArgExpression.java
package com.geekyarticles.lambda;public interface SingleArgExpression<P, R> {public R function(P param); }Option.java
package com.geekyarticles.javamonads;import com.geekyarticles.lambda.public class Option<T> {T value;public Option(T value){this.value = value;}public <E> Option<E> map(SingleArgExpression<T,E> mapper){if(value == null){return new Option<E>(null);}else{return new Option<E>(mapper.function(value));}} @Overridepublic boolean equals(Object rhs){if(rhs instanceof Option){Option o = (Option)rhs;if(value == null) return (o.value==null);else{return value.equals(o.value);}}else{return false;}}@Overridepublic int hashCode(){return value==null? 0 : value.hashCode();}public T get(){return value;}}OptionExample.java
package com.geekyarticles.javamonads.examples;import com.geekyarticles.javamonads.public class OptionExample{public static class Userdetails{public Option<Address> address = new Option<>(null);public Option<Name> name = new Option<>(null);}public static class Name{public Option<String> firstName = new Option<String>(null);public Option<String> lastName = new Option<String>(null); }public static class Address{public Option<String> houseNumber;public Option<Street> street;public Option<City> city;}public static class Street{public Option<String> name; }public static class City{public Option<String> name; }public static void main(String [] args){Option<Userdetails> userOpt = new Option<>(new Userdetails());//And look how simple it is nowString streetName = userOpt.flatMap(user -> user.address).map(address -> address.street).map(street -> street.name).get();System.out.println(streetName);}}所以現在,基本上的想法是,只要方法有機會返回null,就返回Option。 將確保該方法的使用者了解該值可以為null,并且還使該使用者隱式地移過null檢查,如圖所示。 現在,我們從可能必須返回null的所有方法中返回Option,那么映射內的表達式也可能會將Option作為返回類型。 為了避免每次都調用get(),我們可以有一個與map類似的方法flatMap,除了它接受Option用作傳遞給它的lambda的返回類型。
public <E> Option<E> flatMap(SingleArgExpression<T, Option<E>> mapper){if(value == null){return new Option<E>(null);}return mapper.function(value);}我要說的最后一種方法是過濾器。 它將讓我們在映射鏈中放置一個if條件,以便僅當條件為true時才獲得一個值。 請注意,這也是null安全的。 在此特定的monad中,filter的用法并不明顯,但稍后我們將看到其用法。 以下是一個示例,其中所有可為空的字段均已升級為Option,因此將flatMap用于地圖的讀取。
Option.java
package com.geekyarticles.javamonads;import com.geekyarticles.lambda.public class Option<T> {T value;public Option(T value){this.value = value;}public <E> Option<E> map(SingleArgExpression<T,E> mapper){if(value == null){return new Option<E>(null);}else{return new Option<E>(mapper.function(value));}}public <E> Option<E> flatMap(SingleArgExpression<T, Option<E>> mapper){if(value == null){return new Option<E>(null);}return mapper.function(value);}public Option<T> filter(SingleArgExpression<T, Boolean> filter){if(value == null){return new Option<T>(null);}else if(filter.function(value)){return this;}else{return new Option<T>(null);}}@Overridepublic boolean equals(Object rhs){if(rhs instanceof Option){Option o = (Option)rhs;if(value == null) return (o.value==null);else{return value.equals(o.value);}}else{return false;}}@Overridepublic int hashCode(){return value==null? 0 : value.hashCode();}public T get(){return value;}}OptionExample.java
package com.geekyarticles.javamonads.examples;import com.geekyarticles.javamonads.public class OptionExample{public static class Userdetails{public Option<Address> address = new Option<>(null);public Option<Name> name = new Option<>(null);}public static class Name{public Option<String> firstName = new Option<String>(null);public Option<String> lastName = new Option<String>(null); }public static class Address{public Option<String> houseNumber;public Option<Street> street;public Option<City> city;}public static class Street{public Option<String> name; }public static class City{public Option<String> name; }public static void main(String [] args){//This part is just the setup code for the example to workOption<Userdetails> userOpt = new Option<>(new Userdetails());userOpt.get().address = new Option<>(new Address());userOpt.get().address.get().street=new Option<>(new Street());userOpt.get().address.get().street.get().name = new Option<>("H. Street");//And look how simple it is nowString streetName = userOpt.flatMap(user -> user.address).flatMap(address -> address.street).flatMap(street -> street.name).get();System.out.println(streetName);}}集合和Monad: Monad對集合框架也很有用。 盡管最好的方法是讓每個收集類自己成為單子,以獲得最佳性能(將來可能會成為單子),但目前我們可以將它們包裝起來。 這也帶來了必須破壞類型檢查系統的問題,因為我們事先不知道構建器的通用返回類型。
NoArgExpression.java
package com.geekyarticles.lambda;public interface NoArgExpression<R> {public R function(); }SingleArgExpression.java
package com.geekyarticles.lambda;public interface SingleArgExpression<P, R> {public R function(P param); }CollectionMonad.java
package com.geekyarticles.javamonads;import com.geekyarticles.lambda. import java.util.Collection; import java.util.ArrayList; import java.util.Arrays;public class CollectionMonad<T> {Collection<T> value;NoArgExpression<Collection> builder;public CollectionMonad(Collection<T> value, NoArgExpression<Collection> builder){this.value = value;this.builder = builder;}public CollectionMonad(T[] elements){this.value = new ArrayList<T>(elements.length);this.value.addAll(Arrays.asList(elements));this.builder = () -> new ArrayList();}@SuppressWarnings("unchecked")public <E> CollectionMonad<E> map(SingleArgExpression<T,E> mapper){Collection<E> result = (Collection<E>)builder.function(); for(T item:value){result.add(mapper.function(item));} return new CollectionMonad<E>(result, builder); }//What flatMap does is to flatten out the CollectionMonad returned by the lambda that is provided//It really shrinks a nested loop.@SuppressWarnings("unchecked")public <E> CollectionMonad<E> flatMap(SingleArgExpression<T, CollectionMonad<E>> mapper){Collection<E> result = (Collection<E>)builder.function(); for(T item:value){CollectionMonad<E> forItem = mapper.function(item);for(E e : forItem.get()){result.add(e);}}return new CollectionMonad<E>(result, builder);}@SuppressWarnings("unchecked")public CollectionMonad<T> filter(SingleArgExpression<T, Boolean> filter){Collection<T> result = (Collection<T>)builder.function(); for(T item:value){if(filter.function(item)){result.add(item);}} return new CollectionMonad<T>(result, builder); }public Collection<T> get(){return value;}@Overridepublic String toString(){ return value.toString();}}ListMonadTest.java
package com.geekyarticles.javamonads.examples;import com.geekyarticles.javamonads. import java.util.public class ListMonadTest {public static void main(String [] args){mapExample();flatMapExample();filterExample();}public static void mapExample(){List<Integer> list = new ArrayList<>();list.add(10);list.add(1);list.add(210);list.add(130);list.add(2);CollectionMonad<Integer> c = new CollectionMonad<>(list, () -> new ArrayList());//Use of mapSystem.out.println(c.map(v -> v.toString()).map(v -> v.charAt(0)));System.out.println();}public static void flatMapExample(){List<Integer> list = new ArrayList<>();list.add(10);list.add(1);list.add(210);list.add(130);list.add(2);CollectionMonad<Integer> c = new CollectionMonad<>(list, () -> new ArrayList());//Use of flatMapSystem.out.println(c.flatMap(v -> new CollectionMonad<Integer>(Collections.nCopies(v,v), () -> new ArrayList())));System.out.println();}public static void filterExample(){List<Integer> list = new ArrayList<>();list.add(10);list.add(1);list.add(210);list.add(130);list.add(2);CollectionMonad<Integer> c = new CollectionMonad<>(list, () -> new ArrayList());//Use of flatMap and filterSystem.out.println(c.flatMap(v -> new CollectionMonad<Integer>(Collections.nCopies(v,v), () -> new ArrayList())).filter(v -> v<=100));System.out.println();}}乍一看,在這里使用flatmap似乎很麻煩,因為我們需要從lambda創建一個CollectionMonad。 但是,如果考慮使用嵌套的for循環的等效代碼,它仍然非常簡潔。
流和Monad:在這一點上,您可能正在考慮InputStream,但是我們將討論比這更籠統的內容。 流基本上是可能是無限的序列。 例如,可以使用公式或實際上是InputStream來創建它。 就像Iterator一樣,我們將擁有具有hasNext()和next()方法的流。 實際上,我們將使用Iterator接口,以便可以使用增強的for循環。 但是,我們還將使流變為單聲道。 這種情況特別有趣,因為流可能是無限的,因此映射必須返回延遲處理lambda的流。 在我們的示例中,我們將創建具有特定分布的專用隨機數生成器。 通常,所有值都是同等概率的。 但是我們可以通過映射來改變它。 讓我們看一下示例以更好地理解。
讓我們創建一個可以包裝任意Iterator的通用Stream。 這樣,我們也可以使用它現有的收集框架。
Stream.java
package com.geekyarticles.javamonads;import java.util.Iterator; import com.geekyarticles.lambda. import java.util.NoSuchElementException;public class Stream<T> implements Iterable<Option<T>>, Iterator<Option<T>>{//Provides a map on the underlying streamprivate class MapperStream<T,R> extends Stream<R>{private Stream<T> input;private SingleArgExpression<T, R> mapper;public MapperStream(Stream<T> input, SingleArgExpression<T, R> mapper){this.input = input;this.mapper = mapper;}@Overridepublic Option<R> next(){if(!hasNext()){//This is to conform to Iterator documentationthrow new NoSuchElementException();}return input.next().map(mapper);}@Overridepublic boolean hasNext(){return input.hasNext();}}//Provides a flatMap on the underlying streamprivate class FlatMapperStream<T,R> extends Stream<R>{private Stream<T> input;private SingleArgExpression<T, Stream<R>> mapper;private Option<Stream<R>> currentStream = new Option<>(null);public FlatMapperStream(Stream<T> input, SingleArgExpression<T, Stream<R>> mapper){this.input = input;this.mapper = mapper;}@Overridepublic Option<R> next(){if(hasNext()){return currentStream.flatMap(stream -> stream.next());}else{//This is to conform to Iterator documentationthrow new NoSuchElementException();}}@Overridepublic boolean hasNext(){if(currentStream.map(s -> s.hasNext()) //Now Option(false) and Option(null) should be treated same.equals(new Option<Boolean>(Boolean.TRUE))){return true;}else if(input.hasNext()){currentStream=input.next().map(mapper);return hasNext();}else{return false;}}}//Puts a filter on the underlying streamprivate class FilterStream<T> extends Stream<T>{private Stream<T> input;private SingleArgExpression<T, Boolean> filter;private Option<T> next = new Option<>(null);public FilterStream(Stream<T> input, SingleArgExpression<T, Boolean> filter){this.input = input;this.filter = filter;updateNext();}public boolean hasNext(){return next != null; }//We always keep one element calculated in advance.private void updateNext(){next = input.hasNext()? input.next(): new Option<T>(null);if(!next.map(filter).equals(new Option<Boolean>(Boolean.TRUE))){if(input.hasNext()){updateNext(); }else{next = null; }}}public Option<T> next(){Option<T> res = next;updateNext(); if(res == null){throw new NoSuchElementException();} return res;}}protected Iterator<T> input;public Stream(Iterator<T> input){this.input=input;}//Dummy constructor for the use of subclassesprotected Stream(){}@Overridepublic boolean hasNext(){return input.hasNext();}@Overridepublic Option<T> next(){return new Option<>(input.next());}@Overridepublic void remove(){throw new UnsupportedOperationException();}public <R> Stream<R> map(SingleArgExpression<T,R> mapper){return new MapperStream<T, R>(this, mapper);}public <R> Stream<R> flatMap(SingleArgExpression<T, Stream<R>> mapper){return new FlatMapperStream<T, R>(this, mapper);}public Stream<T> filter(SingleArgExpression<T, Boolean> filter){ return new FilterStream<T>(this, filter);}public Iterator<Option<T>> iterator(){return this;}}StreamExample.java
package com.geekyarticles.javamonads.examples;import com.geekyarticles.javamonads. import java.util.public class StreamExample{public static void main(String [] args){iteratorExample();infiniteExample();}static void iteratorExample(){System.out.println("iteratorExample");List<Integer> l = new ArrayList<>();l.addAll(Arrays.asList(new Integer[]{1,2,5,20,4,51,7,30,4,5,2,2,1,30,9,2,1,3}));Stream<Integer> stream = new Stream<>(l.iterator());//Stacking up operations//Multiply each element by 10 and only select if less than 70//Then take the remainder after dividing by 13for(Option<Integer> i : stream.map(i -> i*10).filter(i -> i < 70).map(i -> i%13)){System.out.println(i.get());}System.out.println();}static void infiniteExample(){System.out.println("infiniteExample");Iterator<Double> randomGenerator = new Iterator<Double>(){@Overridepublic Double next(){return Math.random();}@Overridepublic boolean hasNext(){//Infinite iteratorreturn true;}public void remove(){throw new UnsupportedOperationException();}};Stream<Double> randomStream = new Stream<>(randomGenerator);//Now generate a 2 digit integer every second, for ever.for(Option<Integer> val:randomStream.map(v -> (int)(v*100))){System.out.println(val.get());try{Thread.sleep(1000);}catch(InterruptedException ex){ex.printStackTrace();}}}}這個例子很復雜,所以花一些時間閱讀一下。 但是,Stream類僅需要創建一次。 一旦到達,它就可以包裝任何Iterator,它將為您免費提供所有monadic功能。
在我的下一篇文章中,我將解釋更多單子。
翻譯自: https://www.javacodegeeks.com/2014/03/functional-programming-with-java-8-lambda-expressions-monads.html
lambda 函數式編程
總結
以上是生活随笔為你收集整理的lambda 函数式编程_Java 8 Lambda表达式的函数式编程– Monads的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 超氧化钠的化学式是什么 超氧化钠的解释
- 下一篇: 己烷的同分异构体知识点 己烷的同分异构体