Ⅰ 北大青鳥java培訓:map和flatmap的共同點和區別
在函數式語言中,函數作為一等公民,可以在任何地方定義,在函數內或函數外,可以作為函數的參數和返回值,可以對函數進行組合。
由於命令式編程語言也可以通過類似函數指針的方式來實現高階函數,函數式的最主要的好處主要是不可變性帶來的。
沒有可變的狀態,函數就是引用透明(Referentialtransparency)的和沒有副作用(NoSideEffect)。
IT培訓http://www.kmbdqn.cn/就來為大家介紹介紹。
任何一種函數式語言中,都有map函數與faltMap這兩個函數,比如python雖然不是純函哪橋數式語言,也有這兩個函數。
再比如在jdk1.8之後,也加入了Lambda表達式,自然也支持map函數。
map和faltMap的共同點和區別1、共同點都是依賴FuncX(入參,返回值)進行轉換(將一個類型依據程序邏輯轉換成另一種類型,根據入參和返回值)都能在轉換後直接被subscribe2、區別map返回的是結果集,flatmap返回的是包含結果集的Observable(返回結果不同)map被訂閱時搏態每傳遞一個事件執行一次onNext方法,flatmap多用於多對多,一對多,再被轉化為多個時,一般利用from/just進行一一分發,被訂閱時將所有數據傳遞完畢匯總到一個Observable然後一一執行onNext方法(執行順序不同)>>>>(如單純用於一對一轉換則和map相同)map只能單一轉換,單一隻的是只能一對一進行轉換,指一個對象可以轉化為另一個對象但是不能轉換成對象數組(map返回結果基緩源集不能直接使用from/just再次進行事件分發,一旦轉換成對象數組的話,再處理集合/數組的結果時需要利用for一一遍歷取出,而使用RxJava就是為了剔除這樣的嵌套結構,使得整體的邏輯性更強。
)flatmap既可以單一轉換也可以一對多/多對多轉換,flatmap要求返回Observable,因此可以再內部進行from/just的再次事件分發,一一取出單一對象(轉換對象的能力不同)map函數的用法,顧名思義,將一個函數傳入map中,然後利用傳入的這個函數,將集合中的每個元素處理,並將處理後的結果返回。
而flatMap與map唯一不一樣的地方就是傳入的函數在處理完後返回值必須是List,其實這也不難理解,既然是flatMap,那除了map以外必然還有flat的操作,所以需要返回值是List才能執行flat這一步。
Ⅱ scala中map和rece的區別
Scala中的集合對象都有foreach和map兩個方法。兩個方法的共同點在於:都是用於遍歷集合對象,並對每一項執行指定的方法。而兩者的差異在於:foreach無返回值(准確說返回void),map返回集合對象。見如下代碼及運行結果:b.getClass 得到的是void, 而c.getClass得到的是colletion 。再看代碼的第9-11行,foreach和map的運行結果一致。結論就州坦是:foreach 無法代替map. 而map方法卻可以代替冊昌桐foreach。
問題:為什麼scala提供foreach和map兩個方法呢?本人看法是scala做為一種支持函數式編程範式的語言,必然要引入一種機制以支持數學中函數概念,而在數學中函迅稿數就是映射,所以scala中有map方法一點都不奇怪。而foreach只是用在不需要對集合執行映射操作,但需要遍歷集合時才用到。總而言之,foreach用於遍歷集合,而map用於映射(轉換)集合到另一個集合。
[java]view plain
objectarrayTestextendsApp{
varincrease=(x:Int)=>x+1
valsomeNumbers=List(-11,-10,-5,0,5,10)
varb=someNumbers.foreach(increase)
println(b.getClass)
varc=someNumbers.map(increase)
println(c.getClass)
c.foreach((x:Int)=>print(x+""))
println()
c.map((x:Int)=>print(x+""))
}
Ⅲ 什麼是函數式編程思維
回答都有跑題,show概念之嫌,題主問的是函數式思維,這個問題我一直在思考,畢竟是方法論,能力有限,只能從切身實踐告訴你
1.表達式化
在
最初的時候,需要轉變觀念,去可變數,去循環,把命令式改成表達式,注意,這只是把你丟在荒山野嶺讓你感受一下,離開熟悉的環境,地球依然在轉,但是有個
重點,那就是一切都是表達式; 為什麼是表達式呢?這個問題就像為什麼魚在水裡?
因為函數式建立在lambda演算之上而非圖靈機,只不過兩者被證明等價,所以你可以在你的機器上跑全是表達式的代碼,就如有人證明天空適合魚生存,所以
魚可以在天上游
當你接受了魚可以在天上游之後,就該上正餐了
1.5 數據與行為分離
這也是和面向對象不一致的地方,面向對象強調數據與行為綁定,但函數式不是,確切的說函數式 函數與數據等價,所以你才可以將函數當參數與返回值,你在設計時,切勿讓數據自己長腿能跑,其次,行為必須消除副作用,不可以偷偷把數據改了,習慣第一條後,應嘩虧耐該不會的
2.高階邏輯
用
了函數式,就不要在想循環,賦值這些低階邏輯了,而應該更高階的思考問題,這比轉化表達式更難,函數式又叫聲明式,也就是你要做什麼,只要說一下就行,而
非寫個遍歷,做個狀態判斷,空棗用函數式你不需要考慮這些,你不知道函數式的列表是怎麼遍歷的,中間向兩邊?
從後往前?這也是為何函數式適合並發的原因之一,你想知道列表中大於3的數有多少,只要,list.count(_ > 3)
而不是寫循環,你可以直接寫你的業務,不要拘泥於細節,有點像sql, 你需要什麼告訴電腦就行,你或許會問,count foreach filter
這些函數怎麼來的? 因為有了他們你才不需要寫循環,他們把你留在高階邏輯中亂春,這個問題的答案請看下面
3.組合子邏輯 或又叫 自底向上的設計
函
數式和OO是反的,面向對象是自頂向下的設計,函數式是自底向上的設計,也就是先定義最基本的操作,然後不斷組合,不斷堆積以滿足你的所有需要,如sql
定義了select, from, where...這幾個組合子,來滿足你的查詢需求,同理函數式語言會提供foreach,
map等組合子(操作)來滿足你的需求,所以你必須自下而上的設計你的代碼結構,並且滿足你的需求,當你只用組合子寫代碼時,你會發現你寫的全是高階邏輯
如
果這些已有組合子滿足不了你,你就得自己寫,foreach不行,你就自己寫遞歸,我告訴你,遞歸背後也是組合子,這里一些'大神'應該不知道,在圖靈機
里,遞歸就是方法不斷調用自己沒什麼好說的,但是在lambda演算中,匿名函數是沒法調用自己的,所以遞歸是用Y組合子(又叫不動點組合子)把遞歸函數
自己求解出來再調用的,這才可以實現遞歸,並與圖靈機的循環等價,有點跑題了,總之要想順手的寫函數式,最好用面向組合子的設計,注意,不是必須,組合子
演算和lambda演算可以相互轉化,也就是,你完全可以寫一堆雜亂的表達式,但沒有組合子邏輯來得清爽,Haskell大規模使用monad這個特殊組
合子,始其變得統一整潔
好了,總結一下
函數式思維,其實就是組合子邏輯,用簡單的幾個函數組合來構建復雜邏輯,始終以高階的角度去表達問題,而非依賴副作用。
知道這點,你用java也可以寫函數式代碼了
但是,這也只是本人積累得來的感悟,絕不敢大肆伸張這就是函數式,我也在不斷研究中,如有問題,還望大神指正
Ⅳ java函數式編程是炫技編程嗎
函數式編程(Functional Programming)是一種編程風格,它是相對於指令式編程風格而言的,常見的面向對象編程就是指令式鬧哪蔽編程風格。
指令式編程是面向計算機硬體的抽象,有變數(對應著存儲單元),賦值語句(獲取、存儲指令),緩李表達式(內存引用和算術運算)和控制語句(跳轉語句)。
而函數式編程是面向數學的抽象,將計算描述為一種表達式求值。這里的函數實際就是數學中的函數,即自變數到因變數的映射。也就是說,一個函數的值僅決定於函數參數的值,不依賴其他狀態。
函數式編程是一種抽象程度很高的編程範式,純粹的函數式編程語言編寫的函數沒有變數,因此,任意一個函數,只要輸入是確定的,輸出就是確定的,這種純函數我們稱之為沒有液州副作用。而允許使用變數的程序設計語言,由於函數內部的變數狀態不確定,同樣的輸入,可能得到不同的輸出,因此,這種函數是有副作用的。
在函數式語言當中,函數作為一等公民,可以在任何地方定義,在函數內或函數外,可以作為函數的參數或返回值,可以對函數進行組合,也可以將函數賦值給變數。嚴格意義上的函數式編程意味著不適用可變的變數,賦值,循環和其他命令式控制結構進行編程。
函數式編程風格帶來的好處是:
函數式編程使用不可變對象作為變數,不會修改變數的值,而是返回一個新的值,如此這樣,更容易理清頭緒,使得單元測試和調試更加容易;
可以很自由地傳遞不可變對象,但對於可變對象來說,傳遞給其他代碼之前,需要先建造一個以防萬一的副本;
一旦不可變對象完成構造以後,就不會有線程因為並發訪問而破壞對象內部狀態,因為根本沒有線程可以改變不可變對象的狀態;
不可變對象讓哈希表鍵值更安全,所以哈希表鍵要求必須是不可變對象,否則使用可變對象,如果對象狀態發生變化,那麼在哈希表中就找不到這個對象了;
具體到編程語言,Scala(靜態語言)和Python(動態語言)都能比較的支持函數式編程風格,但是它們都不是純函數式的,也就是說它們同時支持指令式風格和函數式風格。而Java基本是指令式風格,但自從Java8引入lambda表達式以後也開始部分支持函數式風格。函數式編程最典型的是諸如map, flatMap, rece, filter等函數,它們的特點是支持某個函數作為上面這些函數的參數
Ⅳ 函數式編程和響應式編程有什麼區別
1. 我暫且認為你說的RP是指Rx*框架的Reactive programming,(如果不是,就先認為是一下吧)
Rx*框架的RP,其實應該叫FRP(Functional Reactive Programming)(誤,感謝 邵成的指正,具體見補充部分),那和FP基本上就是一種派生(derive)關系了
FRP基本上就是面向非同步事件流的編程了,這個非同步事件流叫:Observable,一般叫:Stream
Stream就是一個 按時間排序的Events(Ongoing events ordered in time)序列
Stream是不可變(Immutability)的,任何操作都返回新的Stream, 且它是一個Monad(它有map和flatMap方法)。
FRP的關注點在Stream,而FP的關注點在(Type, Operate),Stream -> (Type, Operate)是一種泛化(generic),(Type, Operate) -> Stream 是一種派生。
RP本身是建立於觀察者模式之上的一種編程範式(級別同MV*),FP則更偏向底層解決一般化問題。
Ⅵ map模式是什麼意思
map模式是一種函數式編程模式,它可以將一個列表中的元素通過一個函數映射到另一個列表中。map模式提供了一個簡單的方法來將一組輸入數據映射到一組輸出數據,而不需要編寫大量的循環代碼。它的基本形式是將一個函數應用於一個列表中的每個元素,然後將每個函數的輸出結果放入另一個列表中。map模式可以用來處理大量數據,可以迅速地將一組數據轉換為另一組數據鏈改,而不需要編寫大量的繁瑣的循環代碼。它還可以用於把一組數據棚咐判轉換為另一組更簡磨有用的數據,以滿足特定的應用場景。
Ⅶ Java8的函數式編程怎麼樣
使用函數式代碼的好處:
減少了可變數(Immutable Variable)的聲明
能夠更好的利用並行(Parallelism)
代碼更加簡潔和可讀
函數式介面
函數式介面就是僅聲明了一個方法的介面,比如我們熟悉的Runnable,Callable,Comparable等都可以作為函數式介面。當然,在Java 8中,新添加了一類函數式介面,如Function,Predicate,Consumer,Supplier等。
Ⅷ Python和lisp在函數式編程上有哪些異同
Python內在的函數式功能
自Python 1.0起,Python就已具有了以上所列中的絕大多數特點。但是就象Python所具有的大多數特性一樣,這些特點出現在了一種混合了各種特性的語言 中。和Python的OOP(面向對象編程) 特性非常象,你想用多少就用多少,剩下的都可以不管(直到你隨後需要用到它們為止)。在Python 2.0中,加入了列表解析(list comprehensions)這個非常好用的」語法糖「。 盡管列表解析沒有添加什麼新功能,但它讓很多舊功能看起來好了不少。
Python中函數式編程的基本要素包括functionsmap()、rece()、filter()和lambda運算元(operator)。 在Python 1.x中,apply()函數也可以非常方便地拿來將一個函數的列表返回值直接用於另外一個函數。Python 2.0為此提供了一個改進後的語法。可能有點讓人驚奇,使用如此之少的函數(以及基本的運算元)幾乎就足以寫出任何Python程序了;更加特別的是,幾乎 用不著什麼執行流程式控制制語句。
所有(if,elif,else,assert,try,except,finally,for,break,continue,while,def)這 些都都能通過僅僅使用函數式編程中的函數和運算元就能以函數式編程的風格處理好。盡管真正地在程序中完全排除使用所有流程式控制制命令可能只在想參 加」Python混亂編程「大賽(可將Python代碼寫得跟Lisp代碼非常象)時才有意義,但這對理解函數式編程如何通過函數和遞歸表達流程式控制制很有 價值。
剔除流程式控制制語句
剔除練習首先要考慮的第一件事是,實際上,Python會對布爾表達式求值進行「短路」處理。這就為我們提供了一個if/elif/else分支語句的表達式版(假設每個分支只調用一個函數,不是這種情況時也很容易組織成重新安排成這種情況)。 這里給出怎麼做:
對Python中的條件調用進行短路處理
Python
# Normal statement-based flow control
if <cond1>: func1()
elif <cond2>: func2()
else: func3()
# Equivalent "short circuit" expression
(<cond1> and func1()) or (<cond2> and func2()) or (func3())
# Example "short circuit" expression
>>> x = 3
>>> def pr(s): return s
>>> (x==1 and pr('one')) or (x==2 and pr('two')) or (pr('other'))
'other'
>>> x = 2
>>> (x==1 and pr('one')) or (x==2 and pr('two')) or (pr('other'))
'two'
我們的表達式版本的條件調用看上去可能不算什麼,更象是個小把戲;然而,如果我們注意到lambda運算元必須返回一個表達式,這就更值得關注了。既然如我 們所示,表達式能夠通過短路包含一個條件判斷,那麼,lambda表達式就是個完全通用的表達條件判斷返回值的手段了。我們來一個例子:
Python中短路的Lambda
Python
>>> pr = lambda s:s
>>> namenum = lambda x: (x==1 and pr("one"))
....or (x==2 and pr("two"))
....or (pr("other"))
>>> namenum(1)
'one'
>>> namenum(2)
'two'
>>> namenum(3)
'other'
將函數作為具有首要地位的對象
前面的例子已經表明了Python中函數具有首要地位,但有點委婉。當我們用lambda操作創建一個函數對象時, 我們所得到的東西是完全通用的。就其本質而言,我們可以將我們的對象同名字」pr」和」namenum」綁定到一起, 以完全相同的方式,我們也也完全可以將數字23或者字元串」spam」 同這些名字綁定到一起。但是,就象我們可以無需將其綁定到任何名字之上就能直接使用數字23(也就是說,它可以用作函數的參數)一樣,我們也可以直接使用 我們使用lambda創建的函數對象,而無需將其綁定到任何名字之上。在Python中,函數就是另外一種我們能夠就像某種處理的值。
我們對具有首要地位的對象做的比較多的事情就是,將它們作為參數傳遞給函數式編程固有的函數map()、rece()和filter()。這三個函數接受的第一個參數都是一個函數對象。
map()針對指定給它的一個或多個列表中每一項對應的內容,執行一次作為參數傳遞給它的那個函數 ,最後返回一個結果列表。
rece()針對每個後繼項以及最後結果的累積結果,執行一次作為參數傳遞給它的那個函數;例如,rece(lambda n,m:n*m, range(1,10))是求」10的階乘」的意思(換言之,將每一項和前面所得的乘積進行相乘)
filter()使用那個作為參數傳遞給它的函數,對一個列表中的所有項進行」求值「,返回一個由所有能夠通過那個函數測試的項組成的經過遴選後的列表。
我們經常也會把函數對象傳遞給我們自己定義的函數,不過一般情況下這些自定義的函數就是前文提及的內建函數的某種形式的組合。
通過組合使用這三種函數式編程內建的函數, 能夠實現范圍驚人的「執行流程」操作(全都不用語句,僅僅使用表達式實現)。
Ⅸ 函數式編程-Lambda與Stream
我們在創建線程並啟動時可以使用匿名內部類的寫法:
可以使用Lambda的格式對其進行修改。修改後如下:
現有方法定義如下,其中IntBinaryOperator是一個介面。先使用匿名內部類的寫法調用該方法。
Lambda寫法:
現有方法定義如下,其中IntPredicate是一個介面。先使用匿名內部類的寫法調用該方法。
Lambda寫法:
現有方法定義如下,其中Function是一個介面。先使用匿名內部類的寫法調用卜行握該方法。
Lambda寫法:
現有方法定義如下,其中IntConsumer是一個介面。先使用匿名內部類的寫法調用該方法。
Lambda寫法:
Java8的Stream使用的是函數式編程模式,如同它的名字一樣,它可以被用來對集合或數組進行鏈狀流式的操作。可以更方便的讓我們對集合或數組操作。
我們可以調用getAuthors方法獲取到作家的集合。現在需要列印所有年齡小於18的作家的名字,並且要注意去重。
單列集合: 集合對象.stream()
數組:Arrays.stream(數組)或者使用Stream.of來創建
雙列集合:轉換成單列集合後再創建
可以對流中的元素進行條件過濾,符合過濾條件的才能繼續留在流中。
例如:
列印所有姓名長度大於1的作家的姓名
可以把對流中的元素進行計算或轉換。
例如:
列印所有作家的姓名
可以去除流中的重復元素。
例如:
列印所有作家的姓名,並且要求其中不能有重復元素。
注意:distinct方法是依賴Object的equals方法來判斷是否是相同對象的。所以需要注意重寫equals方法。
可以對流中的元素進行排序。
例如:
對流中的元素按照年齡進行降序排序,並且要求不能有重復的元素。
注意:如果調用空參的sorted()方法,需要流中的元素是實現了Comparable。
可以設置流的最大長度,超出的部分將被拋棄。帶昌
例如:
對流中的元素按照年齡進行降序排序,並且要求不能有重復的元素,然後列印其中年齡最大的兩個作家的姓名。
跳過流中的前n個元素,返回剩下的元素
例如:
列印除了年齡最大的作家外的其他作家,要求不能有重復元素,並且按照年齡降序排序。
map只能把一個對象轉換成另一個對象來作為流中的元素。而flatMap可以把一個對象轉換成多個對象作為流中的元素。
例一:
列印所有書籍的名字。要求對重復的元素進行去重。
例二:
列印現有數據的所有分類。要求對分類進行去重。不能出現這種格式:哲學,愛情
對流中的元素進行遍歷操作,我們通過傳入的型慶參數去指定對遍歷到的元素進行什麼具體操作。
例子:
輸出所有作家的名字
可以用來獲取當前流中元素的個數。
例子:
列印這些作家的所出書籍的數目,注意刪除重復元素。
可以用來或者流中的最值。
例子:
分別獲取這些作家的所出書籍的最高分和最低分並列印。
把當前流轉換成一個集合。
例子:
獲取一個存放所有作者名字的List集合。
獲取一個所有書名的Set集合。
獲取一個Map集合,map的key為作者名,value為List
可以用來判斷是否有任意符合匹配條件的元素,結果為boolean類型。
例子:
判斷是否有年齡在29以上的作家
可以用來判斷是否都符合匹配條件,結果為boolean類型。如果都符合結果為true,否則結果為false。
例子:
判斷是否所有的作家都是成年人
可以判斷流中的元素是否都不符合匹配條件。如果都不符合結果為true,否則結果為false
例子:
判斷作家是否都沒有超過100歲的。
獲取流中的任意一個元素。該方法沒有辦法保證獲取的一定是流中的第一個元素。
例子:
獲取任意一個年齡大於18的作家,如果存在就輸出他的名字
獲取流中的第一個元素。
例子:
獲取一個年齡最小的作家,並輸出他的姓名。
對流中的數據按照你指定的計算方式計算出一個結果。(縮減操作)
rece的作用是把stream中的元素給組合起來,我們可以傳入一個初始值,它會按照我們的計算方式依次拿流中的元素和初始化值進行計算,計算結果再和後面的元素計算。
rece兩個參數的重載形式內部的計算方式如下:
其中identity就是我們可以通過方法參數傳入的初始值,accumulator的apply具體進行什麼計算也是我們通過方法參數來確定的。
例子:
使用rece求所有作者年齡的和
使用rece求所有作者中年齡的最大值
使用rece求所有作者中年齡的最小值
rece一個參數的重載形式內部的計算
如果用一個參數的重載方法去求最小值代碼如下:
我們在編寫代碼的時候出現最多的就是空指針異常。所以在很多情況下我們需要做各種非空的判斷。
例如:
尤其是對象中的屬性還是一個對象的情況下。這種判斷會更多。
而過多的判斷語句會讓我們的代碼顯得臃腫不堪。
所以在JDK8中引入了Optional,養成使用Optional的習慣後你可以寫出更優雅的代碼來避免空指針異常。
並且在很多函數式編程相關的API中也都用到了Optional,如果不會使用Optional也會對函數式編程的學習造成影響。
Optional就好像是包裝類,可以把我們的具體數據封裝Optional對象內部。然後我們去使用Optional中封裝好的方法操作封裝進去的數據就可以非常優雅的避免空指針異常。
我們一般使用 Optional 的 靜態方法ofNullable 來把數據封裝成一個Optional對象。無論傳入的參數是否為null都不會出現問題。
你可能會覺得還要加一行代碼來封裝數據比較麻煩。但是如果改造下getAuthor方法,讓其的返回值就是封裝好的Optional的話,我們在使用時就會方便很多。
而且在實際開發中我們的數據很多是從資料庫獲取的。Mybatis從3.5版本可以也已經支持Optional了。我們可以直接把方法的返回值類型定義成Optional類型,MyBastis會自己把數據封裝成Optional對象返回。封裝的過程也不需要我們自己操作。
如果你 確定一個對象不是空 的則可以使用 Optional 的 靜態方法of 來把數據封裝成Optional對象。
但是一定要注意,如果使用of的時候傳入的參數必須不為null。(嘗試下傳入null會出現什麼結果)
如果一個方法的返回值類型是Optional類型。而如果我們經判斷發現某次計算得到的返回值為null,這個時候就需要把null封裝成Optional對象返回。這時則可以使用 Optional 的 靜態方法empty 來進行封裝。
所以最後你覺得哪種方式會更方便呢? ofNullable
我們獲取到一個Optional對象後肯定需要對其中的數據進行使用。這時候我們可以使用其 ifPresent 方法對來消費其中的值。
這個方法會判斷其內封裝的數據是否為空,不為空時才會執行具體的消費代碼。這樣使用起來就更加安全了。
例如,以下寫法就優雅的避免了空指針異常。
如果我們想獲取值自己進行處理可以使用get方法獲取,但是不推薦。因為當Optional內部的數據為空的時候會出現異常。
如果我們期望安全的獲取值。我們不推薦使用get方法,而是使用Optional提供的以下方法。
我們可以使用filter方法對數據進行過濾。如果原本是有數據的,但是不符合判斷,也會變成一個無數據的Optional對象。
我們可以使用isPresent方法進行是否存在數據的判斷。如果為空返回值為false,如果不為空,返回值為true。但是這種方式並不能體現Optional的好處, 更推薦使用ifPresent方法 。
Optional還提供了map可以讓我們的對數據進行轉換,並且轉換得到的數據也還是被Optional包裝好的,保證了我們的使用安全。
例如我們想獲取作家的書籍集合。
只有一個抽象方法 的介面我們稱之為函數介面。
JDK的函數式介面都加上了 @FunctionalInterface 註解進行標識。但是無論是否加上該註解只要介面中只有一個抽象方法,都是函數式介面。
我們在使用lambda時,如果方法體中只有一個方法的調用的話(包括構造方法),我們可以用方法引用進一步簡化代碼。
我們在使用lambda時不需要考慮什麼時候用方法引用,用哪種方法引用,方法引用的格式是什麼。我們只需要在寫完lambda方法發現方法體只有一行代碼,並且是方法的調用時使用快捷鍵嘗試是否能夠轉換成方法引用即可。
當我們方法引用使用的多了慢慢的也可以直接寫出方法引用。
類名或者對象名::方法名
其實就是引用類的靜態方法
如果我們在重寫方法的時候,方法體中 只有一行代碼 ,並且這行代碼是 調用了某個類的靜態方法 ,並且我們把要重寫的 抽象方法中所有的參數都按照順序傳入了這個靜態方法中 ,這個時候我們就可以引用類的靜態方法。
例如:
如下代碼就可以用方法引用進行簡化
注意,如果我們所重寫的方法是沒有參數的,調用的方法也是沒有參數的也相當於符合以上規則。
優化後如下:
如果我們在重寫方法的時候,方法體中 只有一行代碼 ,並且這行代碼是 調用了某個對象的成員方法 ,並且我們把要重寫的 抽象方法中所有的參數都按照順序傳入了這個成員方法中 ,這個時候我們就可以引用對象的實例方法
例如:
優化後:
如果我們在重寫方法的時候,方法體中 只有一行代碼 ,並且這行代碼是 調用了第一個參數的成員方法 ,並且我們把要 重寫的抽象方法中剩餘的所有的參數都按照順序傳入了這個成員方法中 ,這個時候我們就可以引用類的實例方法。
例如:
優化後如下:
如果方法體中的一行代碼是構造器的話就可以使用構造器引用。
如果我們在重寫方法的時候,方法體中 只有一行代碼 ,並且這行代碼是 調用了某個類的構造方法 ,並且我們把 要重寫的抽象方法中的所有的參數都按照順序傳入了這個構造方法中 ,這個時候我們就可以引用構造器。
例如:
優化後:
我們之前用到的很多Stream的方法由於都使用了泛型。所以涉及到的參數和返回值都是引用數據類型。
即使我們操作的是整數小數,但是實際用的都是他們的包裝類。JDK5中引入的自動裝箱和自動拆箱讓我們在使用對應的包裝類時就好像使用基本數據類型一樣方便。但是你一定要知道裝箱和拆箱肯定是要消耗時間的。雖然這個時間消耗很下。但是在大量的數據不斷的重復裝箱拆箱的時候,你就不能無視這個時間損耗了。
所以為了讓我們能夠對這部分的時間消耗進行優化。Stream還提供了很多專門針對基本數據類型的方法。
例如:mapToInt,mapToLong,mapToDouble,flatMapToInt,flatMapToDouble等。
當流中有大量元素時,我們可以使用並行流去提高操作的效率。其實並行流就是把任務分配給多個線程去完全。如果我們自己去用代碼實現的話其實會非常的復雜,並且要求你對並發編程有足夠的理解和認識。而如果我們使用Stream的話,我們只需要修改一個方法的調用就可以使用並行流來幫我們實現,從而提高效率。
parallel方法可以把串列流轉換成並行流。
也可以通過parallelStream直接獲取並行流對象。
Ⅹ 前端必學-函數式編程(六)
我們前篇談了很多關於【閉包】的理解了,所以你應該會知道,我們現在將要談的就是 ——【非同步】。
我們為什麼覺得「非同步問題」復雜呢?
其中很重要的一個原因是 —— 時間!時間將我們對數據的操判運作、管理,變復雜了好幾個量級!
(需要特別提出並明確的是: 非同步和同步之間是可以相互轉化的! 我們使用非同步或者同步取決於 —— 如何使代碼更加可讀!)
函數式編程給出了實現「代碼更可讀」的落地原則(已多次回顧):
所以我們可以期待,非同步在函數式編程中的表現!
上代碼:
onCustomer(..) 和 onOrders(..) 是兩個【回調函數】釋義,兩者執行的先後順序並不能確定,所以它是一個基於時間掘喚梁的復雜狀態。
釋義:回調函數其實就是一個參數,將這個函數作為參數傳到另一個函數裡面,當那個函數執行完之後,再執行傳進去的這個函數。
通常來說,我們最先想到的是:把 lookupOrders(..) 寫到 onCustomer(..) 裡面,那我們就可以確認 onOrders(..) 會在 onCustomer(..) 之後運行。
這樣寫,對嗎?
不對!因為 onCustomer(..) 、 onOrders(..) 這兩個回調函數的關系更像是一種競爭關系(都是賦值 customer.orders ), 它們應該並行執行 , 而不是串列執行 。
即:我不管你們誰先執行,誰先執行完,誰就賦值給 customer.orders !
那我們的思路應該是:
不過,這樣讓代碼又變得更加難閱讀!!函數內部賦值依賴於外部變數、甚至受外部回調函數的影響。
那究竟怎麼辦呢?
最終,我們借用 JS promise 減少這個時間狀態,將非同步轉成同步:
兩個 .then(..) 運行之前, lookupCustomer(..) 和 lookupOrders(..) 已被同步調用,滿足並行執行,誰先結束,誰賦值給 customer.orders ,所以我們不需要知道誰先誰後!
在這樣的實現下,不再需要時間先後的概念!減少了時間狀態!!代碼的可讀性更高了!!
這是一個 積極的數組 ,因為它們同步(即時)地操作著離散的即時值或值的列表/結構上的值。
什麼意思?
a 映射到 b,再去修改 a ,b 不會收到影響。
而這,是一個 惰性的數組 , mapLazy(..) 本質上 「監聽」 了數組 a,只要一個新的值添加到數組的末端(push(..)),它都會運行映射函數 v => v * 2 並把改變後的值添加到數組 b 里。
什麼意思?
a 映射到 b,再去修改 a ,b 也會修改。
原來,後者存在 非同步 的概念。
讓我們來想像這樣一個鏈櫻數組,它不只是簡單地獲得值,它還是一個懶惰地接受和響應(也就是「反應」)值的數組,比如:
設置「懶惰的數組」 a 的過程是非同步的!
b ,是 map 映射後的數組,但更重要的是,b 是 反應性 的,我們對 b 加了一個類似監聽器的東西。
這里直接給出解答:
這里再多小結一句:時間讓非同步更加復雜,函數式編程在非同步下的運用就是減少或直接幹掉時間狀態。
想像下 a 還可以被綁定上一些其他的事件上,比如說用戶的滑鼠點擊事件和鍵盤按鍵事件,服務端來的 websocket 消息等。
上述的 LazyArray 又可叫做 observable !(當然,它不止用在 map 方法中)
現在已經有各種各樣的 Observables 的庫類,最出名的是 RxJS 和 Most 。
以 RxJS 為例:
不僅如此,RxJS 還定義了超過 100 個可以在有新值添加時才觸發的方法。就像數組一樣。每個 Observable 的方法都會返回一個新的 Observable,意味著他們是鏈式的。如果一個方法被調用,則它的返回值應該由輸入的 Observable 去返回,然後觸發到輸出的 Observable里,否則拋棄。
比如:
本篇介紹了【非同步】在函數式編程中的表現。
原則是:對於那些非同步中有時態的操作,基礎的函數式編程原理就是將它們變為無時態的應用。即 減少時間狀態 !
就像 promise 創建了一個單一的未來值,我們可以創建一個積極的列表的值來代替像惰性的observable(事件)流的值。
我們介紹了 RxJS 庫,後續我們還會介紹更多優美的 JS 函數式編程庫!
(俗話說的好,三方庫選的好,下班都很早!!)
現在本瓜有點明白那句話了:看一門語言是不是函數式編程,取決於它的核心庫是不是函數式編程。
也許我們還不熟悉像 RxJS 這類庫,但我們慢慢就會越來越重視它們,越來越使用它們,越來越領會到它們!!
非同步,以上。