Entiendo el rendimiento de Ruby y Python. ¿Qué hace el rendimiento de Scala?
312
Entiendo el rendimiento de Ruby y Python. ¿Qué hace el rendimiento de Scala?
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 for
y 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 yield
tiene un efecto diferente.
Creo que la respuesta aceptada es excelente, pero parece que muchas personas no han captado algunos puntos fundamentales.
Primero, las
for
comprensiones 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
for
comprensión de Scala es azúcar sintáctica para la composición de múltiples operaciones con mapaflatMap
yfilter
. Oforeach
. Scala en realidad traduce unafor
-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
se traduce a
Esta
se traduce a
Esta
se traduce en Scala 2.7 en
o, en Scala 2.8, en
con un retroceso al primero si el método
withFilter
no está disponible pero lofilter
está. Consulte la sección a continuación para obtener más información al respecto.Esta
se traduce a
Cuando observas
for
comprensiones muy simples , lasmap
/foreach
alternativas 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, lasfor
comprensiones 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.
o
withFilter
Scala 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. Elfilter
mé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 conList
(estricto) yStream
(no estricto):La diferencia ocurre porque
filter
se aplica inmediatamente conList
, devolviendo una lista de probabilidades, ya quefound
esfalse
. Solo entoncesforeach
se ejecuta, pero, en este momento, el cambio nofound
tiene sentido, comofilter
ya se ha ejecutado.En el caso de
Stream
, la condición no se aplica inmediatamente. En cambio, cuando cada elemento es solicitado porforeach
,filter
prueba la condición, lo que permiteforeach
influir en ellafound
. Solo para dejarlo en claro, aquí está el código equivalente de comprensión:Esto causó muchos problemas, porque la gente esperaba
if
que 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 muestraList
con ambos métodos en Scala 2.8:Esto produce el resultado que la mayoría de la gente espera, sin cambiar cómo se
filter
comporta. Como nota al margen,Range
se cambió de no estricto a estricto entre Scala 2.7 y Scala 2.8.fuente
withFilter
se 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 enc.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 traducidoc.map(x => (x, ...)).map((x,y) => {...})
? ¿Creo que está traducidoc.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
select
y tiene muy poco que ver con Ruby's y Pythonyield
. Básicamente, donde en C # escribiríasen Scala tienes en su lugar
También es importante entender que las comprensiones
for
no solo funcionan con secuencias, sino con cualquier tipo que defina ciertos métodos, como LINQ:map
, permitefor
-expresiones que consisten en un solo generador.flatMap
tan bien comomap
, permitefor
-expresiones que consisten en varios generadores.foreach
, permitefor
-loops sin rendimiento (tanto con generadores simples como múltiples).filter
, permitefor
expresiones -filter que comienzan con unif
en lafor
expresión.fuente
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:
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
).fuente
La palabra clave
yield
en Scala es simplemente azúcar sintáctico que puede reemplazarse fácilmente por unmap
, como Daniel Sobral ya explicó en detalle.Por otro lado,
yield
es 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?fuente
Considere lo siguiente para la comprensión
Puede ser útil leerlo en voz alta de la siguiente manera
" Para cada número entero
i
, si es mayor que3
, entonces produzca (produzca)i
y agréguelo a la listaA
".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 ".
fuente
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.
res: scala.collection.immutable.IndexedSeq [Int] = Vector (3, 6, 9, 12, 15)
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!!
fuente
Estas dos piezas de código son equivalentes.
Estas dos piezas de código también son equivalentes.
El mapa es tan flexible como el rendimiento y viceversa.
fuente
el rendimiento es más flexible que map (), ver ejemplo a continuación
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.
fuente