withFilter zamiast filter

82

Czy zawsze bardziej wydajne jest użycie withFilter zamiast filtra, gdy później stosuje się funkcje takie jak mapa, mapa płaska itp.?

Dlaczego obsługiwane są tylko mapy, mapy płaskie i wszystkie? (Oczekiwane funkcje, takie jak forall / również istnieją)

Kigyo
źródło
Ta część dokumentu Scala ma również szczegółowe wyjaśnienie.
ohkts11

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.

Shadowlands
źródło
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

James
źródło
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)

frhack
źródło
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?

lznt
źródło
-3

Używanie do uzyskiwania plonów może być obejściem, na przykład:

for {
  e <- col;
  if e isNotEmpty
} yield e.get(0)
facebook-100001836197736
źródło
-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…

Yann Moisan
źródło