函数式编程中有一个比较重要的观点就是函数组合(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 挪用要领相反。
ES6 完成Compose function
先看下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
就能够应用reduceRightcompose = (...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)
- 进口 composeFunc(arr), 第一次迭代,reduce函数实行 (getFirst, trace) => (…args)=>getFirst(trace(…args)),函数
末了
无论是compose, 照样背面提到的pipe,观点异常简朴,都能够运用异常奇妙的体式格局完成(大部份运用reduce),而且在编程中很大程度上简化代码。末了列出优异框架中运用compose的示例:
参考链接: