javascript: fonction anonyme récursive?

120

Disons que j'ai une fonction récursive de base:

function recur(data) {
    data = data+1;
    var nothing = function() {
        recur(data);
    }
    nothing();
}

Comment pourrais-je faire cela si j'ai une fonction anonyme telle que ...

(function(data){
    data = data+1;
    var nothing = function() {
        //Something here that calls the function?
    }
    nothing();
})();

J'aimerais un moyen d'appeler la fonction qui a appelé cette fonction ... J'ai vu des scripts quelque part (je ne me souviens pas où) qui peuvent vous dire le nom d'une fonction appelée, mais je ne me souviens d'aucun des cette information dès maintenant.

Incognito
la source
Y a-t-il une raison pour laquelle vous en avez besoin ou êtes-vous simplement curieux? Il me semble qu'il serait plus clair de lui donner simplement un nom ...
rfunduk
1
@thenduks: Pour la même raison que l'on utiliserait une fonction anonyme. Juste que parfois la récursivité est nécessaire.
poke
5
C'est dommage qu'il arguments.calleeexiste, et cette fonction ne fait rien d'utile. Je regardais Y Combinator :P . Merde, ce truc ne sera jamais utile ...
Kobi
1
Oui, comme Kobi est lié, utilisez un combinateur à virgule fixe tel que Y pour faire des fonctions récursives anonymes sans arguments.
bateau à vapeur25
1
Voir w3future.com/weblog/stories/2002/02/22/… pour un exemple du combinateur Y dans JS.
bateau à vapeur25

Réponses:

145

Vous pouvez donner un nom à la fonction, même lorsque vous créez la fonction en tant que valeur et non en tant qu'instruction de "déclaration de fonction". En d'autres termes:

(function foo() { foo(); })();

est une fonction récursive époustouflante. Cela dit, vous ne voudrez probablement pas faire cela en général, car il y a des problèmes étranges avec diverses implémentations de Javascript. ( Remarque - c'est un commentaire assez ancien; certains / nombreux / tous les problèmes décrits dans l'article de blog de Kangax peuvent être résolus dans des navigateurs plus modernes.)

Lorsque vous donnez un nom comme celui-là, le nom n'est pas visible en dehors de la fonction (enfin, il n'est pas censé l'être; c'est l'une des bizarreries). C'est comme "letrec" en Lisp.

Quant à cela arguments.callee, c'est interdit en mode "strict" et est généralement considéré comme une mauvaise chose, car cela rend certaines optimisations difficiles. C'est aussi beaucoup plus lent qu'on pourrait s'y attendre.

edit - Si vous voulez avoir l'effet d'une fonction "anonyme" qui peut s'appeler elle-même, vous pouvez faire quelque chose comme ça (en supposant que vous passez la fonction comme un rappel ou quelque chose comme ça):

asyncThingWithCallback(params, (function() {
  function recursive() {
    if (timeToStop())
      return whatever();
    recursive(moreWork);
  }
  return recursive;
})());

Cela définit une fonction avec une instruction de déclaration de fonction agréable, sûre et non interrompue dans IE , créant une fonction locale dont le nom ne polluera pas l'espace de noms global. La fonction wrapper (vraiment anonyme) renvoie simplement cette fonction locale.

Pointu
la source
Pouvons-nous éviter de polluer l'espace de noms global d'une autre manière avec ES5 sctrict (je n'ai pas encore lu profondément dans ES5)?
Incognito
@pointy pouvez-vous s'il vous plaît regarder cette quête. stackoverflow.com/questions/27473450/…
Gladson Robinson
Je suppose qu'il n'est pas possible de l'utiliser (() => { call_recursively_self_here() })()et de s'appeler récursivement, non? Je dois lui donner un nom.
Qwerty
1
@Qwerty, vous pourriez faire quelque chose comme le dernier exemple de ma réponse. Liez la fonction flèche à une variable locale dans une fonction wrapper afin que votre fonction flèche puisse se référer à elle-même avec le nom de la variable. L'encapsuleur renverrait alors la variable (qui fait référence à la fonction de flèche).
Pointy
1
@Pointy peut-être que certains hackers trouveront une application;)
Kamil Kiełczewski
31

