Powiedzmy, że mam podstawową funkcję rekurencyjną:
function recur(data) {
data = data+1;
var nothing = function() {
recur(data);
}
nothing();
}
Jak mogę to zrobić, jeśli mam anonimową funkcję, taką jak ...
(function(data){
data = data+1;
var nothing = function() {
//Something here that calls the function?
}
nothing();
})();
Chciałbym sposób wywołać funkcję, która wywołała tę funkcję ... Widziałem gdzieś skrypty (nie pamiętam gdzie), które mogą podać nazwę wywoływanej funkcji, ale nie mogę sobie przypomnieć żadnej z nich te informacje w tej chwili.
javascript
recursion
scope
anonymous-function
Incognito
źródło
źródło
arguments.callee
istnieje, a ta functnio nie robi nic pożytecznego. Szukałem w górę Y Combinator:P
. Cholera, te rzeczy nigdy się nie przydadzą ...Odpowiedzi:
Państwo może dać funkcja imię, nawet podczas tworzenia funkcji jako wartość, a nie jako „deklaracja funkcji” oświadczeniu. Innymi słowy:
jest rekurencyjną funkcją zrzucającą stosy. To powiedziawszy,
prawdopodobnienie chcesz tego robić w ogóle, ponieważ są pewne dziwne problemy z różnymi implementacjami JavaScript. ( uwaga - to dość stary komentarz; niektóre / wiele / wszystkie problemy opisane w poście na blogu Kangax mogą zostać naprawione w nowszych przeglądarkach).Kiedy podajesz taką nazwę, nie jest ona widoczna poza funkcją (cóż, tak nie powinno być; to jedna z dziwactw). To jest jak „letrec” w Lisp.
Co do
arguments.callee
tego, jest to niedozwolone w trybie „ścisłym” i ogólnie jest uważane za złe, ponieważ utrudnia niektóre optymalizacje. Jest również znacznie wolniejszy, niż można by się spodziewać.edycja - jeśli chcesz uzyskać efekt „anonimowej” funkcji, która może wywołać samą siebie, możesz zrobić coś takiego (zakładając, że przekazujesz funkcję jako wywołanie zwrotne lub coś w tym rodzaju):
To, co robi, to zdefiniowanie funkcji za pomocą ładnej, bezpiecznej, niezepsutej w IE instrukcji deklaracji funkcji , tworząc lokalną funkcję, której nazwa nie zanieczyści globalnej przestrzeni nazw. Funkcja opakowująca (prawdziwie anonimowa) po prostu zwraca tę funkcję lokalną.
źródło
(() => { call_recursively_self_here() })()
i wywoływać siebie rekurencyjnie, prawda? Muszę nadać temu imię.Ludzie mówili o kombinatorze Y w komentarzach, ale nikt nie napisał tego jako odpowiedzi.
Kombinator Y można zdefiniować w javascript w następujący sposób: (dzięki steamer25 za link)
A kiedy chcesz przekazać swoją anonimową funkcję:
Najważniejszą rzeczą, na którą należy zwrócić uwagę w przypadku tego rozwiązania, jest to, że nie należy go używać.
źródło
Kombinator U
Przekazując sobie funkcję jako argument, funkcja może powtarzać się przy użyciu swojego parametru zamiast nazwy! Zatem funkcja podana do
U
powinna mieć co najmniej jeden parametr, który będzie wiązał się z funkcją (samą).W poniższym przykładzie nie mamy warunku wyjścia, więc będziemy po prostu pętli w nieskończoność, aż nastąpi przepełnienie stosu
Możemy zatrzymać nieskończoną rekurencję za pomocą różnych technik. Tutaj napiszę naszą anonimową funkcję zwracającą inną anonimową funkcję, która czeka na dane wejściowe; w tym przypadku pewna liczba. Po podaniu liczby, jeśli jest ona większa niż 0, będziemy kontynuować powtarzanie, w przeciwnym razie zwrócimy 0.
Nie widać tutaj od razu, że nasza funkcja, gdy po raz pierwszy zastosowana do siebie za pomocą
U
kombinatora, zwraca funkcję oczekującą na pierwsze wejście. Jeśli nadaliśmy temu nazwę, możemy skutecznie konstruować funkcje rekurencyjne przy użyciu lambd (funkcji anonimowych)Tyle że to nie jest bezpośrednia rekursja - funkcja, która wywołuje samą siebie używając własnej nazwy. Nasza definicja
countDown
nie odwołuje się do siebie w swoim ciele i nadal możliwa jest rekurencjaJak usunąć samoodniesienie z istniejącej funkcji za pomocą kombinatora U.
Tutaj pokażę ci, jak wziąć funkcję rekurencyjną, która używa odwołania do samej siebie i zmienić ją na funkcję, która używa kombinatora U do zamiast odniesienia do siebie
Teraz używamy kombinatora U, aby zastąpić odwołanie wewnętrzne do
factorial
Podstawowy wzór wymiany jest następujący. Zwróć uwagę, że w następnej sekcji będziemy używać podobnej strategii
Kombinator Y.
W poprzedniej sekcji widzieliśmy, jak przekształcić rekursję z samoodniesieniami w funkcję rekurencyjną, która nie polega na nazwanej funkcji przy użyciu kombinatora U. Jest trochę irytujące, że trzeba pamiętać, aby zawsze przekazywać tę funkcję do siebie jako pierwszy argument. Cóż, kombinator Y opiera się na kombinatorze U i usuwa ten żmudny fragment. To dobrze, ponieważ usuwanie / zmniejszanie złożoności jest głównym powodem, dla którego tworzymy funkcje
Najpierw wyprowadźmy nasz własny kombinator Y
Teraz zobaczymy, jak jego użycie wypada w porównaniu z kombinatorem U. Zauważ, aby się powtórzyć, zamiast tego
U (f)
możemy po prostu zadzwonićf ()
Teraz zademonstruję
countDown
program przy użyciuY
- zobaczysz, że programy są prawie identyczne, ale kombinator Y sprawia, że wszystko jest trochę czystszeA teraz zobaczymy
factorial
równieżJak widać, sam
f
staje się mechanizmem rekursji. Aby się powtarzać, nazywamy to zwykłą funkcją. Możemy to wywołać wiele razy z różnymi argumentami, a wynik nadal będzie poprawny. A ponieważ jest to zwykły parametr funkcji, możemy nazwać go dowolnie, na przykładrecur
poniżej -Kombinator U i Y z więcej niż 1 parametrem
W powyższych przykładach widzieliśmy, jak możemy zapętlić i przekazać argument, aby śledzić „stan” naszych obliczeń. Ale co, jeśli musimy śledzić dodatkowy stan?
My mogli korzystać z danych złożonych jak tablica lub coś ...
Ale to jest złe, ponieważ ujawnia stan wewnętrzny (liczniki
a
ib
). Byłoby miło, gdybyśmy mogli po prostu zadzwonić,fibonacci (7)
aby uzyskać odpowiedź, której szukamy.Korzystając z tego, co wiemy o funkcjach curried (sekwencje funkcji jednoargumentowych (1-paramter)), możemy łatwo osiągnąć nasz cel bez konieczności modyfikowania naszej definicji
Y
danych złożonych lub zaawansowanych funkcji językowych lub polegania na nich.Przyjrzyj się
fibonacci
bliżej definicji poniżej. Natychmiast aplikujemy0
i1
które są zobowiązane doa
ib
odpowiednio. Teraz fibonacci po prostu czeka na podanie ostatniego argumentu, do którego zostanie przypisanyx
. Kiedy powtarzamy, musimy wywołaćf (a) (b) (x)
(nief (a,b,x)
), ponieważ nasza funkcja ma postać curry.Ten rodzaj wzorca może być przydatny do definiowania różnego rodzaju funkcji. Poniżej widzimy dwie kolejne funkcje zdefiniowane za pomocą
Y
syntezatora (range
ireduce
) i pochodnąreduce
,map
.TO WSZYSTKO ANONIMOWE OMG
Ponieważ pracujemy tutaj z czystymi funkcjami, możemy zastąpić dowolną nazwaną funkcję jej definicją. Zobacz, co się dzieje, gdy bierzemy Fibonacciego i zastępujemy nazwane funkcje ich wyrażeniami
I
fibonacci (7)
gotowe - wyliczane rekurencyjnie, używając wyłącznie funkcji anonimowychźródło
Zamiast tego najprościej jest użyć „obiektu anonimowego”:
Twoja globalna przestrzeń jest całkowicie nieskażona. To całkiem proste. Możesz łatwo wykorzystać nieglobalny stan obiektu.
Możesz także użyć metod obiektowych ES6, aby uczynić składnię bardziej zwięzłą.
źródło
Nie zrobiłbym tego jako funkcji inline. Przekracza granice dobrego smaku i tak naprawdę nic ci nie daje.
Jeśli naprawdę musisz, jest
arguments.callee
odpowiedź Fabrizia. Jest to jednak ogólnie uważane za niewskazane i niedozwolone w „trybie ścisłym” ECMAScript piątego wydania. Chociaż ECMA 3 i tryb nie-ścisły nie znikają, praca w trybie ścisłym obiecuje więcej możliwych optymalizacji języka.Można również użyć nazwanej funkcji inline:
Jednak najlepiej unikać nazwanych wyrażeń funkcji wbudowanych, ponieważ język JScript przeglądarki IE robi z nimi kilka złych rzeczy. W powyższym przykładzie
foo
niepoprawnie zanieczyszcza zakres nadrzędny w IE, a element nadrzędnyfoo
jest oddzielną instancją odfoo
widocznej w środkufoo
.Jaki jest cel umieszczenia tego w anonimowej funkcji wbudowanej? Jeśli chcesz po prostu uniknąć zanieczyszczania zakresu nadrzędnego, możesz oczywiście ukryć swój pierwszy przykład w innej samowywołującej się funkcji anonimowej (przestrzeni nazw). Czy naprawdę musisz tworzyć nową kopię za
nothing
każdym razem wokół rekurencji? Lepszym rozwiązaniem może być przestrzeń nazw zawierająca dwie proste, wzajemnie rekurencyjne funkcje.źródło
"pushing against the boundaries of good taste"
- (no i dobra informacja).recur_foo
kolidowała z funkcją w zakresie nadrzędnym (lub zachorowała) -używany) .źródło
arguments.callee
zdadzą sobie sprawę z problemów : jest ona niedozwolona w trybie ścisłym iw ES5.Możesz zrobić coś takiego:
lub w Twoim przypadku:
źródło
recur
najpierw za pomocąvar
oświadczenia. Nie wiem, czy to łamie reguły pytania, ale tak jak masz to teraz, bezvar
instrukcji otrzymasz błąd w trybie ścisłym ECMAScript 5.var
słowo kluczowe, ale kiedy przetestowałem ten kod, generował błędy, ponieważ nie możesz tak naprawdę zadeklarować zmiennej wewnątrz samowywołującego się bloku, a moje podejście polega na automatycznej deklaracji niezdefiniowanej zmiennej, a zatem @ Pointy's rozwiązanie jest bardziej poprawne. Ale nadal głosowałem na odpowiedź Fabrizio Calderana;)(var recur = function() {...})();
nie zadziała, ponieważ jest to teraz instrukcja, a nie wyrażenie przypisania (które zwraca przypisaną wartość).var recur; (recur = function() {...})();
Zamiast tego sugerowałem .Kiedy deklarujesz anonimową funkcję, taką jak ta:
Jest uważane za wyrażenie funkcyjne i ma opcjonalną nazwę (której możesz użyć do wywołania go z samego siebie. Ale ponieważ jest to wyrażenie funkcyjne (a nie instrukcja), pozostaje anonimowe (ale ma nazwę, którą możesz wywołać). ta funkcja może wywołać samą siebie:
źródło
foo
nie jest to deklarowane w obecnym kontekście, ale to mniej więcej nieistotne. Funkcja o nazwie jest nadal funkcją nazwaną - nie anonimową.Dlaczego nie przekazać funkcji do samego functio?
źródło
W niektórych sytuacjach musisz polegać na anonimowych funkcjach. Given jest
map
funkcją rekurencyjną :Należy pamiętać, że
map
nie może modyfikować struktury tablicy. Więc akumulatoracc
nie musi być odsłonięty. Możemymap
na przykład zawinąć w inną funkcję:Ale to rozwiązanie jest dość rozwlekłe. Użyjmy niedocenianego
U
kombinatora:Zwięzłe, prawda?
U
jest śmiertelnie prosty, ale ma tę wadę, że wywołanie rekurencyjne jest nieco zaciemniane:sum(...)
staje sięh(h)(...)
- to wszystko.źródło
Nie jestem pewien, czy odpowiedź jest nadal wymagana, ale można to również zrobić za pomocą delegatów utworzonych za pomocą function.bind:
Nie dotyczy to nazwanych funkcji ani argumentów. Callee.
źródło
Jak napisał bobince, po prostu nazwij swoją funkcję.
Ale zgaduję, że chcesz również przekazać wartość początkową i ostatecznie zatrzymać funkcję!
działający przykład jsFiddle (używa danych + = dane dla zabawy)
źródło
However named inline function expressions are also best avoided.
. Ale OP też mija się z celem ... :)Potrzebowałem (a raczej chciałem) jednowierszowej anonimowej funkcji, aby przejść w górę obiektu tworzącego ciąg, i obsługiwałem to w następujący sposób:
co daje ciąg, taki jak „Root: foo: bar: baz: ...”
źródło
Dzięki ES2015 możemy trochę pobawić się składnią i nadużywać domyślnych parametrów i thunks. Te ostatnie to po prostu funkcje bez żadnych argumentów:
Należy pamiętać, że
f
jest to parametr z funkcją anonimową(x, y, n) => n === 0 ? x : f(y, x + y, n - 1)
jako wartością domyślną. Kiedyf
jest wywoływane przezapplyT
to wywołanie, musi odbywać się bez argumentów, aby została użyta wartość domyślna. Wartością domyślną jest funkcja, a zatemf
jest to nazwana funkcja, która może wywoływać samą siebie rekurencyjnie.źródło
Kolejna odpowiedź, która nie zawiera nazwanej funkcji ani arguments.callee
źródło
To jest przeróbka odpowiedzi jforjs z różnymi nazwami i nieco zmodyfikowanym wpisem.
Nie było potrzeby rozwijania pierwszej rekursji. Funkcja odbierająca siebie jako odniesienie nawiązuje do pierwotnego szlamu OOP.
źródło
To jest wersja odpowiedzi @ zem z funkcjami strzałkowymi.
Możesz użyć kombinatora
U
lubY
. Najprostszy w użyciu kombinator Y.U
kombinator, z tym musisz kontynuować przekazywanie funkcji:const U = f => f(f) U(selfFn => arg => selfFn(selfFn)('to infinity and beyond'))
Y
kombinator, dzięki temu nie musisz przekazywać funkcji:const Y = gen => U(f => gen((...args) => f(f)(...args))) Y(selfFn => arg => selfFn('to infinity and beyond'))
źródło
Jeszcze jedno rozwiązanie z kombinatorem Y, wykorzystujące łącze z kodem Rosetta (myślę, że ktoś wcześniej wspomniał o tym łączu gdzieś na stackOverflow.
Strzałki są dla funkcji anonimowych, które są dla mnie bardziej czytelne:
źródło
To może nie działać wszędzie, ale możesz użyć,
arguments.callee
aby odnieść się do bieżącej funkcji.Tak więc silnię można zrobić w ten sposób:
źródło