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



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



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

638   5  

5 ответов:

С Scala docs:

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

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

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

в дополнение к отличный ответ 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-comprehension переводится в

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

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

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 метод.

ссылка: - p.120, Scala in action (обложки Scala 2.10), Manning Publications, Milanjan Raychaudhuri - мысли Одерского о переводе для понимания

основная причина, потому что forall / существует не реализованы в том, что случай использования таков:

  • вы можете лениво применить withFilter к бесконечному потоку / iterable
  • вы можете лениво применить другой 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 по-прежнему является итератором. только когда мы называем в список случайные числа будут генерироваться и фильтруется в цепи

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

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

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

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

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

Comments

    Ничего не найдено.