JS中精巧的自动柯里化实现方法(2)

multiPlus(1)(2)(3); // => 6

这种写法看着很奇怪吧?不过如果入了JS的函数式编程这个大坑的话,这会是常态。

JS中自动柯里化的精巧实现

柯里化(Currying)是函数式编程中很重要的一环,很多函数式语言(eg. Haskell)都会默认将函数自动柯里化。然而JS并不会这样,因此我们需要自己来实现自动柯里化的函数。

先上代码:

// ES5
function curry(fn) {
 function _c(restNum, argsList) {
  return restNum === 0 ?
   fn.apply(null, argsList) :
   function(x) {
    return _c(restNum - 1, argsList.concat(x));
   };
 }
 return _c(fn.length, []);
}
// ES6
const curry = fn => {
 const _c = (restNum, argsList) => restNum === 0 ?
  fn(...argsList) : x => _c(restNum - 1, [...argsList, x]);
 return _c(fn.length, []);
}
/***************** 使用 *********************/
var plus = curry(function(a, b) {
 return a + b;
});
// ES6
const plus = curry((a, b) => a + b);
plus(2)(4); // => 6

这样就实现了自动的柯里化!

如果你看得懂发生了什么的话,那么恭喜你!大家口中的大佬就是你!,快留下赞然后去开始你的函数式生涯吧(滑稽

如果你没看懂发生了什么,别担心,我现在开始帮你理一下思路。

需求分析

我们需要一个 curry 函数,它接受一个待柯里化的函数为参数,返回一个用于接收一个参数的函数,接收到的参数放到一个列表中,当参数数量足够时,执行原函数并返回结果。

实现方式

简单思考可以知道,柯里化部分配置函数的步骤数等于 fn 的参数个数,也就是说有两个参数的 plus 函数需要分两步来部分配置。函数的参数个数可以通过fn.length获取。

总的想法就是每传一次参,就把该参数放入一个参数列表 argsList 中,如果已经没有要传的参数了,那么就调用fn.apply(null, argsList)将原函数执行。要实现这点,我们就需要一个内部的判断函数 _c(restNum, argsList),函数接受两个参数,一个是剩余参数个数 restNum,另一个是已获取的参数的列表 argsList;_c 的功能就是判断是否还有未传入的参数,当 restNum 为零时,就是时候通过fn.apply(null, argsList)执行原函数并返回结果了。如果还有参数需要传递的话,也就是说 restNum 不为零时,就需要返回一个单参数函数

function(x) {
 return _c(restNum - 1, argsList.concat(x));
}

来继续接收参数。这里形成了一个尾递归,函数接受了一个参数后,剩余需要参数数量 restNum 减一,并将新参数 x 加入 argsList 后传入 _c 进行递归调用。结果就是,当参数数量不足时,返回负责接收新参数的单参数函数,当参数够了时,就调用原函数并返回。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/866.html