當前位置:
首頁 > 知識 > 到處是map、flatMap,啥意思?

到處是map、flatMap,啥意思?

最近入職一個有趣的年輕同事,提交了大量大量的代碼。翻開git記錄一看,原來是用了非常多的java8的語法特性,重構了代碼。用的最多的,就是map、flatMap之類的。

但是其他小夥伴不願意了,雖然有的人感覺代碼變的容易懂了,但有更多的人感覺代碼變的很晦澀。

那感覺就像是:脫了褲子放屁,多此一舉。

這些函數的作用域,根據級別,我覺得可以分為三類。簡直是無所不在。

不要過分使用

我也不知道這些函數是從什麼時候流行起來的,但它們與函數編程的關係肯定是非常密切的。好像是2004年的Scala開始的。

沒什麼神奇的,它們全部是語法糖,作用是讓你的程序更簡潔。你要是想,完全可以用多一點的代碼去實現。不要為了炫技刻意去使用,物極必反,用不好的話,產生的效果會是非常負面的。比如java,它並不是一門函數編程語言,那麼lambda就只是一種輔助;而你用java那一套去寫Lisp代碼的話,也只會不倫不類。

但語言還是要融合的,因為潮流就是這樣。不去看他們背後的設計,我們僅從api的語義表象,橫向看一下它們所表達的東西。

我們首先看一下其中的共性(注意:邏輯共性,並不適合所有場景),然後拿幾個典型的實現,看一下在這個星球上,程序員們的表演。

這些抽象的概念

這些函數的作用對象,據說是一種稱之為流的東西。那流到底是一種什麼東西呢?請原諒我用一些不專業的話去解釋。

不論是在語言層面還是分散式數據結構上,它其實是一個簡單的數組。它有時候真的是一個簡單的數組,有時候是存在於多台機器的分散式數組。在下文中,我們統稱為數組流。

我們簡單分為兩類。

語言層面的:比如Java的Stream分散式層面的:比如Spark的RDD

它們都有以下幾個比較重要的點。

函數可以作為參數

C語言當然是沒問題的,可以把函數作為指針傳入。但在不久之前,在Java中,這還得繞著彎子去實現(使用java概念中的Class去模擬函數,你會見到很多Func1、Func0這樣奇怪的java類)。

函數作參數,是使得代碼變得簡潔的一個必要條件。我們通常的編程方法,大多是順序執行一些操作。

array = new Array()array = func1(array)if(func2(array)){ array = func3(array)}array = func4(array)

而如果函數能夠當參數,我就能夠盡量的將操作平鋪。最終,還是要翻譯成上面的語句進行執行的。

array = new Array()array.stream().map(func1).filter(func2).flatMap(func3).sorted(func4)...

編程模式完全變了,函數也有了語義。

sequential parallel

如果我們的數組流太大,對於單機來說,就有順序處理和並行處理兩種方式。

通常,可以通過parallel函數進入並行處理模式。對於大多數本地操作,開了並行不見得一定會快。

java中使用ForkJoin那一套,線程的速度,你知道的…

而對於分散式數據流來說,本來就是並行的,這種參數意義就不大了。

函數種類

一般作用在數據流上的函數,會分為兩類。

轉換。Transformation動作。Action

轉換,典型的特點就是lazy。

只有action執行的時候,才會真正參與運算。所以,你可以認為這些轉換動作是一套被緩衝的操作。典型的函數如:map、flatMap等。它們就像烤串一樣被串在一起,等著被擼。

動作。真正觸發代碼的運行,上面的一系列轉換,也會像開了閘的洪水一樣,一瀉而下。典型的如reduce函數,就是這種。

以上的描述也不盡然,比如python的map,執行後就可以輸出結果。這讓人很沒面子啊。

map reduce

談到map和reduce,大家就不約而同的想到了hadoop。然而,它不僅僅是大數據中的概念。

對於它倆的概念,我們僅做下面兩行介紹。

map

