Risposte:
È usato nelle comprensioni di sequenze (come la lista-comprensioni e generatori di Python, dove puoi yield
anche usare ).
Viene applicato in combinazione con for
e scrive un nuovo elemento nella sequenza risultante.
Esempio semplice (da 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)
}
}
L'espressione corrispondente in F # sarebbe
[ for a in args -> a.toUpperCase ]
o
from a in args select a.toUpperCase
a Linq.
Ruby yield
ha un effetto diverso.
Penso che la risposta accettata sia ottima, ma sembra che molte persone non siano riuscite a cogliere alcuni punti fondamentali.
In primo luogo, le for
comprensioni di Scala sono equivalenti alla do
notazione di Haskell , e non è altro che uno zucchero sintattico per la composizione di molteplici operazioni monadiche. Poiché questa affermazione molto probabilmente non aiuterà nessuno che ha bisogno di aiuto, riproviamo ... :-)
La for
comprensione di Scala è lo zucchero sintattico per la composizione di più operazioni con la mappa flatMap
e filter
. Or foreach
. Scala in realtà traduce for
un'espressione in chiamate a quei metodi, quindi qualsiasi classe che li fornisce, o un sottoinsieme di essi, può essere usata per comprendere.
Innanzitutto, parliamo delle traduzioni. Ci sono regole molto semplici:
Questo
for(x <- c1; y <- c2; z <-c3) {...}
è tradotto in
c1.foreach(x => c2.foreach(y => c3.foreach(z => {...})))
Questo
for(x <- c1; y <- c2; z <- c3) yield {...}
è tradotto in
c1.flatMap(x => c2.flatMap(y => c3.map(z => {...})))
Questo
for(x <- c; if cond) yield {...}
è tradotto in Scala 2.7 in
c.filter(x => cond).map(x => {...})
o, su Scala 2.8, in
c.withFilter(x => cond).map(x => {...})
con un fallback nel primo se il metodo withFilter
non è disponibile ma lo filter
è. Per ulteriori informazioni, consultare la sezione seguente.
Questo
for(x <- c; y = ...) yield {...}
è tradotto in
c.map(x => (x, ...)).map((x,y) => {...})
Quando guardi a for
comprensioni molto semplici , le alternative map
/ foreach
sembrano davvero migliori. Una volta che inizi a comporli, tuttavia, puoi facilmente perderti tra parentesi e livelli di annidamento. Quando ciò accade, le for
comprensioni sono generalmente molto più chiare.
Mostrerò un semplice esempio e ometterò intenzionalmente qualsiasi spiegazione. Puoi decidere quale sintassi era più facile da capire.
l.flatMap(sl => sl.filter(el => el > 0).map(el => el.toString.length))
o
for {
sl <- l
el <- sl
if el > 0
} yield el.toString.length
withFilter
Scala 2.8 ha introdotto un metodo chiamato withFilter
, la cui principale differenza è che, invece di restituire una nuova raccolta filtrata, filtra su richiesta. Il filter
metodo ha il suo comportamento definito in base alla rigidità della raccolta. Per capirlo meglio, diamo un'occhiata ad alcuni Scala 2.7 con List
(rigoroso) e Stream
(non rigoroso):
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
La differenza si verifica perché filter
viene immediatamente applicato con List
, restituendo un elenco di probabilità - poiché lo found
è false
. Solo allora foreach
viene eseguito, ma, a questo punto, il cambiamento non ha found
senso, come filter
è già stato eseguito.
Nel caso di Stream
, la condizione non viene applicata immediatamente. Invece, come richiesto da ogni elemento foreach
, filter
verifica la condizione, che consente foreach
di influenzarla found
. Giusto per chiarire, ecco l'equivalente codice di comprensione:
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)
Ciò ha causato molti problemi, perché le persone si aspettavano if
che venissero considerate su richiesta, anziché essere applicate in anticipo all'intera collezione.
Introduzione di Scala 2.8 withFilter
, che è sempre non rigorosa, indipendentemente dalla severità della collezione. L'esempio seguente mostra List
con entrambi i metodi su 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
Questo produce il risultato che molte persone si aspettano, senza cambiare il filter
comportamento. Come nota a margine, è Range
stato cambiato da non severo a rigoroso tra Scala 2.7 e Scala 2.8.
withFilter
dovrebbe anche essere non rigoroso, anche per le raccolte rigorose, che merita qualche spiegazione. Lo prenderò in considerazione ...
for(x <- c; y <- x; z <-y) {...}
è tradotto in c.foreach(x => x.foreach(y => y.foreach(z => {...})))
2. for(x <- c; y <- x; z <- y) yield {...}
è tradotto inc.flatMap(x => x.flatMap(y => y.map(z => {...})))
for(x <- c; y = ...) yield {...}
davvero tradotto in c.map(x => (x, ...)).map((x,y) => {...})
? Penso che sia tradotto in c.map(x => (x, ...)).map(x => { ...use x._1 and x._2 here...})
o mi manchi qualcosa?
Sì, come diceva Earwicker, è praticamente l'equivalente di LINQ select
e ha ben poco a che fare con Ruby e Python yield
. Fondamentalmente, dove scrivere in C #
from ... select ???
in Scala invece hai
for ... yield ???
È anche importante capire che le comprensioni for
non funzionano solo con le sequenze, ma con qualsiasi tipo che definisce determinati metodi, proprio come LINQ:
map
, consente for
-espressioni costituite da un singolo generatore.flatMap
oltre map
, consente for
-espressioni costituite da più generatori.foreach
, consente for
-loops senza rendimento (sia con generatori singoli che multipli).filter
, permette for
espressioni -Filter iniziano con l'espressione.if
for
A meno che tu non ottenga una risposta migliore da un utente di Scala (cosa che io non sono), ecco la mia comprensione.
Appare solo come parte di un'espressione che inizia con for
, che indica come generare un nuovo elenco da un elenco esistente.
Qualcosa di simile a:
var doubled = for (n <- original) yield n * 2
Quindi c'è un elemento di output per ogni input (anche se credo che ci sia un modo per eliminare i duplicati).
Questo è abbastanza diverso dalle "continuazioni imperative" abilitate dalla resa in altre lingue, dove fornisce un modo per generare un elenco di qualsiasi lunghezza, da un codice imperativo con quasi qualsiasi struttura.
(Se hai familiarità con C #, è più vicino all'operatore di LINQ select
che a yield return
).
La parola chiave yield
in Scala è semplicemente zucchero sintattico che può essere facilmente sostituito da un map
, come Daniel Sobral ha già spiegato in dettaglio.
D'altra parte, yield
è assolutamente fuorviante se stai cercando generatori (o continuazioni) simili a quelli in Python . Vedi questo thread SO per ulteriori informazioni: Qual è il modo preferito di implementare 'yield' in Scala?
Considera la seguente comprensione
val A = for (i <- Int.MinValue to Int.MaxValue; if i > 3) yield i
Potrebbe essere utile leggerlo ad alta voce come segue
" Per ogni numero intero i
, se è maggiore di 3
, allora produce (produce) i
e aggiungilo alla lista A
."
In termini di notazione matematica del set-builder , la comprensione di cui sopra è analoga a
che può essere letto come
" Per ogni numero intero , se è maggiore di , allora è un membro dell'insieme ."
o in alternativa come
" è l'insieme di tutti i numeri interi , tale che ciascuno è maggiore di ."
Il rendimento è simile al ciclo for che ha un buffer che non possiamo vedere e per ogni incremento continua ad aggiungere l'elemento successivo al buffer. Quando il ciclo for termina l'esecuzione, restituisce la raccolta di tutti i valori prodotti. La resa può essere utilizzata come semplici operatori aritmetici o anche in combinazione con array. Ecco due semplici esempi per una migliore comprensione
scala>for (i <- 1 to 5) yield i * 3
res: scala.collection.immutable.IndexedSeq [Int] = Vector (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)] = List ((1, a), (1, b), (1, c), (2, a), (2, b), (2, c), ( 3, a), (3, b), (3, c))
Spero che questo ti aiuti!!
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 )
Queste due parti di codice sono equivalenti.
val res3 = for (al <- aList) yield al + 1 > 3
val res4 = aList.map( _+ 1 > 3 )
println( res3 )
println( res4 )
Anche queste due parti di codice sono equivalenti.
La mappa è flessibile quanto la resa e viceversa.
la resa è più flessibile di map (), vedi esempio sotto
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 )
la resa stamperà il risultato come: Elenco (5, 6), il che è buono
mentre map () restituirà risultati come: List (false, false, true, true, true), che probabilmente non è quello che intendi.