Javascript: rekursive anonyme Funktion?


120

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.


Gibt es einen Grund, warum du das brauchst oder bist du nur neugierig? Mir scheint, es wäre klarer, ihm einfach einen Namen zu geben ...
rfunduk

1
@thenduks: Aus dem gleichen Grund, warum man eine anonyme Funktion verwenden würde. Nur dass manchmal eine Rekursion notwendig ist.
Poke

5
Es ist eine Schande 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 ...
Kobi

1
Ja, wie Kobi verknüpft hat, verwenden Sie einen Festkomma-Kombinator wie Y, um anonyme rekursive Funktionen ohne Argumente auszuführen.
Dampfer25

1
Ein Beispiel für den Y-Kombinator in JS finden Sie unter w3future.com/weblog/stories/2002/02/22/… .
Dampfer25

Antworten:


145

Sie können der Funktion einen Namen geben, auch wenn Sie die Funktion als Wert und nicht als Anweisung "Funktionsdeklaration" erstellen. Mit anderen Worten:

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

ist eine rekursive Funktion zum Stapeln von Stapeln. Nun, das heißt, Sie möchten dies wahrscheinlich nicht generell 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):

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

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.


Können wir vermeiden, den globalen Namespace mit ES5 sctrict auf andere Weise zu verschmutzen (ich habe noch nicht tief in ES5 gelesen)?
Inkognito

@pointy kannst du dir bitte diese Quest ansehen. stackoverflow.com/questions/27473450/…
Gladson Robinson

Ich denke, es ist nicht möglich, (() => { call_recursively_self_here() })()sich selbst rekursiv zu verwenden und aufzurufen, oder? Ich muss ihm einen Namen geben.
Qwerty

1
@Qwerty gut du könntest so etwas wie das letzte Beispiel in meiner Antwort machen. Binden Sie die Pfeilfunktion an eine lokale Variable in einer Wrapper-Funktion, damit Ihre Pfeilfunktion mit dem Variablennamen auf sich selbst verweisen kann. Der Wrapper würde dann die Variable zurückgeben (die sich auf die Pfeilfunktion bezieht).
Pointy

1
@Pointy vielleicht finden einige Hacker Anwendung;)
Kamil Kiełczewski

31

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)

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

Und wenn Sie Ihre anonyme Funktion übergeben möchten:

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

Das Wichtigste an dieser Lösung ist, dass Sie sie nicht verwenden sollten.


16
"Das Wichtigste an dieser Lösung ist, dass Sie sie nicht verwenden sollten." Warum?
Nyuszika7h

7
Es wird nicht schnell gehen. Es ist hässlich zu benutzen (obwohl konzeptionell schön!). Sie müssen Ihrer Funktion keinen Tag- oder Variablennamen geben (und ich verstehe nicht, warum dies ein Problem darstellt), aber Sie geben ihr dennoch einen Namen als Parameter für die an Y übergebene äußere Funktion. Sie tun dies also nicht Gewinnen Sie alles, indem Sie all diese Schwierigkeiten durchmachen.
zem

Vergessen Sie nicht zu erwähnen, dass diese Funktion nicht stapelsicher ist. Nur ein paar tausend Schleifen führen zu einem Stapelüberlauf.
Vielen Dank, dass Sie

Hallo, ich würde eine etwas "sauberere" Modifikation vorschlagen, da mir .apply (null, Argumente) hässlich erscheint: var Y = Funktion (gen) {return (Funktion (f) {return f (f);} (Funktion (f)) {return gen (Funktion (x) {return f (f) (x);});})); } Oder äquivalent ((Funktion (x) {return y} gleich (x => y))) unter Verwendung der Pfeilnotation (gültiger js-Code): var Y = gen => (f => f (f)) (f = > gen (x => f (f) (x)))
myfirstAnswer

23

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

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

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

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.

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

Was hier nicht sofort ersichtlich ist, ist, dass unsere Funktion, wenn sie zum ersten Mal mit dem UKombinator 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.

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

Nur ist dies keine direkte Rekursion - eine Funktion, die sich selbst mit ihrem eigenen Namen aufruft. Unsere Definition von countDownbezieht sich nicht auf sich selbst in seinem Körper und dennoch ist eine Rekursion möglich

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

Entfernen 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

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

Verwenden Sie jetzt den U-Kombinator, um den inneren Verweis auf zu ersetzen factorial

const U = f => f (f)

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

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

Das grundlegende Ersatzmuster ist dieses. Machen Sie sich eine mentale Notiz, wir werden im nächsten Abschnitt eine ähnliche Strategie anwenden

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

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

Y Kombinator

