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