Les gens ont parlé du combinateur Y dans les commentaires, mais personne ne l'a écrit comme réponse.

Le combinateur Y peut être défini en javascript comme suit: (merci à steamer25 pour le lien)

var Y = function (gen) {
  return (function(f) {
    return f(f);
  }(function(f) {
    return gen(function() {
      return f(f).apply(null, arguments);
    });
  }));
}

Et lorsque vous souhaitez passer votre fonction anonyme:

(Y(function(recur) {
  return function(data) {
    data = data+1;
    var nothing = function() {
      recur(data);
    }
    nothing();
  }
})());

La chose la plus importante à noter à propos de cette solution est que vous ne devez pas l'utiliser.

zem
la source
16
"La chose la plus importante à noter à propos de cette solution est que vous ne devez pas l'utiliser." Pourquoi?
nyuszika7h
7
Ce ne sera pas rapide. C'est moche à utiliser (bien que conceptuellement beau!). Vous évitez d'avoir à donner à votre fonction un nom de balise ou de variable (et je ne vois pas pourquoi cela serait un problème), mais vous lui donnez quand même un nom comme paramètre de la fonction externe passée à Y. gagner quoi que ce soit en traversant tous ces problèmes.
zem
N'oubliez pas de mentionner que cette fonction n'est pas sûre pour la pile. Une boucle seulement quelques milliers de fois entraînera un débordement de pile.
Merci
Salut, je proposerais une modification légèrement "plus propre" puisque .apply (null, arguments) me semble moche: var Y = function (gen) {return (function (f) {return f (f);} (function (f) {return gen (function (x) {return f (f) (x);});})); } Ou de manière équivalente ((function (x) {return y} equals (x => y))) en utilisant la notation fléchée (code js valide): var Y = gen => (f => f (f)) (f = > gen (x => f (f) (x)))
myfirstAnswer
23

Combinateur U

En se passant une fonction à elle-même comme argument, une fonction peut se reproduire en utilisant son paramètre au lieu de son nom! Ainsi, la fonction donnée à Udoit avoir au moins un paramètre qui se liera à la fonction (elle-même).

Dans l'exemple ci-dessous, nous n'avons pas de condition de sortie, nous allons donc simplement boucler indéfiniment jusqu'à ce qu'un débordement de pile se produise

const U = f => f (f) // call function f with itself as an argument

U (f => (console.log ('stack overflow imminent!'), U (f)))

Nous pouvons arrêter la récursion infinie en utilisant une variété de techniques. Ici, j'écrirai notre fonction anonyme pour renvoyer une autre fonction anonyme qui attend une entrée; dans ce cas, un certain nombre. Lorsqu'un nombre est fourni, s'il est supérieur à 0, nous continuerons de nous répéter, sinon nous retournerons 0.

const log = x => (console.log (x), x)

const U = f => f (f)

// when our function is applied to itself, we get the inner function back
U (f => x => x > 0 ? U (f) (log (x - 1)) : 0)
// returns: (x => x > 0 ? U (f) (log (x - 1)) : 0)
// where f is a reference to our outer function

// watch when we apply an argument to this function, eg 5
U (f => x => x > 0 ? U (f) (log (x - 1)) : 0) (5)
// 4 3 2 1 0

Ce qui n'est pas immédiatement apparent ici, c'est que notre fonction, lorsqu'elle est appliquée pour la première fois à elle-même à l'aide du Ucombinateur, renvoie une fonction en attente de la première entrée. Si nous avons donné un nom à cela, peut effectivement construire des fonctions récursives en utilisant des lambdas (fonctions anonymes)

const log = x => (console.log (x), x)

const U = f => f (f)

const countDown = U (f => x => x > 0 ? U (f) (log (x - 1)) : 0)

countDown (5)
// 4 3 2 1 0

countDown (3)
// 2 1 0

Seulement ce n'est pas une récursivité directe - une fonction qui s'appelle elle-même en utilisant son propre nom. Notre définition de countDownne se référence pas à l'intérieur de son corps et toujours la récursivité est possible

// direct recursion references itself by name
const loop = (params) => {
  if (condition)
    return someValue
  else
    // loop references itself to recur...
    return loop (adjustedParams)
}

