摘要:函數(shù)式編程中有一個(gè)比較重要的概念就是函數(shù)組合組合多個(gè)函數(shù),同時(shí)返回一個(gè)新的函數(shù)。深入理解認(rèn)識函數(shù)式編程里面跟類似的方法,就是。主要作用也是組合多個(gè)函數(shù),稱之為流,肯定得按照正常方法,從左往右調(diào)用函數(shù),與調(diào)用方法相反。
函數(shù)式編程中有一個(gè)比較重要的概念就是函數(shù)組合(compose),組合多個(gè)函數(shù),同時(shí)返回一個(gè)新的函數(shù)。調(diào)用時(shí),組合函數(shù)按順序從右向左執(zhí)行。右邊函數(shù)調(diào)用后,返回的結(jié)果,作為左邊函數(shù)的參數(shù)傳入,嚴(yán)格保證了執(zhí)行順序,這也是compose 主要特點(diǎn)。
入門簡介 組合兩個(gè)函數(shù)compose 非常簡單,通過下面示例代碼,就非常清楚
function compose (f, g) { return function(x) { return f(g(x)); } } var arr = [1, 2, 3], reverse = function(x){ return x.reverse()}, getFirst = function(x) {return x[0]}, compseFunc = compose(getFirst, reverse); compseFunc(arr); // 3
參數(shù)在函數(shù)間就好像通過‘管道’傳輸一樣,最右邊的函數(shù)接收外界參數(shù),返回結(jié)果傳給左邊的函數(shù),最后輸出結(jié)果。
組合任意個(gè)函數(shù)上面組合了兩個(gè)函數(shù)的compose,也讓我們了解了組合的特點(diǎn),接著我們看看如何組合更多的函數(shù),因?yàn)樵趯?shí)際應(yīng)用中,不會(huì)像入門介紹的代碼那么簡單。
主要注意幾個(gè)關(guān)鍵點(diǎn):
利用arguments的長度得到所有組合函數(shù)的個(gè)數(shù)
reduce 遍歷執(zhí)行所有函數(shù)。
var compose = function() { var args = Array.prototype.slice.call(arguments); return function(x) { if (args.length >= 2) { return args.reverse().reduce((p, c) => { return p = c(p) }, x) } else { return args[1] && args[1](x); } } } // 利用上面示例 測試一下。 var arr = [1, 2, 3], reverse = function(x){ return x.reverse()}, getFirst = function(x) {return x[0]}, trace = function(x) { console.log("執(zhí)行結(jié)果:", x); return x} compseFunc = compose(trace, getFirst, trace, reverse); compseFunc(arr); // 執(zhí)行結(jié)果: (3)?[3, 2, 1] // 執(zhí)行結(jié)果: 3 // 3
如此實(shí)現(xiàn),基本沒什么問題,變量arr 在管道中傳入后,經(jīng)過各種操作,最后返回了結(jié)果。
深入理解 認(rèn)識pipe函數(shù)式編程(FP)里面跟compose類似的方法,就是pipe。
pipe,主要作用也是組合多個(gè)函數(shù),稱之為"流", 肯定得按照正常方法,從左往右調(diào)用函數(shù),與compose 調(diào)用方法相反。
先看下compose 最基礎(chǔ)的兩參數(shù)版本,
const compose = (f1, f2) => value => f1(f2(value));
利用箭頭函數(shù),非常直接的表明兩個(gè)函數(shù)嵌套執(zhí)行的關(guān)系,
接著看多層嵌套。
(f1, f2, f3...) => value => f1(f2(f3));
抽象出來表示:
() => () => result;
先提出這些基礎(chǔ)的組合方式,對我們后面理解高級es6方法實(shí)現(xiàn)compose有很大幫助。
實(shí)現(xiàn)pipe前面提到pipe 是反向的compose,pipe正向調(diào)用也導(dǎo)致它實(shí)現(xiàn)起來更容易。
pipe = (...fns) => x => fns.reduce((v, f) => f(v), x)
一行代碼就實(shí)現(xiàn)了pipe, 套用上面抽象出來的表達(dá)式,reduce剛好正向遍歷所有函數(shù), 參數(shù)x作為傳遞給函數(shù)的初始值, 后面每次f(v)執(zhí)行的結(jié)果,作為下一次f(v)調(diào)用的參數(shù)v,完成了函數(shù)組合調(diào)用。
或者,可以把函數(shù)組合中,第一個(gè)函數(shù)獲取參數(shù)后,得到的結(jié)果,最為reduce遍歷的初始值。
pipe = (fn,...fns) => (x) => fns.reduce( (v, f) => f(v), fn(x));
利用es6提供的rest 參數(shù) ,用于獲取函數(shù)的多余參數(shù).提取出第一個(gè)函數(shù)fn,多余函數(shù)參數(shù)放到fns中,fns可以看成是數(shù)組,也不用像arguments那種事先通過Array.prototype.slice.call轉(zhuǎn)為數(shù)組,arguments對性能損耗也可以避免。 fn(x) 第一個(gè)函數(shù)執(zhí)行結(jié)果作為reduce 初始值。
實(shí)現(xiàn)compose
pipe 部分,利用reduce實(shí)現(xiàn),反過來看,compose就可以利用reduceRight
compose = (...fns) => x => fns.reduceRight((v, f) => f(v), x);
利用遞歸
compose = (fn, ...fns) => fns.length === 0 ? fn: (...args) => fn(compose(...fns)(...args))
遞歸代碼,首先看出口條件, fns.length === 0, 最后一定執(zhí)行最左邊的函數(shù),然后把剩下的函數(shù)再經(jīng)過compose調(diào)用,
利用reduce實(shí)現(xiàn)。
具體實(shí)現(xiàn)代碼點(diǎn)擊這里,一行實(shí)現(xiàn),而且還是用正向的 reduce。
const compose = (...fns) => fns.reduce((f, g) => (...args) => f(g(...args)))
作者其實(shí)用例子做了解釋,可以看下reduce 迭代的方向是從左往右的,而compose 要求執(zhí)行的方向是從從右往左。對數(shù)組中每一項(xiàng)執(zhí)行函數(shù),正常情況下都應(yīng)該放回執(zhí)行結(jié)果,比如(v, f) => f(v),返回f(v)執(zhí)行結(jié)果,這里是(f, g) => (...args) => f(g(...args))返回一個(gè)函數(shù)(...args) => f(g(...args)),這樣就可以保證后面的函數(shù)g在被作為參數(shù)傳入時(shí)比前面的函數(shù)f先執(zhí)行。
簡單利用前面的組合兩個(gè)函數(shù)的例子分析一下。
... composeFunc = compose(getFirst, trace, reverse); composeFunc(arr);
主要看reduce 函數(shù)里面的執(zhí)行過程:
入口 composeFunc(arr), 第一次迭代,reduce函數(shù)執(zhí)行 (getFirst, trace) => (...args)=>getFirst(trace(...args)),函數(shù)(...args)=>getFirst(trace(...args))作為下一次迭代中累計(jì)器f的值。
第二次迭代,reduce函數(shù)中
f == (...args)=>getFirst(trace(...args)) g == reverse。 // 替換一下 (f, g) => (...args) => f(g(...args)) ((...args)=>getFirst(trace(...args)), reverse) => (...args) => ((...args)=>getFirst(trace(...args)))(reverse(...args))
迭代結(jié)束,最后得到的comoseFunc就是
// 對照第二次的執(zhí)行結(jié)果, (...args) => f(g(...args)) (...args) => ((...args)=>getFirst(trace(...args)))(reverse(...args))
調(diào)用函數(shù)composeFunc(arr)。
(arr) => ((...args)=>getFirst(trace(...args)))(reverse(arr)) ===》reverse(arr) 執(zhí)行結(jié)果[3, 2, 1] 作為參數(shù) ((...args)=>getFirst(trace(...args)))([3,2,1]) ==》入?yún)⒄{(diào)用函數(shù) getFirst(trace[3,2,1]) ===》 getFirst([3, 2, 1]) ===》 結(jié)果為 3
非常巧妙的把后一個(gè)函數(shù)的執(zhí)行結(jié)果作為包裹著前面函數(shù)的空函數(shù)的參數(shù),傳入執(zhí)行。其中大量用到下面的結(jié)構(gòu)
((arg)=> f(arg))(arg) // 轉(zhuǎn)換一下。 (function(x) { return f(x) })(x)最后
無論是compose, 還是后面提到的pipe,概念非常簡單,都可以使用非常巧妙的方式實(shí)現(xiàn)(大部分使用reduce),而且在編程中很大程度上簡化代碼。最后列出優(yōu)秀框架中使用compose的示例:
redux/compose
koa-Compose
underscorejs/compose
參考鏈接:
Creating an ES6ish Compose in Javascript
compose.js
Optimization-killers
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/102266.html
摘要:在函數(shù)式編程的組合中,我們是從右到左執(zhí)行的,上述的例子中我們借助函數(shù)實(shí)現(xiàn)組合,當(dāng)然,我們也可以用自己的方式實(shí)現(xiàn)。小結(jié)函數(shù)式編程隨著多核的發(fā)展,開始再次出現(xiàn)在我們的視野中,有時(shí)候也會(huì)擔(dān)心過于吹捧函數(shù)式,反而落入俗套。 程序的本質(zhì)是什么?數(shù)據(jù)結(jié)構(gòu)+算法?。?!我想這也是很多程序員給出的答案,我自己也認(rèn)可這一觀點(diǎn),當(dāng)我們了解了某一門編程語之后,接下來我們面對的往往是數(shù)據(jù)結(jié)構(gòu)和算法的學(xué)習(xí)。而現(xiàn)在...
摘要:函數(shù)組合是函數(shù)式編程中非常重要的思想,它的實(shí)現(xiàn)的思路也沒有特別復(fù)雜。前者從左向右組合函數(shù),后者方向相反。下面就是一個(gè)最簡單的可以組合兩個(gè)函數(shù)的在實(shí)際應(yīng)用中,只能組合兩個(gè)函數(shù)的組合函數(shù)顯然不能滿足要求,我們需要可以組合任意個(gè)函數(shù)的組合函數(shù)。 函數(shù)組合是函數(shù)式編程中非常重要的思想,它的實(shí)現(xiàn)的思路也沒有特別復(fù)雜。有兩種函數(shù)組合的方式,一種是pipe,另一種是compose。前者從左向右組合函...
摘要:期函數(shù)式編程中代碼組合如何理解定義顧名思義,在函數(shù)式編程中,就是將幾個(gè)有特點(diǎn)的函數(shù)拼湊在一起,讓它們結(jié)合,產(chǎn)生一個(gè)嶄新的函數(shù)代碼理解一個(gè)將小寫轉(zhuǎn)大寫的函數(shù)一個(gè)在字符后加的函數(shù)將兩個(gè)函數(shù)組合起來這里假設(shè)我們實(shí)現(xiàn)了每日一題每日一題顯示結(jié)果里上面 20190315期 函數(shù)式編程中代碼組合(compose)如何理解? 定義: 顧名思義,在函數(shù)式編程中,Compose就是將幾個(gè)有特點(diǎn)的函數(shù)拼湊在...
摘要:把數(shù)據(jù)的流向想象成糖果工廠的一條傳送帶,每一次操作其實(shí)都是冷卻切割包裝糖果中的一步。在該章節(jié)中,我們將會(huì)用糖果工廠的類比來解釋什么是組合。糖果工廠靠這套流程運(yùn)營的很成功,但是和所有的商業(yè)公司一樣,管理者們需要不停的尋找增長點(diǎn)。 原文地址:Functional-Light-JS 原文作者:Kyle Simpson-《You-Dont-Know-JS》作者 關(guān)于譯者:這是一個(gè)流淌...
摘要:組合的概念是非常直觀的,并不是函數(shù)式編程獨(dú)有的,在我們生活中或者前端開發(fā)中處處可見。其實(shí)我們函數(shù)式編程里面的組合也是類似,函數(shù)組合就是一種將已被分解的簡單任務(wù)組織成復(fù)雜的整體過程。在函數(shù)式編程的世界中,有這樣一種很流行的編程風(fēng)格。 JavaScript函數(shù)式編程,真香之認(rèn)識函數(shù)式編程(一) 該系列文章不是針對前端新手,需要有一定的編程經(jīng)驗(yàn),而且了解 JavaScript 里面作用域,閉...
閱讀 4642·2021-10-25 09:48
閱讀 3220·2021-09-07 09:59
閱讀 2203·2021-09-06 15:01
閱讀 2704·2021-09-02 15:21
閱讀 2741·2019-08-30 14:14
閱讀 2193·2019-08-29 13:59
閱讀 2526·2019-08-29 11:02
閱讀 2544·2019-08-26 13:33