Всегда ли эффективнее использовать withFilter вместо filter при последующем применении таких функций, как карта, плоская карта и т. Д.?
Почему поддерживаются только map, flatmap и foreach? (Ожидаемые функции типа forall / также существуют)
Всегда ли эффективнее использовать withFilter вместо filter при последующем применении таких функций, как карта, плоская карта и т. Д.?
Почему поддерживаются только map, flatmap и foreach? (Ожидаемые функции типа forall / также существуют)
Ответы:
Из документации Scala :
Примечание: разница между
c filter pиc withFilter pв том , что бывший создает новую коллекцию, в то время как последний только ограничивает область последующихmap,flatMap,foreachиwithFilterопераций.
Таким образом, filterмы возьмем исходную коллекцию и создадим новую коллекцию, но withFilterнестрого (т.е. лениво) будет передавать неотфильтрованные значения более поздним вызовам map/ flatMap/ withFilter, сохраняя второй проход через (отфильтрованную) коллекцию. Следовательно, переход к этим последующим вызовам методов будет более эффективным.
Фактически, withFilterон специально разработан для работы с цепочками этих методов, что и является предметом для понимания. Никаких других методов (таких как forall/ exists) для этого не требуется, поэтому они не были добавлены к FilterMonadicтипу возвращаемого значения withFilter.
viewесли хотите, чтобы карты / фильтры были ленивыми.
viewи withFilter? Почему не используется просмотр for-loops?
Don’t create temporary collectionsв связанном разделе.
withFilter, сам Мартин Одерский явно использует его в своих курсах Scala на Coursera, которые я настоятельно рекомендую. Учитывая, что он это делает, это может утешить других, хотя разница обычно составляет всего 1 символ. Например , seq.view filter pпротив прогноза seq withFilter p.
В дополнение к отличному ответу 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, Миланян Райчаудхури - мысли Одерского о переводе для понимания
Основная причина того, что forall / exists не реализован, заключается в следующем:
Для реализации 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 обратно в исходный тип (например, коллекция, поток, итератор)
Использование для yield может быть обходным решением, например:
for {
e <- col;
if e isNotEmpty
} yield e.get(0)
В качестве обходного пути вы можете реализовать другие функции только с помощью mapи flatMap.
Более того, эта оптимизация бесполезна для небольших коллекций…