// U combinator does not need a named reference
// no reference to `countDown` inside countDown's definition
const countDown = U (f => x => x > 0 ? U (f) (log (x - 1)) : 0)

Comment supprimer l'auto-référence d'une fonction existante à l'aide du combinateur U

Ici, je vais vous montrer comment prendre une fonction récursive qui utilise une référence à elle-même et la changer en une fonction qui utilise le combinateur U à la place de l'auto-référence

const factorial = x =>
  x === 0 ? 1 : x * factorial (x - 1)
  
console.log (factorial (5)) // 120

Maintenant, en utilisant le combinateur U pour remplacer la référence interne à factorial

const U = f => f (f)

const factorial = U (f => x =>
  x === 0 ? 1 : x * U (f) (x - 1))

console.log (factorial (5)) // 120

Le modèle de remplacement de base est le suivant. Notez mentalement, nous utiliserons une stratégie similaire dans la section suivante

// self reference recursion
const foo =         x => ...   foo (nextX) ...

// remove self reference with U combinator
const foo = U (f => x => ... U (f) (nextX) ...)

Combinateur Y

en relation: les combinateurs U et Y expliqués en utilisant une analogie de miroir

Dans la section précédente, nous avons vu comment transformer la récursivité d'auto-référence en une fonction récursive qui ne repose pas sur une fonction nommée à l'aide du combinateur U. Il y a un peu de gêne à ne pas oublier de toujours se passer la fonction comme premier argument. Eh bien, le combinateur Y s'appuie sur le combinateur U et supprime ce bit fastidieux. C'est une bonne chose car supprimer / réduire la complexité est la principale raison pour laquelle nous créons des fonctions

Tout d'abord, dérivons notre propre combinateur Y

// standard definition
const Y = f => f (Y (f))

// prevent immediate infinite recursion in applicative order language (JS)
const Y = f => f (x => Y (f) (x))

// remove reference to self using U combinator
const Y = U (h => f => f (x => U (h) (f) (x)))

Nous allons maintenant voir comment son utilisation se compare au combinateur U. Remarquez, pour se reproduire, au lieu de U (f)nous pouvons simplement appelerf ()

const U = f => f (f)

const Y = U (h => f => f (x => U (h) (f) (x)))

Y (f => (console.log ('stack overflow imminent!'),  f ()))

Maintenant, je vais vous montrer le countDownprogramme en utilisant Y- vous verrez que les programmes sont presque identiques mais le combinateur Y garde les choses un peu plus propres

const log = x => (console.log (x), x)

const U = f => f (f)

const Y = U (h => f => f (x => U (h) (f) (x)))

const countDown = Y (f => x => x > 0 ? f (log (x - 1)) : 0)

countDown (5)
// 4 3 2 1 0

countDown (3)
// 2 1 0

Et maintenant nous verrons factorialaussi

const U = f => f (f)

const Y = U (h => f => f (x => U (h) (f) (x)))

const factorial = Y (f => x =>
  x === 0 ? 1 :  x * f (x - 1))

console.log (factorial (5)) // 120

Comme vous pouvez le voir, fdevient le mécanisme de récursivité lui-même. Pour se reproduire, nous l'appelons comme une fonction ordinaire. Nous pouvons l'appeler plusieurs fois avec des arguments différents et le résultat sera toujours correct. Et comme c'est un paramètre de fonction ordinaire, nous pouvons le nommer comme nous voulons, comme recurci-dessous -

const U = f => f (f)

const Y = U (h => f => f (x => U (h) (f) (x)))

const fibonacci = Y (recur => n =>
  n < 2 ? n : recur (n - 1) +  (n - 2))

console.log (fibonacci (10)) // 55


Combinateur U et Y avec plus d'un paramètre

Dans les exemples ci-dessus, nous avons vu comment nous pouvons boucler et passer un argument pour garder une trace de "l'état" de notre calcul. Mais que se passe-t-il si nous devons suivre l'état supplémentaire?

Nous pourrions utiliser des données composées comme un tableau ou quelque chose comme ça ...

const U = f => f (f)

const Y = U (h => f => f (x => U (h) (f) (x)))

