javascript: rekurencyjna funkcja anonimowa?

120

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.

Incognito
źródło
Czy jest jakiś powód, dla którego tego potrzebujesz, czy po prostu jesteś ciekawy? Wydaje mi się, że jaśniejsze byłoby nadanie mu nazwy ...
rfunduk
1
@thenduks: Z tego samego powodu, dla którego można by użyć funkcji anonimowej. Po prostu czasami rekurencja jest konieczna.
poke
5
Szkoda, że arguments.calleeistnieje, a ta functnio nie robi nic pożytecznego. Szukałem w górę Y Combinator :P . Cholera, te rzeczy nigdy się nie przydadzą ...
Kobi
1
Tak, ponieważ Kobi jest połączony, użyj kombinatora stałoprzecinkowego, takiego jak Y, aby wykonywać anonimowe funkcje rekurencyjne bez argumentów. Callee.
parowiec 25
1
Zobacz w3future.com/weblog/stories/2002/02/22/ ... przykład kombinatora Y w JS.
parowiec 25

Odpowiedzi:

145

Państwo może dać funkcja imię, nawet podczas tworzenia funkcji jako wartość, a nie jako „deklaracja funkcji” oświadczeniu. Innymi słowy:

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

jest rekurencyjną funkcją zrzucającą stosy. To powiedziawszy, prawdopodobnie nie 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.calleetego, 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):

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

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ą.

Spiczasty
źródło
Czy możemy uniknąć zanieczyszczania globalnej przestrzeni nazw w inny sposób za pomocą ES5 sctrict (nie czytałem jeszcze głęboko w ES5)?
Incognito
@pointy, czy możesz spojrzeć na to zadanie. stackoverflow.com/questions/27473450/…
Gladson Robinson
Myślę, że nie można używać (() => { call_recursively_self_here() })()i wywoływać siebie rekurencyjnie, prawda? Muszę nadać temu imię.
Qwerty,
1
@Qwerty, możesz zrobić coś podobnego do ostatniego przykładu w mojej odpowiedzi. Powiąż funkcję strzałkową ze zmienną lokalną w funkcji opakowania, aby funkcja strzałkowa mogła odwoływać się do siebie za pomocą nazwy zmiennej. Opakowanie zwróci wtedy zmienną (która odnosi się do funkcji strzałkowej).
Pointy
1
@Pointy może jacyś hakerzy znajdą aplikację;)
Kamil Kiełczewski
31

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)

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

A kiedy chcesz przekazać swoją anonimową funkcję:

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

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ć.

zem
źródło
16
„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ć”. Czemu?
nyuszika7h
7
To nie będzie szybkie. W rzeczywistości jest brzydki (choć koncepcyjnie piękny!). Unikasz nadawania swojej funkcji nazwy tagu lub zmiennej (i nie rozumiem, dlaczego miałoby to być problemem), ale nadal nadajesz jej nazwę jako parametr funkcji zewnętrznej przekazanej do Y. Więc nie zyskać wszystko, przechodząc przez te wszystkie kłopoty.
zem
Nie zapomnij wspomnieć, że ta funkcja nie jest bezpieczna dla stosu. Zapętlenie zaledwie kilka tysięcy razy spowoduje przepełnienie stosu.
Dziękuję
Cześć, proponuję nieco "czystszą" modyfikację, ponieważ .apply (null, argumenty) wydaje mi się brzydka: var Y = function (gen) {return (function (f) {return f (f);} (function (f) {return gen (function (x) {return f (f) (x);});})); } Lub równoważnie ((function (x) {return y} equals (x => y))) za pomocą notacji strzałki (prawidłowy kod js): var Y = gen => (f => f (f)) (f = > gen (x => f (f) (x)))
myfirstAnswer
23

Kombinator U

Przekazując sobie funkcję jako argument, funkcja może powtarzać się przy użyciu swojego parametru zamiast nazwy! Zatem funkcja podana do Upowinna 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

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

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

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.

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

Nie widać tutaj od razu, że nasza funkcja, gdy po raz pierwszy zastosowana do siebie za pomocą Ukombinatora, 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)

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

Tyle że to nie jest bezpośrednia rekursja - funkcja, która wywołuje samą siebie używając własnej nazwy. Nasza definicja countDownnie odwołuje się do siebie w swoim ciele i nadal możliwa jest rekurencja

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

Jak 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

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

Teraz używamy kombinatora U, aby zastąpić odwołanie wewnętrzne do factorial

const U = f => f (f)

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

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

Podstawowy wzór wymiany jest następujący. Zwróć uwagę, że w następnej sekcji będziemy używać podobnej strategii

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

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

Kombinator Y.

powiązane: kombinatory U i Y wyjaśniono za pomocą analogii lustrzanej

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

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

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

