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 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 Haskelldonotació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 mapaflatMapyfilter. 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
withFilterno está disponible pero lofilterestá. Consulte la sección a continuación para obtener más información al respecto.Esta
se traduce a
Cuando observas
forcomprensiones muy simples , lasmap/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, lasforcomprensiones 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
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. Elfiltermé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
filterse aplica inmediatamente conList, devolviendo una lista de probabilidades, ya quefoundesfalse. Solo entoncesforeachse ejecuta, pero, en este momento, el cambio nofoundtiene sentido, comofilterya se ha ejecutado.En el caso de
Stream, la condición no se aplica inmediatamente. En cambio, cuando cada elemento es solicitado porforeach,filterprueba la condición, lo que permiteforeachinfluir en ellafound. Solo para dejarlo en claro, aquí está el código equivalente de comprensión: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 muestraListcon ambos métodos en Scala 2.8: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.fuente
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 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
selecty 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
forno solo funcionan con secuencias, sino con cualquier tipo que defina ciertos métodos, como LINQ:map, permitefor-expresiones que consisten en un solo generador.flatMaptan bien comomap, permitefor-expresiones que consisten en varios generadores.foreach, permitefor-loops sin rendimiento (tanto con generadores simples como múltiples).filter, permiteforexpresiones -filter que comienzan con unifen laforexpresió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
selectoperador de LINQ de lo que estáyield return).fuente
La palabra clave
yielden Scala es simplemente azúcar sintáctico que puede reemplazarse fácilmente por unmap, 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?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)iy 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