1

J'essayais d'implémenter le combinateur Y en Javascript.En Javascript, pourquoi ne puis-je pas remplacer x => f (f) (x) par f (f)?

j'ai réussi à mettre en œuvre les éléments suivants:

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 

Il fonctionne bien.

Ensuite, je considérais l'expression x => f(f)(x). Je comprends qu'une expression x => g(x) est équivalente à g. L'application de y à x => g(x) est évaluée à g(y), tandis que l'application y à g est également évaluée à g(y). J'ai donc remplacé x => f(f)(x) par 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 

Mais cette version se bloque avec un débordement de pile.

Alors quelle est la différence entre x => f(f)(x) et f(f) de sorte que l'un fonctionne et l'autre se bloque.

+1

Parce que l'évaluation stricte. – Bergi

+1

@Bergi Trois mots - J'appelle cela une explication paresseuse: D – ftor

Répondre

2

Ce que je pense est que ces 2 expressions ne sont pas exactement les mêmes.

D'une part x => f(f)(x) - cela crée un nouveau littéral fonction (il n'est pas invoquaient tout de suite - il est invoqué que lorsque cette fonction est appelée)

D'autre part f(f) - ce en Javascript est un expression qui appelle la fonction f. Il en résulte un débordement de pile dans votre cas.

+1

Ou, en d'autres termes, 'x => f (f) (x)' est la version paresseuse de 'f (f)'. Puisque Javascript est strictement évalué, nous devons envelopper des motifs comme 'f (f)' dans un lambda pour éviter une récursion infinie. – ftor

+0

Je pense que c'est le noeud du problème. L'expression x => f (f) (x) retarde l'évaluation de f (f) jusqu'à ce que x soit fourni. –

4

bien

x => f(f)(x) 

est une fonction qui prend un paramètre, x. Lorsque la fonction est appelée, elle appelle à son tour la fonction f, en passant une référence à f en tant que paramètre. La fonction f renvoie une autre fonction, puis celle-ci est appelée, avec x passé en paramètre.

Dans la syntaxe de la vieille école, il est

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

C'est très différent de celui que f(f) par lui-même. C'est juste une invocation de la fonction "f", avec "f" passé en paramètre.

donc à la fois x => f(f)(x) et f(f) sont expressions, mais ils représentent une sémantique très différents. La valeur du premier est une référence à une fonction; l'expression elle-même ne fait rien d'autre — en particulier, la fonction f() n'est pas appelée. La valeur de f(f) est quelle que soit la fonction f() renvoie lorsqu'il est appelé — que l'expression fait faire quelque chose, cela étant n'importe quelle fonction f() fait.