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?
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))
);
}
}
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);
}
};
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) …
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
É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:
var rec = () => {rec()};
rec();
Serait-ce une option?
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
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
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'))