最近有看到一些柯里化的文章,怎么说呢,感受很奇怪。一篇是阿里云的译文,文章末尾给出了这样一个 "curry":app
function curry(fn, ...args) {
return (..._arg) => {
return fn(...args, ..._arg);
}
}
复制代码
做者前面明明例举了柯里化和部分应用的区别,结果最后说咱们实现下柯里化吧,而后写了个部分应用……太假了,我忍不住评论了一下:函数
而后今天看到咱们组欢哥的文章,说实话看了一下开头这段代码我就不太有耐心看下面具体的分析了:post
// 定义占位符
var _ = '_';
function magician3 (targetfn, ...preset) {
var numOfArgs = targetfn.length;
var nextPos = 0; // 下一个有效输入位置的索引,能够是'_',也能够是preset的结尾
// 查看是否有足够的有效参数
if (preset.filter(arg=> arg !== _).length === numOfArgs) {
return targetfn.apply(null, preset);
} else {
// 返回'helper'函数
return function (...added) {
// 循环并将added参数添加到preset参数
while(added.length > 0) {
var a = added.shift();
// 获取下一个占位符的位置,能够是'_'也能够是preset的末尾
while (preset[nextPos] !== _ && nextPos < preset.length) {
nextPos++
}
// 更新preset
preset[nextPos] = a;
nextPos++;
}
// 绑定更新后的preset
return magician3.call(null, targetfn, ...preset);
}
}
}
复制代码
这是在干吗……而后欢哥他们发现了这段代码有 bug,分析了一通,解决了 bug,美好的青春啊朋友们,出去喝酒蹦迪大保健很差么,非得这么挥霍生命么……ui
在咱们本身实现以前,对柯里化没什么概念的同窗能够看下 wiki(要看英文 wiki,中文 wiki 对柯里化的解释写得又乱又不许确,容易和部分应用混淆),简单来讲柯里化就是把一个多参函数转换成接受单参的一系列函数。它跟部分应用的概念不太同样,部分应用是把一个多参函数“切”一刀,而柯里化是把函数“切”好多刀,直到中间每一个函数都是单参的,最后获得的结果就是所谓的柯里化函数(curried function)。在 JS 里要手写个 curried function 其实就是手写个高阶函数,没什么特别的。那要实现一个通用的 curry,该怎么作呢,我不是针对谁,我是说上面那两个实现都在卖萌……阿里云
const curry = (fn) => {
if (fn.length <= 1) return fn;
// 这是我一开始的实现
// 后来发现 rest 是多余的,下面这样就好了,fn.length 多处用到,能够提出来
// const generator = (args) => (args.length === fn.length ? fn(...args) : arg => generator([...args, arg]));
const generator = (args, rest) => (!rest ? fn(...args) : arg => generator([...args, arg], rest - 1));
return generator([], fn.length);
};
复制代码
这不就三行代码搞定了么(不算函数声明),测一下:spa
const sum = (a, b, c) => a + b + c;
const curriedSum = curry(sum);
const res = curriedSum(1)(2)(3)
console.log(res); // 6
const log = (a, b, c) => {
console.log(a, b, c);
};
const curriedLog = curry(log);
curriedLog('a')('b')('c'); // a b c
复制代码
好像没啥问题吧……emmmmm……欢迎打脸。3d
因而可知,在折腾什么 curry 什么 partial application 以前,仍是多琢磨琢磨递归这种基本概念,顺便附送一个 FP Style 的快排吧,忽然感受我也挺挥霍青春的……rest
const quickSort = (list) => {
if (!list || !list.length) return [];
if (list.length === 1) return list;
const [middle, ...rest] = list;
const reducer = (acc, x) => (
x <= middle ?
{ ...acc, left: [...acc.left, x] } :
{ ...acc, right: [...acc.right, x] }
);
const { left, right } = rest.reduce(reducer, { left: [], right: [] });
return [...quickSort(left), middle, ...quickSort(right)];
};
const list = [2, 3, 1, 8, 8, 1, 2, 18, 6, 2333];
const sorted = quickSort(list); // [ 1, 1, 2, 2, 3, 6, 8, 8, 18, 2333 ]
复制代码
PS:评论里有位大佬给了一个一行的实现:code
const curry = (fn, arr = []) => (...args) => (
arg => arg.length === fn.length
? fn(...arg)
: curry(fn, arg)
)([...arr, ...args])
复制代码
我也只能开动小脑筋把个人实现改为了一行……cdn
// 跟三行的思路是同样的,就是强行写到了一行……
const curry = fn =>
(arg, args = [arg], rest = fn.length - 1) =>
(rest < 1 ? fn(...args) : newArg => curry(fn)(newArg, [...args, newArg], rest - 1));
复制代码
对比上面两个实现,咱们会发现个人实现……败了,由于我既然有 args,这个 rest 就是多余的,因此改为这样:
// 感受仍是多拆几行比较好……
const curry = fn =>
(arg, args = [arg]) =>
(!fn.length || args.length === fn.length ? fn(...args) : newArg => curry(fn)(newArg, [...args, newArg]));
复制代码