web-dev-qa-db-fra.com

withFilter au lieu de filter

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) 

68
Kigyo

De les documents Scala :

Remarque: la différence entre c filter p et c withFilter p est celle de l'ancien crée une nouvelle collection, alors que cette dernière ne limite que le domaine de Opérations map, flatMap, foreach et withFilter 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

105
Shadowlands

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 gofalse 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

5
ZenLulz

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 ()?

0
lznt

La raison principale pour laquelle forall/exist ne sont pas implémentés est que le cas d'utilisation est le suivant:

  • vous pouvez appliquer paresseusement withFilter à un flux infini/itérable
  • vous pouvez appliquer paresseusement un autre avec Filter (et encore et encore) 

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)

0
frhack