withFilter замість фільтра


82

Чи завжди ефективніше використовувати withFilter замість фільтра, коли згодом застосовуються такі функції, як 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-expressions). Використовуйте, 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). Це стосується шкали 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, Скала в дії (охоплює Скалу 2.10), Публікації Меннінга, Міланджан Райчаудхурі - Думки Одерського про переклад для розуміння


1

Основна причина, оскільки forall / Існує не реалізована, полягає в тому, що варіант використання полягає в тому, що:

  • ви можете ліниво застосовувати за допомогою Filter до нескінченного потоку / ітерабельності
  • Ви можете ліниво застосувати інший за допомогою фільтра (і знову і знову)

Для реалізації 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 назад у вихідний тип (наприклад, колекція, потік, ітератор)


1

Для загальної / існує частини:

someList.filter(conditionA).forall(conditionB)

буде таким же, як (хоча трохи неінтуїтивний)

!someList.exists(conditionA && !conditionB)

Подібним чином, .filter (). Існує () можна об'єднати в одну перевірку існує ()?


-3

Використання для врожайності може бути обхідним шляхом, наприклад:

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

-5

Як обхідний шлях ви можете реалізувати інші функції лише за допомогою mapі flatMap.

Більше того, ця оптимізація марна для невеликих колекцій ...

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.