const fibonacci = Y (f => ([a, b, x]) =>
  x === 0 ? a : f ([b, a + b, x - 1]))

// starting with 0 and 1, generate the 7th number in the sequence
console.log (fibonacci ([0, 1, 7])) 
// 0 1 1 2 3 5 8 13

Mais c'est mauvais car cela expose l'état interne (compteurs aet b). Ce serait bien si nous pouvions simplement appeler fibonacci (7)pour obtenir la réponse que nous voulons.

En utilisant ce que nous savons sur les fonctions curry (séquences de fonctions unaires (1 paramètre)), nous pouvons atteindre notre objectif facilement sans avoir à modifier notre définition You à nous fier aux données composées ou aux fonctionnalités avancées du langage.

Regardez la définition de fibonacciprès ci-dessous. Nous appliquons immédiatement 0et 1qui sont liés à aet brespectivement. Maintenant fibonacci attend simplement que le dernier argument soit fourni auquel sera lié x. Lorsque nous récurons, nous devons appeler f (a) (b) (x)(non f (a,b,x)) car notre fonction est sous forme curry.

const U = f => f (f)

const Y = U (h => f => f (x => U (h) (f) (x)))

const fibonacci = Y (f => a => b => x =>
  x === 0 ? a : f (b) (a + b) (x - 1)) (0) (1)

console.log (fibonacci (7)) 
// 0 1 1 2 3 5 8 13


Ce type de modèle peut être utile pour définir toutes sortes de fonctions. Ci - dessous , nous verrons deux fonctions définies en utilisant le Ycombinateur ( rangeet reduce) et un dérivé de reduce, map.

const U = f => f (f)

const Y = U (h => f => f (x => U (h) (f) (x)))

const range = Y (f => acc => min => max =>
  min > max ? acc : f ([...acc, min]) (min + 1) (max)) ([])

const reduce = Y (f => g => y => ([x,...xs]) =>
  x === undefined ? y : f (g) (g (y) (x)) (xs))
  
const map = f =>
  reduce (ys => x => [...ys, f (x)]) ([])
  
const add = x => y => x + y

const sq = x => x * x

console.log (range (-2) (2))
// [ -2, -1, 0, 1, 2 ]

console.log (reduce (add) (0) ([1,2,3,4]))
// 10

console.log (map (sq) ([1,2,3,4]))
// [ 1, 4, 9, 16 ]


C'EST TOUT ANONYME OMG

Comme nous travaillons ici avec des fonctions pures, nous pouvons substituer n'importe quelle fonction nommée à sa définition. Regardez ce qui se passe lorsque nous prenons fibonacci et remplaçons les fonctions nommées par leurs expressions

/* const U = f => f (f)
 *
 * const Y = U (h => f => f (x => U (h) (f) (x)))
 *
 * const fibonacci = Y (f => a => b => x => x === 0 ? a : f (b) (a + b) (x - 1)) (0) (1)
 *
 */

/*
 * given fibonacci (7)
 *
 * replace fibonacci with its definition
 * Y (f => a => b => x => x === 0 ? a : f (b) (a + b) (x - 1)) (0) (1) (7)
 *
 * replace Y with its definition
 * U (h => f => f (x => U (h) (f) (x))) (f => a => b => x => x === 0 ? a : f (b) (a + b) (x - 1)) (0) (1) (7)
//
 * replace U with its definition
 * (f => f (f)) U (h => f => f (x => U (h) (f) (x))) (f => a => b => x => x === 0 ? a : f (b) (a + b) (x - 1)) (0) (1) (7)
 */

let result =
  (f => f (f)) (h => f => f (x => h (h) (f) (x))) (f => a => b => x => x === 0 ? a : f (b) (a + b) (x - 1)) (0) (1) (7)
  
console.log (result) // 13

Et là vous l'avez - fibonacci (7)calculé récursivement en utilisant rien d'autre que des fonctions anonymes

Je vous remercie
la source
14

Il peut être plus simple d'utiliser à la place un "objet anonyme":

({
  do: function() {
    console.log("don't run this ...");
    this.do();
  }
}).do();

Votre espace global est totalement non pollué. C'est assez simple. Et vous pouvez facilement profiter de l'état non global de l'objet.

