1

Ich habe versucht, den Y-Kombinator in Javascript zu implementieren.Warum kann ich in Javascript nicht x => f (f) (x) durch f (f) ersetzen?

ich es geschafft, die folgende Maßnahmen durchzuführen:

const y0 = gen => (f => f(f))(f => gen(x => f(f)(x))); 
const factorial0 = y0(fact => n => n<=2 ? n : n * fact(n-1)); 
console.log(factorial0(5)); 
// 120 

Es funktioniert gut.

Dann erwog ich den Ausdruck x => f(f)(x).

Mein Verständnis ist, dass ein Ausdruck x => g(x) entspricht g entspricht. Die Anwendung von y auf x => g(x) ergibt g(y), während die Anwendung y auf g auch g(y) ergibt.

So ersetzt ich x => f(f)(x) von f(f).

const y = gen => (f => f(f))(f => gen(f(f))); 
const factorial = y(fact => n => n<=2 ? n : n * fact(n-1)); 
console.log(factorial(5)); 
// RangeError: Maximum call stack size exceeded 

Aber diese Version stürzt mit einem Stapelüberlauf ab.

Also was ist der Unterschied zwischen x => f(f)(x) und f(f) so dass der eine funktioniert und der andere abstürzt.

+1

Weil strenge Bewertung. – Bergi

+1

@Bergi Drei Wörter - ich nenne das eine faule Erklärung: D – ftor

Antwort

2

Was ich denke ist, dass diese 2 Ausdrücke nicht genau gleich sind.

Auf der einen Seite x => f(f)(x) - dies eine neue Funktionsliteral erzeugt (so wird es nicht sofort aufgerufen - es ist nur aufgerufen, wenn diese Funktion aufgerufen wird)

Auf der anderen Seite f(f) - dies in Javascript ist ein Ausdruck, der die f-Funktion aufruft. Es kommt also in Ihrem Fall zu einem Stapelüberlauf.

+1

Oder mit anderen Worten, 'x => f (f) (x)' ist die Lazy-Version von 'f (f)'. Da Javascript strikt ausgewertet wird, müssen wir Muster wie 'f (f)' in ein Lambda umwandeln, um eine unendliche Rekursion zu vermeiden. – ftor

+0

Ich denke, das ist der Kern des Problems. Der Ausdruck x => f (f) (x) verzögert die Auswertung von f (f) bis x geliefert wird. –

4

Gut

x => f(f)(x) 

ist eine Funktion ein Parameter genommen wird, x. Wenn die Funktion aufgerufen wird, ruft sie ihrerseits die Funktion f auf und übergibt als Parameter einen Verweis auf f. Die Funktion f gibt eine andere Funktion zurück, die dann aufgerufen wird, wobei x als Parameter übergeben wird.

In der alten Schule Syntax, dann ist es

function(x) { 
    return f(f)(x); 
} 

Das als nur deutlich anders f(f) von selbst aus. Das ist nur ein Aufruf der Funktion "f", wobei "f" als Parameter übergeben wird.

So beide x => f(f)(x) und f(f) sind Ausdrücke, aber sie repräsentieren deutlich unterschiedliche Semantik. Der Wert des ersten ist eine Referenz auf eine Funktion; der Ausdruck selbst tut nichts anderes — insbesondere wird die Funktion f() nicht aufgerufen. Der Wert f(f) ist, was auch immer f() Funktion — tut, was f() zurückgibt, was f() tut.