const U = f => f (f)

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

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

Teraz zademonstruję countDownprogram przy użyciu Y- zobaczysz, że programy są prawie identyczne, ale kombinator Y sprawia, że ​​wszystko jest trochę czystsze

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

A teraz zobaczymy factorialrównież

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

Jak widać, sam fstaje 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ład recurponiżej -

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


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ś ...

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

Ale to jest złe, ponieważ ujawnia stan wewnętrzny (liczniki ai b). 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 Ydanych złożonych lub zaawansowanych funkcji językowych lub polegania na nich.

Przyjrzyj się fibonaccibliżej definicji poniżej. Natychmiast aplikujemy 0i 1które są zobowiązane do ai bodpowiednio. Teraz fibonacci po prostu czeka na podanie ostatniego argumentu, do którego zostanie przypisany x. Kiedy powtarzamy, musimy wywołać f (a) (b) (x)(nie f (a,b,x)), ponieważ nasza funkcja ma postać 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


Ten rodzaj wzorca może być przydatny do definiowania różnego rodzaju funkcji. Poniżej widzimy dwie kolejne funkcje zdefiniowane za pomocą Ysyntezatora ( rangei reduce) i pochodną 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 ]


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

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

I fibonacci (7)gotowe - wyliczane rekurencyjnie, używając wyłącznie funkcji anonimowych

Dziękuję Ci
źródło
14

Zamiast tego najprościej jest użyć „obiektu anonimowego”:

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

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łą.

({
  do() {
    console.log("don't run this ...");
    this.do();
  }
}).do();
svidgen
źródło
13

Nie zrobiłbym tego jako funkcji inline. Przekracza granice dobrego smaku i tak naprawdę nic ci nie daje.

Jeśli naprawdę musisz, jest arguments.calleeodpowiedź 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:

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

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 fooniepoprawnie zanieczyszcza zakres nadrzędny w IE, a element nadrzędny foojest oddzielną instancją od foowidocznej w środku foo.

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 nothingkażdym razem wokół rekurencji? Lepszym rozwiązaniem może być przestrzeń nazw zawierająca dwie proste, wzajemnie rekurencyjne funkcje.

bobince
źródło
Zgadzam się, nazwana funkcja jest bardziej odpowiednia niż arguments.callee nie tylko dla ścisłego trybu ecmascript, ale także dla kwestii optymalizacji, ponieważ przy każdej rekurencji musi uzyskać odniesienie do wywoływanego (a to prawdopodobnie mogłoby zmniejszyć szybkość wykonywania )
+1 dla poetyckiego, "pushing against the boundaries of good taste"- (no i dobra informacja).
Peter Ajtai
co z prostym prefiksem / postfiksem, jeśli zanieczyszczenie jest tutaj naprawdę problemem? Biorąc pod uwagę, że nie jest to zakres globalny (nawet jeśli funkcja jest na najwyższym poziomie, powinien już mieć anonimową funkcję opakowującą cały kod), jest naprawdę mało prawdopodobne, aby nazwa taka recur_fookolidowała z funkcją w zakresie nadrzędnym (lub zachorowała) -używany) .
gblazex
Bardzo interesujące - jsfiddle.net/hck2A - IE zanieczyszcza w tym przypadku rodzica, tak jak powiedziałeś. Nigdy nie zdawałem sobie z tego sprawy.
Peter Ajtai
1
@Peter: kangax.github.com/nfe (szczególnie „błędy JScript”), bo więcej niż kiedykolwiek chciałeś wiedzieć na ten temat. Ostatecznie zostało to naprawione w IE9 (ale tylko w trybie standardów IE9).
bobince
10
(function(data){
    var recursive = arguments.callee;
    data = data+1;
    var nothing = function() {
        recursive(data)
    }
    nothing();
})();

źródło
34
Mam nadzieję, że wszyscy głosujący na tę (technicznie poprawną) odpowiedź arguments.calleezdadzą sobie sprawę z problemów : jest ona niedozwolona w trybie ścisłym iw ES5.
Pointy
Odrzucono, arguments.callee jest przestarzałe w ES5
Jaime Rodriguez
Działa w NodeJS. Nie mogłem mniej przejmować się ES5, o ile działa w przewidywalny sposób w ustalonym środowisku.
Angad
1
To jest bomba zegarowa. Nie ma czegoś takiego jak „stałe” środowisko, jak sugeruje powyższy komentarz. Prawie zawsze dokonałbyś aktualizacji z dowolnego z tysięcy powodów, aby to zrobić.
sampathsris
6

Możesz zrobić coś takiego:

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

lub w Twoim przypadku:

