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, flatmap et foreach sont-elles prises en charge? (Les fonctions attendues comme forall/existe aussi)
De les documents Scala :
Remarque: la différence entre
c filter p
etc withFilter p
est celle de l'ancien crée une nouvelle collection, alors que cette dernière ne limite que le domaine de Opérationsmap
,flatMap
,foreach
etwithFilter
suivantes.
Donc, filter
prendra la collection d'origine et produira une nouvelle collection, mais withFilter
transmettra de manière non stricte (c'est-à-dire paresseusement) les valeurs non filtrées aux map
/flatMap
/withFilter
appelant, en sauvegardant un second passage dans la collection (filtrée). Par conséquent, il sera plus efficace de passer aux appels de méthodes suivants.
En fait, withFilter
est spécialement conçu pour travailler avec des chaînes de ces méthodes, ce qui permet de réduire la compréhension à la compréhension. Aucune autre méthode (telle que forall
/exists
) n'est requise pour cela; elles n'ont donc pas été ajoutées au type de retour FilterMonadic
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
égal à List(1)
. C’est le cas depuis Scala 2.8, car le for-comprehension est traduit en
val result = list withFilter {
case i => go
} map {
case i => {
go = false
i
}
}
Comme vous pouvez le constater, la traduction convertit la condition en appel à withFilter
. Avant Scala 2.8, pour-compréhension ont été traduits en quelque chose comme ce qui suit:
val r2 = list filter {
case i => go
} map {
case i => {
go = false
i
}
}
En utilisant filter
, la valeur de result
serait assez différente: List(1, 2, 3)
. Le fait que nous ayons créé l'indicateur go
false
n'a aucun effet sur le filtre, car le filtre est déjà terminé. Encore une fois, dans Scala 2.8, ce problème est résolu à l'aide de withFilter
. Lorsque withFilter
est utilisé, la condition est évaluée chaque fois qu'un élément est accédé dans une méthode map
.
Référence: - p.120, Scala en action (couvre Scala 2.10), Manning Publications, Milanjan Raychaudhuri - Réflexions d'Odersky sur la traduction pour la compréhension
Pour la partie forall/existe:
someList.filter(conditionA).forall(conditionB)
serait le même que (bien qu'un peu peu intuitif)
!someList.exists(conditionA && !conditionB)
De même, .filter (). Exist () peut être combiné en un contrôle exist ()?
La raison principale pour laquelle forall/exist ne sont pas implémentés est que le cas d'utilisation est le suivant:
Pour implémenter forall/existe, 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 itérateur. Only lorsque nous appelons toList, les nombres aléatoires seront générés et filtrés en chaîne
Notez que map (identité) équivaut à map (i => i) et est utilisé ici pour reconvertir un objet withFilter en type original itérateur)