Respuestas:
Se usa en comprensiones de secuencia (como las listas de comprensión y generadores de Python, donde también puede usar yield).
Se aplica en combinación con fory escribe un nuevo elemento en la secuencia resultante.
Ejemplo simple (de 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)
}
}
La expresión correspondiente en F # sería
[ for a in args -> a.toUpperCase ]
o
from a in args select a.toUpperCase
en Linq.
Ruby yieldtiene un efecto diferente.
Creo que la respuesta aceptada es excelente, pero parece que muchas personas no han captado algunos puntos fundamentales.
Primero, las forcomprensiones de Scala son equivalentes a las de Haskelldo notación , y no es más que un azúcar sintáctico para la composición de múltiples operaciones monádicas. Como esta declaración probablemente no ayudará a nadie que necesite ayuda, intentemos de nuevo ... :-)
La forcomprensión de Scala es azúcar sintáctica para la composición de múltiples operaciones con mapa flatMapy filter. O foreach. Scala en realidad traduce una for-expresión en llamadas a esos métodos, por lo que cualquier clase que los proporcione, o un subconjunto de ellos, puede usarse para comprender.
Primero, hablemos de las traducciones. Hay reglas muy simples:
Esta
for(x <- c1; y <- c2; z <-c3) {...}
se traduce a
c1.foreach(x => c2.foreach(y => c3.foreach(z => {...})))Esta
for(x <- c1; y <- c2; z <- c3) yield {...}
se traduce a
c1.flatMap(x => c2.flatMap(y => c3.map(z => {...})))Esta
for(x <- c; if cond) yield {...}
se traduce en Scala 2.7 en
c.filter(x => cond).map(x => {...})
o, en Scala 2.8, en
c.withFilter(x => cond).map(x => {...})
con un retroceso al primero si el método withFilterno está disponible pero lo filterestá. Consulte la sección a continuación para obtener más información al respecto.
Esta
for(x <- c; y = ...) yield {...}
se traduce a
c.map(x => (x, ...)).map((x,y) => {...})Cuando observas forcomprensiones muy simples , las map/ foreachalternativas se ven, de hecho, mejores. Sin embargo, una vez que comience a componerlos, puede perderse fácilmente en paréntesis y niveles de anidación. Cuando eso sucede, las forcomprensiones suelen ser mucho más claras.
Mostraré un ejemplo simple y omitiré intencionalmente cualquier explicación. Puede decidir qué sintaxis fue más fácil de entender.
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
withFilterScala 2.8 introdujo un método llamado withFilter, cuya principal diferencia es que, en lugar de devolver una nueva colección filtrada, filtra bajo demanda. El filtermétodo tiene su comportamiento definido en función de la rigurosidad de la colección. Para entender esto mejor, echemos un vistazo a algunos Scala 2.7 con List(estricto) y Stream(no estricto):
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 diferencia ocurre porque filterse aplica inmediatamente con List, devolviendo una lista de probabilidades, ya que foundes false. Solo entonces foreachse ejecuta, pero, en este momento, el cambio no foundtiene sentido, como filterya se ha ejecutado.
En el caso de Stream, la condición no se aplica inmediatamente. En cambio, cuando cada elemento es solicitado por foreach, filterprueba la condición, lo que permite foreachinfluir en ella found. Solo para dejarlo en claro, aquí está el código equivalente de comprensión:
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)
Esto causó muchos problemas, porque la gente esperaba ifque se considerara a pedido, en lugar de aplicarse de antemano a toda la colección.
Se presenta Scala 2.8 withFilter, que siempre es no estricto, sin importar la rigurosidad de la colección. El siguiente ejemplo se muestra Listcon ambos métodos en 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
Esto produce el resultado que la mayoría de la gente espera, sin cambiar cómo se filtercomporta. Como nota al margen, Rangese cambió de no estricto a estricto entre Scala 2.7 y Scala 2.8.
withFilterse supone que no es estricto también, incluso para colecciones estrictas, lo que merece una explicación. Consideraré esto ...
for(x <- c; y <- x; z <-y) {...}se traduce en c.foreach(x => x.foreach(y => y.foreach(z => {...}))) 2. for(x <- c; y <- x; z <- y) yield {...}se traduce enc.flatMap(x => x.flatMap(y => y.map(z => {...})))
for(x <- c; y = ...) yield {...}realmente traducido c.map(x => (x, ...)).map((x,y) => {...})? ¿Creo que está traducido c.map(x => (x, ...)).map(x => { ...use x._1 and x._2 here...})o me falta algo?
Sí, como dijo Earwicker, es más o menos el equivalente a LINQ selecty tiene muy poco que ver con Ruby's y Python yield. Básicamente, donde en C # escribirías
from ... select ???
en Scala tienes en su lugar
for ... yield ???
También es importante entender que las comprensiones forno solo funcionan con secuencias, sino con cualquier tipo que defina ciertos métodos, como LINQ:
map, permite for-expresiones que consisten en un solo generador.flatMaptan bien como map, permite for-expresiones que consisten en varios generadores.foreach, permite for-loops sin rendimiento (tanto con generadores simples como múltiples).filter, permite forexpresiones -filter que comienzan con un if
en la forexpresión.A menos que obtenga una mejor respuesta de un usuario de Scala (que no soy), aquí tengo mi entendimiento.
Solo aparece como parte de una expresión que comienza for, que establece cómo generar una nueva lista a partir de una lista existente.
Algo como:
var doubled = for (n <- original) yield n * 2
Entonces, hay un elemento de salida para cada entrada (aunque creo que hay una forma de soltar duplicados).
Esto es bastante diferente de las "continuaciones imperativas" habilitadas por el rendimiento en otros idiomas, donde proporciona una manera de generar una lista de cualquier longitud, a partir de algún código imperativo con casi cualquier estructura.
(Si está familiarizado con C #, está más cerca del select operador de LINQ de lo que está yield return).
La palabra clave yielden Scala es simplemente azúcar sintáctico que puede reemplazarse fácilmente por un map, como Daniel Sobral ya explicó en detalle.
Por otro lado, yieldes absolutamente engañoso si está buscando generadores (o continuaciones) similares a los de Python . Consulte este hilo SO para obtener más información: ¿Cuál es la forma preferida de implementar 'rendimiento' en Scala?
Considere lo siguiente para la comprensión
val A = for (i <- Int.MinValue to Int.MaxValue; if i > 3) yield i
Puede ser útil leerlo en voz alta de la siguiente manera
" Para cada número entero i, si es mayor que 3, entonces produzca (produzca) iy agréguelo a la lista A".
En términos de notación matemática del generador de conjuntos , la comprensión anterior es análoga a
que puede leerse como
" Para cada entero , si es mayor que
, entonces es un miembro del conjunto
".
o alternativamente como
" es el conjunto de todos los enteros
, de modo que cada uno
es mayor que
".
El rendimiento es similar al ciclo for que tiene un búfer que no podemos ver y para cada incremento, sigue agregando el siguiente elemento al búfer. Cuando el ciclo for termina de ejecutarse, devolverá la colección de todos los valores producidos. El rendimiento puede usarse como operadores aritméticos simples o incluso en combinación con matrices. Aquí hay dos ejemplos simples para su mejor comprensión.
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)] = Lista ((1, a), (1, b), (1, c), (2, a), (2, b), (2, c), ( 3, a), (3, b), (3, c))
¡¡Espero que esto ayude!!
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 )
Estas dos piezas de código son equivalentes.
val res3 = for (al <- aList) yield al + 1 > 3
val res4 = aList.map( _+ 1 > 3 )
println( res3 )
println( res4 )
Estas dos piezas de código también son equivalentes.
El mapa es tan flexible como el rendimiento y viceversa.
el rendimiento es más flexible que map (), ver ejemplo a continuación
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 )
el rendimiento imprimirá resultados como: Lista (5, 6), que es bueno
mientras que map () devolverá resultados como: Lista (falso, falso, verdadero, verdadero, verdadero), que probablemente no sea lo que pretendes.