【发布时间】:2016-04-30 20:06:21
【问题描述】:
reduce(在 FP 中也称为 foldL)是 Javascript 中最通用的迭代高阶函数。例如,您可以根据reduce 实现map 或filter。我使用命令式循环来更好地说明算法:
const foldL = f => acc => xs => {
for (let i = 0; i < xs.length; i++) {
acc = f(acc)(xs[i]);
}
return acc;
};
const map = f => xs => {
return foldL(acc => x => acc.concat([f(x)]))([])(xs);
}
let xs = [1, 2, 3, 4];
const inc = x => ++x;
const result = map(inc)(xs);
console.log(result); // [2, 3, 4, 5]
但是您不能从reduce 派生出some 或every,因为两者都可以提前返回。
那么更通用的部分归约函数应该是什么样子的呢?到目前为止,我已经提出了以下幼稚的实现:
const foldLP = f => pred => acc => xs => {
for (let i = 0, r; i < xs.length; i++) {
r = pred(i, acc, xs[i]);
if (r === true) { // normal iteration
acc = f(acc)(xs[i]);
} else if (r === false) { // early exit
break;
} /* else { // skip iteration
continue;
} */
}
return acc;
};
const takeN = n => (idx, acc, x) => idx < n;
const append = xs => ys => xs.concat(ys);
let xs = [1,2,3,4,5];
const result = foldLP(append)(takeN(3))([])(xs);
console.log(result); // [1,2,3]
我也可以用foldLP来实现map:
const always = x => y => x;
const map = f => xs => {
return foldLP(acc => x => acc.concat([f(x)]))(always(true))([])(xs);
}
map(inc)(xs); // [2,3,4,5,6]
缺点很明显:只要不需要提前退出机制,就会不必要地调用always。转换和提前退出函数由foldLP静态组成,不能单独使用。有没有更高效的组合器,可以实现泛化的Array.prototype.reduce?
如果您查看调用堆栈,则归约函数acc => x => acc.concat([f(x)]) 的返回语句必须跳过几个堆栈帧。这种堆栈操作让我想到了延续。也许有一种有效的方法可以在继续传递样式中解决这个问题以及一个经过调整的 call/cc 函数 - 或者至少使用一个生成器。
【问题讨论】:
标签: javascript arrays functional-programming reduce control-flow