Есть ли способ избежать компромисса между удобочитаемостью и производительностью при цикле? - PullRequest
0 голосов
/ 06 декабря 2018

Так что это читабельный способ (код не имеет значения, важен стиль):

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

И циклы считаются более быстрым способом:

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

Таким образом, мой вопрос: есть ли способ, может быть, из мира функционального программирования, совместить удобочитаемость первого с производительностью второго?

PS Существует тенденция к снижению таких вопросов.Если хотите, напишите также причину.

Ответы [ 2 ]

0 голосов
/ 06 декабря 2018

Лично я не думаю, что наличие некоторых циклов for в вашем коде делает его нечитаемым, но я полагаю, что это основано на мнении.

Есть много способов сделать ваш код более читабельным.Если вы собираетесь часто использовать эту функциональность, вы можете создать метод для добавления в Array.prototype - таким образом, вы можете написать цикл for один раз и вызывать его, когда вам это нужно, без необходимости видеть то, что вы считаете уродливым кодом.Ниже приведен пример:

//This method will now be available to all Arrays instances:
Array.prototype.prettyLoop = function() {
  console.log('All I do is execute a basic for-loop');
  for (let i = 0; i < this.length; i++) {
    console.log(this[i]);
  }
};

//Call the method from your script
["a", 1, null, "x", 1989, false, {}].prettyLoop();
0 голосов
/ 06 декабря 2018

Конечно, есть.

1-й вариант: преобразователь

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);

2-й вариант: голая рекурсия с эффектом оптимизации хвостового вызова

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);

Я бы сказал, что преобразователь предназначен для обычных и более простых итераций, тогда как рекурсия подходит для более сложных, например, когда вам нужно короткое замыкание (преждевременный выход).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...