withFilter en lugar de filter

Respuestas:

121

De los documentos de Scala :

Nota: la diferencia entre c filter py c withFilter pes que el primero crea una nueva colección, mientras que el segundo sólo se restringe el dominio del subsiguientes map, flatMap, foreach, y withFilteroperaciones.

Por filterlo tanto , tomará la colección original y producirá una nueva colección, pero withFilterno estrictamente (es decir, perezosamente) pasará valores sin filtrar a llamadas posteriores 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 de método posteriores.

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

Shadowlands
fuente
Espero que todavía agreguen estos métodos algún día.
Kigyo
1
@Kigyo No creo que se suponga que debas usar withFilter tú mismo (aparte de implícitamente dentro de las expresiones for). Úselo viewsi desea que los mapas / filtros sean perezosos.
Luigi Plinge
Veo. ¿Cuál es la diferencia exacta entre viewy withFilter? ¿Por qué no se usa la vista for-loops?
Kigyo
5
Solo como referencia, creo que Colecciones: consejos y trucos proporciona información excepcional. Los H5 no están anclados, pero puede buscarlos Don’t create temporary collectionsen la sección vinculada.
sthzg
4
En cuanto al uso explícito de withFilter, el propio Martin Odersky lo usa explícitamente en sus cursos Scala en Coursera, que recomiendo encarecidamente. Dado que lo hace, puede que los demás también se sientan cómodos al hacerlo, aunque la diferencia suele ser de solo un carácter. Por ejemplo seq.view filter pvs seq withFilter p.
Chuck Daniels
9

Además de la excelente respuesta de Shadowlands , me gustaría traer un ejemplo intuitivo de la diferencia entre filtery 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 la gente espera resultser igual a List(1). Este es el caso desde Scala 2.8, porque la para-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. Antes de Scala 2.8, para la comprensión se tradujeron a algo como lo siguiente:

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

El uso filter, el valor de resultsería bastante diferente: List(1, 2, 3). El hecho de que estemos creando la gobandera falseno 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 withFilterse usa, la condición se evalúa cada vez que se accede a un elemento dentro de un mapmétodo.

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

James
fuente
1

La razón principal por la que forall / exist no está implementado es que el caso de uso es que:

  • puede aplicar perezosamente withFilter a una secuencia infinita / iterable
  • puede aplicar perezosamente otro con Filtro (y una y otra vez)

Para implementar forall / exist 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 toList, los números aleatorios se generarán y filtrarán en cadena

Tenga en cuenta que map (identidad) es equivalente a map (i => i) y se usa aquí para convertir un objeto withFilter de nuevo al tipo original (por ejemplo, una colección, una secuencia, un iterador)

frhack
fuente
1

Para la parte forall / existe:

someList.filter(conditionA).forall(conditionB)

sería lo mismo que (aunque un poco poco intuitivo)

!someList.exists(conditionA && !conditionB)

Del mismo modo, .filter (). Existing () se puede combinar en una existe () check?

lznt
fuente
-3

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

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

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

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

Yann Moisan
fuente