Vous pouvez également utiliser des méthodes d'objet ES6 pour rendre la syntaxe plus concise.

({
  do() {
    console.log("don't run this ...");
    this.do();
  }
}).do();
Svidgen
la source
13

Je ne ferais pas cela comme une fonction en ligne. Cela va à l'encontre des limites du bon goût et ne vous apporte vraiment rien.

Si vous devez vraiment, il y a arguments.calleecomme dans la réponse de Fabrizio. Cependant, cela est généralement considéré comme déconseillé et n'est pas autorisé dans le «mode strict» d'ECMAScript Fifth Edition. Bien que l'ECMA 3 et le mode non strict ne disparaissent pas, le travail en mode strict promet davantage d'optimisations de langage possibles.

On peut également utiliser une fonction en ligne nommée:

(function foo(data){
    data++;
    var nothing = function() {
        foo(data);
    }
    nothing();
})();

Cependant, les expressions de fonction en ligne nommées sont également mieux évitées, car JScript d'IE leur fait de mauvaises choses. Dans l'exemple ci-dessus, foopollue incorrectement la portée du parent dans IE, et le parent fooest une instance distincte de la foovue à l'intérieur foo.

Quel est le but de mettre cela dans une fonction anonyme en ligne? Si vous voulez juste éviter de polluer la portée parent, vous pouvez bien sûr cacher votre premier exemple dans une autre fonction auto-appelante anonyme (espace de noms). Avez-vous vraiment besoin de créer une nouvelle copie de nothingchaque fois autour de la récursivité? Vous pourriez être mieux avec un espace de noms contenant deux simples fonctions réciproquement récursives.

