scala lazy-evaluation scala-collections for-comprehension

scala - withFilter en lugar de filtro



lazy-evaluation scala-collections (5)

Además de la excelente respuesta de Shadowlands , me gustaría brindar un ejemplo intuitivo de la diferencia entre filter y withFilter .

Consideremos el siguiente código

val list = List(1, 2, 3) var go = true val result = for(i <- list; if(go)) yield { go = false i }

La mayoría de las personas esperan que el result sea ​​igual a la List(1) . Este es el caso desde Scala 2.8, porque la comprensión se traduce en

val result = list withFilter { case i => go } map { case i => { go = false i } }

Como puede ver, la traducción convierte la condición en una llamada a withFilter . Anterior Scala 2.8, for-comprensión se tradujo en algo como lo siguiente:

val r2 = list filter { case i => go } map { case i => { go = false i } }

Usando el filter , el valor del result sería bastante diferente: List(1, 2, 3) . El hecho de que estamos haciendo que el indicador de go false no tiene ningún efecto en el filtro, porque el filtro ya está hecho. Nuevamente, en Scala 2.8, este problema se resuelve usando withFilter . Cuando se usa withFilter , la condición se evalúa cada vez que se accede a un elemento dentro de un método de map .

Referencia : - p.120, Scala en acción (cubre Scala 2.10), Manning Publications, Milanjan Raychaudhuri - Pensamientos de Odersky sobre la traducción de la comprensión

¿Es siempre más eficaz usar con filtro en lugar de filtro, cuando luego se aplican funciones como mapa, mapa plano, etc.?

¿Por qué solo se admiten map, flatmap y foreach? (Funciones esperadas como forall / exists también)


Como solución alternativa, puede implementar otras funciones solo con map y flatMap .

Además, esta optimización es inútil en colecciones pequeñas ...


De los documentos de Scala :

Nota: la diferencia entre c filter p y c withFilter p es que la primera crea una nueva colección, mientras que la segunda solo restringe el dominio de map subsiguiente, flatMap , foreach y withFilter operaciones de withFilter .

Así que el filter tomará la colección original y producirá una nueva colección, pero con withFilter no pasará (por pereza) de forma no estricta los valores sin filtrar a las llamadas de map / map / flatMap withFilter , guardando una segunda pasada a través de la colección (filtrada). Por lo tanto, será más eficiente al pasar a estas llamadas a métodos posteriores.

De hecho, withFilter está específicamente diseñado para trabajar con cadenas de estos métodos, que es lo que se withFilter para la comprensión. No se requieren otros métodos (como forall / exists ) para esto, por lo que no se han agregado al tipo de withFilter de withFilter .


La razón principal porque forall / exists no está implementado es que el caso de uso es que:

  • puede aplicar de forma perezosa con Filter a una secuencia infinita / iterable
  • puede aplicar otra forma perezosa con Filter (y una y otra vez)

Para implementar for all / exists necesitamos obtener todos los elementos, perdiendo la pereza.

Así por ejemplo:

import scala.collection.AbstractIterator class RandomIntIterator extends AbstractIterator[Int] { val rand = new java.util.Random def next: Int = rand.nextInt() def hasNext: Boolean = true } //rand_integers is an infinite random integers iterator val rand_integers = new RandomIntIterator val rand_naturals = rand_integers.withFilter(_ > 0) val rand_even_naturals = rand_naturals.withFilter(_ % 2 == 0) println(rand_even_naturals.map(identity).take(10).toList) //calling a second time we get //another ten-tuple of random even naturals println(rand_even_naturals.map(identity).take(10).toList)

Tenga en cuenta que ten_rand_even_naturals sigue siendo un iterador. Solo cuando llamemos a List, los números aleatorios serán generados y filtrados en cadena

Tenga en cuenta que el mapa (identidad) es equivalente al mapa (i => i) y se usa aquí para convertir un objeto con filtro de nuevo al tipo original (por ejemplo, una colección, un flujo, un iterador)


Usar para el rendimiento puede ser una solución, por ejemplo:

for { e <- col; if e isNotEmpty } yield e.get(0)