verwandt: Die U- und Y-Kombinatoren werden anhand einer Spiegelanalogie erklärt

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

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

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

const U = f => f (f)

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

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

Jetzt werde ich das countDownProgramm mit demonstrieren Y- Sie werden sehen, dass die Programme fast identisch sind, aber der Y-Kombinator hält die Dinge ein bisschen sauberer

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

Und jetzt werden wir sehen , factorialwie gut

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

Wie Sie sehen können, fwird 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, wie recurunten -

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


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

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

Dies ist jedoch schlecht, da der interne Status (Zähler aund b) angezeigt wird . Es wäre schön, wenn wir einfach anrufen könnten fibonacci (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 Yzusammengesetzten Daten oder erweiterten Sprachmerkmalen ändern oder uns darauf verlassen zu müssen.

Schauen Sie sich die Definition von fibonacciunten an. Wir sind sofort anwenden 0und 1die an gebunden sind aund bjeweils. Jetzt wartet Fibonacci einfach darauf, dass das letzte Argument geliefert wird, an das gebunden wird x. Wenn wir wiederkehren, müssen wir f (a) (b) (x)(nicht f (a,b,x)) aufrufen, weil unsere Funktion in Curry-Form vorliegt.

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


Diese Art von Muster kann nützlich sein, um alle Arten von Funktionen zu definieren. Unten sehen wir zwei weitere Funktionen, die mit dem YKombinator ( rangeund reduce) und einer Ableitung von 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 ]


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

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

Und da haben Sie es - fibonacci (7)rekursiv berechnet mit nichts als anonymen Funktionen


14

Es kann am einfachsten sein, stattdessen ein "anonymes Objekt" zu verwenden:

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

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.

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

13

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

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

Benannte Inline-Funktionsausdrücke werden jedoch am besten vermieden, da das JScript des IE einige schlechte Dinge mit ihnen macht. Im obigen Beispiel foowird der übergeordnete Bereich im IE fälschlicherweise verschmutzt, und der übergeordnete Bereich fooist eine separate Instanz gegenüber dem darin foogezeigten foo.

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


Ich bin damit einverstanden, dass eine benannte Funktion besser geeignet ist als Argumente. Dies gilt nicht nur für den strengen Ecmascript-Modus, sondern auch für Optimierungsfragen, da er bei jeder Rekursion einen Verweis auf den Angerufenen erhalten muss (und dies könnte wahrscheinlich die Ausführungsgeschwindigkeit verringern )

+1 für das Poetische, "pushing against the boundaries of good taste"- (na ja, und die guten Infos).
Peter Ajtai

Was ist mit einem einfachen Pre / Postfix, wenn die Umweltverschmutzung hier wirklich ein Problem darstellt? In Anbetracht dessen, dass es nicht im globalen Bereich liegt (selbst wenn die Funktion top lvl ist, sollte er bereits eine anonyme Funktion haben, die seinen gesamten Code umschließt), ist es wirklich unwahrscheinlich, dass ein Name wie recur_foomit einer Funktion im übergeordneten Bereich kollidiert (oder krank ist) -gebraucht) .
Gblazex

Sehr interessant - jsfiddle.net/hck2A - IE verschmutzt in diesem Fall die Eltern, wie Sie sagten. Das habe ich nie bemerkt.
Peter Ajtai

1
@Peter: kangax.github.com/nfe (insbesondere 'JScript-Fehler') für mehr, als Sie jemals zu diesem Thema wissen wollten. Es ist endlich in IE9 behoben (aber nur im IE9-Standardmodus).
Bobince

10
(function(data){
    var recursive = arguments.callee;
    data = data+1;
    var nothing = function() {
        recursive(data)
    }
    nothing();
})();

34
Ich hoffe, jeder, der für diese (technisch korrekte) Antwort stimmt, erkennt die Probleme mit arguments.callee: Sie ist im strengen Modus und in ES5 nicht zulässig.
Pointy

Abgestimmt, ist argument.callee in ES5
Jaime Rodriguez

Es funktioniert in NodeJS. ES5 ist mir egal, solange es in einer festen Umgebung vorhersehbar funktioniert.
Angad

1
Dies ist eine Zeitbombe. Es gibt keine so genannte "feste" Umgebung, wie der obige Kommentar nahelegt. Sie würden fast immer aus einem der Tausenden von Gründen ein Upgrade durchführen.
Sampathsris

6

Sie könnten so etwas tun wie:

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

oder in deinem Fall:

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

Sie könnten es tun, recurzuerst mit einer varAussage zu deklarieren . Keine Ahnung, ob dies gegen die Regeln der Frage verstößt, aber wie Sie es jetzt haben, erhalten Sie ohne die varAnweisung einen Fehler im strengen ECMAScript 5-Modus.
Tim Down

