函数组合

先介绍一个概念,函数组合:函数组合是函数式编程中非常重要的思想,它的实现的思路也没有特别复杂。

函数组合的两种形式(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的实现

 

posted on 2020-06-15 14:43  ygunoil  阅读(1477)  评论(0编辑  收藏  举报