函數組合
先介紹一個概念,函數組合:函數組合是函數式編程中非常重要的思想,它的實現的思路也沒有特別復雜。
函數組合的兩種形式(JavaScript函數式編程之函數組合函數compose和pipe的實現)
有兩種函數組合的方式,一種是pipe,另一種是compose。前者從左向右組合函數,后者方向相反。
下面就是一個最簡單的可以組合兩個函數的compose
let compose = (f, g) => (...args) => f(g(...args));
在實際應用中,只能組合兩個函數的組合函數顯然不能滿足要求,我們需要可以組合任意個函數的組合函數。下面提供兩種思路。
兩種方法一種是遞歸,一種是循環,其實都是一樣的。具體的思路就是,先寫一個組合可以兩個函數的compose2,用compose2先把傳進來的末尾的兩個函數組合了,返回一個函數func,然后再compose2把func和傳進來的下一個函數組合起來,以此類推。
循環的方法
let compose2 = (f, g) => (...args) => f(g(...args)); let compose = (...funcArgs) => (...args) => { let funced = funcArgs[funcArgs.length - 1]; for (let i = funcArgs.length - 2; i >= 0; i--) { if (i === 0) { return compose2(funcArgs[i], funced)(...args); } funced = compose2(funcArgs[i], funced); } } // 與compose組合方向相反的函數組合函數 let pipe = (...funcArgs) => compose(...funcArgs.reverse());
遞歸的方法
let compose2 = (f, g) => (...args) => f(g(...args)); let compose = (...funcArgs) => (...args) => { let [...funcArgsCopy] = funcArgs; let callSelf = func => { if (funcArgsCopy.length === 0) return func; let funced = compose2(funcArgsCopy.pop(), func); return callSelf(funced); } return callSelf(funcArgsCopy.pop())(...args); } let pipe = (...funcArgs) => compose(...funcArgs.reverse());
更簡單的思路
上面的思路還是有點麻煩,其實不用糾結在組合,直接把前一個函數的處理參數之后的返回值傳給下一個函數就可以了。
循環的方法
let compose = (...funcArgs) => (...args) => { for (let i = funcArgs.length - 1; i >= 0; i--) { args = i === funcArgs.length - 1 ? funcArgs[i](...args) : funcArgs[i](args); } return args; } let pipe = (...funcArgs) => compose(...funcArgs.reverse());
遞歸的方法
let compose = (...funcArgs) => (...args) => { let [...funcArgsCopy] = funcArgs; let funced = (...func) => { if (funcArgsCopy.length === 0) return func[0]; func = funcArgsCopy.pop()(...func); return funced(func); } return funced(...args); } let pipe = (...funcArgs) => compose(...funcArgs.reverse());
然后我們來看一下webpack采用的組合方式
webpack的加載從右往左進行
其實為啥是從右往左,而不從左往右,只是Webpack選擇了compose方式,而不是pipe的方式而已,在技術上實現從左往右也不會有難度
在Uninx有pipeline的概念,平時應該也有接觸,比如 ps aux | grep node,這些都是從左往右的。
但是在函數式編程中有組合的概念,我們數學中常見的f(g(x)),在函數式編程一般的實現方式是從右往左
const compose = (...fns) => x => fns.reduceRight((v, f) => f(v), x); const add1 = n => n + 1; //加1 const double = n => n * 2; // 乘2 const add1ThenDouble = compose( double, add1 ); add1ThenDouble(2); // 6 // ((2 + 1 = 3) * 2 = 6)
這里可以看到我們先執行的加1,然后執行的double,在compose中是采用reduceRight,所以我們傳入參數的順序編程了先傳入double,后傳入add1
那么其實也可以實現從左往右
———————————————————————————————————————————————
參考文獻:
JavaScript函數式編程之函數組合函數compose和pipe的實現