Antworten:
Es wird in Sequenzverständnissen verwendet (wie Pythons Listenverständnis und Generatoren, wo Sie es auch verwenden können yield
).
Es wird in Kombination mit angewendet for
und schreibt ein neues Element in die resultierende Sequenz.
Einfaches Beispiel (von scala-lang )
/** Turn command line arguments to uppercase */
object Main {
def main(args: Array[String]) {
val res = for (a <- args) yield a.toUpperCase
println("Arguments: " + res.toString)
}
}
Der entsprechende Ausdruck in F # wäre
[ for a in args -> a.toUpperCase ]
oder
from a in args select a.toUpperCase
in Linq.
Ruby's yield
hat einen anderen Effekt.
Ich denke, die akzeptierte Antwort ist großartig, aber es scheint, dass viele Menschen einige grundlegende Punkte nicht verstanden haben.
Erstens entspricht Scalas for
Verständnis dem von Haskelldo
Notation , und es ist nichts weiter als ein syntaktischer Zucker für die Zusammensetzung mehrerer monadischer Operationen. Da diese Aussage höchstwahrscheinlich niemandem helfen wird, der Hilfe benötigt, versuchen wir es noch einmal… :-)
Scalas for
Verständnis ist syntaktischer Zucker für die Komposition mehrerer Operationen mit Karte flatMap
und filter
. Oder foreach
. Scala übersetzt einen for
Ausdruck tatsächlich in Aufrufe dieser Methoden, sodass jede Klasse, die sie bereitstellt, oder eine Teilmenge davon zum Verständnis verwendet werden kann.
Lassen Sie uns zunächst über die Übersetzungen sprechen. Es gibt sehr einfache Regeln:
Diese
for(x <- c1; y <- c2; z <-c3) {...}
wird übersetzt in
c1.foreach(x => c2.foreach(y => c3.foreach(z => {...})))
Diese
for(x <- c1; y <- c2; z <- c3) yield {...}
wird übersetzt in
c1.flatMap(x => c2.flatMap(y => c3.map(z => {...})))
Diese
for(x <- c; if cond) yield {...}
wird auf Scala 2.7 in übersetzt
c.filter(x => cond).map(x => {...})
oder auf Scala 2.8 in
c.withFilter(x => cond).map(x => {...})
mit einem Fallback in die erstere, wenn Methode withFilter
nicht verfügbar ist, aber filter
ist. Weitere Informationen hierzu finden Sie im folgenden Abschnitt.
Diese
for(x <- c; y = ...) yield {...}
wird übersetzt in
c.map(x => (x, ...)).map((x,y) => {...})
Wenn Sie sich sehr einfache for
Erkenntnisse ansehen, sehen die map
/ foreach
Alternativen tatsächlich besser aus. Sobald Sie mit dem Komponieren beginnen, können Sie sich jedoch leicht in Klammern und Verschachtelungsebenen verlieren. Wenn dies geschieht, ist das for
Verständnis normalerweise viel klarer.
Ich werde ein einfaches Beispiel zeigen und absichtlich jede Erklärung weglassen. Sie können entscheiden, welche Syntax leichter zu verstehen war.
l.flatMap(sl => sl.filter(el => el > 0).map(el => el.toString.length))
oder
for {
sl <- l
el <- sl
if el > 0
} yield el.toString.length
withFilter
In Scala 2.8 wurde eine Methode namens aufgerufen withFilter
, deren Hauptunterschied darin besteht, dass anstelle einer neuen, gefilterten Sammlung bei Bedarf gefiltert wird. Das filter
Verhalten der Methode wird basierend auf der Strenge der Sammlung definiert. Um dies besser zu verstehen, werfen wir einen Blick auf einige Scala 2.7 mit List
(streng) und Stream
(nicht streng):
scala> var found = false
found: Boolean = false
scala> List.range(1,10).filter(_ % 2 == 1 && !found).foreach(x => if (x == 5) found = true else println(x))
1
3
7
9
scala> found = false
found: Boolean = false
scala> Stream.range(1,10).filter(_ % 2 == 1 && !found).foreach(x => if (x == 5) found = true else println(x))
1
3
Der Unterschied tritt auf, weil filter
sofort angewendet List
wird und eine Liste der Gewinnchancen zurückgegeben wird - da found
ist false
. Erst dann foreach
wird ausgeführt, aber zu diesem Zeitpunkt ist eine Änderung found
bedeutungslos, wie filter
bereits ausgeführt.
Im Falle von Stream
wird die Bedingung nicht sofort angewendet. Testen Sie stattdessen, wie jedes Element von angefordert wird foreach
, filter
die Bedingung, foreach
um sie zu beeinflussen found
. Um es klar zu machen, hier ist der äquivalente Code für das Verständnis:
for (x <- List.range(1, 10); if x % 2 == 1 && !found)
if (x == 5) found = true else println(x)
for (x <- Stream.range(1, 10); if x % 2 == 1 && !found)
if (x == 5) found = true else println(x)
Dies verursachte viele Probleme, da die Leute erwarteten if
, dass das On-Demand-Verfahren berücksichtigt wird, anstatt es zuvor auf die gesamte Sammlung anzuwenden.
Es wurde Scala 2.8 eingeführt withFilter
, die unabhängig von der Strenge der Sammlung immer nicht streng ist. Das folgende Beispiel zeigt List
mit beiden Methoden in Scala 2.8:
scala> var found = false
found: Boolean = false
scala> List.range(1,10).filter(_ % 2 == 1 && !found).foreach(x => if (x == 5) found = true else println(x))
1
3
7
9
scala> found = false
found: Boolean = false
scala> List.range(1,10).withFilter(_ % 2 == 1 && !found).foreach(x => if (x == 5) found = true else println(x))
1
3
Dies führt zu dem Ergebnis, das die meisten Menschen erwarten, ohne das Verhalten zu ändern filter
. Als Randnotiz Range
wurde zwischen Scala 2.7 und Scala 2.8 von nicht streng zu streng geändert.
withFilter
soll auch für strenge Sammlungen nicht streng sein, was eine Erklärung verdient. Ich werde das in Betracht ziehen ...
for(x <- c; y <- x; z <-y) {...}
wird übersetzt in c.foreach(x => x.foreach(y => y.foreach(z => {...})))
2. for(x <- c; y <- x; z <- y) yield {...}
wird übersetzt inc.flatMap(x => x.flatMap(y => y.map(z => {...})))
for(x <- c; y = ...) yield {...}
wirklich übersetzt c.map(x => (x, ...)).map((x,y) => {...})
? Ich denke, es ist übersetzt c.map(x => (x, ...)).map(x => { ...use x._1 and x._2 here...})
oder ich vermisse etwas?
Ja, wie Earwicker sagte, es ist so ziemlich das Äquivalent zu LINQs select
und hat sehr wenig mit Rubys und Pythons zu tun yield
. Grundsätzlich, wo in C # würden Sie schreiben
from ... select ???
in Scala haben Sie stattdessen
for ... yield ???
Es ist auch wichtig zu verstehen, dass for
-Verständnis nicht nur mit Sequenzen funktioniert, sondern mit jedem Typ, der bestimmte Methoden definiert, genau wie LINQ:
map
, erlaubt er for
Ausdrücke, die aus einem einzelnen Generator bestehen.flatMap
so gut wie definiert map
, erlaubt es for
Ausdrücke, die aus mehreren Generatoren bestehen.foreach
, erlaubt es for
-schleifen ohne Ausbeute (sowohl mit einem als auch mit mehreren Generatoren).filter
, werden for
-filter-Ausdrücke zugelassen, die mit einem if
im for
Ausdruck beginnen.Sofern Sie keine bessere Antwort von einem Scala-Benutzer erhalten (was ich nicht bin), ist hier mein Verständnis.
Es wird nur als Teil eines Ausdrucks angezeigt for
, der mit beginnt und angibt, wie eine neue Liste aus einer vorhandenen Liste generiert wird.
Etwas wie:
var doubled = for (n <- original) yield n * 2
Es gibt also ein Ausgabeelement für jede Eingabe (obwohl ich glaube, dass es eine Möglichkeit gibt, Duplikate zu löschen).
Dies unterscheidet sich erheblich von den "imperativen Fortsetzungen", die durch die Ausbeute in anderen Sprachen ermöglicht werden. Dort können Sie eine Liste beliebiger Länge aus einem imperativen Code mit nahezu beliebiger Struktur erstellen.
(Wenn Sie mit C # vertraut sind, ist es dem select
Operator von LINQ näher als dem yield return
).
Das Schlüsselwort yield
in Scala ist einfach syntaktischer Zucker, der leicht durch a ersetzt werden kann map
, wie Daniel Sobral bereits ausführlich erklärt hat.
Auf der anderen Seite yield
ist es absolut irreführend, wenn Sie nach Generatoren (oder Fortsetzungen) suchen, die denen in Python ähnlich sind . Weitere Informationen finden Sie in diesem SO-Thread: Was ist der bevorzugte Weg, um 'Yield' in Scala zu implementieren?
Beachten Sie zum Verständnis Folgendes
val A = for (i <- Int.MinValue to Int.MaxValue; if i > 3) yield i
Es kann hilfreich sein, es wie folgt laut vorzulesen
„ Für jede ganze Zahl i
, wenn sie größer als 3
, dann ergibt (produzieren) i
und füge sie die Liste A
.“
In Bezug auf die mathematische Set-Builder-Notation ist das obige Verständnis analog zu
was gelesen werden kann als
„ Für jede ganze Zahl , wenn sie größer als , dann ist ein Mitglied des Satzes .“
oder alternativ als
" ist die Menge aller ganzen Zahlen , so dass jede größer als ist ."
Die Ausbeute ähnelt der for-Schleife mit einem Puffer, den wir nicht sehen können, und fügt für jedes Inkrement das nächste Element zum Puffer hinzu. Wenn die for-Schleife beendet ist, wird die Sammlung aller erhaltenen Werte zurückgegeben. Die Ausbeute kann als einfache arithmetische Operatoren oder sogar in Kombination mit Arrays verwendet werden. Hier sind zwei einfache Beispiele zum besseren Verständnis
scala>for (i <- 1 to 5) yield i * 3
res: scala.collection.immutable.IndexedSeq [Int] = Vektor (3, 6, 9, 12, 15)
scala> val nums = Seq(1,2,3)
nums: Seq[Int] = List(1, 2, 3)
scala> val letters = Seq('a', 'b', 'c')
letters: Seq[Char] = List(a, b, c)
scala> val res = for {
| n <- nums
| c <- letters
| } yield (n, c)
res: Seq [(Int, Char)] = Liste ((1, a), (1, b), (1, c), (2, a), (2, b), (2, c), ( 3, a), (3, b), (3, c))
Hoffe das hilft!!
val aList = List( 1,2,3,4,5 )
val res3 = for ( al <- aList if al > 3 ) yield al + 1
val res4 = aList.filter(_ > 3).map(_ + 1)
println( res3 )
println( res4 )
Diese beiden Codeteile sind äquivalent.
val res3 = for (al <- aList) yield al + 1 > 3
val res4 = aList.map( _+ 1 > 3 )
println( res3 )
println( res4 )
Diese beiden Codeteile sind ebenfalls gleichwertig.
Die Karte ist so flexibel wie der Ertrag und umgekehrt.
Der Ertrag ist flexibler als map (), siehe Beispiel unten
val aList = List( 1,2,3,4,5 )
val res3 = for ( al <- aList if al > 3 ) yield al + 1
val res4 = aList.map( _+ 1 > 3 )
println( res3 )
println( res4 )
Ausbeute druckt Ergebnis wie folgt: Liste (5, 6), was gut ist
while map () gibt das folgende Ergebnis zurück: List (false, false, true, true, true), was wahrscheinlich nicht das ist, was Sie beabsichtigen.