(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
źródło
Możesz zadeklarować recurnajpierw za pomocą varoświadczenia. Nie wiem, czy to łamie reguły pytania, ale tak jak masz to teraz, bez varinstrukcji otrzymasz błąd w trybie ścisłym ECMAScript 5.
Tim Down
Mój początkowy komentarz zawierał varsł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;)
ArtBIT
Tak, działanie (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 .
Tim Down
3

Kiedy deklarujesz anonimową funkcję, taką jak ta:

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

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:

(function foo () {
    foo();
}());
foo //-> undefined
xj9
źródło
„pozostaje anonimowy” - nie, nie jest. Funkcja anonimowa nie ma nazwy. Rozumiem, że foonie jest to deklarowane w obecnym kontekście, ale to mniej więcej nieistotne. Funkcja o nazwie jest nadal funkcją nazwaną - nie anonimową.
Dziękuję
3

Dlaczego nie przekazać funkcji do samego functio?

    var functionCaller = function(thisCaller, data) {
        data = data + 1;
        var nothing = function() {
            thisCaller(thisCaller, data);
        };
        nothing();
    };
    functionCaller(functionCaller, data);
Riccardo Bassilichi
źródło
3

W niektórych sytuacjach musisz polegać na anonimowych funkcjach. Given jest mapfunkcją rekurencyjną :

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

Należy pamiętać, że mapnie może modyfikować struktury tablicy. Więc akumulator accnie musi być odsłonięty. Możemy mapna przykład zawinąć w inną funkcję:

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

  return next([])(xs);
}

Ale to rozwiązanie jest dość rozwlekłe. Użyjmy niedocenianego Ukombinatora:

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

Zwięzłe, prawda? Ujest śmiertelnie prosty, ale ma tę wadę, że wywołanie rekurencyjne jest nieco zaciemniane: sum(...)staje się h(h)(...)- to wszystko.


źródło
2

Nie jestem pewien, czy odpowiedź jest nadal wymagana, ale można to również zrobić za pomocą delegatów utworzonych za pomocą 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);

Nie dotyczy to nazwanych funkcji ani argumentów. Callee.

Nitij
źródło
1

Jak napisał bobince, po prostu nazwij swoją funkcję.

Ale zgaduję, że chcesz również przekazać wartość początkową i ostatecznie zatrzymać funkcję!

var initialValue = ...

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

działający przykład jsFiddle (używa danych + = dane dla zabawy)


Peter Ajtai
źródło
1
+1, To bardzo przydatna odpowiedź i powinieneś uzyskać więcej głosów za nią, ale nie jest to anonimowe.
Incognito,
wyraźnie nie czytał co napisałem bobince: However named inline function expressions are also best avoided.. Ale OP też mija się z celem ... :)
gblazex
@Galamb - przeczytałem. Niedozwolone w trybie ścisłym i ES5 to nie to samo, co zanieczyszczanie zakresu nadrzędnego i tworzenie dodatkowych instancji.
Peter Ajtai
1

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:

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

co daje ciąg, taki jak „Root: foo: bar: baz: ...”

radio_babylon
źródło
1

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:

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...

Należy pamiętać, że fjest to parametr z funkcją anonimową (x, y, n) => n === 0 ? x : f(y, x + y, n - 1)jako wartością domyślną. Kiedy fjest wywoływane przez applyTto wywołanie, musi odbywać się bez argumentów, aby została użyta wartość domyślna. Wartością domyślną jest funkcja, a zatem fjest to nazwana funkcja, która może wywoływać samą siebie rekurencyjnie.


źródło
0

Kolejna odpowiedź, która nie zawiera nazwanej funkcji ani arguments.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
jforjs
źródło
nice: wiąże anonimową funkcję z parametrem lokalnym, a następnie wywołuje funkcję za pomocą parametru lokalnego, ale także przekazuje funkcję do siebie w celu rekursji.
englebart
0

To jest przeróbka odpowiedzi jforjs z różnymi nazwami i nieco zmodyfikowanym wpisem.

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

Nie było potrzeby rozwijania pierwszej rekursji. Funkcja odbierająca siebie jako odniesienie nawiązuje do pierwotnego szlamu OOP.

englebart
źródło
0

To jest wersja odpowiedzi @ zem z funkcjami strzałkowymi.

Możesz użyć kombinatora Ulub Y. 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'))

Ricardo Freitas
źródło
0

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:

var Y = f => (x => x(x))(y => f(x => y(y)(x)));
myfirstAnswer
źródło
-1

To może nie działać wszędzie, ale możesz użyć, arguments.calleeaby odnieść się do bieżącej funkcji.

Tak więc silnię można zrobić w ten sposób:

var fac = function(x) { 
    if (x == 1) return x;
    else return x * arguments.callee(x-1);
}
Dan Jones
źródło