跳到主要内容

柯里化与偏函数

柯里化是什么?​

  • 柯里化 (currying): 把可按 f(a, b, c) 调用的函数, 变换成可按 f(a)(b)(c) 调用的形式;
  • 只变换: 柯里化本身不调用函数, 只做转换;
  • 偏函数 (partial): 固定了部分参数后得到的函数, 如 log(new Date());
  • JS 特色: 主流实现是增强版 —— 转换后既支持一次传全, 也支持分段传参;

两参柯里化怎么手写?​

function curry(f) {
return function (a) {
return function (b) {
return f(a, b);
};
};
}

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

curriedSum(1)(2); // 3
  • 返回值: curry(func) 返回包装 function (a);
  • 第一层: curriedSum(1) 把 1 存进闭包, 返回新包装 function (b);
  • 第二层: curriedSum(1)(2) 把两个参数一起交给原函数 sum;
  • 机制: 依赖闭包保存已收参数, 见 闭包;

柯里化有什么用?​

以三参日志函数为例:

function log(date, importance, message) {
alert(`[${date.getHours()}:${date.getMinutes()}] [${importance}] ${message}`);
}

log = _.curry(log);

log(new Date(), "DEBUG", "some debug"); // 正常调用
log(new Date())("DEBUG")("some debug"); // 柯里化形式

const logNow = log(new Date()); // 固定第一个参数 → 偏函数
logNow("INFO", "message"); // [HH:mm] INFO message

const debugNow = logNow("DEBUG"); // 继续固定
debugNow("message"); // [HH:mm] DEBUG message
  • 结论 1: 柯里化不损失能力, log 照常可调用;
  • 结论 2: 固定环境参数后可批量生产语义化短函数 (今日日志、今日 DEBUG 日志);
  • 模式: 通用函数 → 固定环境 / 日期 / 通道 → 专用函数;

怎样实现支持任意参数个数的 curry?​

  • 增强版: lodash 的 _.curry 这类实现同时兼容 curriedSum(1, 2) 与 curriedSum(1)(2);
  • 判据: 参数够就直接执行原函数, 不够才返回偏函数;
function curry(func) {
return function curried(...args) {
if (args.length >= func.length) {
return func.apply(this, args);
}
return function (...args2) {
return curried.apply(this, args.concat(args2));
};
};
}
function sum(a, b, c) {
return a + b + c;
}
const curriedSum = curry(sum);

curriedSum(1, 2, 3); // 6, 正常调用
curriedSum(1)(2, 3); // 6, 只柯里化第一个参数
curriedSum(1)(2)(3); // 6, 完全柯里化
  • 分支一: 已收参数数量 ≥ func.length 时, 直接 func.apply(this, args) 转交;
  • 分支二: 否则不调用 func, 返回新包装, 把旧参数与新参数 concat 后递归调用 curried;
  • this: 用 apply(this, ...) 透传, 方法柯里化后 this 语义不丢, 见 call、apply、bind 与手写实现;

curry 有哪些限制?​

  • 定长函数: 依赖 func.length 判断参数是否收齐, 用剩余参数的 f(...args) 无法这样柯里化;
  • 定义与实现有偏差: 严格按定义 f(a, b, c) 只能变成 f(a)(b)(c), 而 JS 实现通常额外保留多参调用;
  • 偏函数不是新语义: 它只是"已经收了一部分参数的柯里化函数", 没有独立的类型;