對(duì)于函數(shù)式的版本,乍一看抑淫,的確令人非常費(fèi)解,仔細(xì)看一下姥闪,你可能就暈掉了始苇,似乎完全就是天書(shū),看上去非常裝逼筐喳,哈哈催式。不過(guò),我感覺(jué)解析那段函數(shù)式的代碼可能會(huì)一個(gè)比較有趣過(guò)程避归。
先看代碼
這個(gè)代碼平淡無(wú)奇荣月,就是從一個(gè)數(shù)組中找到一個(gè)數(shù),O(n)的算法梳毙,找不到就返回 null哺窄。
下面是正常的 old-school 的方式。不用多說(shuō)账锹。
結(jié)果到了函數(shù)式成了下面這個(gè)樣子(好像上面的那些代碼在下面若影若現(xiàn)萌业,不過(guò)又有點(diǎn)不太一樣,為了消掉if語(yǔ)言奸柬,讓其看上去更像一個(gè)表達(dá)式生年,動(dòng)用了 ? 號(hào)表達(dá)式):
為了講清這個(gè)代碼,需要先補(bǔ)充一些知識(shí)廓奕。
Javascript的箭頭函數(shù)
首先先簡(jiǎn)單說(shuō)明一下抱婉,ECMAScript2015 引入的箭頭表達(dá)式档叔。箭頭函數(shù)其實(shí)都是匿名函數(shù),其基本語(yǔ)法如下:
下面是一些示例:
看上去不復(fù)雜吧蒸绩。不過(guò)衙四,上面前兩個(gè) simple 和 max 的例子都把這箭頭函數(shù)賦值給了一個(gè)變量,于是它就有了一個(gè)名字侵贵。有時(shí)候届搁,某些函數(shù)在聲明的時(shí)候就是調(diào)用的時(shí)候,尤其是函數(shù)式編程中窍育,一個(gè)函數(shù)還對(duì)外返回函數(shù)的時(shí)候卡睦。比如下在這個(gè)例子:
其實(shí),在 MakePowerFn 函數(shù)里的那個(gè) PowerFn 根本不需要命名漱抓,完全可以寫(xiě)成:
如果用箭頭函數(shù)表锻,可以寫(xiě)成:
我們還可以寫(xiě)得更簡(jiǎn)潔(如果用表達(dá)式的話,就不需要 { 和 }乞娄, 以及 return 語(yǔ)句 ):
我還是加上括號(hào)瞬逊,和換行可能會(huì)更清楚一些:
好了,有了上面的知識(shí)仪或,我們就可以進(jìn)入一個(gè)更高級(jí)的話題——匿名函數(shù)的遞歸确镊。
匿名函數(shù)的遞歸
函數(shù)式編程立志于用函數(shù)表達(dá)式消除有狀態(tài)的函數(shù),以及for/while循環(huán)范删,所以蕾域,在函數(shù)式編程的世界里是不應(yīng)該用for/while循環(huán)的,而要改用遞歸(遞歸的性能很差到旦,所以旨巷,一般是用尾遞歸來(lái)做優(yōu)化,也就是把函數(shù)的計(jì)算的狀態(tài)當(dāng)成參數(shù)一層一層的往下傳遞添忘,這樣語(yǔ)言的編譯器或解釋器就不需要用函數(shù)棧來(lái)幫你保存函數(shù)的內(nèi)部變量的狀態(tài)了)采呐。
好了,那么搁骑,匿名函數(shù)的遞歸該怎么做斧吐?
一般來(lái)說(shuō),遞歸的代碼就是函數(shù)自己調(diào)用自己靶病,比如我們求階乘的代碼:
在匿名函數(shù)下会通,這個(gè)遞歸該怎么寫(xiě)呢?對(duì)于匿名函數(shù)來(lái)說(shuō)娄周,我們可以把匿名函數(shù)當(dāng)成一個(gè)參數(shù)傳給另外一個(gè)函數(shù)涕侈,因?yàn)楹瘮?shù)的參數(shù)有名字,所以就可以調(diào)用自己了煤辨。?如下所示:
這個(gè)是不是有點(diǎn)作弊的嫌疑裳涛?Anyway木张,我們?cè)偻拢焉厦孢@個(gè)函數(shù)整成箭頭函數(shù)式的匿名函數(shù)的樣子端三。
現(xiàn)在你似乎就不像作弊了吧舷礼。把上面那個(gè)求階乘的函數(shù)套進(jìn)來(lái)是這個(gè)樣子:
首先,先重構(gòu)一下fact郊闯,把fact中自己調(diào)用自己的名字去掉:
然后妻献,我們?cè)侔焉厦孢@個(gè)版本變成箭頭函數(shù)的匿名函數(shù)版:
這里,我們依然還要用一個(gè)fact來(lái)保存這個(gè)匿名函數(shù)团赁,我們繼續(xù)育拨,我們要讓匿名函數(shù)聲明的時(shí)候,就自己調(diào)用自己欢摄。
也就是說(shuō)熬丧,我們要把?
這個(gè)函數(shù)當(dāng)成調(diào)用參數(shù),傳給下面這個(gè)函數(shù):
最終我們得到下面的代碼:
好像有點(diǎn)繞怀挠,anyway, 你看懂了嗎析蝴?沒(méi)事,我們繼續(xù)绿淋。
動(dòng)用高階函數(shù)的遞歸
但是上面這個(gè)遞歸的匿名函數(shù)在自己調(diào)用自己闷畸,所以,代碼中有hard code的實(shí)參吞滞。我們想實(shí)參去掉腾啥,如何去掉呢?我們可以參考前面說(shuō)過(guò)的那個(gè)
我們可以看冯吓,上面的代碼簡(jiǎn)單說(shuō)來(lái)就是,需要一個(gè)函數(shù)做參數(shù)疮跑,然后返回這個(gè)函數(shù)的遞歸版本组贺。那么,我們?cè)趺凑{(diào)用呢祖娘?
連起來(lái)寫(xiě)就是:
但是失尖,這樣讓用戶來(lái)調(diào)用很不爽,所以渐苏,以我們一個(gè)函數(shù)把?HighOrderFact ( HighOrderFact )?給代理一下:
用箭頭函數(shù)重構(gòu)一下掀潮,是不是簡(jiǎn)潔了一些?
上面就是我們最終版的階乘的函數(shù)式代碼琼富。
回顧之前的程序
我們?cè)賮?lái)看那個(gè)查找數(shù)組的正常程序:
先把for干掉仪吧,搞成遞歸版本:
然后,寫(xiě)出帶實(shí)參的匿名函數(shù)的版本(注:其中的if代碼被重構(gòu)成了 鞠眉?號(hào)表達(dá)式):
最后薯鼠,引入高階函數(shù)择诈,去除實(shí)參:
注:函數(shù)式編程裝逼時(shí)一定要用const字符,這表示我寫(xiě)的函數(shù)里的狀態(tài)是 immutable 的出皇,天生驕傲羞芍!
再注:我寫(xiě)的這個(gè)比原來(lái)版的那個(gè)簡(jiǎn)單了很多,原來(lái)版本的那個(gè)又在函數(shù)中套了一套 next郊艘, 而且還動(dòng)用了不定參數(shù)荷科,當(dāng)然,如果你想裝逼裝到天上的纱注,理論上來(lái)說(shuō)畏浆,你可以套N層,呵呵奈附。
現(xiàn)在全度,你可以體會(huì)到,如此逼裝的是怎么來(lái)的了吧斥滤?
轉(zhuǎn)自:https://mp.weixin.qq.com/s/DYTAS5r5tFZyT-JotC4Pkg