將傳入的函數依次作用到序列的每個元素,並把結果作為新的數組流返回。

reduce

reduce類似於一個遞歸的概念。最終會歸約成一個值。看看這個公式:)

reduce([p1,p2,p3,p4],fn) = reduce([fn(p2,p4),fn(p1,p3)])

具體還是看谷歌的經典論文吧。

《MapReduce: Simplified Data

Processing on Large Clusters》

https://ai.google/research/pubs/pub62

你能訪問么?:)

map flatMap

這兩個函數經常被使用。它們有如下區別:

map

把數組流中的每一個值,使用所提供的函數執行一遍,一一對應。得到元素個數相同的數組流。

flatMap

flat是扁平的意思。它把數組流中的每一個值,使用所提供的函數執行一遍,一一對應。得到元素相同的數組流。只不過,裡面的元素也是一個子數組流。把這些子數組合併成一個數組以後,元素個數大概率會和原數組流的個數不同。

程序員們的表演

java8種的Stream

java8開始,加入了一個新的抽象,一個稱之為流的東西:Stream。配合lambda語法,可以使代碼變的特別的清爽、乾淨(有木有發現都快成了Scala了)。

一個非常好的嚮導:

https://stackify.com/streams-guide-java-8/

Spark的RDD操作

spark的核心數據模型就是RDD,是一個有向無環圖。它代表一個不可變、可分區、其內元素可並行計算的集合。

它是分散式的,但我們可以看下一個WordCount的例子。

JavaRDDtextFile = sc.textFile(hdfs://...);

JavaPairRDDcounts = textFile

.flatMap(s - Arrays.asList(s.split( )).iterator())

.mapToPair(word - new Tuple2(word, 1))

.reduceByKey((a, b) - a b);

counts.saveAsTextFile(hdfs://...);

多麼熟悉的Api啊,你一定在Hadoop里見過。

Flink 的 DataStream

Flink程序是執行分散式集合轉換(例如,filtering, mapping, updating state, joining, grouping, defining windows, aggregating)的常規程序。Flink中的DataStream程序是實現在數據流上的transformation。

我們同樣看一下它的一段代碼。

DataStreamtuple2 counts =// split up the lines in pairs (2-tuples) containing: (word,1)text.flatMap(new Tokenizer())// group by the tuple field 0 and sum up tuple field 1.keyBy(0).sum(1);

kafka stream的操作

kafka已經變成了一個分散式的流式計算平台。他抽象出一個KStream和KTable,與Spark的RDD類似,也有類似的操作。

KStream可以看作是KTable的更新日誌(changlog),數據流中的每一個記錄對應資料庫中的每一次更新。

我們來看下它的一段代碼。

KTablewordCounts = textLines

.flatMapValues(value - Arrays.asList(value.toLowerCase().split(\\W )))

.groupBy((key, value) - value)

.count();

wordCounts.toStream().to(streams-wordcount-output, Produced.with(stringSerde, longSerde));

RxJava

RxJava是一個基於觀察者模式的非同步任務框架,經常看到會被用到Android開發中(服務端採用的也越來越多)。

RxJava再語言層面進行了一些創新,有一部分忠實的信徒。

語言層面的lambda

當然,對Haskell這種天生的函數編程語言來說,是自帶光環的。但其他的一些語言,包括腳本語言,編譯性語言,也吸收了這些經驗。

它們統稱為lambda。

Python

作為最流行的腳本語言,python同樣也有它的lambda語法。最基本的map、reduce、filter等函數同樣是存在的。

JavaScript

js也不能拉下,比如Array.prototype.*()等。它該有的,也都有了。

End

另外還有很多很多,就不一一羅列了。話說,這些函數可以申請專利么?我很喜歡,雖然我很少用。

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 千鋒JAVA開發學院 的精彩文章:

當Golang遇到高並發秒殺,世界開始變得簡單
MySQL存儲引擎、MyISAM、InnoDB

TAG:千鋒JAVA開發學院 |