所以这是一种可读的方式(代码无关紧要,样式重要):
arr.map().filter() // looping 2 times
然后循环被认为是一种更快的方法:
for(/* whatever */) {
// looping once, and doing all we need in the same loop
}
所以我的问题是:也许从函数式编程世界来看,有没有一种方法可以将前者的可读性与后者的性能结合起来?
P.S。目前有否决这类问题的趋势。如果需要,也请写下原因。
答案 0 :(得分:4)
当然有。
第一种选择:换能器
const mapReduce = map => reduce => (acc, x) =>
reduce(acc, map(x));
const filterReduce = filter => reduce => (acc, x) =>
filter(x)
? reduce(acc, x)
: acc;
const transduce = (...ts) => xs =>
xs.reduce(ts.reduce(comp, id) (concat), []);
const comp = (f, g) =>
x => f(g(x));
const id = x => x;
const concat = (xs, ys) =>
xs.concat(ys);
const sqr = n => n * n;
const isOdd = n => n & 1 === 1;
const log = console.log;
// the upper code is usually library code
// so you don't have to deal with its complexity but only with its API
const tx = filterReduce(isOdd),
ty = mapReduce(sqr);
const r = transduce(tx, ty) ([1,2,3,4,5]); // filter/map in same iteration
log(r);
第二种选择:具有尾调用优化效果的裸递归
const loop = f => {
let acc = f();
while (acc && acc.type === tailRec)
acc = f(...acc.args);
return acc;
};
const tailRec = (...args) =>
({type: tailRec, args});
const comp = (f, g) => x =>
f(g(x));
const sqr = n => n * n;
const isOdd = n => n & 1 === 1;
const log = console.log;
// the upper code is usually library code
// so you don't have to deal with its complexity but only with its API
const r = loop((xs = [1,2,3,4,5], acc = [], i = 0) => {
if (i === xs.length)
return acc;
else
return tailRec( // filter/map in same iteration
xs,
isOdd(xs[i]) ? acc.concat(sqr(xs[i])) : acc,
i + 1);
});
log(r);
我想说换能器是用于普通的,更简单的迭代,而递归适用于更复杂的迭代,例如当您需要短路(过早退出)时。
答案 1 :(得分:0)
就我个人而言,我认为您的代码中不会包含一些for循环会使其变得不可读,但是我想这是基于观点的。
有很多方法可以使您的代码更具可读性。如果您将经常使用此功能,则可以创建一个添加到^(..)*
的方法-这样,您可以编写一次for循环,并在需要时调用它,而不必看难看的东西码。下面是一个示例:
Array.prototype