WithFilter вместо фильтра

Всегда ли более эффективно использовать withFilter вместо фильтра, когда впоследствии применяют такие функции, как map, flatmap и т.д.?

Почему поддерживаются только карта, плоская карта и foreach? (Ожидаемые функции, такие как forall/существуют также)

Ответ 1

Из документов Scala:

Примечание: разница между c filter p и c withFilter p заключается в том, что первый     создает новую коллекцию, в то время как последняя ограничивает домен     последующие операции map, flatMap, foreach и withFilter.

Таким образом, filter возьмет исходную коллекцию и создаст новую коллекцию, но withFilter будет не строго (то есть лениво) передавать нефильтрованные значения последующим вызовам map/flatMap/withFilter, сохраняя второй проход через (отфильтрованную) коллекцию. Следовательно, он будет более эффективным при переходе к последующим вызовам методов.

На самом деле, withFilter специально разработан для работы с цепочками этих методов, и именно для этого десагарируется понимание. Никаких других методов (таких как forall/exists) для этого не требуется, поэтому они не были добавлены к типу возврата FilterMonadic withFilter.

Ответ 2

Помимо превосходного ответа Shadowlands, я хотел бы привести интуитивный пример различия между filter и withFilter.

Давайте рассмотрим следующий код

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

Большинство людей ожидают, что result будет равен List(1). Это относится к Scala 2.8, потому что для понимания это переведено на

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

Как видите, перевод преобразует условие в вызов withFilter. До Scala 2.8, для понимания были переведены что-то вроде следующего:

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

При использовании filter значение result будет довольно разным: List(1, 2, 3). Тот факт, что мы делаем флаг go false, не влияет на фильтр, потому что фильтр уже сделан. Опять же, в Scala 2.8 эта проблема решается с помощью withFilter. Когда используется withFilter, условие оценивается каждый раз, когда к элементу обращаются внутри метода map.

Ссылка: - с .120, Scala в действии (охватывает Scala 2.10), Manning Publications, Миланян Райчаудхури - Одерские мысли о понятном переводе

Ответ 3

Основная причина того, что forall/exist не реализована, заключается в том, что сценарий использования:

  • Вы можете лениво применять withFilter к бесконечному потоку/итерируемому
  • Вы можете лениво применить другой с фильтром (и снова и снова)

Для реализации forall/существующие мы должны получить все элементы, теряя лень.

Так, например:

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)

Обратите внимание, что ten_rand_even_naturals все еще является итератором. Только, когда мы вызываем toList, случайные числа будут генерироваться и фильтроваться в цепочке

Обратите внимание, что map (identity) эквивалентно map (i => i) и используется здесь для преобразования объекта withFilter обратно в исходный тип (например, коллекция, поток, итератор).

Ответ 4

Для части/существует часть:

someList.filter(conditionA).forall(conditionB)

будет таким же, как (хотя и немного не интуитивно понятно)

!someList.exists(conditionA && !conditionB)

Точно так же .filter(). exist() можно объединить в одну проверку there()?

Ответ 5

Использование для урока может быть выполнено, например:

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

Ответ 6

В качестве обходного пути вы можете реализовать другие функции только с map и flatMap.

Кроме того, эта оптимизация бесполезна для небольших коллекций...