tl; dr : Es gibt Kompromisse zwischen sp eed und Bequemlichkeit; Sie müssen Ihren Anwendungsfall kennen, um entsprechend auszuwählen.
Wenn Sie wissen beide Arrays die gleiche Länge haben und Sie brauchen sich keine Sorgen zu machen, wie schnell es ist, die einfachste und kanonische ist zip
in einem für Verständnis zu verwenden:
for ((a,b) <- aList zip bList) { ??? }
Die Methode zip
erstellt jedoch ein neues einzelnes Array. Um diesen Aufwand zu vermeiden Sie zipped
auf einem Tupel verwenden können, welche die Elemente paarweise Methoden präsentieren wie foreach
und map
:
(aList, bList).zipped.foreach{ (a,b) => ??? }
Schneller noch zu Index in den Arrays, vor allem, wenn die Arrays Primitiven wie Int
enthalten, da der generische Code oben sie boxen muss.Es ist eine praktische Methode, indices
die Sie verwenden können:
for (i <- aList.indices) { ??? }
Schließlich, wenn Sie so schnell gehen müssen, wie Sie vielleicht können Sie während Schleifen oder Rekursion zurück auf manuelle fallen kann, etwa so:
// While loop
var i = 0
while (i < aList.length) {
???
i += 1
}
// Recursion
def loop(i: Int) {
if (i < aList.length) {
???
loop(i+1)
}
}
loop(0)
wenn Sie etwas Wert sind Rechen, anstatt es eine Nebenwirkung zu sein hat, ist es manchmal mit Rekursion schneller, wenn Sie es entlang passieren:
// Recursion with explicit result
def loop(i: Int, acc: Int = 0): Int =
if (i < aList.length) {
val nextAcc = ???
loop(i+1, nextAcc)
}
else acc
Da Sie eine Methodendefinition fallen kann i n überall können Sie Rekursion ohne Einschränkung verwenden. Sie können eine @annotation.tailrec
Annotation hinzufügen, um sicherzustellen, dass sie zu einer schnellen Schleife mit Sprüngen anstelle einer tatsächlichen Rekursion kompiliert werden kann, die Stapelplatz verbraucht.
public class DotProd {
public static int dot(int[] a, int[] b) {
int s = 0;
for (int i = 0; i < a.length; i++) s += a[i]*b[i];
return s;
}
}
sowie eine äquivalente Version, wo wir das Skalarprodukt der Längen nehmen:
all diese verschiedenen Ansätze Einen Punkt Produkt auf Länge 1024 Vektoren zu berechnen, können wir diese zu einer Referenz-Implementierung in Java vergleichen Dies ist besonders schlecht von Strings
normalized time
-----------------
primitive object method
--------- ------ ---------------------------------
100% 100% Java indexed for loop (reference)
100% 100% Scala while loop
100% 100% Scala recursion (either way)
185% 135% Scala for comprehension on indices
2100% 130% Scala zipped
3700% 800% Scala zip
(so können wir Objekte vs. Primitiven beurteilen), natürlich mit Primitiven! (Sie erhalten ähnlich große Zeitsprünge, wenn Sie versuchen, ArrayList
s von Integer
anstelle von Array
von int
in Java zu verwenden.) Beachten Sie insbesondere, dass zipped
eine recht vernünftige Wahl ist, wenn Sie Objekte gespeichert haben.
Vorsicht vor vorzeitiger Optimierung! Es gibt Vorteile in Bezug auf Klarheit und Sicherheit für funktionelle Formen wie zip
. Wenn Sie immer while-Schleifen schreiben, weil Sie denken, dass "jedes kleine Bit hilft", machen Sie wahrscheinlich einen Fehler, weil es mehr Zeit zum Schreiben und Debuggen benötigt und Sie könnten diese Zeit nutzen, um einen wichtigeren Teil Ihres Programms zu optimieren.
Aber vorausgesetzt, Ihre Arrays sind die gleiche Länge ist gefährlich. Sind Sie sicher? Wie viel Aufwand wirst du machen, um sicher zu gehen? Vielleicht solltest du diese Annahme nicht machen?
Wenn Sie es nicht benötigen, um schnell zu sein, nur korrigieren, dann müssen Sie wählen, was zu tun ist, wenn die beiden Arrays nicht die gleiche Länge haben.
Wenn Sie etwas mit allen Elementen bis zur Länge der kürzeren tun wollen, dann zip
ist nach wie vor, was Sie verwenden:
// The second is just shorthand for the first
(aList zip bList).foreach{ case (a,b) => ??? }
for ((a,b) <- (aList zip bList)) { ??? }
// This avoids an intermediate array
(aList, bList).zipped.foreach{ (a,b) => ??? }
Wenn Sie stattdessen Pad wollen die kürzere mit einem Standardwert Sie
aList.zipAll(bList, aDefault, bDefault).foreach{ case (a,b) => ??? }
for ((a,b) <- aList.zipAll(bList, aDefault, bDefault)) { ??? }
In jedem dieser Fälle würde, können Sie yield
mit for
oder map
statt foreach
verwenden, um eine Sammlung zu erzeugen.
Wenn Sie den Index für eine Berechnung benötigen oder es wirklich ein Array ist und Sie es wirklich schnell benötigen, müssen Sie die Berechnung manuell durchführen.Fehlende Elemente Klotzen umständlich ist (überlasse ich den Leser als Übung), aber die Grundform wäre:
for (i <- 0 until math.min(aList.length, bList.length)) { ??? }
, wo man dann i
in aList
und bList
zu Index verwenden.
Wenn Sie wirklich Höchstgeschwindigkeit benötigen würden Sie wieder verwenden (Schwanz) Rekursion oder While-Schleifen:
val n = math.min(aList.length, bList.length)
var i = 0
while (i < n) {
???
i += 1
}
def loop(i: Int) {
if (i < aList.length && i < bList.length) {
???
loop(i+1)
}
}
loop(0)
Was ist los mit dem, was Sie mit fangen? Wenn das Problem ist, dass es nicht kompiliert, fügen Sie einfach Definitionen zum Tupel hinzu: 'for ((aListItem: Int, bListItem: Int) ...' –
Related: http://Stackoverflow.com/q/17199534/406435 – senia
Die Scala Way (TM) verwendet keine Arrays (die änderbar sind), sondern Listen, und diese sind ineffektiv für den wahlfreien Zugriff (dh per Index). Wie auch immer Sie die richtige Lösung verwenden, ist in Rex Kerrs Antwort begraben: '(aList, bList) .zip.foreach {(a, b) => ???}. Und ja, es ist effizient, weil es keine Tupel für jedes Element erzeugen muss (anders als' zip'). –