bobince
la source
Je suis d'accord, une fonction nommée convient mieux que arguments.callee non seulement pour le mode strict ecmascript, mais aussi pour une question d'optimisation car à chaque récursion il a besoin d'obtenir une référence à l'appelé (et cela pourrait probablement réduire la vitesse d'exécution )
+1 pour le poétique, "pushing against the boundaries of good taste"- (enfin, et la bonne info).
Peter Ajtai
qu'en est-il d'un simple pré / postfix si la pollution est vraiment un problème ici? Considérant que ce n'est pas dans la portée globale (même si la fonction est de niveau supérieur, il devrait déjà avoir une fonction anonyme enveloppant tout son code), il est vraiment peu probable qu'un nom comme recur_fooentre en collision avec une fonction dans la portée parente (ou soit malade -utilisé) .
gblazex
Très intéressant - jsfiddle.net/hck2A - IE pollue le parent dans ce cas, comme vous l'avez dit. Jamais réalisé cela.
Peter Ajtai
1
@Peter: kangax.github.com/nfe (en particulier 'bugs JScript') pour plus que vous n'avez jamais voulu en savoir sur ce sujet. Il est finalement corrigé dans IE9 (mais uniquement en mode Normes IE9).
bobince
10
(function(data){
    var recursive = arguments.callee;
    data = data+1;
    var nothing = function() {
        recursive(data)
    }
    nothing();
})();

la source
34
J'espère que tout le monde votant pour cette réponse (techniquement correcte) se rend compte des problèmes avec arguments.callee: elle est interdite en mode strict et dans ES5.
Pointy
Voté contre, arguments.callee est obsolète dans ES5
Jaime Rodriguez
Cela fonctionne dans NodeJS. Je me fiche d'ES5 tant qu'il fonctionne de manière prévisible dans un environnement fixe.
Angad
1
C'est une bombe à retardement. Il n’existe pas d’environnement «fixe», comme le suggère le commentaire ci-dessus. Vous procéderiez presque toujours à une mise à niveau pour l'une des milliers de raisons de le faire.
sampathsris
6

Vous pouvez faire quelque chose comme:

(foo = function() { foo(); })()

ou dans votre cas:

(recur = function(data){
    data = data+1;
    var nothing = function() {
        if (data > 100) return; // put recursion limit
        recur(data);
    }
    nothing();
})(/* put data init value here */ 0);
ArtBIT
la source
Vous pourriez faire avec une déclaration d' recurabord avec une varinstruction. Je ne sais pas si cela enfreint les règles de la question, mais comme vous l'avez maintenant, sans la vardéclaration, vous obtiendrez une erreur en mode strict d'ECMAScript 5.
Tim Down
Mon commentaire initial incluait le varmot - clé, mais une fois que j'ai testé ce code, il lançait des erreurs, car vous ne pouvez pas vraiment déclarer une variable à l'intérieur d'un bloc auto-appelant, et mon approche repose sur la déclaration automatique d'une variable non définie, et donc @ Pointy la solution est plus correcte. Mais j'ai quand même voté pour la réponse de Fabrizio Calderan;)
ArtBIT
Oui, cela (var recur = function() {...})();ne fonctionnera pas car c'est maintenant une instruction plutôt qu'une expression d'affectation (qui renvoie la valeur attribuée). Je suggérais var recur; (recur = function() {...})();plutôt.
Tim Down
3

Lorsque vous déclarez une fonction anonyme comme celle-ci:

(function () {
    // Pass
}());

Il est considéré comme une expression de fonction et il a un nom facultatif (que vous pouvez utiliser pour l'appeler de l'intérieur. Mais comme il s'agit d'une expression de fonction (et non d'une instruction), il reste anonyme (mais a un nom que vous pouvez appeler). cette fonction peut s'appeler:

(function foo () {
    foo();
}());
foo //-> undefined
xj9
la source
"ça reste anonyme" - non ça ne l'est pas. Une fonction anonyme n'a pas de nom. Je comprends que cela foone soit pas déclaré dans le contexte actuel, mais c'est plus ou moins hors de propos. Une fonction avec un nom est toujours une fonction nommée - pas anonyme.
Merci
3

Pourquoi ne pas passer la fonction à la functio elle-même?

    var functionCaller = function(thisCaller, data) {
        data = data + 1;
        var nothing = function() {
            thisCaller(thisCaller, data);
        };
        nothing();
    };
    functionCaller(functionCaller, data);
Riccardo Bassilichi
la source
3

Dans certaines situations, vous devez vous fier à des fonctions anonymes. Donnée est une mapfonction récursive :

const map = f => acc => ([head, ...tail]) => head === undefined 
 ? acc
 : map (f) ([...acc, f(head)]) (tail);

const sqr = x => x * x;
const xs = [1,2,3,4,5];

console.log(map(sqr) ([0]) (xs)); // [0] modifies the structure of the array

Veuillez noter que mapne doit pas modifier la structure du tableau. L'accumulateur accn'a donc pas besoin d'être exposé. Nous pouvons encapsuler mapdans une autre fonction par exemple:

const map = f => xs => {
  let next = acc => ([head, ...tail]) => head === undefined
   ? acc
   : map ([...acc, f(head)]) (tail);

  return next([])(xs);
}

Mais cette solution est assez verbeuse. Utilisons le Ucombinateur sous-estimé :

const U = f => f(f);

const map = f => U(h => acc => ([head, ...tail]) => head === undefined 
 ? acc
 : h(h)([...acc, f(head)])(tail))([]);

const sqr = x => x * x;
const xs = [1,2,3,4,5];

console.log(map(sqr) (xs));

Concis, n'est-ce pas? Uest très simple mais présente l'inconvénient que l'appel récursif est un peu obscurci: sum(...)devient h(h)(...)- c'est tout.


la source
2

Je ne sais pas si la réponse est toujours requise, mais cela peut également être fait à l'aide de délégués créés à l'aide de function.bind:

    var x = ((function () {
        return this.bind(this, arguments[0])();
    }).bind(function (n) {
        if (n != 1) {
            return n * this.bind(this, (n - 1))();
        }
        else {
            return 1;
        }
    }))(5);

    console.log(x);

Cela n'implique ni fonctions nommées ni arguments.callee.

Nitij
la source
1

Comme Bobince l'a écrit, nommez simplement votre fonction.

Mais, je suppose que vous voulez également passer une valeur initiale et arrêter votre fonction finalement!

var initialValue = ...

(function recurse(data){
    data++;
    var nothing = function() {
        recurse(data);
    }
    if ( ... stop condition ... )
        { ... display result, etc. ... }
    else
        nothing();
}(initialValue));

exemple de travail de jsFiddle (utilise des données + = des données pour le plaisir)


Peter Ajtai
la source
1
+1, C'est une réponse très utile et vous devriez obtenir plus de votes positifs, mais ce n'est pas anonyme.
Incognito
vous ne l' avez pas lu clairement ce que bobince a écrit: However named inline function expressions are also best avoided.. Mais l'OP manque le point aussi ... :)
gblazex
@Galamb - Je l'ai lu. Interdit en mode strict et dans ES5 n'est pas la même chose que de polluer une portée parent et de créer des instances supplémentaires.
Peter Ajtai du
1

J'avais besoin (ou plutôt, je voulais) une fonction anonyme à une ligne pour remonter un objet en construisant une chaîne, et je l'ai traitée comme ceci:

var cmTitle = 'Root' + (function cmCatRecurse(cmCat){return (cmCat == root) ? '' : cmCatRecurse(cmCat.parent) + ' : ' + cmCat.getDisplayName();})(cmCurrentCat);

qui produit une chaîne comme 'Root: foo: bar: baz: ...'

radio_babylon
la source
1

Avec ES2015, nous pouvons jouer un peu avec la syntaxe et abuser des paramètres par défaut et des thunks. Ces derniers ne sont que des fonctions sans aucun argument:

const applyT = thunk => thunk();

const fib = n => applyT(
  (f = (x, y, n) => n === 0 ? x : f(y, x + y, n - 1)) => f(0, 1, n)
);

console.log(fib(10)); // 55

// Fibonacci sequence: 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55...

Veuillez noter qu'il fs'agit d'un paramètre avec la fonction anonyme (x, y, n) => n === 0 ? x : f(y, x + y, n - 1)comme valeur par défaut. Quand fest invoqué par applyTcet appel doit avoir lieu sans arguments, de sorte que la valeur par défaut soit utilisée. La valeur par défaut est une fonction et donc fune fonction nommée, qui peut s'appeler elle-même de manière récursive.


la source
0

Une autre réponse qui n'implique pas de fonction ou d'arguments nommés.

var sum = (function(foo,n){
  return n + foo(foo,n-1);
})(function(foo,n){
     if(n>1){
         return n + foo(foo,n-1)
     }else{
         return n;
     }
},5); //function takes two argument one is function and another is 5

console.log(sum) //output : 15
jforjs
la source
nice: liez une fonction anonyme à un paramètre local puis appelez la fonction via le paramètre local, mais passez également la fonction à elle-même pour la récursivité.
englebart
0

Il s'agit d'une refonte de la réponse jforjs avec des noms différents et une entrée légèrement modifiée.

// function takes two argument: first is recursive function and second is input
var sum = (function(capturedRecurser,n){
  return capturedRecurser(capturedRecurser, n);
})(function(thisFunction,n){
     if(n>1){
         return n + thisFunction(thisFunction,n-1)
     }else{
         return n;
     }
},5); 

console.log(sum) //output : 15

Il n'était pas nécessaire de dérouler la première récursivité. La fonction se recevant comme référence renvoie au suintement primordial de la POO.

englebart
la source
0

Ceci est une version de la réponse de @ zem avec des fonctions fléchées.

Vous pouvez utiliser le Uou le Ycombinateur. Le combinateur Y étant le plus simple à utiliser.

U combinateur, avec cela, vous devez continuer à passer la fonction: const U = f => f(f) U(selfFn => arg => selfFn(selfFn)('to infinity and beyond'))

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

Ricardo Freitas
la source
0

Encore une autre solution de combinateur Y, utilisant un lien rosetta-code (je pense que quelqu'un a déjà mentionné le lien quelque part sur stackOverflow.

Les flèches sont pour les fonctions anonymes plus lisibles pour moi:

var Y = f => (x => x(x))(y => f(x => y(y)(x)));
ma première réponse
la source
-1

Cela peut ne pas fonctionner partout, mais vous pouvez utiliser arguments.calleepour faire référence à la fonction actuelle.

Ainsi, factorielle pourrait se faire ainsi:

var fac = function(x) { 
    if (x == 1) return x;
    else return x * arguments.callee(x-1);
}
Dan Jones
la source