javascript: fonction anonyme récursive?

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 si j'ai une fonction anonyme comme...

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

je voudrais une façon 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 pas de cette information pour le moment.

103
demandé sur Incognito 2010-10-07 20:34:32

18 réponses

vous can donnez un nom à la fonction, même si vous créez la fonction en tant que valeur et non pas une instruction" déclaration de fonction". En d'autres termes:

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

est une fonction récursive de soufflage de cheminée. Maintenant, cela dit, vous probablement ne pas peut ne pas vouloir faire ce en général parce qu'il ya quelques problèmes bizarres avec diverses implémentations de Javascript. ( note - c'est un commentaire assez ancien; certains/beaucoup/tous les problèmes décrits dans le blog de Kangax peut être corrigé dans les navigateurs plus modernes.)

quand vous donnez un nom comme ça, le nom n'est pas visible en dehors de la fonction (bien, ce n'est pas supposé l'être; c'est une des bizarreries). C'est comme" letrec " dans Lisp.

comme pour arguments.callee , qui est refusé en mode "strict" et est généralement considéré comme une mauvaise chose, parce qu'il fait certains optimisations dur. Il est aussi beaucoup plus lent que l'on pourrait attendre.

edit - si vous voulez avoir l'effet d'une fonction" anonyme "qui peut s'appeler, vous pouvez faire quelque chose comme ceci (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;
})());

ce que cela fait est de définir une fonction avec une fonction agréable, sûre, non-broken-in-IE déclaration déclaration, la création d'un fonction locale dont le nom ne polluera pas l'espace de noms global. La fonction wrapper (truly anonymous) renvoie simplement cette fonction locale.

122
répondu Pointy 2014-11-27 14:27:07

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 quand vous voulez 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 sur cette solution est que vous ne devriez pas l'utiliser.

30
répondu zem 2010-10-11 03:21:33

je ne ferais pas cela comme une fonction inline. Ça va à l'encontre des limites du bon goût et ça ne te rapporte rien.

si vous devez vraiment, il y a arguments.callee comme dans la réponse de Fabrizio. Cependant, cela est généralement considéré comme déconseillé et est rejeté dans la cinquième édition d'ECMAScript "mode strict". Bien QU'ECMA 3 et non-strict-mode ne s'en aillent pas, travailler en mode strict promet des optimisations linguistiques plus possibles.

on peut aussi utiliser une fonction nommée inline:

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

cependant les expressions de fonctions inline nommées sont aussi mieux évitées, car le JScript D'IE leur fait du mal. Dans l'exemple ci-dessus, foo pollue incorrectement le champ parent dans IE, et le parent foo est une instance séparée du foo vu à l'intérieur de foo .

Quel est le but de mettre ceci dans une fonction anonyme en ligne? Si vous voulez juste pour éviter de polluer le champ parent, vous pouvez bien sûr cacher votre premier exemple dans une autre fonction-anonymous-calling (namespace). Avez-vous vraiment besoin de créer une nouvelle copie de nothing chaque fois autour de la récursivité? Vous pourriez être mieux avec un namespace contenant deux fonctions mutuellement récursives simples.

13
répondu bobince 2010-10-07 16:55:38

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

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

votre espace global est complètement non pollué. C'est assez simple. Et vous pouvez facilement profiter de l'objet et non de l'état global.

12
répondu svidgen 2014-02-25 22:35:22
(function(data){
    var recursive = arguments.callee;
    data = data+1;
    var nothing = function() {
        recursive(data)
    }
    nothing();
})();
11
répondu 2010-10-07 16:37:53

U combinator

le combinateur U prend une fonction et l'applique à lui-même. Ainsi, la fonction que vous lui donnez devrait 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, Donc nous allons boucler indéfiniment jusqu'à ce qu'un débordement de pile se produise

const U = f => f (f)

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

nous pouvons arrêter l'infini récursion utilisant une variété de techniques. Ici, je vais écrire notre fonction anonyme pour retourner une autre fonction anonyme qui attend une entrée; dans ce cas, un certain nombre. Quand un nombre est fourni, si elle est supérieure à 0, nous continuerons récurrents, sinon retourne 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 évident ici, c'est que notre fonction, lorsqu'elle a été appliquée pour la première fois à elle-même en utilisant le U combinator, il renvoie une fonction qui attend la première entrée. Si nous avons donné un nom à ceci, peut effectivement construire des fonctions récursives en utilisant 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 direct récursion – une fonction qui s'appelle elle-même en utilisant son propre nom. Notre définition de countDown ne se réfère pas à l'intérieur de son corps et encore récursion 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'autoréférence d'une fonction existante en utilisant U combinator

ici je vais vous montrer comment prendre une fonction récursive qui utilise une référence à elle-même et la changer à une fonction qui emploie 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

utilisant maintenant le combinateur en 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 schéma de remplacement de base est celui-ci. Faire une note mentale, nous allons utiliser 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) ...)

Y combinator

lié: les combinateurs U et Y expliqué en utilisant un miroir analogie

dans la section précédente, nous avons vu comment transformer la récursion de référence en une fonction récursive qui ne repose pas sur une fonction nommée à l'aide du combinateur en U. Il ya un peu d'un ennui tho avec devoir se rappeler de toujours passer la fonction à lui-même comme le premier argument. Eh bien, le combinateur en Y s'appuie sur le combinateur en U et supprime cette partie fastidieuse. C'est une bonne chose parce que supprimer/réduire la complexité est la raison principale pour laquelle nous faisons des fonctions

