Da Scala keine alten Java- for
Schleifen mit Index hat,
// does not work
val xs = Array("first", "second", "third")
for (i=0; i<xs.length; i++) {
println("String #" + i + " is " + xs(i))
}
Wie können wir effizient und ohne Verwendung von var
s iterieren ?
Du könntest das tun
val xs = Array("first", "second", "third")
val indexed = xs zipWithIndex
for (x <- indexed) println("String #" + x._2 + " is " + x._1)
Die Liste wird jedoch zweimal durchlaufen - nicht sehr effizient.
while
Schleife. Soweit ich mich erinnere, gab es vor einigen Jahren eine Debatte darüber, ob Scala Javasfor(;;)
Schleife erben sollte , und es wurde entschieden, dass der Nutzen nicht ausreichte, um die zusätzliche Komplexität zu rechtfertigen.Antworten:
Viel schlimmer als zweimaliges Durchlaufen, erzeugt es ein Zwischenarray von Paaren. Sie können verwenden
view
. Wenn Sie dies tuncollection.view
, können Sie sich nachfolgende Anrufe als träge während der Iteration vorstellen. Wenn Sie eine ordnungsgemäß vollständig realisierte Sammlung zurückerhalten möchten, rufen Sieforce
am Ende an. Hier wäre das nutzlos und teuer. Ändern Sie also Ihren Code inquelle
List
der am häufigsten verwendeten Sammlung in Scala) zu einer schrecklichen Leistung - und nicht nur bei diesen. Überprüfen Sie dieapply
Operation hier . In einer verknüpften listenähnlichen Sammlung führt jeder Zugriff auf ein Element nach Index zu einem Durchlaufen der Liste.Es wurde erwähnt , dass Scala tut haben Syntax für
for
Schleifen:oder einfach
Sie haben jedoch auch nach Effizienz gefragt. Es stellt sich heraus , dass die Scala
for
Syntax höherer Ordnung Methoden wie eigentlich syntaktischer Zucker istmap
,foreach
usw. Als solche in einigen Fällen diese Schleifen kann ineffizient sein, zB Wie optimize for-Comprehensions und Schleifen in Scala?(Die gute Nachricht ist, dass das Scala-Team daran arbeitet, dies zu verbessern. Hier ist das Problem im Bug-Tracker: https://issues.scala-lang.org/browse/SI-4633 )
Um ein Höchstmaß an Effizienz zu erzielen, können Sie eine
while
Schleife oder, wenn Sie darauf bestehen, die Verwendung von zu verwendenvar
, die Schwanzrekursion verwenden:Beachten Sie, dass die optionale
@tailrec
Annotation hilfreich ist, um sicherzustellen, dass die Methode tatsächlich rekursiv ist. Der Scala-Compiler übersetzt rekursive Aufrufe in den Bytecode, der while-Schleifen entspricht.quelle
xs
es sich um eine verknüpfte Liste handelt (wie die weit verbreiteteList
), der Zugriff auf ihre Elemente über einen Indexxs(i)
linear ist und daherfor (i <- xs.indices) println(i + " : " + xs(i))
einefor((x, i) <- xs.zipWithIndex) println(i + " : " + x)
weitaus schlechtere Leistung als gerade erzielt wird , da dies zu viel mehr als nur führt zwei Durchquerungen unter der Haube. Daher sollte die Antwort von @didierd, die die Verwendung von Ansichten vorschlägt, als die allgemeinste und idiomatischste, IMO, akzeptiert werden.view
verwendet wird, übt diese selbst hohe Abstraktionsebene mehr Druck auf den Heap und den GC aus. Nach meiner Erfahrung kann die Leistung häufig um den Faktor 10 gesteigert werden, indem die Heap-Zuordnung im numerischen Code vermieden wird.Noch ein Weg:
quelle
Tatsächlich hat Scala alte Java-Schleifen mit Index:
Wobei
0 until xs.length
oder0.until(xs.length)
ist eineRichInt
Methode, dieRange
für eine Schleife geeignet ist.Sie können auch versuchen, Schleife mit
to
:quelle
xs(i)
auf Listen erhöht die Komplexität auf O (n ^ 2)Wie wäre es damit?
Ausgabe:
quelle
Das Einlaufen in Scala ist ziemlich einfach. Erstellen Sie beispielsweise ein Array Ihrer Wahl.
Arten von Schleifen,
quelle
Wenn Sie
zipWithIndex
eine Sammlung aufrufen, wird diese durchlaufen und eine neue Sammlung für die Paare erstellt. Um dies zu vermeiden, können Sie einfachzipWithIndex
den Iterator für die Sammlung aufrufen . Dadurch wird nur ein neuer Iterator zurückgegeben, der den Index während der Iteration verfolgt, ohne dass eine zusätzliche Sammlung oder ein zusätzliches Durchlaufen erstellt werden muss.So wird
scala.collection.Iterator.zipWithIndex
derzeit in 2.10.3 implementiert:Dies sollte sogar etwas effizienter sein als das Erstellen einer Ansicht für die Sammlung.
quelle
Es gibt nichts in der stdlib, was es für Sie tun könnte, ohne Tupelmüll zu erzeugen, aber es ist nicht zu schwer, Ihren eigenen zu schreiben. Leider habe ich mich nie darum gekümmert, herauszufinden, wie man die richtige implizite CanBuildFrom-Raindance ausführt, um solche Dinge in der Art der Sammlung, auf die sie angewendet werden, generisch zu machen, aber wenn es möglich ist, wird uns sicher jemand aufklären. :) :)
quelle
Einige weitere Möglichkeiten zum Iterieren:
foreach und ähnliche Karte, die etwas zurückgeben würde (die Ergebnisse der Funktion, die für println Unit ist, also eine Liste von Einheiten)
arbeite mit den Elementen, nicht mit dem Index
falten
Das Carry-Teil ist nur ein Beispiel, um etwas mit i und j zu tun. Es muss kein Int sein.
für einfachere Sachen, näher an üblichen For-Loops:
oder ohne Bestellung:
quelle
Ich habe die folgenden Ansätze
quelle
Ein einfacher und effizienter Weg, inspiriert von der Implementierung
transform
in SeqLike.scalaquelle
Die vorgeschlagenen Lösungen leiden unter der Tatsache, dass sie entweder explizit über eine Sammlung iterieren oder die Sammlung in eine Funktion einfügen. Es ist natürlicher, sich an die üblichen Redewendungen von Scala zu halten und den Index in die üblichen Karten- oder Foreach-Methoden einzufügen. Dies kann durch Auswendiglernen erfolgen. Der resultierende Code könnte so aussehen
Hier ist ein Weg, um diesen Zweck zu erreichen. Betrachten Sie das folgende Dienstprogramm:
Das ist schon alles was du brauchst. Sie können dies beispielsweise wie folgt anwenden:
was zu der Liste führt
Auf diese Weise können Sie die üblichen Traversable-Funktionen auf Kosten der Umhüllung Ihrer effektiven Funktion verwenden. Genießen!
quelle