Wie implementiere ich eine allgemeinere Reduktionsfunktion, um ein vorzeitiges Verlassen zu ermöglichen?

reduce (akafoldL in FP) ist die allgemeinste iterative Funktion höherer Ordnung in Javascript. Sie können zum Beispiel implementieren,map oderfilter bezüglichreduce. Ich habe eine imperative Schleife verwendet, um den Algorithmus besser zu veranschaulichen:

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]

Aber Sie können nicht ableitensome oderevery vonreduce, weil beide früh zurückkehren können.

So wie kann eine noch verallgemeinerte Teilreduzierungsfunktion aussehen? Bisher habe ich mir folgende naive Implementierung ausgedacht:

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]

Ich kann auch implementierenmap bezüglichfoldLP:

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]

Der Nachteil liegt auf der Hand: Wenn kein Mechanismus zum vorzeitigen Verlassen erforderlich ist,always wird unnötigerweise aufgerufen. Die transformierende und die frühe Austrittsfunktion setzen sich statisch zusammen ausfoldLP und können nicht unabhängig voneinander verwendet werden. Gibt es einen effizienteren Kombinator, der ein verallgemeinertes @ ermöglichArray.prototype.reduce?

Wenn Sie sich den Aufrufstapel ansehen, erhalten Sie die return-Anweisung der Reduktionsfunktionacc => x => acc.concat([f(x)]) müsste mehrere Stack-Frames überspringen. Diese Art der Stapelmanipulation lässt mich an Fortsetzungen denken. Vielleicht gibt es eine effiziente Möglichkeit, dieses Problem in Continuation Passing Style zusammen mit einer angepassten Call / CC-Funktion zu lösen - oder zumindest mit einem Generator.

Antworten auf die Frage(4)

Ihre Antwort auf die Frage