tout d'abord, dérivons notre propre Y-combinator

// 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)))

maintenant, nous allons voir comment son usage se compare au U-combinator. Remarque, pour revenir, au lieu de U (f) nous pouvons simplement appeler f ()

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 programme countDown 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 allons voir factorial aussi bien

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

U et Y combinator avec plus de 1 paramètre

dans les exemples ci-dessus, nous avons vu comment nous pouvons boucler et passer un argument pour garder la trace de l '"état" de notre calcul. Mais que faire si nous avons besoin de garder une trace de l'état supplémentaire?

Nous pourrait composés de données comme un Tableau ou quelque chose...

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 parce qu'il expose l'état interne (compteurs a et b ). Ce serait bien si nous pouvions simplement s'appeler fibonacci (7) pour obtenir la réponse que nous voulons.

en utilisant ce que nous savons sur les fonctions curries (séquences de fonctions unaires (1-paramter)), nous pouvons atteindre notre objectif facilement sans avoir à modifier notre définition de Y ou de se fonder sur des données composées ou des caractéristiques linguistiques avancées.

regardez attentivement ci-dessous la définition de fibonacci . Nous appliquons immédiatement 0 et 1 qui sont liés à a et b respectivement. Maintenant fibonacci est simplement en attente du dernier argument à être fourni qui sera lié à x . Lorsque nous nous reproduisons, nous devons appeler f (a) (b) (x) (pas f (a,b,x) ) parce que notre fonction est en forme de 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 autres fonctions définies en utilisant le combinateur Y ( range et 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 ]

IT'S ALL ANONYMOUS OMG

parce que nous travaillons avec des fonctions pures ici, nous pouvons substituer n'importe quelle fonction nommée pour sa définition. Regardez ce qui se passe quand nous prenons fibonacci et remplacons 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 => U (h) (f) (x))) (f => a => b => x => x === 0 ? a : f (b) (a + b) (x - 1)) (0) (1) (7)
  
console.log (result) // 13
"1519410920 – et là vous l'avez - fibonacci (7) calculé récursivement en utilisant rien mais des fonctions anonymes

10
répondu user633183 2017-12-08 19:58:42

Vous pourriez 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);
6
répondu ArtBIT 2010-10-07 16:56:02

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

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

son considéré comme une expression de fonction et il a un nom optionnel (que vous pouvez utiliser pour l'appeler de l'intérieur lui-même. Mais parce que c'est une expression de fonction (et pas une instruction) elle reste anonyme (mais a un nom que vous pouvez appeler). Ainsi cette fonction peut s'appeler:

(function foo () {
    foo();
}());
foo //-> undefined
3
répondu xj9 2010-10-08 05:23:26

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);
3
répondu Riccardo Bassilichi 2013-09-18 12:43:54

Dans certaines situations, vous devez compter sur des fonctions anonymes. Donné est une récursive map fonction:

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 map ne doit pas modifier la structure du tableau. Donc l'accumulateur acc n'a pas besoin d'être exposé. Nous pouvons envelopper map dans 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. Nous allons utiliser l' sous-estimé U combinateur:

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? U est mort simple, mais a l'inconvénient que l'appel récursif est un peu obscurci: sum(...) devient h(h)(...) - c'est tout.

3
répondu ftor 2016-07-24 16:13:11

Je ne suis pas sûr que la réponse soit toujours requise, mais cela peut aussi être fait en utilisant des délégués créés en utilisant la fonction.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 pas des fonctions ou des arguments nommés.le destinataire de l'appel.

2
répondu Nitij 2015-02-07 16:32:44

comme a écrit bobince, il suffit de nommer votre fonction.

mais, je devine que vous voulez aussi passer dans une valeur initiale et arrêter votre fonction éventuellement!

var initialValue = ...

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

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



1
répondu Peter Ajtai 2010-10-07 18:05:51

j'avais besoin (ou plutôt, je voulais) d'une fonction anonyme à une seule doublure pour remonter un objet construisant une chaîne, et la manipuler 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:...

1
répondu radio_babylon 2015-11-11 21:54:53

avec ES2015, nous pouvons jouer un peu avec la syntaxe et abuser des paramètres par défaut et des thunks. Ces dernières 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 que f est un paramètre avec la fonction anonyme (x, y, n) => n === 0 ? x : f(y, x + y, n - 1) comme valeur par défaut. Lorsque f est invoqué par applyT cette invocation doit avoir lieu sans arguments, de sorte que la valeur par défaut est utilisée. La valeur par défaut est une fonction et donc f est une fonction nommée, qui peut s'appeler récursivement.

1
répondu ftor 2017-05-09 12:08:08

une autre réponse qui n'implique pas une fonction ou des arguments nommés.callee

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
0
répondu jforjs 2015-04-14 08:46:10

il s'agit d'une reprise de 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écursion. La fonction qui se reçoit comme référence renvoie à la suie primordiale de L'OOP.

0
répondu englebart 2016-05-09 19:17:48

c'est une version de la réponse de @zem avec des fonctions de flèche.

vous pouvez utiliser le combinateur U ou Y . Y combinator étant le plus simple à utiliser.

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

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

0
répondu Ricardo Freitas 2016-12-20 20:12:05

cela ne fonctionne peut-être pas partout, mais vous pouvez utiliser arguments.callee pour faire référence à la fonction courante.

ainsi, factoriel pourrait être fait ainsi:

var fac = function(x) { 
    if (x == 1) return x;
    else return x * arguments.callee(x-1);
}
-1
répondu Dan Jones 2015-10-02 19:34:17