withFilter вместо filter


82

Всегда ли эффективнее использовать withFilter вместо filter при последующем применении таких функций, как карта, плоская карта и т. Д.?

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


Эта часть документа Scala также имеет подробное объяснение.
ohkts11

Ответы:


122

Из документации Scala :

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

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

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


Надеюсь, что когда-нибудь они все еще добавят эти методы.
Kigyo

1
@Kigyo Я не думаю, что вы должны использовать withFilter самостоятельно (кроме неявных внутри выражений for). Используйте, viewесли хотите, чтобы карты / фильтры были ленивыми.
Луиджи Плиндж

Понимаю. В чем точная разница между viewи withFilter? Почему не используется просмотр for-loops?
Кигё

5
Просто для справки, я думаю, что « Коллекции - советы и рекомендации» предоставляет отличную информацию. H5 не привязаны, но вы можете искать Don’t create temporary collectionsв связанном разделе.
sthzg

4
Что касается явного использования withFilter, сам Мартин Одерский явно использует его в своих курсах Scala на Coursera, которые я настоятельно рекомендую. Учитывая, что он это делает, это может утешить других, хотя разница обычно составляет всего 1 символ. Например , seq.view filter pпротив прогноза seq withFilter p.
Чак Дэниэлс

9

В дополнение к отличному ответу 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, потому что for-complation переведен на

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

Как видите, перевод преобразует условие в вызов withFilter. В предыдущих версиях Scala 2.8 функция for-computing была переведена примерно так:

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, Миланян Райчаудхури - мысли Одерского о переводе для понимания


1

Основная причина того, что forall / exists не реализован, заключается в следующем:

  • вы можете лениво применить withFilter к бесконечному потоку / итерации
  • вы можете лениво применить другой с помощьюFilter (и снова и снова)

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

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

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 обратно в исходный тип (например, коллекция, поток, итератор)


1

Для части forall / exists:

someList.filter(conditionA).forall(conditionB)

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

!someList.exists(conditionA && !conditionB)

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


-3

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

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

-5

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

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.