编程题:实现函数柯里化 | 刷题打卡

370 阅读2分钟

一、什么是柯里化?

柯里化(Currying)是一种关于函数的高阶技术。它不仅被用于 JavaScript,还被用于其他编程语言。柯里化是一种函数的转换,它是指将一个函数从可调用的 f(a, b, c) 转换为可调用的 f(a)(b)(c)。柯里化不会调用函数。它只是对函数进行转换。

维基百科版定义:

是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。

简单版定义:

只传递给函数一部分参数来调用它,让它返回一个函数去处理剩下的函数。

二、柯里化有什么用?

举个例子。

var add = function (x) {
  return function (y) {
    return x + y;
  };
};

var increment = add(1);

var addTen = add(10);

console.log(increment(2)); // 3

console.log(addTen(2)); // 12

这表明curry是一种“预加载”函数的能力,通过传递一到两个参数调用函数,就能得到一个记住了这些参数的新函数。

再举个例子:有一个用于格式化和输出日志的函数log(date, level, msg)。在实际项目中,此函数具有很多有用的功能,例如通过网络发送日志等。

function log(date, level, msg) {
  console.log(`[${date.getHours()}:${date.getMinutes()}] [${level}] ${msg}`);
} 

将它柯里化:

var log = _.curry(log); // 使用 lodash 的 curry 方法

柯里化之后,log仍然可以正常运行:

log(new Date(), 'DEBUG', 'some debug'); // log(a, b, c)

同时,也可以以柯里化的形式调用:

log(new Date())('DEBUG')('some debug'); // log(a)(b)(c)

现在,我们可以轻松地为当前日志创建便捷函数:

// logNow 是一个带有第一个固定参数的偏函数(partial)
logNow = log(new Date());

// 使用它
logNow('INFO', 'some info msg'); 

// 进而,也可以创建一个为 当前时间 DEBUG 级别的 便捷方法
debugNow = logNow('DEBUG');

// 使用它, 只需要一个参数
debugNow('only msg here');

所以:

  1. 柯里化之后,我们没有丢失任何东西,如log依然可以正常被调用

  2. 此外还可以轻松地生成偏函数(partial function

三、实现curry函数

提问:请实现一个函数,实现以下功能:

add(1, 2, 3); // 6
add(1,2)(3);  // 6
add(1)(2)(3); // 6 

答案:

function curry(func) {
  return function curried(...args) {
    if (args.length >= func.length) {
      return func.apply(this, args);
    } else {
      return function (...args2) {
        return curried.apply(this, args.concat(args2));
      };
    }
  };
}

// 另一种实现
function curry(fn) {
  return (...args) => {
    if (args.length >= fn.length) {
      return fn.apply(null, args);
    } else {
      return curry(fn.bind(null, ...args));
    }
  };
}

测试一下:

function sum(a, b, c) {
  return a + b + c;
}

var curriedSum = curry(sum);

console.log(curriedSum(1, 2, 3)); // 6,正常形式调用
console.log(curriedSum(1, 2)(3)); // 6,以偏函数的方式调用
console.log(curriedSum(1)(2)(3)); // 6,以偏函数的方式调用