Odpowiedzi:
Jest używany w interpretacjach sekwencyjnych (takich jak listy-generatory Pythona i generatory, z których możesz także korzystać yield).
Jest stosowany w połączeniu z fori zapisuje nowy element w wynikowej sekwencji.
Prosty przykład (ze scala-lang )
/** Turn command line arguments to uppercase */
object Main {
def main(args: Array[String]) {
val res = for (a <- args) yield a.toUpperCase
println("Arguments: " + res.toString)
}
}
Odpowiednie wyrażenie w F # byłoby
[ for a in args -> a.toUpperCase ]
lub
from a in args select a.toUpperCase
w Linq.
Ruby yieldma inny efekt.
Uważam, że zaakceptowana odpowiedź jest świetna, ale wydaje się, że wielu ludziom nie udało się zrozumieć pewnych podstawowych kwestii.
Po pierwsze, forrozumienia Scali są równoważne donotacji Haskella i nie są niczym więcej jak cukrem syntaktycznym do kompozycji wielu operacji monadycznych. Ponieważ to stwierdzenie najprawdopodobniej nie pomoże nikomu, kto potrzebuje pomocy, spróbujmy jeszcze raz… :-)
Zrozumienia Scali forto cukier składniowy do kompozycji wielu operacji z mapą flatMapi filter. Lub foreach. Scala faktycznie tłumaczy for-wyrażenie na wywołania tych metod, więc każda klasa, która je udostępnia, lub ich podzbiór, może być używana ze zrozumieniem.
Najpierw porozmawiajmy o tłumaczeniach. Istnieją bardzo proste zasady:
To
for(x <- c1; y <- c2; z <-c3) {...}
jest przetłumaczone na
c1.foreach(x => c2.foreach(y => c3.foreach(z => {...})))To
for(x <- c1; y <- c2; z <- c3) yield {...}
jest przetłumaczone na
c1.flatMap(x => c2.flatMap(y => c3.map(z => {...})))To
for(x <- c; if cond) yield {...}
jest przetłumaczony na Scala 2.7 na
c.filter(x => cond).map(x => {...})
lub, na Scala 2.8, do
c.withFilter(x => cond).map(x => {...})
z rezerwą na poprzednie, jeśli metoda withFilternie jest dostępna, ale filterjest. Więcej informacji na ten temat znajduje się w sekcji poniżej.
To
for(x <- c; y = ...) yield {...}
jest przetłumaczone na
c.map(x => (x, ...)).map((x,y) => {...})Kiedy patrzysz na bardzo proste forpojęcia, alternatywy map/ foreachwyglądają rzeczywiście lepiej. Gdy jednak zaczniesz je komponować, możesz łatwo zgubić się w poziomach nawiasów i zagnieżdżania. Kiedy tak się dzieje, forrozumienie jest zwykle znacznie wyraźniejsze.
Pokażę jeden prosty przykład i celowo pominę wszelkie wyjaśnienia. Możesz zdecydować, która składnia była łatwiejsza do zrozumienia.
l.flatMap(sl => sl.filter(el => el > 0).map(el => el.toString.length))
lub
for {
sl <- l
el <- sl
if el > 0
} yield el.toString.length
withFilterScala 2.8 wprowadziła metodę o nazwie withFilter, której główną różnicą jest to, że zamiast zwracać nową, przefiltrowaną kolekcję, filtruje na żądanie. filterMetoda ma swoje zachowanie zdefiniowane w oparciu o surowości kolekcji. Aby to lepiej zrozumieć, spójrzmy na Scala 2.7 z List(ścisłe) i Stream(nie ścisłe):
scala> var found = false
found: Boolean = false
scala> List.range(1,10).filter(_ % 2 == 1 && !found).foreach(x => if (x == 5) found = true else println(x))
1
3
7
9
scala> found = false
found: Boolean = false
scala> Stream.range(1,10).filter(_ % 2 == 1 && !found).foreach(x => if (x == 5) found = true else println(x))
1
3
Różnica dzieje się, ponieważ filterjest natychmiast stosowana z List, zwracając listę szans - ponieważ foundjest false. Dopiero wtedy foreachjest wykonywana, ale do tego czasu zmiana foundjest bez znaczenia, tak jak filterjuż została wykonana.
W przypadku Streamwarunek nie jest natychmiast stosowany. Zamiast tego, zgodnie z żądaniem każdego elementu foreach, filtertestuje warunek, który pozwala foreachna jego wpływ found. Żeby było jasne, oto kod równoważny dla zrozumienia:
for (x <- List.range(1, 10); if x % 2 == 1 && !found)
if (x == 5) found = true else println(x)
for (x <- Stream.range(1, 10); if x % 2 == 1 && !found)
if (x == 5) found = true else println(x)
Powodowało to wiele problemów, ponieważ ludzie spodziewali się, że ifbędą one uważane za dostępne na żądanie, zamiast wcześniejszego zastosowania do całej kolekcji.
Wprowadzono Scala 2.8 withFilter, która zawsze nie jest ścisła, bez względu na surowość kolekcji. Poniższy przykład pokazuje Listobie metody w Scali 2.8:
scala> var found = false
found: Boolean = false
scala> List.range(1,10).filter(_ % 2 == 1 && !found).foreach(x => if (x == 5) found = true else println(x))
1
3
7
9
scala> found = false
found: Boolean = false
scala> List.range(1,10).withFilter(_ % 2 == 1 && !found).foreach(x => if (x == 5) found = true else println(x))
1
3
Daje to wynik, którego większość ludzi oczekuje, bez zmiany filterzachowania. Na marginesie, Rangezmieniono z nieprecyzyjnego na ścisłe między Scala 2.7 i Scala 2.8.
withFilterma być również surowe, nawet w przypadku ścisłych kolekcji, które zasługują na wyjaśnienie. Rozważę to ...
for(x <- c; y <- x; z <-y) {...}przetłumaczono na c.foreach(x => x.foreach(y => y.foreach(z => {...}))) 2. for(x <- c; y <- x; z <- y) yield {...}przetłumaczono nac.flatMap(x => x.flatMap(y => y.map(z => {...})))
for(x <- c; y = ...) yield {...}naprawdę jest przetłumaczone na c.map(x => (x, ...)).map((x,y) => {...})? Myślę, że to jest przetłumaczone na, c.map(x => (x, ...)).map(x => { ...use x._1 and x._2 here...})czy coś mi brakuje?
Tak, jak powiedział Earwicker, jest to prawie odpowiednik LINQ selecti ma bardzo niewiele wspólnego z Ruby i Pythonem yield. Zasadniczo, gdzie w C # napiszesz
from ... select ???
w Scali masz zamiast tego
for ... yield ???
Ważne jest również, aby zrozumieć, że for-zrozumienia nie działają tylko z sekwencjami, ale z każdym typem, który definiuje pewne metody, tak jak LINQ:
map, pozwala for-wyrażenia składające się z jednego generatora.flatMaprównież map, pozwala na for-wyrażenia składające się z kilku generatorów.foreachpozwala for-laops bez wydajności (zarówno z jednym, jak i wieloma generatorami).filterumożliwia forwyrażenia -filter zaczynające się if
od forwyrażenia w.O ile nie uzyskasz lepszej odpowiedzi od użytkownika Scali (którego nie jestem), oto moje zrozumienie.
Pojawia się tylko jako część wyrażenia rozpoczynającego się od for, które określa, jak wygenerować nową listę z istniejącej listy.
Coś jak:
var doubled = for (n <- original) yield n * 2
Jest więc jeden element wyjściowy dla każdego wejścia (chociaż uważam, że istnieje sposób na usunięcie duplikatów).
Jest to całkiem odmienne od „kontynuacji imperatywnych” włączonych przez funkcję fed w innych językach, gdzie zapewnia sposób generowania listy o dowolnej długości, z jakiegoś imperatywnego kodu o prawie dowolnej strukturze.
(Jeśli znasz C #, jest bliżej operatora LINQ select niż jest yield return).
Słowem kluczowym yieldw Scali jest po prostu cukier składniowy, który można łatwo zastąpić przez map, jak szczegółowo wyjaśnił Daniel Sobral .
Z drugiej strony yieldjest całkowicie mylące, jeśli szukasz generatorów (lub kontynuacji) podobnych do tych w Pythonie . Zobacz ten wątek SO, aby uzyskać więcej informacji: Jaki jest preferowany sposób implementacji „fedrunku” w Scali?
Rozważ następujące zrozumienie
val A = for (i <- Int.MinValue to Int.MaxValue; if i > 3) yield i
Pomocne może być przeczytanie go na głos w następujący sposób
„ Dla każdej liczby całkowitej i, jeśli jest ona większa niż3 , wydaj (produkuj) ii dodaj ją do listy A”.
Pod względem matematycznym notacji konstruktora zestawów powyższe rozumienie jest analogiczne do
który można odczytać jako
„ Dla każdej liczby całkowitej , jeśli jest ona większa niż
, to należy ona do zbioru
”.
lub alternatywnie jako
„ jest zbiorem wszystkich liczb całkowitych
, z których każda
jest większa niż
”.
Wydajność jest podobna do pętli for, która ma bufor, którego nie widzimy, i dla każdego przyrostu dodaje kolejny element do bufora. Gdy pętla for zakończy działanie, zwróci kolekcję wszystkich uzyskanych wartości. Wydajność może być używana jako proste operatory arytmetyczne lub nawet w połączeniu z tablicami. Oto dwa proste przykłady lepszego zrozumienia
scala>for (i <- 1 to 5) yield i * 3
res: scala.collection.immutable.IndexedSeq [Int] = Vector (3, 6, 9, 12, 15)
scala> val nums = Seq(1,2,3)
nums: Seq[Int] = List(1, 2, 3)
scala> val letters = Seq('a', 'b', 'c')
letters: Seq[Char] = List(a, b, c)
scala> val res = for {
| n <- nums
| c <- letters
| } yield (n, c)
res: Seq [(Int, Char)] = List ((1, a), (1, b), (1, c), (2, a), (2, b), (2, c), ( 3, a), (3, b), (3, c))
Mam nadzieję że to pomoże!!
val aList = List( 1,2,3,4,5 )
val res3 = for ( al <- aList if al > 3 ) yield al + 1
val res4 = aList.filter(_ > 3).map(_ + 1)
println( res3 )
println( res4 )
Te dwa fragmenty kodu są równoważne.
val res3 = for (al <- aList) yield al + 1 > 3
val res4 = aList.map( _+ 1 > 3 )
println( res3 )
println( res4 )
Te dwa fragmenty kodu są również równoważne.
Mapa jest tak elastyczna jak plon i odwrotnie.
wydajność jest bardziej elastyczna niż map (), patrz przykład poniżej
val aList = List( 1,2,3,4,5 )
val res3 = for ( al <- aList if al > 3 ) yield al + 1
val res4 = aList.map( _+ 1 > 3 )
println( res3 )
println( res4 )
wydajność wydrukuje wynik taki jak: Lista (5, 6), co jest dobre
podczas gdy map () zwróci wynik: List (false, false, true, true, true), co prawdopodobnie nie jest tym, co zamierzasz.