Est-il toujours plus performant d’utiliser withFilter au lieu de filter, lorsque vous appliquez ensuite des fonctions telles que map, flatmap etc.?
Pourquoi seules les cartes, les flatmap et les foreach sont-ils pris en charge? (Les fonctions attendues comme forall / existent aussi)
Des documents de Scala :
Note: la différence entre
c filter p
etc withFilter p
est que la première crée une nouvelle collection, alors que la seconde ne restreint que le domaine des opérations ultérieures demap
,flatMap
,foreach
etwithFilter
.
Donc, filter
prendra la collection d’origine et produira une nouvelle collection, mais withFilter
non ssortingctement (c’est-à-dire paresseusement) des valeurs non filtrées aux appels withFilter
map
/ flatMap
/ withFilter
, enregistrant une seconde passe dans la collection (filtrée). Par conséquent, il sera plus efficace lors du passage à ces appels de méthode ultérieurs.
En fait, withFilter
est spécialement conçu pour travailler avec des chaînes de ces méthodes, ce qui constitue une solution pour la compréhension. Aucune autre méthode (telle que forall
/ exists
) n’est requirejse pour cela, elles n’ont donc pas été ajoutées au type de retour withFilter
de withFilter
.
En plus de l’excellente réponse de Shadowlands , je voudrais apporter un exemple intuitif de la différence entre filter
et withFilter
.
Considérons le code suivant
val list = List(1, 2, 3) var go = true val result = for(i <- list; if(go)) yield { go = false i }
La plupart des gens s'attendent à result
que le result
soit égal à la List(1)
. C’est le cas depuis la Scala 2.8, car la compréhension est traduite en
val result = list withFilter { case i => go } map { case i => { go = false i } }
Comme vous pouvez le voir, la traduction convertit la condition en appel à withFilter
. Avant Scala 2.8, la compréhension était traduite en quelque chose comme ceci:
val r2 = list filter { case i => go } map { case i => { go = false i } }
En utilisant filter
, la valeur du result
serait assez différente: List(1, 2, 3)
. Le fait que nous activions le drapeau go
n'a pas d'effet sur le filtre, car le filtre est déjà effectué. Encore une fois, dans Scala 2.8, ce problème est résolu en utilisant withFilter
. Lorsque withFilter
est utilisé, la condition est évaluée chaque fois qu'un élément est accédé à l'intérieur d'une méthode de map
.
Référence : - p.120, Scala en action (couvre Scala 2.10), Manning Publications, Milanjan Raychaudhuri - Les reflections d'Odersky sur la traduction pour la compréhension
La raison principale pour laquelle forall / exist n’est pas implémentée est que le cas d’utilisation est le suivant:
Pour mettre en œuvre forall / existent, nous devons obtenir tous les éléments, en perdant la paresse.
Donc par exemple:
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)
Notez que ten_rand_even_naturals est toujours un iterator. Seulement lorsque nous appelons toList, les nombres aléatoires seront générés et filtrés en chaîne.
Notez que map (identity) est équivalent à map (i => i) et qu’il est utilisé ici pour convertir un object withFilter au type d’origine (par exemple, une collection, un stream, un iterator)
Utiliser pour le rendement peut être un travail, par exemple:
for { e <- col; if e isNotEmpty } yield e.get(0)
Pour contourner ce problème, vous pouvez implémenter d’autres fonctions uniquement avec map
et flatMap
.
De plus, cette optimisation est inutile sur les petites collections…