withFilter zamiast filter


Odpowiedzi:


122

Z dokumentów Scala :

Uwaga: różnica między c filter pi c withFilter pto, że były tworzy nową kolekcję, podczas gdy ten ostatni tylko ogranicza domenę kolejnych map, flatMap, foreachoraz withFilteroperacji.

Więc filterweźmie oryginalną kolekcję i utworzy nową kolekcję, ale withFilternieściśle (tj. Leniwie) przekaże niefiltrowane wartości do późniejszych wywołań map/ flatMap/ withFilter, oszczędzając drugie przejście przez (filtrowaną) kolekcję. W związku z tym będzie bardziej wydajne podczas przechodzenia do tych kolejnych wywołań metod.

W rzeczywistości withFilterjest specjalnie zaprojektowany do pracy z łańcuchami tych metod, co jest tym, do czego służy zrozumienie. Żadne inne metody (takie jak forall/ exists) nie są do tego wymagane, więc nie zostały dodane do FilterMonadiczwracanego typu withFilter.


Mam nadzieję, że pewnego dnia nadal dodadzą te metody.
Kigyo

1
@Kigyo Myślę, że nie powinieneś używać withFilter sam (z wyjątkiem niejawnie wewnątrz wyrażeń for). Użyj, viewjeśli chcesz, aby mapy / filtry były leniwe.
Luigi Plinge

Widzę. Jaka jest dokładna różnica między viewi withFilter? Dlaczego widok nie jest używany do for-loops?
Kigyo

5
Dla odniesienia myślę, że Kolekcje - porady i wskazówki zawierają wyjątkowe informacje. H5 nie są zakotwiczone, ale możesz wyszukiwać Don’t create temporary collectionsw połączonej sekcji.
sthzg

4
Jeśli chodzi o wyraźne użycie withFilter, sam Martin Odersky używa go wyraźnie w swoich kursach Scala na Coursera, które bardzo polecam. Biorąc pod uwagę, że to robi, może to również dać innym pocieszenie, chociaż różnica jest zwykle tylko 1 znakiem. Na przykład seq.view filter pvs. seq withFilter p.
Chuck Daniels

9

Oprócz doskonałej odpowiedzi Shadowlands , chciałbym przedstawić intuicyjny przykład różnicy między filteri withFilter.

Rozważmy następujący kod

val list = List(1, 2, 3)
var go = true
val result = for(i <- list; if(go)) yield {
   go = false
   i
}

Większość ludzi oczekuje, resultże będzie im równa List(1). Dzieje się tak od wersji Scala 2.8, ponieważ rozumienie tekstu jest tłumaczone na

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

Jak widać, tłumaczenie konwertuje warunek na wywołanie withFilter. Wcześniej Scala 2.8, rozumienie zostało przetłumaczone na coś takiego:

val r2 = list filter {
  case i => go
} map {
  case i => {
    go = false
    i
  }
}

Korzystanie filterwartość resultbyłaby całkiem inna: List(1, 2, 3). Fakt, że tworzymy goflagę, falsenie ma wpływu na filtr, ponieważ filtr jest już gotowy. Ponownie, w Scali 2.8 ten problem został rozwiązany za pomocą withFilter. Gdy withFilterjest używany, warunek jest oceniany za każdym razem, gdy uzyskuje się dostęp do elementu wewnątrz mapmetody.

Referencyjne : - str.120, Scala w działaniu (pokrowce Scala 2.10), Manning Publications, Milanjan Raychaudhuri - myśli Odersky chodzi o do-zrozumienia tłumaczenia


1

Głównym powodem, dla którego nie zaimplementowano forall / existie, jest taki przypadek użycia:

  • możesz leniwie zastosować withFilter do nieskończonego strumienia / iterowalnego
  • możesz leniwie zastosować inny z filtrem (i znowu i znowu)

Aby zaimplementować wszystko / istnieje , musimy uzyskać wszystkie elementy, tracąc lenistwo.

Na przykład:

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)

Zauważ, że ten_rand_even_naturals nadal jest iteratorem. Dopiero gdy wywołasz toList, losowe liczby zostaną wygenerowane i odfiltrowane w łańcuchu

Zauważ, że map (identity) jest równoważne map (i => i) i jest używane tutaj w celu konwersji obiektu withFilter z powrotem do typu oryginalnego (np. Kolekcja, strumień, iterator)


1

Dla części dla wszystkich / istniejących:

someList.filter(conditionA).forall(conditionB)

byłoby takie samo jak (choć trochę nieintuicyjne)

!someList.exists(conditionA && !conditionB)

Podobnie, .filter (). Exist () może zostać połączone w jeden exist () check?



-5

Aby obejść ten problem, możesz zaimplementować inne funkcje tylko z mapi flatMap.

Ponadto ta optymalizacja jest bezużyteczna w przypadku małych kolekcji…

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.