实现柯里化函数

253 阅读2分钟

简介

柯里化函数是一种高阶函数,经过柯里化的函数的调用形式从原来的func(a, b, c, d)变成支持func(a)(b)(c)(d)

柯里化的意义

假设有个将十进制数转为其他进制的函数,其定义如下:

// base为进制,比如2 
function convertTo(base, number){
    ...
    return x; //转换后的值
}
convertTo(2, 10); //10转为二进制
convertTo(2, 100);//100转为二进制
convertTo(2, 20); //20转为二进制
convertTo(16, 20); //20转为十六进制

现在将其柯里化,那么它的调用形式可以是这样的:

const convertToCurried  = curry(convertTo);
convertToCurried(2, 10); //可以和之前一样调用
convertToCurried(2)(10); //10转为二进制
convertToCurried(2)(100);//100转为二进制
convertToCurried(2)(20); //20转为二进制
convertToCurried(16)(20); //20转为十六进制

现在,对于常用进制可以得到相应的进制转换函数了:

const convertToCurried  = curry(convertTo);
//十进制转二进制函数
const convertToBinary  = convertToCurried(2);
//十进制转十六进制函数
const convertToHexadecimal = convertToCurried(16);

convertToBinary(10); //10转为二进制
convertToBinary(100);//100转为二进制
convertToBinary(20); //20转为二进制
convertToHexadecimal(20); //20转为十六进制

实现

function curry(func) {
  if(typeof func !== 'function'){
    throw new TypeError("fisrt argument must be a function");
  }
  return function curriedFunc(...args) {
    //函数都有个length属性表示该函数的形参个数
    //只有当args的长度达到原函数的形参个数时才会真正执行原函数
    if (args.length >= func.length) {
      return func.apply(this, args);
    } else {
      //返回一个函数
      return function(...args2) {
        //调用该函数会将上次调用参数args与本次调用参数args2合并,继续柯里化,其实就是个递归过程
        return curriedFunc.apply(this, args.concat(args2));
      }
    }
  };
}

举个例子说明其过程

function sum(a,b,c){
    return a + b +c;
}
const sumCurried = curry(sum);

/*
 sum1 = function(...args2){
            return curriedFunc.apply(this, [1].concat(args2));
        }
*/
const sum1 = sumCurried(1);
/*
 sum2 = function(...args2){
            return curriedFunc.apply(this, [1,2].concat(args2));
        }
*/
const sum2 = sum1(2);
// 调用sum2(3),由于此时args.length=3,满足if条件,所以 sum3 = sum.apply(this,[1,2,3])
const sum3 = sum2(3);
//整个过程完成,等价于sum3 = sumCurried(1)(2)(3);

需要注意的是,只有具有固定数量的参数的函数才能进行柯里化,使用剩余(rest)参数的函数,例如 function f(...args){}是无法柯里化的。