| 
2 | 2 | layout: tour  | 
3 | 3 | title: For Comprehensions  | 
4 | 4 | partof: scala-tour  | 
 | 5 | + | 
 | 6 | +num: 16  | 
5 | 7 | language: pl  | 
 | 8 | +next-page: generic-classes  | 
 | 9 | +previous-page: extractor-objects  | 
6 | 10 | ---  | 
 | 11 | + | 
 | 12 | +Trudno znaleźć dobre tłumaczenie _for comprehensions_ w języku polskim, dlatego stosujemy wersję angielską.   | 
 | 13 | + | 
 | 14 | +Scala oferuje prostą w zapisie formę wyrażania _sequence comprehensions._  | 
 | 15 | +_For comprehensions_ przedstawione jest w formie `for (enumerators) yield e`, gdzie `enumerators` to lista enumeratorów oddzielonych średnikami. _Enumerator_ może być zarówno generatorem nowych wartości lub filtrem dla wartości przetwarzanych. Wyrażenie to definiuje ciało `e` dla każdej wartości wywołanej przez enumerator i zwraca te wartości w postaci sekwencji.   | 
 | 16 | + | 
 | 17 | +Poniżej znajduje się przykład, który przekształca listę osób na listę imion osób, których wiek mieści się w przedziale od 30 do 40 lat.  | 
 | 18 | + | 
 | 19 | +```tut  | 
 | 20 | +case class Person(name: String, age: Int)  | 
 | 21 | +
  | 
 | 22 | +val people = List(  | 
 | 23 | +  Person("Monika", 25),  | 
 | 24 | +  Person("Czarek", 35),  | 
 | 25 | +  Person("Marcin", 26),  | 
 | 26 | +  Person("Filip", 25)  | 
 | 27 | +)  | 
 | 28 | +
  | 
 | 29 | +val names = for (  | 
 | 30 | +  person <- people if (person.age >=30 && person.age < 40)  | 
 | 31 | +) yield person.name  // czyli dodaj do listy wynikowej  | 
 | 32 | +
  | 
 | 33 | +names.foreach(name => println(name))  // wydrukowane zostanie: Czarek  | 
 | 34 | +```  | 
 | 35 | + | 
 | 36 | +Na początku `for` znajduje się generator `person <- people`. Następujące po tym wyrażenie warunkowe `if (person.age >=30 && person.age < 40)` odfiltrowuje wszystkie osoby poniżej 30 i powyżej 40 roku życia. W powyższym przykładzie po wyrażeniu `yield` wywołano `person.name`, `name` jest typu `String`, więc lista wynikowa będzie typu `List[String]`. W ten sposób lista typu `List[Person]` została przekształcona na listę `Lista[String]`.  | 
 | 37 | + | 
 | 38 | +Poniżej znajduje się bardziej złożony przykład, który używa dwóch generatorów. Jego zadaniem jest sprawdzenie wszystkich par liczb od `0` do `n-1` i wybór tylko tych par, których wartości są sobie równe.  | 
 | 39 | + | 
 | 40 | +```tut  | 
 | 41 | +def someTuple(n: Int) =  | 
 | 42 | +  for (  | 
 | 43 | +    i <- 0 until n;  | 
 | 44 | +    j <- 0 until n if i == j  | 
 | 45 | +  ) yield (i, j)  | 
 | 46 | +
  | 
 | 47 | +someTuple(10) foreach {  | 
 | 48 | +  case (i, j) =>  | 
 | 49 | +    println(s"($i, $j) ")  // drukuje (0, 0) (1, 1) (2, 2) (3, 3) (4, 4) (5, 5) (6, 6) (7, 7) (8, 8) (9, 9)  | 
 | 50 | +}  | 
 | 51 | +```  | 
 | 52 | + | 
 | 53 | +Załóżmy, że wartością początkową jest `n == 10`. W pierwszej iteracji `i` przyjmuje wartość równą `0` tak samo jak `j`, filtr `i == j` zwróci `true` więc zostanie przekazane do `yield`. W kolejnej iteracji `j` przyjmie wartość równą `1`, więc `i == j` zwróci `false`, ponieważ `0 != 1` i nie zostanie przekazane do `yield`. Kolejne osiem iteracji to zwiększanie wartości `j` aż osiągnie wartość równą `9`. W następnej iteracji `j` powraca do wartości `0`, a `i` zostaje zwiększona o `1`. Gdyby w powyższym przykładzie nie umieszczono filtra `i == j` wydrukowana zostałaby prosta sekwencja:  | 
 | 54 | + | 
 | 55 | +```  | 
 | 56 | +(0, 0) (0, 1) (0, 2) (0, 3) (0, 4) (0, 5) (0, 6) (0, 7) (0, 8) (0, 9) (1, 0) ...  | 
 | 57 | +```  | 
 | 58 | + | 
 | 59 | +Bardzo istotne jest to, że comprehensions nie są ograniczone do list. Każdy typ danych, który wspiera operację `withFilter`, `map` czy `flatMap` (z odpowiednim typem) może być użyty w _sequence comprehensions_.  | 
 | 60 | + | 
 | 61 | +Przykładem innego użycia _comprehensions_ jest jego wykorzystanie do obsługi typu `Option`.  | 
 | 62 | +Załóżmy, że mamy dwie wartości `Option[String]` i chcielibyśmy zwrócić obiekt `Student(imie: String, nazwisko: String` tylko gdy obie wartości są zadeklarowane - nie są `None`.  | 
 | 63 | + | 
 | 64 | +Spójrzmy poniżej:  | 
 | 65 | + | 
 | 66 | +```tut  | 
 | 67 | +case class Student(name: String, surname: String)  | 
 | 68 | +
  | 
 | 69 | +val nameOpt: Option[String] = Some("John")  | 
 | 70 | +val surnameOpt: Option[String] = Some("Casey")  | 
 | 71 | +
  | 
 | 72 | +val student = for {  | 
 | 73 | +    name <- nameOpt  | 
 | 74 | +    surname <- surnameOpt  | 
 | 75 | +  } yield Student(name, surname) // wynik będzie typu Option[Student].  | 
 | 76 | +```  | 
 | 77 | + | 
 | 78 | +Jeżeli `name` lub `surname` nie byłyby określone, np. przyjmowałyby wartość równą `None` to zmienna `student` również byłaby `None`. Powyższy przykład to przekształcenie dwóch wartości `Option[String]` na `Option[Student]`.   | 
 | 79 | + | 
 | 80 | +Wszystkie powyższe przykłady posiadały wyrażenie `yield` na końcu _comprehensions_, jednak nie jest to obligatoryjne. Gdy `yield` nie zostanie dodanie zwrócony zostanie `Unit`. Takie rozwiązanie może być przydatne gdy chcemy uzyskać jakieś skutki uboczne. Poniższy przykład wypisuje liczby od 0 do 9 bez użycia `yield`.  | 
 | 81 | + | 
 | 82 | + | 
 | 83 | +```tut  | 
 | 84 | +def count(n: Int) =  | 
 | 85 | +    for (i <- 0 until n)  | 
 | 86 | +    println(s"$i ")  | 
 | 87 | +
  | 
 | 88 | +count(10) // wyświetli  "0 1 2 3 4 5 6 7 8 9 "  | 
 | 89 | +```  | 
 | 90 | + | 
0 commit comments