web-dev-qa-db-fra.com

Comment écrire une fonction de flèche dans ES6 de manière récursive?

Les fonctions de flèche dans ES6 ne possèdent pas de propriété arguments et, par conséquent, arguments.callee ne fonctionnera pas et ne fonctionnerait de toute façon pas en mode strict, même si une fonction anonyme était utilisée.

Les fonctions fléchées ne peuvent pas être nommées, ainsi l'astuce de l'expression fonctionnelle nommée ne peut pas être utilisée.

Alors ... Comment écrit-on une fonction de flèche récursive? C’est une fonction de flèche qui s’appelle de manière récursive en fonction de certaines conditions et bien sûr, bien sûr?

24
Siddharth

Claus Reinke a répondu à votre question lors d’une discussion sur le site Web esdiscuss.org .

Dans ES6, vous devez définir ce qu'il appelle un combinateur de récursivité.

 let rec = (f)=> (..args)=> f( (..args)=>rec(f)(..args), ..args )

Si vous souhaitez appeler une fonction de flèche récursive, vous devez appeler le combinateur de récursivité avec la fonction de flèche en paramètre, le premier paramètre de la fonction de flèche est une fonction récursive et les autres paramètres. Le nom de la fonction récursive n'a pas d'importance car elle ne serait pas utilisée en dehors du combinateur récursif. Vous pouvez ensuite appeler la fonction flèche anonyme. Ici, nous calculons la factorielle de 6.

 rec( (f,n) => (n>1 ? n*f(n-1) : n) )(6)

Si vous voulez le tester dans Firefox, vous devez utiliser la traduction ES5 du combinateur de récursivité:

function rec(f){ 
    return function(){
        return f.apply(this,[
                               function(){
                                  return rec(f).apply(this,arguments);
                                }
                            ].concat(Array.prototype.slice.call(arguments))
                      );
    }
}
14
Ortomala Lokni

Il semble que vous puissiez affecter des fonctions fléchées à une variable et l'utiliser pour appeler la fonction de manière récursive.

var complex = (a, b) => {
    if (a > b) {
        return a;
    } else {
        complex(a, b);
    }
};
9
user405398

Utilisez une variable à laquelle vous affectez la fonction, par exemple.

const fac = (n) => n>0 ? n*fac(n-1) : 1;

Si vous en avez vraiment besoin, utilisez le Y Combinator , comme ceci:

const Y = (f) => ((x)=>f((v)=>x(x)(v)))((x)=>f((v)=>x(x)(v)))
… Y((fac)=>(n)=> n>0 ? n*fac(n-1) : 1) …

( moche, n'est-ce pas? )

6
Bergi

Un combinateur à usage général pour les définitions de fonctions récursives d'un nombre quelconque d'arguments (sans utiliser la variable à l'intérieur de lui-même) serait:

const rec = (le => ((f => f(f))(f => (le((...x) => f(f)(...x))))));

Ceci pourrait être utilisé par exemple pour définir factorial:

const factorial = rec( fact => (n => n < 2 ? 1 : n * fact(n - 1)) );
//factorial(5): 120

ou chaîne inverse:

const reverse = rec(
  rev => (
    (w, start) => typeof(start) === "string" 
                ? (!w ? start : rev(w.substring(1), w[0] + start)) 
                : rev(w, '')
  )
);
//reverse("olleh"): "hello"

ou traversée d'arbre en ordre:

const inorder = rec(go => ((node, visit) => !!(node && [go(node.left, visit), visit(node), go(node.right, visit)])));

//inorder({left:{value:3},value:4,right:{value:5}}, function(n) {console.log(n.value)})
// calls console.log(3)
// calls console.log(4)
// calls console.log(5)
// returns true
3
Bill Barry

Étant donné que arguments.callee est une mauvaise option en raison de la dépréciation/ne fonctionne pas en mode strict, et que faire quelque chose comme var func = () => {} est également mauvais, cette astuce comme celle décrite dans cette réponse est probablement votre seule option:

javascript: fonction anonyme récursive?

2
Jonathan Lerner
var rec = () => {rec()};
rec();

Serait-ce une option? 

1
philipp

Vous pouvez assigner votre fonction à une variable dans une vie

var countdown = f=>(f=a=>{
  console.log(a)
  if(a>0) f(--a)
})()

countdown(3)

//3
//2
//1
//0
0
william malo

J'ai trouvé les solutions fournies très compliquées et, honnêtement, je ne les comprenais pas. J'ai donc imaginé moi-même une solution plus simple (je suis sûr que c'est déjà connu, mais voici ma procédure de réflexion):

Donc, vous faites une fonction factorielle

x => x < 2 ? x : x * (???)

le (???) est l'endroit où la fonction est supposée s'appeler elle-même, mais comme vous ne pouvez pas le nommer, la solution évidente est de le passer comme argument à lui-même

f => x => x < 2 ? x : x * f(x-1)

Cela ne fonctionnera pas si. parce que lorsque nous appelons f(x-1), nous appelons cette fonction elle-même et nous définissons simplement ses arguments comme suit: 1) f: la fonction elle-même, à nouveau et 2) x la valeur. Eh bien, nous avons la fonction elle-même, f rappelez-vous? alors passez-le d'abord:

f => x => x < 2 ? x : x * f(f)(x-1)
                            ^ the new bit

Et c'est tout. Nous venons de créer une fonction qui prend pour premier argument la fonction factorielle! Juste passer littéralement à lui-même:

(f => x => x < 2 ? x : x * f(f)(x-1))(f => x => x < 2 ? x : x * f(f)(x-1))(5)
>120

Au lieu de l'écrire deux fois, vous pouvez créer une autre fonction qui passe son argument à elle-même:

y => y(y)

et passez votre fonction factorielle à celle-ci:

(y => y(y))(f => x => x < 2 ? x : x * f(f)(x-1))(5)
>120

Boom. Voici une petite formule:

(y => y(y))(f => x => endCondition(x) ? default(x) : operation(x)(f(f)(NeXTSTEP(x))))

Pour une fonction de base qui ajoute des nombres de 0 à x, endCondition est le moment où vous devez arrêter de se répéter, donc x => x == 0. default est la dernière valeur que vous donnez une fois que endCondition est rencontré, donc x => x. operation est simplement l'opération que vous effectuez à chaque récursivité, comme multiplier dans Factorial ou ajouter dans Fibonacci: x1 => x2 => x1 + x2. et enfin NeXTSTEP est la prochaine valeur à transmettre à la fonction, qui correspond généralement à la valeur actuelle moins un: x => x - 1. Appliquer:

(y => y(y))(f => x => x == 0 ? x : x + f(f)(x - 1))(5)
>15
0
H. Saleh

Ceci est une version de cette réponse, https://stackoverflow.com/a/3903334/689223 , avec des fonctions de flèche.

Vous pouvez utiliser le combinateur U ou Y. Y Combinator étant le plus simple à utiliser.

U combinator, avec ceci vous devez continuer à transmettre la fonction: const U = f => f(f) U(selfFn => arg => selfFn(selfFn)('to infinity and beyond'))

Y combinator, avec cela vous n'avez pas à continuer à passer la fonction: const Y = gen => U(f => gen((...args) => f(f)(...args))) Y(selfFn => arg => selfFn('to infinity and beyond'))

0
Ricardo Freitas