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?

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


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


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.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.