¿Cuál es el rendimiento de Scala?

Resuelto Geo asked hace 15 años • 11 respuestas

Entiendo el rendimiento de Ruby y Python. ¿Qué hace el rendimiento de Scala?

Geo avatar Jun 27 '09 16:06 Geo
Aceptado

Creo que la respuesta aceptada es excelente, pero parece que muchas personas no han logrado comprender algunos puntos fundamentales.

Primero, forlas comprensiones de Scala son equivalentes a dola notación de Haskell, y no es más que un azúcar sintáctico para la composición de múltiples operaciones monádicas. Como lo más probable es que esta afirmación no ayude a nadie que necesite ayuda, intentémoslo de nuevo… :-)

Las comprensiones de Scala forson azúcar sintáctica para la composición de múltiples operaciones con map flatMapy filter. O foreach. Scala en realidad traduce una forexpresión - en llamadas a esos métodos, por lo que cualquier clase que los proporcione, o un subconjunto de ellos, puede usarse para comprensión.

Primero, hablemos de las traducciones. Hay reglas muy simples:

  1. Este

    for(x <- c1; y <- c2; z <-c3) {...}
    

    se traduce en

    c1.foreach(x => c2.foreach(y => c3.foreach(z => {...})))
    
  2. Este

    for(x <- c1; y <- c2; z <- c3) yield {...}
    

    se traduce en

    c1.flatMap(x => c2.flatMap(y => c3.map(z => {...})))
    
  3. Este

    for(x <- c; if cond) yield {...}
    

    se traduce en Scala 2.7 a

    c.filter(x => cond).map(x => {...})
    

    o, en Scala 2.8, en

    c.withFilter(x => cond).map(x => {...})
    

    con un recurso alternativo al primero si el método withFilterno está disponible pero sí filterlo está. Consulte la sección siguiente para obtener más información al respecto.

  4. Este

    for(x <- c; y = ...) yield {...}
    

    se traduce en

    c.map(x => (x, ...)).map((x,y) => {...})
    

Cuando nos fijamos en forcomprensiones muy simples, las alternativas map/ foreachparecen, de hecho, mejores. Sin embargo, una vez que comienzas a componerlos, puedes perderte fácilmente entre paréntesis y niveles de anidamiento. Cuando eso sucede, forlas 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.

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 introdujo un método llamado withFilter, cuya principal diferencia es que, en lugar de devolver una colección nueva filtrada, filtra según demanda. El filtermétodo tiene su comportamiento definido en función del rigor de la colección. Para entender esto mejor, echemos un vistazo a 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. Sólo entonces foreachse ejecuta, pero, en ese momento, el cambio foundno tiene sentido, como filterya se ha ejecutado.

En el caso de Stream, la condición no se aplica inmediatamente. En cambio, a medida que cada elemento es solicitado por foreach, filterprueba la condición, lo que permite foreachinfluir en él a través de found. Para que quede claro, aquí está el código de comprensión equivalente:

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 que ifse considerara bajo demanda, en lugar de aplicarse a toda la colección de antemano.

Se introdujo Scala 2.8 withFilter, que siempre no es estricto, sin importar el rigor de la colección. El siguiente ejemplo muestra Listambos 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 su filtercomportamiento. Como nota al margen, Rangese cambió de no estricto a estricto entre Scala 2.7 y Scala 2.8.

Daniel C. Sobral avatar Jun 29 '2009 17:06 Daniel C. Sobral

Se usa en comprensión de secuencias (como las listas de comprensión y generadores de Python, donde también puede usarlo yield).

Se aplica en combinación con forun nuevo elemento y lo escribe 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.

Dario avatar Jun 27 '2009 09:06 Dario

Sí, como dijo Earwicker, es prácticamente equivalente a LINQ selecty tiene muy poco que ver con Ruby y Python yield. Básicamente, ¿dónde en C# escribirías?

from ... select ??? 

en Scala tienes en su lugar

for ... yield ???

También es importante entender que forlas comprensiones no sólo funcionan con secuencias, sino con cualquier tipo que defina ciertos métodos, al igual que LINQ:

  • Si su tipo define just map, permite forexpresiones que constan de un único generador.
  • Si define flatMapademás de map, permite forexpresiones que constan de varios generadores.
  • Si define foreach, permite forbucles sin rendimiento (tanto con generadores únicos como múltiples).
  • Si define filter, permite forfiltrar expresiones que comiencen con an if en la forexpresión.
Alexey Romanov avatar Jun 27 '2009 12:06 Alexey Romanov