Как реализовать более общую функцию сокращения, чтобы позволить ранний выход?

reduce (акаfoldL в FP) - самая общая итерационная функция высшего порядка в Javascript. Вы можете реализовать, например,map или жеfilter с точки зренияreduce, Я использовал императивный цикл, чтобы лучше проиллюстрировать алгоритм:

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;

map(inc)(xs); // [2, 3, 4, 5]

Но вы не можете получитьsome или жеevery отreduceпотому что оба могут вернуться рано.

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

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];
foldLP(append)(takeN(3))([])(xs); // [1,2,3]

Я также могу реализоватьmap с точки зренияfoldLP:

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)]) пришлось бы пропустить несколько стековых кадров. Этот вид стековых манипуляций заставляет меня думать о продолжениях. Может быть, есть эффективный способ решить эту проблему в Continuation Passing Style вместе с адаптированной функцией call / cc - или, по крайней мере, с генератором.

Ответы на вопрос(2)

Ваш ответ на вопрос