一、什么是函数柯里化

以下面这个例子为例,利用闭包将函数的参数打散的方式就是柯里化(curry),同理,还有偏函数(partial)

  1. add(1,2,3) == add(1)(2,3) == add(1,2)(3) == add(1)(2)(3)

二、实现柯里化

  1. // es6 通用的柯里化写法 (只适合函数参数长度固定的情况)
  2. function curry(fn, ...args) {
  3. return fn.length <= args.length ? fn(...args) : curry.bind(void 0, fn, ...args)
  4. }

函数参数固定时

  1. const add = (a,b,c) => a + b + c
  2. const curriedAdd = curry(add)
  3. console.log(curriedAdd(1)(2)(3)) // 6

函数参数不固定时

  1. function curry(fn, ...args) {
  2. return fn.length <= args.length ? fn(...args) : curry.bind(void 0, fn, ...args)
  3. const add = (...args) => args.reduce((a,b)=>a+b,0)
  4. const curriedAdd = curry(add)
  5. console.log(curriedAdd(1)(2)(3)) // 报错

三、例题

  1. // 请实现一个 add 函数, 满足以下功能
  2. add(1); // 1
  3. add(1)(2); // 3
  4. add(1)(2)(3); // 6
  5. add(1)(2, 3); // 6
  6. add(1, 2)(3); // 6
  7. add(1, 2, 3); // 6

直接套模板得到结果如下:

  1. // es6
  2. function curry(fn, ...args) {
  3. return fn.length <= args.length ? fn(...args) : curry.bind(void 0, fn, ...args)
  4. }
  5. const add = (a,b,c) => a + b + c
  6. // const add = (...args) => args.reduce((a,b) => a + b, 0)
  7. const curriedAdd = curry(add)
  8. // 通用的柯里化方法限制了函数的参数,如下,打印结果 为 [Function function]
  9. console.log(curriedAdd(1))
  10. // 结果正确
  11. console.log(curriedAdd(1)(2)(3))
  12. const curriedAdd1 = curry(add, 1,2)
  13. // 结果正确
  14. console.log(curriedAdd1(3))
  15. // 抛出异常
  16. console.log(curriedAdd1(3)(3))

当入参不足函数形参长度时,会递归会返回函数本身,所以本题的难度在于重写函数的 toString 方法。

实现 add(1)(2)(3)

函数柯里化概念: 柯里化(Currying)是把接受多个参数的函数转变为接受一个单一参数的函数,并且返回接受余下的参数且返回结果的新函数的技术。

1)粗暴版

  1. function add (a) {
  2. return function (b) {
  3. return function (c) {
  4. return a + b + c;
  5. }
  6. }
  7. }
  8. console.log(add(1)(2)(3)); // 6

2)柯里化解决方案

  • 参数长度固定
    1. var add = function (m) {
    2. var temp = function (n) {
    3. return add(m + n);
    4. }
    5. temp.toString = function () {
    6. return m;
    7. }
    8. return temp;
    9. };
    10. console.log(add(3)(4)(5)); // 12
    11. console.log(add(3)(6)(9)(25)); // 43
    对于add(3)(4)(5),其执行过程如下:
  1. 先执行add(3),此时m=3,并且返回temp函数;
  2. 执行temp(4),这个函数内执行add(m+n),n是此次传进来的数值4,m值还是上一步中的3,所以add(m+n)=add(3+4)=add(7),此时m=7,并且返回temp函数
  3. 执行temp(5),这个函数内执行add(m+n),n是此次传进来的数值5,m值还是上一步中的7,所以add(m+n)=add(7+5)=add(12),此时m=12,并且返回temp函数
  4. 由于后面没有传入参数,等于返回的temp函数不被执行而是打印,了解JS的朋友都知道对象的toString是修改对象转换字符串的方法,因此代码中temp函数的toString函数return m值,而m值是最后一步执行函数时的值m=12,所以返回值是12。
  • 参数长度不固定
    1. function add (...args) {
    2. //求和
    3. return args.reduce((a, b) => a + b)
    4. }
    5. function currying (fn) {
    6. let args = []
    7. return function temp (...newArgs) {
    8. if (newArgs.length) {
    9. args = [
    10. ...args,
    11. ...newArgs
    12. ]
    13. return temp
    14. } else {
    15. let val = fn.apply(this, args)
    16. args = [] //保证再次调用时清空
    17. return val
    18. }
    19. }
    20. }
    21. let addCurry = currying(add)
    22. console.log(addCurry(1)(2)(3)(4, 5)()) //15
    23. console.log(addCurry(1)(2)(3, 4, 5)()) //15
    24. console.log(addCurry(1)(2, 3, 4, 5)()) //15
    第 84 题:请实现一个 add 函数,满足以下功能
    14. 函数柯里化的实现