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.
javascript
recursion
scope
anonymous-function
Incognito
la source
la source
arguments.callee
existe, et cette fonction ne fait rien d'utile. Je regardais Y Combinator:P
. Merde, ce truc ne sera jamais utile ...Réponses:
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:
est une fonction récursive époustouflante. Cela dit, vous
nevoudrezprobablement pasfaire 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):
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.
la source
(() => { call_recursively_self_here() })()
et de s'appeler récursivement, non? Je dois lui donner un nom.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)
Et lorsque vous souhaitez passer votre fonction anonyme:
La chose la plus importante à noter à propos de cette solution est que vous ne devez pas l'utiliser.
la source
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 à
U
doit 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
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.
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
U
combinateur, 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)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
countDown
ne se référence pas à l'intérieur de son corps et toujours la récursivité est possibleComment 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
Maintenant, en utilisant le combinateur U pour remplacer la référence interne à
factorial
Le modèle de remplacement de base est le suivant. Notez mentalement, nous utiliserons une stratégie similaire dans la section suivante
Combinateur Y
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
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 ()
Maintenant, je vais vous montrer le
countDown
programme en utilisantY
- vous verrez que les programmes sont presque identiques mais le combinateur Y garde les choses un peu plus propresEt maintenant nous verrons
factorial
aussiComme vous pouvez le voir,
f
devient 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, commerecur
ci-dessous -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 ...
Mais c'est mauvais car cela expose l'état interne (compteurs
a
etb
). Ce serait bien si nous pouvions simplement appelerfibonacci (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
Y
ou à nous fier aux données composées ou aux fonctionnalités avancées du langage.Regardez la définition de
fibonacci
près ci-dessous. Nous appliquons immédiatement0
et1
qui sont liés àa
etb
respectivement. Maintenant fibonacci attend simplement que le dernier argument soit fourni auquel sera liéx
. Lorsque nous récurons, nous devons appelerf (a) (b) (x)
(nonf (a,b,x)
) car notre fonction est sous forme curry.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
Y
combinateur (range
etreduce
) et un dérivé dereduce
,map
.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
Et là vous l'avez -
fibonacci (7)
calculé récursivement en utilisant rien d'autre que des fonctions anonymesla source
Il peut être plus simple d'utiliser à la place un "objet anonyme":
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.
la source
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.callee
comme 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:
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,
foo
pollue incorrectement la portée du parent dans IE, et le parentfoo
est une instance distincte de lafoo
vue à l'intérieurfoo
.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
nothing
chaque fois autour de la récursivité? Vous pourriez être mieux avec un espace de noms contenant deux simples fonctions réciproquement récursives.la source
"pushing against the boundaries of good taste"
- (enfin, et la bonne info).recur_foo
entre en collision avec une fonction dans la portée parente (ou soit malade -utilisé) .la source
arguments.callee
: elle est interdite en mode strict et dans ES5.Vous pouvez faire quelque chose comme:
ou dans votre cas:
la source
recur
abord avec unevar
instruction. Je ne sais pas si cela enfreint les règles de la question, mais comme vous l'avez maintenant, sans lavar
déclaration, vous obtiendrez une erreur en mode strict d'ECMAScript 5.var
mot - 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;)(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éraisvar recur; (recur = function() {...})();
plutôt.Lorsque vous déclarez une fonction anonyme comme celle-ci:
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:
la source
foo
ne 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.Pourquoi ne pas passer la fonction à la functio elle-même?
la source
Dans certaines situations, vous devez vous fier à des fonctions anonymes. Donnée est une
map
fonction récursive :Veuillez noter que
map
ne doit pas modifier la structure du tableau. L'accumulateuracc
n'a donc pas besoin d'être exposé. Nous pouvons encapsulermap
dans une autre fonction par exemple:Mais cette solution est assez verbeuse. Utilisons le
U
combinateur sous-estimé :Concis, n'est-ce pas?
U
est très simple mais présente l'inconvénient que l'appel récursif est un peu obscurci:sum(...)
devienth(h)(...)
- c'est tout.la source
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:
Cela n'implique ni fonctions nommées ni arguments.callee.
la source
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!
exemple de travail de jsFiddle (utilise des données + = des données pour le plaisir)
la source
However named inline function expressions are also best avoided.
. Mais l'OP manque le point aussi ... :)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:
qui produit une chaîne comme 'Root: foo: bar: baz: ...'
la source
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:
Veuillez noter qu'il
f
s'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. Quandf
est invoqué parapplyT
cet 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 doncf
une fonction nommée, qui peut s'appeler elle-même de manière récursive.la source
Une autre réponse qui n'implique pas de fonction ou d'arguments nommés.
la source
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.
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.
la source
Ceci est une version de la réponse de @ zem avec des fonctions fléchées.
Vous pouvez utiliser le
U
ou leY
combinateur. 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'))
la source
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:
la source
Cela peut ne pas fonctionner partout, mais vous pouvez utiliser
arguments.callee
pour faire référence à la fonction actuelle.Ainsi, factorielle pourrait se faire ainsi:
la source