Mein erster Kommentar enthielt das varSchlü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;)
ArtBIT

Ja, dies (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 schlug var recur; (recur = function() {...})();stattdessen vor.
Tim Down

3

Wenn Sie eine anonyme Funktion wie folgt deklarieren:

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

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:

(function foo () {
    foo();
}());
foo //-> undefined

"es bleibt anonym" - nein, tut es nicht. Eine anonyme Funktion hat keinen Namen. Ich verstehe, dass foodies 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.
Vielen Dank, dass Sie

3

Warum nicht die Funktion an die Funktion selbst übergeben?

    var functionCaller = function(thisCaller, data) {
        data = data + 1;
        var nothing = function() {
            thisCaller(thisCaller, data);
        };
        nothing();
    };
    functionCaller(functionCaller, data);

3

In bestimmten Situationen müssen Sie sich auf anonyme Funktionen verlassen. Gegeben ist eine rekursive mapFunktion:

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

Bitte beachten Sie, dass mapdie Struktur des Arrays nicht geändert werden darf. Der Akku accmuss also nicht freigelegt werden. Wir können mapzum Beispiel in eine andere Funktion einwickeln :

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

  return next([])(xs);
}

Aber diese Lösung ist ziemlich ausführlich. Verwenden wir den unterschätzten UKombinator:

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

Prägnant, nicht wahr? Uist denkbar einfach, hat aber den Nachteil, dass der rekursive Aufruf etwas verschleiert wird: sum(...)wird h(h)(...)- das ist alles.


2

Ich bin nicht sicher, ob die Antwort noch erforderlich ist, aber dies kann auch mit Delegaten erfolgen, die mit function.bind erstellt wurden:

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

Dies beinhaltet keine benannten Funktionen oder Argumente.


1

Wie Bobince schrieb, benennen Sie einfach Ihre Funktion.

Aber ich vermute, Sie möchten auch einen Anfangswert übergeben und Ihre Funktion schließlich beenden!

var initialValue = ...

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

funktionierendes jsFiddle-Beispiel (verwendet Daten + = Daten zum Spaß)



1
+1, Dies ist eine sehr nützliche Antwort und Sie sollten mehr Upvotes dafür erhalten, aber sie ist nicht anonym.
Inkognito

Sie haben offensichtlich nicht gelesen, was Bobince geschrieben hat : However named inline function expressions are also best avoided.. Aber das OP verfehlt auch den Punkt ... :)
gblazex

@ Galamb - ich habe es gelesen. Im strengen Modus und in ES5 nicht zulässig, bedeutet nicht, einen übergeordneten Bereich zu verschmutzen und zusätzliche Instanzen zu erstellen.
Peter Ajtai

1

Ich brauchte (oder wollte) eine anonyme einzeilige Funktion, um ein Objekt zu erreichen, das eine Zeichenfolge aufbaut, und behandelte es folgendermaßen:

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

das erzeugt eine Zeichenfolge wie 'Root: foo: bar: baz: ...'


1

Mit ES2015 können wir ein bisschen mit der Syntax herumspielen und Standardparameter und Thunks missbrauchen. Letztere sind nur Funktionen ohne Argumente:

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

Bitte beachten Sie, dass dies fein Parameter ist, (x, y, n) => n === 0 ? x : f(y, x + y, n - 1)dessen Standardwert die anonyme Funktion ist. Wann fdurch applyTdiesen Aufruf aufgerufen wird, muss ohne Argumente erfolgen, damit der Standardwert verwendet wird. Der Standardwert ist eine Funktion und daher feine benannte Funktion, die sich selbst rekursiv aufrufen kann.


0

Eine andere Antwort, die keine benannte Funktion oder Argumente beinhaltet

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

nice: binde eine anonyme Funktion an einen lokalen Parameter und rufe die Funktion dann über den lokalen Parameter auf, übergebe die Funktion aber auch für die Rekursion an sich selbst.
Englebart

0

Dies ist eine Überarbeitung der Antwort von jforjs mit verschiedenen Namen und einem leicht modifizierten Eintrag.

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

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.


0

Dies ist eine Version der Antwort von @ zem mit Pfeilfunktionen.

Sie können den Uoder den YKombinator 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'))


0

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:

var Y = f => (x => x(x))(y => f(x => y(y)(x)));

-1

Dies funktioniert möglicherweise nicht überall, aber Sie können arguments.calleeauf die aktuelle Funktion verweisen.

Fakultät könnte also so gemacht werden:

var fac = function(x) { 
    if (x == 1) return x;
    else return x * arguments.callee(x-1);
}
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.