Java 8 Lambda表达式的函数式编程– Monads
什么是monad ?: monad是一種設(shè)計(jì)模式概念,用于大多數(shù)功能編程語言(如Lisp)或現(xiàn)代世界的Clojure或Scala中。 (實(shí)際上,我會(huì)從scala復(fù)制一些內(nèi)容。)現(xiàn)在,為什么它在Java中變得很重要? 因?yàn)閖ava從版本8獲得了新的lambda功能。Lambda或閉包是一種功能編程功能。 它使您可以將代碼塊用作變量,并像這樣傳遞代碼塊。 我在上一篇文章Java 8中的烹飪-項(xiàng)目Lambda中討論了Java的“項(xiàng)目Lambda”。 現(xiàn)在,您可以在此處提供的JDK 8預(yù)覽版中進(jìn)行嘗試。 現(xiàn)在我們可以在Java 8之前做monad嗎? 當(dāng)然,畢竟Java的lambda在語義上只是實(shí)現(xiàn)接口的另一種方式(實(shí)際上并不是因?yàn)榫幾g器知道它的使用位置),而是太多混亂的代碼,幾乎可以殺死其實(shí)用程序。
現(xiàn)在,讓我在Java中建立一個(gè)用例,就像沒有monad一樣,而不是向您描述一個(gè)抽象的,看似毫無意義的想法。
討厭的null檢查:如果您編寫了任何非平凡的(例如Hello-World)java程序,則可能已經(jīng)做了一些null檢查。 它們就像編程必不可少的弊端,您不能沒有它們,但是它們會(huì)使您的程序雜亂無章。 讓我們以帶有一組Java數(shù)據(jù)對(duì)象的以下示例為例。 注意,無論如何我都沒有使用過反模式的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; }現(xiàn)在說您想從UserDetails用戶訪問街道名稱,并且任何屬性都可能為null。 沒有monad,您可能會(huì)編寫如下代碼。
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;理想情況下,它應(yīng)該是單線的。 我們真正關(guān)心的代碼周圍雜亂無章。 現(xiàn)在讓我們看看如何解決這個(gè)問題。 讓我們創(chuàng)建一個(gè)代表可選值的Option類。 然后讓我們有一個(gè)map方法,該方法將在其包裝后的值上運(yùn)行l(wèi)ambda并返回另一個(gè)選項(xiàng)。 如果包裝的值為null,則它將返回一個(gè)包含null的Option而不處理lambda,從而避免使用null指針異常。 請(qǐng)注意,map方法實(shí)際上需要使用lambda作為參數(shù),但是我們將需要?jiǎng)?chuàng)建一個(gè)接口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);}}所以現(xiàn)在,基本上的想法是,只要方法有機(jī)會(huì)返回null,就返回Option。 將確保該方法的使用者理解該值可以為null,并且還使該使用者隱式地通過null檢查,如圖所示。 現(xiàn)在,我們從所有可能必須返回null的方法中返回Option,那么映射內(nèi)的表達(dá)式也可能會(huì)將Option用作返回類型。 為了避免每次都調(diào)用get(),我們可以有一個(gè)與map類似的方法flatMap,除了它接受一個(gè)Option作為傳遞給它的lambda的返回類型。
public <E> Option<E> flatMap(SingleArgExpression<T, Option<E>> mapper){if(value == null){return new Option<E>(null);}return mapper.function(value);}我要說的最后一種方法是過濾器。 它將使我們?cè)谟成滏溨蟹胖靡粋€(gè)if條件,以便僅當(dāng)條件為true時(shí)才獲得一個(gè)值。 請(qǐng)注意,這也是null安全的。 在此特定的monad中,filter的用法并不明顯,但稍后我們將看到其用法。 以下是一個(gè)示例,其中所有可為空的字段都已升級(jí)為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對(duì)集合框架也很有用。 盡管最好的方法是讓每個(gè)收集類自己成為最佳性能的單子(將來可能會(huì)成為單子),但目前我們可以將它們包裝起來。 這也帶來了必須破壞類型檢查系統(tǒng)的問題,因?yàn)槲覀兪孪炔恢罉?gòu)建器的通用返回類型。
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似乎很麻煩,因?yàn)槲覀冃枰獜膌ambda創(chuàng)建一個(gè)CollectionMonad。 但是,如果考慮使用嵌套的for循環(huán)的等效代碼,它仍然非常簡(jiǎn)潔。
Streams和Monads:在這一點(diǎn)上,您可能正在考慮InputStream,但我們將討論比這更籠統(tǒng)的內(nèi)容。 流基本上是可能是無限的序列。 例如,可以使用公式或?qū)嶋H上是InputStream來創(chuàng)建它。 就像Iterator一樣,我們將具有具有hasNext()和next()方法的流。 實(shí)際上,我們將使用Iterator接口,以便可以使用增強(qiáng)的for循環(huán)。 但是,我們還將使流變?yōu)閱温暤馈?這種情況特別有趣,因?yàn)榱骺赡苁菬o限的,因此映射必須返回延遲處理lambda的流。 在我們的示例中,我們將創(chuàng)建具有特定分布的專用隨機(jī)數(shù)生成器。 通常,所有值都是同等概率的。 但是我們可以通過映射來改變它。 讓我們看一下示例以更好地理解。
讓我們創(chuàng)建一個(gè)可以包裝任意Iterator的通用Stream。 這樣,我們也可以使用它現(xiàn)有的收集框架。
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();}}}}這個(gè)例子很復(fù)雜,所以花一些時(shí)間閱讀一下。 但是,Stream類僅需要?jiǎng)?chuàng)建一次。 一旦到達(dá),它就可以包裝任何Iterator,它將為您免費(fèi)提供所有monadic功能。
 在我的下一篇文章中,我將解釋更多單子。 
翻譯自: https://www.javacodegeeks.com/2014/03/functional-programming-with-java-8-lambda-expressions-monads.html
總結(jié)
以上是生活随笔為你收集整理的Java 8 Lambda表达式的函数式编程– Monads的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: 打野皎月天赋s6(打野皎月天赋2022)
- 下一篇: 大连租房备案需要什么材料(大连租房备案)
