Die typische Methode zum Schleifen von x
Zeiten in JavaScript ist:
for (var i = 0; i < x; i++)
doStuff(i);
Aber ich möchte den ++
Operator nicht verwenden oder überhaupt keine veränderlichen Variablen haben. Gibt es in ES6 eine Möglichkeit, x
Zeiten auf eine andere Weise zu wiederholen ? Ich liebe Rubys Mechanismus:
x.times do |i|
do_stuff(i)
end
Ähnliches in JavaScript / ES6? Ich könnte irgendwie schummeln und meinen eigenen Generator bauen:
function* times(x) {
for (var i = 0; i < x; i++)
yield i;
}
for (var i of times(5)) {
console.log(i);
}
Natürlich benutze ich immer noch i++
. Zumindest ist es außer Sicht :), aber ich hoffe, dass es in ES6 einen besseren Mechanismus gibt.
Antworten:
OK!
Der folgende Code wurde mit ES6-Syntax geschrieben, kann aber genauso gut in ES5 oder noch weniger geschrieben werden. ES6 ist nicht erforderlich, um einen "Mechanismus zum x-maligen Schleifen" zu erstellen.
Wenn Sie den Iterator im Rückruf nicht benötigen , ist dies die einfachste Implementierung
Wenn Sie den Iterator benötigen , können Sie eine benannte innere Funktion mit einem Zählerparameter verwenden, um für Sie zu iterieren
Aber etwas sollte sich an diesen ...
if
sind hässlich - was passiert auf der anderen Verzweigung?undefined
- Hinweis auf eine unreine Nebenwirkung"Gibt es keinen besseren Weg?"
Es gibt. Lassen Sie uns zunächst unsere erste Implementierung erneut betrachten
Sicher, es ist einfach, aber beachten Sie, wie wir einfach anrufen
f()
und nichts damit anfangen. Dies schränkt die Art der Funktion, die wir mehrmals wiederholen können, wirklich ein. Selbst wenn wir den Iterator zur Verfügung haben,f(i)
ist er nicht viel vielseitiger.Was ist, wenn wir mit einer besseren Art der Funktionswiederholung beginnen? Vielleicht etwas, das Input und Output besser nutzt.
Generische Funktionswiederholung
Oben haben wir eine generische
repeat
Funktion definiert, die eine zusätzliche Eingabe benötigt, mit der die wiederholte Anwendung einer einzelnen Funktion gestartet wird.Implementierung
times
mitrepeat
Nun, das ist jetzt einfach. Fast die gesamte Arbeit ist bereits erledigt.
Da unsere Funktion
i
als Eingabe verwendet und zurückkehrti + 1
, funktioniert dies effektiv als unser Iterator, an den wir übergebenf
jedes Mal übergeben.Wir haben auch unsere Aufzählungsliste mit Problemen behoben
if
Aussagenundefined
JavaScript-Kommaoperator, der
Falls Sie Probleme haben zu sehen, wie das letzte Beispiel funktioniert, hängt es davon ab, ob Sie eine der ältesten Kampfachsen von JavaScript kennen. der Kommaoperator - kurz gesagt, er wertet Ausdrücke von links nach rechts aus und gibt den Wert des zuletzt ausgewerteten Ausdrucks zurück
In unserem obigen Beispiel verwende ich
Das ist nur eine prägnante Art zu schreiben
Tail Call-Optimierung
So sexy die rekursiven Implementierungen auch sind, an diesem Punkt wäre es für mich unverantwortlich, sie zu empfehlen, da keine JavaScript-VM, die ich mir vorstellen kann, die ordnungsgemäße Eliminierung von Tail Calls unterstützt - Babel, das zum Transpilieren verwendet wird, aber in "defekt; wird neu implementiert" "Status für weit über ein Jahr.
Als solches sollten wir unsere Implementierung von überdenken
repeat
, um sie stapelsicher zu machen.Der folgende Code tut verwenden änderbare Variablen
n
undx
aber zur Kenntnis , dass alle Mutationen auf die lokalisiertenrepeat
Funktion - keine staatlichen Veränderungen (Mutationen) sind sichtbar von außerhalb der FunktionViele von Ihnen werden sagen: "Aber das funktioniert nicht!" - Ich weiß, entspann dich einfach. Wir können einen Clojure-Stil
loop
/ eine Clojure-recur
Schnittstelle für Konstantraumschleifen unter Verwendung reiner Ausdrücke implementieren . keines davonwhile
.Hier abstrahieren wir
while
mit unsererloop
Funktion - sie sucht nach einem speziellenrecur
Typ, um die Schleife am Laufen zu halten. Wenn ein Nicht-recur
Typ angetroffen wird, ist die Schleife beendet und das Ergebnis der Berechnung wird zurückgegebenquelle
g => g(g)(x)
). Gibt es einen Vorteil einer Funktion höherer Ordnung gegenüber einer Funktion erster Ordnung, wie in meiner Lösung?Verwenden des Spread-Operators ES2015 :
[...Array(n)].map()
Oder wenn Sie das Ergebnis nicht brauchen:
Oder verwenden Sie den Operator ES2015 Array.from :
Array.from(...)
Beachten Sie, dass Sie String.prototype.repeat verwenden können, wenn Sie nur eine Zeichenfolge wiederholen müssen .
quelle
Array.from(Array(10), (_, i) => i*10)
[...Array(10)].forEach(() => console.log('looping 10 times');
quelle
Array
Schlüssel verwendet werden.[0..x]
in JS ein Haskell-Synonym gibt, das prägnanter ist als in meiner Antwort.Array.prototype.keys
und funktioniertObject.prototype.keys
, aber es ist auf den ersten Blick verwirrend.Ich denke, die beste Lösung ist
let
:Dadurch wird
i
für jede Körperbewertung eine neue (veränderbare) Variable erstellt, und es wird sichergestellt, dass die Variablei
nur im Inkrementausdruck in dieser Schleifensyntax geändert wird, nicht von irgendwo anders.Das sollte imo reichen. Selbst in reinen Sprachen bestehen alle Operationen (oder zumindest ihre Interpreten) aus Grundelementen, die Mutationen verwenden. Solange es richtig ist, kann ich nicht sehen, was daran falsch ist.
Du solltest in Ordnung sein mit
Dann besteht Ihre einzige Wahl darin, die Rekursion zu verwenden. Sie können diese Generatorfunktion auch ohne veränderliche Funktion definieren
i
:Aber das scheint mir übertrieben und könnte Leistungsprobleme haben (da die Eliminierung von Tail Calls nicht verfügbar ist
return yield*
).quelle
Dieses Snippet wird
console.log
test
4 mal.quelle
Ich finde es ziemlich einfach:
oder
quelle
Antwort: 09. Dezember 2015
Persönlich fand ich die akzeptierte Antwort sowohl prägnant (gut) als auch knapp (schlecht). Schätzen Sie, dass diese Aussage möglicherweise subjektiv ist. Lesen Sie daher diese Antwort und prüfen Sie, ob Sie zustimmen oder nicht
Das Beispiel in der Frage war so etwas wie Rubys:
Wenn Sie dies in JS mit den folgenden Angaben ausdrücken, können Sie Folgendes tun:
Hier ist der Code:
Das ist es!
Einfache Beispielverwendung:
Alternativ folgen Sie den Beispielen der akzeptierten Antwort:
Randnotiz - Definieren einer Bereichsfunktion
Eine ähnliche / verwandte Frage, die grundsätzlich sehr ähnliche Codekonstrukte verwendet, könnte sein, dass es in (Kern-) JavaScript eine praktische Bereichsfunktion gibt, die der Bereichsfunktion des Unterstrichs ähnelt.
Erstellen Sie ein Array mit n Zahlen, beginnend mit x
Unterstreichen
ES2015
Einige Alternativen:
Demo mit n = 10, x = 1:
In einem schnellen Test, den ich mit unserer Lösung und der doStuff-Funktion jeweils millionenfach ausgeführt habe, hat sich der frühere Ansatz (Array (n) .fill ()) als etwas schneller erwiesen.
quelle
Diese Version erfüllt die Anforderungen des OP an die Unveränderlichkeit. Erwägen Sie auch die Verwendung von
reduce
anstelle vonmap
abhängig von Ihrem Anwendungsfall.Dies ist auch eine Option, wenn Ihnen eine kleine Mutation in Ihrem Prototyp nichts ausmacht.
Jetzt können wir das tun
+1 an arcseldon für den
.fill
Vorschlag.quelle
Nicht etwas, das ich lehren würde (oder jemals in meinem Code verwenden würde), aber hier ist eine Codegolf-würdige Lösung, ohne eine Variable zu mutieren, ohne dass ES6 erforderlich ist:
Eigentlich eher eine interessante Proof-of-Concept-Sache als eine nützliche Antwort.
quelle
Array.apply(null, {length: 10})
nicht einfach seinArray(10)
?Hier ist eine weitere gute Alternative:
Wie @Dave Morse in den Kommentaren hervorhob, können Sie den
map
Aufruf vorzugsweise auch entfernen , indem Sie den zweiten Parameter derArray.from
Funktion wie folgt verwenden:quelle
Array.from
bei MDN: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…Array.from({ length: label.length }, (_, i) => (...))
Dies erspart das Erstellen eines leeren temporären Arrays, um einen Aufruf der Karte zu starten.Ich komme zu spät zur Party, aber da diese Frage in den Suchergebnissen häufig auftaucht, möchte ich nur eine Lösung hinzufügen, die meiner Meinung nach die beste Lesbarkeit ist, wenn sie nicht lang ist (ideal für jede Codebasis-IMO). . Es mutiert, aber ich würde diesen Kompromiss für die KISS-Prinzipien eingehen.
quelle
Afaik, es gibt keinen Mechanismus in ES6, der Rubys
times
Methode ähnelt . Sie können jedoch eine Mutation vermeiden, indem Sie die Rekursion verwenden:Demo: http://jsbin.com/koyecovano/1/edit?js,console
quelle
Wenn Sie bereit sind, eine Bibliothek zu nutzen, gibt es auch Lodash
_.times
oder Unterstrich_.times
:Beachten Sie, dass dies ein Array der Ergebnisse zurückgibt, also eher wie dieser Rubin:
quelle
Im funktionalen Paradigma
repeat
ist normalerweise eine unendliche rekursive Funktion. Um es zu verwenden, benötigen wir entweder eine verzögerte Bewertung oder einen Weitergabestil.Lazy bewertete Funktionswiederholung
Ich benutze einen Thunk (eine Funktion ohne Argumente), um eine verzögerte Auswertung in Javascript zu erreichen.
Funktionswiederholung mit Fortsetzungsstil
CPS ist zunächst etwas beängstigend. Es folgt jedoch immer dem gleichen Muster: Das letzte Argument ist die Fortsetzung (eine Funktion), die ihren eigenen Körper aufruft :
k => k(...)
. Bitte beachten Sie, dass CPS die Anwendung von innen nach außen dreht, alsotake(8) (repeat...)
wird ,k(take(8)) (...)
wok
die teilweise angewandtrepeat
.Fazit
Durch die Trennung der Wiederholung (
repeat
) von der Beendigungsbedingung (take
) erhalten wir Flexibilität - Trennung von Bedenken bis zu ihrem bitteren Ende: D.quelle
Vorteile dieser Lösung
Nachteile - Mutation. Nur intern zu sein ist mir egal, vielleicht werden es auch einige andere nicht.
Beispiele und Code
TypeScipt-Version
https://codepen.io/whitneyland/pen/aVjaaE?editors=0011
quelle
Adressierung des funktionalen Aspekts:
quelle
i
. Was ist der Grund, um danntimes
über einfach alt zu verwendenfor
?var twice = times(2);
.for
zweimal verwenden?i++
. Es ist nicht offensichtlich, wie es besser ist, etwas inakzeptables in eine Funktion zu packen.Generatoren? Rekursion? Warum so viel Mutatin? ;-);
Wenn es akzeptabel ist, solange wir es "verstecken", akzeptieren Sie einfach die Verwendung eines unären Operators und wir können die Dinge einfach halten :
Genau wie in Rubin:
quelle
Ich habe die Antwort von @Tieme mit einer Hilfsfunktion versehen.
In TypeScript:
Jetzt können Sie ausführen:
quelle