纯函数他柯里化很容易写出洋葱代码h(g(f(x)))
比如获取数组的最后一个元素再转换成大写字母,.toUpper(.first(_.reverse(array)));
函数组合可以让我们把细粒度的函数重新组合生成一个新的函数。
管道
下面张图表示程序中使用函数处理数据的过程,给fn函数输入参数a,返回结果b.可以想想a数据通过一个管道得到了b数据。
当fn函数比较复杂的时候,我们可以把函数fn拆分成多个小函数,此时多了中间运算过程产生的m和n.
下面这张图中可以想象成把fn这个管道拆分成3个管道f1,f2,f3,数据a通过管道f3得到结果m,m再通过管道f2得到结果n,n通过管道f1得到最终结果b
函数组合
函数组合(compose):如果一个函数要经过多个函数处理才能得到最终值,这个时候可以把中间过程的函数合并成一个函数
函数就像是数据的管理,函数组合就是把这些管道连接起来,让数据穿过多个管道形成最终结果。
函数组合默认是从右到左执行。
function compose(f,g){
return function(value){
return f(g(value));
}
}
function reverse(array){
return array.reverse();
}
function first(array){
return array[0];
}
const last=compose(first,reverse);
console.log(last([1,2,3,4]));
上面是一个简单的函数组合例子,先把这个数组反转,再取数组第一个,再compose把这两个方法组合成一个新的函数去调用。从右到左执行,先执行reverse函数,返回的结果再给first去执行。
在lodash中也有组合函数
lodash中组合函数flow()或者flowRight(),他们都可以组合多个函数。
flow()是从左到右运行。
flowRight()是从右到左执行,使用的更多一些
const _ = require("lodash");
const reverse = (arr) => arr.reverse();
const first = (arr) => arr[0];
const toUpper = (s) => s.toUpperCase();
const f = _.flowRight(toUpper, first, reverse);
console.log(f(["one", "two", "three"]));
看了 lodash中如何使用函数组合的,那我们也来模拟 flowRight函数吧。
function compose(...args){
return function(value){
return args.reverse().reduce(function(acc,fn){
return fn(acc);
},value)
}
}
const reverse = (arr) => arr.reverse();
const first = (arr) => arr[0];
const toUpper = (s) => s.toUpperCase();
const f = compose(toUpper, first, reverse);
console.log(f(["one", "two", "three"]));
同样的例子把flowRight换成compose函数效果是一样的。也可以简写成
const compose = (...args) => (value) => args.reverse().reduce((acc, fun) => fun(acc), value);
函数的组合要满足结合律(associativity):
我们既可以把g和 h组合,还可以把f和 g结合,结果都 是一样
let f = compose(f, g, h);
let associative = compose(compose(f, g), h) == compose(f, compose(g, h));
//true
调试
现在可以通过函数的组合解决一些问题,当我们使用函数组合的时候,如果组合执行的结果跟我们预期的不一致,怎么去调试这多个函数的时候哪个函数出现的问题呢?我们可以在函数执行之后把结果打印出来。
把NEVER SAY DIE转换成never-say-die
const _ = require("lodash");
const split = _.curry((sep, str) => _.split(str, sep));
const join = _.curry((sep, array) => _.join(array, sep));
const f = _.flowRight(join("-"), _.toLower, split(" "));
console.log(f("NEVER SAY DIE"));
打印出来的结果竟是这样的
可以写一个log函数把每个函数调用后的值打印出来,看哪个函数最终输出的值有问题再做具体的调试。
const _ = require("lodash");
const log = (v) => {
console.log(v);
return v;
};
const split = _.curry((sep, str) => _.split(str, sep));
const join = _.curry((sep, array) => _.join(array, sep));
const f = _.flowRight(join("-"), log, _.toLower, log, split(" "));
console.log(f("NEVER SAY DIE"));