Всегда ли эффективнее использовать 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
.
Более того, эта оптимизация бесполезна для небольших коллекций…