Sind Funktionen in JavaScript Tail Call optimiert?

72

Ich habe versucht zu verstehen Tail call optimization im Kontext von JavaScript und habe die folgenden rekursiven und schwanzrekursiven Methoden für geschrieben factorial().

Rekursiv:

function factorial (n) {
  if (n < 2) {
    return 1;
  } else {
    return n * factorial(n-1);
  }
}

Schwanzrekursiv:

function factorial (n) {
  function fact(n, acc) {
    if (n < 2) {
      return acc;
    } else {
      return fact(n-1, n * acc);
    }
  }

  return fact(n, 1)
}

Ich bin mir jedoch nicht sicher, ob die tail-recursiveVersion der Funktion vom JavaScript-Compiler optimiert wird, da sie in anderen Sprachen wie Scala usw. ausgeführt wird. Kann mir jemand dabei helfen?

Aditya Singh
quelle

Antworten:

64

Update: Ab dem 1. Januar 2020 ist Safari der einzige Browser, der die Tail Call-Optimierung unterstützt.

Das Chrom-Team gibt ausdrücklich an, dass sich die Tail Call-Optimierung nicht in der aktiven Entwicklung befindet und hier verfolgt werden kann .

Die Implementierung für Firefox kann hier verfolgt werden

Ursprünglicher Beitrag

Ja, ES2015 bietet Tail-Call-Optimierung im strengen Modus. Dr. Axel Rauschmayer legt es unter dem folgenden Link wunderschön dar, so dass ich seine Worte hier nicht wiederholen werde.

Hinweis: ES 5 optimiert keine Tail Calls.

http://www.2ality.com/2015/06/tail-call-optimization.html

sheeldotme
quelle
Wichtiger Vorbehalt: Es kann nur im strengen Modus optimiert werden.
Barmar
15
Die Tail-Call-Optimierung in ES2015 unterscheidet sich deutlich von allen oder sogar den meisten Implementierungen, die ansonsten behaupten, ES2015-konform zu sein, und eine ordnungsgemäße Tail-Call-Optimierung durchführen. Die richtige Antwort auf die Frage "Sind Funktionen in JavaScript-Tail-Call optimiert?" ist "sie sind, wenn der Motor tut, sonst sind sie nicht." Siehe chromestatus.com/feature/5516876633341952 .
5
Ich bin mir ziemlich sicher, dass Browser auch ES5-Code optimieren, wenn sie ihn für ES6 implementieren. Es ist nur so, dass die ES6-Spezifikation dies jetzt explizit erfordert (und es noch keine Engine implementiert hat).
Bergi
1
Die 'ECMAScript-Kompatibilitätstabelle' ist eine gute Ressource, um die Kompatibilität zu überprüfen. kangax.github.io/compat-table/es6
Eldho NewAge
15

Theoretisch ja. Wie die andere Antwort besagt.

In der Praxis wird dies jedoch ab Juli 2017 nur von Safari unterstützt.

Kompatibilität mit Javascript ES6 (ES2015): https://kangax.github.io/compat-table/es6/

AK_
quelle
2
Richtig - und da Chrome überhaupt nicht daran arbeitet, scheint es eine hervorragende Wahrscheinlichkeit zu geben, dass Tail Calls im Produktionscode niemals verwendet werden können. chromestatus.com/feature/5516876633341952
Freewalker
4

Wie die anderen Antworten gesagt haben, nicht in der Praxis. Sie können jedoch ein Hilfsprogramm definieren, das Ihnen hilft.

class Tco {
  constructor(func) {
    this.func = func;
  }
  execute() {
    let value = this;
    while (value instanceof Tco)
      value = value.func();
    return value;
  }
}

const tco = (f) => new Tco(f);
function factorial (n) {
  const fact = (n, acc) => tco(() => {
    if (n < 2) {
      return acc;
    } else {
      return fact(n-1, n * acc);
    }
  });

  return fact(n, 1).execute();
}

console.log(factorial(2000000)); // Infinity

Wie Sie sehen können, können Sie damit rekursive Endfunktionen mit nur geringem Unterschied in der Syntax schreiben, ohne dass ein maximaler Aufrufstapelfehler auftritt.

tjjfvi
quelle