Angenommen, ich habe eine grundlegende rekursive Funktion:
function recur(data) {
data = data+1;
var nothing = function() {
recur(data);
}
nothing();
}
Wie könnte ich das tun, wenn ich eine anonyme Funktion wie ...
(function(data){
data = data+1;
var nothing = function() {
//Something here that calls the function?
}
nothing();
})();
Ich möchte eine Möglichkeit haben, die Funktion aufzurufen, die diese Funktion aufgerufen hat ... Ich habe irgendwo Skripte gesehen (ich kann mich nicht erinnern, wo), die Ihnen den Namen einer aufgerufenen Funktion mitteilen können, aber ich kann mich an keine erinnern diese Informationen im Moment.
javascript
recursion
scope
anonymous-function
Inkognito
quelle
quelle
arguments.callee
, dass es diese Funktion gibt, und diese Funktion macht nichts Nützliches. Ich habe nach Y Combinator gesucht:P
. Verdammt, das Zeug wird niemals nützlich werden ...Antworten:
Sie können der Funktion einen Namen geben, auch wenn Sie die Funktion als Wert und nicht als Anweisung "Funktionsdeklaration" erstellen. Mit anderen Worten:
ist eine rekursive Funktion zum Stapeln von Stapeln. Nun, das heißt, Sie möchten dies
wahrscheinlich nichtgenerell tun, da es einige seltsame Probleme mit verschiedenen Implementierungen von Javascript gibt. ( Hinweis - das ist ein ziemlich alter Kommentar; einige / viele / alle der in Kangax 'Blogbeitrag beschriebenen Probleme können in moderneren Browsern behoben werden.)Wenn Sie einen solchen Namen angeben, ist der Name außerhalb der Funktion nicht sichtbar (nun, das sollte nicht sein; das ist eine der Verrücktheiten). Es ist wie "letrec" in Lisp.
Was
arguments.callee
, die in „streng“ Modus nicht zulässig ist und in der Regel eine schlechte Sache betrachtet, weil es einige Optimierungen schwierig macht. Es ist auch viel langsamer als man erwarten könnte.Bearbeiten - Wenn Sie die Wirkung einer "anonymen" Funktion erzielen möchten, die sich selbst aufrufen kann, können Sie Folgendes tun (vorausgesetzt, Sie übergeben die Funktion als Rückruf oder ähnliches):
Was das tut , ist eine Funktion mit einem schönen zu definieren, sicher, nicht-gebrochen-in-IE Funktion Erklärung Anweisung, eine lokale Funktion , dessen Namen zu schaffen den globalen Namensraum nicht verschmutzen. Die Wrapper-Funktion (wirklich anonym) gibt nur diese lokale Funktion zurück.
quelle
(() => { call_recursively_self_here() })()
sich selbst rekursiv zu verwenden und aufzurufen, oder? Ich muss ihm einen Namen geben.In Kommentaren wurde über den Y-Kombinator gesprochen, aber niemand schrieb ihn als Antwort.
Der Y-Kombinator kann in Javascript wie folgt definiert werden: (danke an steamer25 für den Link)
Und wenn Sie Ihre anonyme Funktion übergeben möchten:
Das Wichtigste an dieser Lösung ist, dass Sie sie nicht verwenden sollten.
quelle
U Kombinator
Durch Übergeben einer Funktion an sich selbst als Argument kann eine Funktion unter Verwendung ihres Parameters anstelle ihres Namens wiederholt werden! Die gegebene Funktion
U
sollte also mindestens einen Parameter haben, der an die Funktion (selbst) gebunden wird.Im folgenden Beispiel haben wir keine Exit-Bedingung, daher werden wir nur eine unbegrenzte Schleife ausführen, bis ein Stapelüberlauf auftritt
Wir können die unendliche Rekursion mit einer Vielzahl von Techniken stoppen. Hier schreibe ich unsere anonyme Funktion, um eine andere anonyme Funktion zurückzugeben, die auf eine Eingabe wartet. in diesem Fall eine Nummer. Wenn eine Zahl angegeben wird und diese größer als 0 ist, werden wir weiterhin wiederholt, andernfalls wird 0 zurückgegeben.
Was hier nicht sofort ersichtlich ist, ist, dass unsere Funktion, wenn sie zum ersten Mal mit dem
U
Kombinator auf sich selbst angewendet wird, eine Funktion zurückgibt, die auf die erste Eingabe wartet. Wenn wir dem einen Namen geben, können wir effektiv rekursive Funktionen mit Lambdas (anonyme Funktionen) erstellen.Nur ist dies keine direkte Rekursion - eine Funktion, die sich selbst mit ihrem eigenen Namen aufruft. Unsere Definition von
countDown
bezieht sich nicht auf sich selbst in seinem Körper und dennoch ist eine Rekursion möglichEntfernen der Selbstreferenz aus einer vorhandenen Funktion mit dem U-Kombinator
Hier zeige ich Ihnen, wie Sie eine rekursive Funktion, die eine Referenz auf sich selbst verwendet, in eine Funktion ändern, die den U-Kombinator anstelle der Selbstreferenz verwendet
Verwenden Sie jetzt den U-Kombinator, um den inneren Verweis auf zu ersetzen
factorial
Das grundlegende Ersatzmuster ist dieses. Machen Sie sich eine mentale Notiz, wir werden im nächsten Abschnitt eine ähnliche Strategie anwenden
Y Kombinator
Im vorherigen Abschnitt haben wir gesehen, wie die Selbstreferenzrekursion mit dem U-Kombinator in eine rekursive Funktion umgewandelt wird, die nicht auf einer benannten Funktion beruht. Es ist ein bisschen ärgerlich, sich daran erinnern zu müssen, die Funktion immer als erstes Argument an sich selbst zu übergeben. Nun, der Y-Kombinator baut auf dem U-Kombinator auf und entfernt dieses mühsame Stück. Dies ist eine gute Sache, da das Entfernen / Reduzieren der Komplexität der Hauptgrund ist, warum wir Funktionen erstellen
Lassen Sie uns zunächst unseren eigenen Y-Kombinator ableiten
Jetzt werden wir sehen, wie die Verwendung im Vergleich zum U-Kombinator ist. Beachten Sie, um zu wiederholen, anstatt
U (f)
wir einfach anrufen könnenf ()
Jetzt werde ich das
countDown
Programm mit demonstrierenY
- Sie werden sehen, dass die Programme fast identisch sind, aber der Y-Kombinator hält die Dinge ein bisschen saubererUnd jetzt werden wir sehen ,
factorial
wie gutWie Sie sehen können,
f
wird der Mechanismus für die Rekursion selbst. Um es noch einmal zu wiederholen, nennen wir es wie eine gewöhnliche Funktion. Wir können es mehrmals mit verschiedenen Argumenten aufrufen und das Ergebnis wird immer noch korrekt sein. Und da es sich um eine gewöhnliche Funktionsparameter ist, können wir es nennen , was wir wollen, wierecur
unten -U- und Y-Kombinator mit mehr als 1 Parameter
In den obigen Beispielen haben wir gesehen, wie wir ein Argument schleifen und übergeben können, um den "Zustand" unserer Berechnung zu verfolgen. Aber was ist, wenn wir den zusätzlichen Zustand im Auge behalten müssen?
Wir könnten zusammengesetzte Daten wie ein Array oder so etwas verwenden ...
Dies ist jedoch schlecht, da der interne Status (Zähler
a
undb
) angezeigt wird . Es wäre schön, wenn wir einfach anrufen könntenfibonacci (7)
, um die gewünschte Antwort zu erhalten.Mit dem, was wir über Curry-Funktionen wissen (Sequenzen von unären (1-Parameter-) Funktionen), können wir unser Ziel leicht erreichen, ohne unsere Definition von
Y
zusammengesetzten Daten oder erweiterten Sprachmerkmalen ändern oder uns darauf verlassen zu müssen.Schauen Sie sich die Definition von
fibonacci
unten an. Wir sind sofort anwenden0
und1
die an gebunden sinda
undb
jeweils. Jetzt wartet Fibonacci einfach darauf, dass das letzte Argument geliefert wird, an das gebunden wirdx
. Wenn wir wiederkehren, müssen wirf (a) (b) (x)
(nichtf (a,b,x)
) aufrufen, weil unsere Funktion in Curry-Form vorliegt.Diese Art von Muster kann nützlich sein, um alle Arten von Funktionen zu definieren. Unten sehen wir zwei weitere Funktionen, die mit dem
Y
Kombinator (range
undreduce
) und einer Ableitung vonreduce
,map
.ES IST ALLES ANONYM OMG
Da wir hier mit reinen Funktionen arbeiten, können wir die Definition durch eine beliebige benannte Funktion ersetzen. Beobachten Sie, was passiert, wenn wir Fibonacci nehmen und benannte Funktionen durch ihre Ausdrücke ersetzen
Und da haben Sie es -
fibonacci (7)
rekursiv berechnet mit nichts als anonymen Funktionenquelle
Es kann am einfachsten sein, stattdessen ein "anonymes Objekt" zu verwenden:
Ihr globaler Raum ist völlig unverschmutzt. Es ist ziemlich einfach. Und Sie können den nicht globalen Status des Objekts problemlos nutzen.
Sie können auch ES6-Objektmethoden verwenden, um die Syntax präziser zu gestalten.
quelle
Ich würde dies nicht als Inline-Funktion tun. Es stößt an die Grenzen des guten Geschmacks und bringt dir nichts.
Wenn Sie wirklich müssen, gibt es
arguments.callee
wie in Fabrizios Antwort. Dies wird jedoch im Allgemeinen als nicht ratsam angesehen und ist im "strengen Modus" von ECMAScript Fifth Edition nicht zulässig. Obwohl ECMA 3 und der nicht strenge Modus nicht verschwinden, verspricht das Arbeiten im strengen Modus mehr mögliche Sprachoptimierungen.Man kann auch eine benannte Inline-Funktion verwenden:
Benannte Inline-Funktionsausdrücke werden jedoch am besten vermieden, da das JScript des IE einige schlechte Dinge mit ihnen macht. Im obigen Beispiel
foo
wird der übergeordnete Bereich im IE fälschlicherweise verschmutzt, und der übergeordnete Bereichfoo
ist eine separate Instanz gegenüber dem darinfoo
gezeigtenfoo
.Was ist der Zweck, dies in eine anonyme Inline-Funktion zu integrieren? Wenn Sie nur vermeiden möchten, den übergeordneten Bereich zu verschmutzen, können Sie Ihr erstes Beispiel natürlich in einer anderen selbstaufrufenden anonymen Funktion (Namespace) ausblenden. Müssen Sie wirklich
nothing
jedes Mal um die Rekursion herum eine neue Kopie erstellen ? Mit einem Namespace, der zwei einfache, gegenseitig rekursive Funktionen enthält, sind Sie möglicherweise besser dran.quelle
"pushing against the boundaries of good taste"
- (na ja, und die guten Infos).recur_foo
mit einer Funktion im übergeordneten Bereich kollidiert (oder krank ist) -gebraucht) .quelle
arguments.callee
: Sie ist im strengen Modus und in ES5 nicht zulässig.Sie könnten so etwas tun wie:
oder in deinem Fall:
quelle
recur
zuerst mit einervar
Aussage zu deklarieren . Keine Ahnung, ob dies gegen die Regeln der Frage verstößt, aber wie Sie es jetzt haben, erhalten Sie ohne dievar
Anweisung einen Fehler im strengen ECMAScript 5-Modus.var
Schlüsselwort, aber als ich diesen Code getestet habe, wurden Fehler ausgelöst, da Sie eine Variable innerhalb eines selbstaufrufenden Blocks nicht wirklich deklarieren können und mein Ansatz auf der automatischen Deklaration einer undefinierten Variablen und damit von @ Pointy's beruht Lösung ist korrekter. Aber ich habe trotzdem für die Antwort von Fabrizio Calderan gestimmt;)(var recur = function() {...})();
funktioniert nicht, da es sich jetzt eher um eine Anweisung als um einen Zuweisungsausdruck handelt (der den zugewiesenen Wert zurückgibt). Ich schlugvar recur; (recur = function() {...})();
stattdessen vor.Wenn Sie eine anonyme Funktion wie folgt deklarieren:
Es wird als Funktionsausdruck betrachtet und hat einen optionalen Namen (mit dem Sie ihn aus sich heraus aufrufen können. Da es sich jedoch um einen Funktionsausdruck (und nicht um eine Anweisung) handelt, bleibt es anonym (hat jedoch einen Namen, den Sie aufrufen können) Diese Funktion kann sich selbst aufrufen:
quelle
foo
dies im aktuellen Kontext nicht deklariert wird, aber das ist mehr oder weniger irrelevant. Eine Funktion mit einem Namen ist immer noch eine benannte Funktion - nicht anonym.Warum nicht die Funktion an die Funktion selbst übergeben?
quelle
In bestimmten Situationen müssen Sie sich auf anonyme Funktionen verlassen. Gegeben ist eine rekursive
map
Funktion:Bitte beachten Sie, dass
map
die Struktur des Arrays nicht geändert werden darf. Der Akkuacc
muss also nicht freigelegt werden. Wir könnenmap
zum Beispiel in eine andere Funktion einwickeln :Aber diese Lösung ist ziemlich ausführlich. Verwenden wir den unterschätzten
U
Kombinator:Prägnant, nicht wahr?
U
ist denkbar einfach, hat aber den Nachteil, dass der rekursive Aufruf etwas verschleiert wird:sum(...)
wirdh(h)(...)
- das ist alles.quelle
Ich bin nicht sicher, ob die Antwort noch erforderlich ist, aber dies kann auch mit Delegaten erfolgen, die mit function.bind erstellt wurden:
Dies beinhaltet keine benannten Funktionen oder Argumente.
quelle
Wie Bobince schrieb, benennen Sie einfach Ihre Funktion.
Aber ich vermute, Sie möchten auch einen Anfangswert übergeben und Ihre Funktion schließlich beenden!
funktionierendes jsFiddle-Beispiel (verwendet Daten + = Daten zum Spaß)
quelle
However named inline function expressions are also best avoided.
. Aber das OP verfehlt auch den Punkt ... :)Ich brauchte (oder wollte) eine anonyme einzeilige Funktion, um ein Objekt zu erreichen, das eine Zeichenfolge aufbaut, und behandelte es folgendermaßen:
das erzeugt eine Zeichenfolge wie 'Root: foo: bar: baz: ...'
quelle
Mit ES2015 können wir ein bisschen mit der Syntax herumspielen und Standardparameter und Thunks missbrauchen. Letztere sind nur Funktionen ohne Argumente:
Bitte beachten Sie, dass dies
f
ein Parameter ist,(x, y, n) => n === 0 ? x : f(y, x + y, n - 1)
dessen Standardwert die anonyme Funktion ist. Wannf
durchapplyT
diesen Aufruf aufgerufen wird, muss ohne Argumente erfolgen, damit der Standardwert verwendet wird. Der Standardwert ist eine Funktion und daherf
eine benannte Funktion, die sich selbst rekursiv aufrufen kann.quelle
Eine andere Antwort, die keine benannte Funktion oder Argumente beinhaltet
quelle
Dies ist eine Überarbeitung der Antwort von jforjs mit verschiedenen Namen und einem leicht modifizierten Eintrag.
Die erste Rekursion musste nicht abgewickelt werden. Die Funktion, die sich selbst als Referenz empfängt, geht auf den ursprünglichen Schlamm von OOP zurück.
quelle
Dies ist eine Version der Antwort von @ zem mit Pfeilfunktionen.
Sie können den
U
oder denY
Kombinator verwenden. Der Y-Kombinator ist am einfachsten zu bedienen.U
Kombinator, damit müssen Sie die Funktion weiter übergeben:const U = f => f(f) U(selfFn => arg => selfFn(selfFn)('to infinity and beyond'))
Y
Kombinator, damit müssen Sie die Funktion nicht weiter übergeben:const Y = gen => U(f => gen((...args) => f(f)(...args))) Y(selfFn => arg => selfFn('to infinity and beyond'))
quelle
Noch eine andere Y-Kombinator-Lösung, die einen Rosetta-Code- Link verwendet (ich glaube, jemand hat den Link zuvor irgendwo auf stackOverflow erwähnt.
Pfeile sind für anonyme Funktionen für mich besser lesbar:
quelle
Dies funktioniert möglicherweise nicht überall, aber Sie können
arguments.callee
auf die aktuelle Funktion verweisen.Fakultät könnte also so gemacht werden:
quelle