有没有一种方法可以避免在循环时在可读性和性能之间进行权衡?

时间:2018-12-06 18:13:21

标签: javascript functional-programming

所以这是一种可读的方式(代码无关紧要,样式重要):

arr.map().filter() // looping 2 times

然后循环被认为是一种更快的方法:

for(/* whatever */) { 
  // looping once, and doing all we need in the same loop
}

所以我的问题是:也许从函数式编程世界来看,有没有一种方法可以将前者的可读性与后者的性能结合起来?

P.S。目前有否决这类问题的趋势。如果需要,也请写下原因。

2 个答案:

答案 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