You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Scala pozwala na definiowanie funkcji wyższego rzędu. Są to funkcje, które przyjmują funkcje jako parametry lub których wynik jest też funkcją. Poniżej znajduje się przykład funkcji `apply`, która pobiera inną funkcję `f` i wartość `v` po to, by zwrócić wynik zastosowania `f` do `v`:
15
+
Scala pozwala na definiowanie funkcji wyższego rzędu.
16
+
Są to funkcje, które przyjmują funkcje jako parametry lub których wynik również jest funkcją.
17
+
Jest to możliwe, ponieważ w Scali funkcje są wartościami pierwszej kategorii (first-class values).
18
+
Terminologia może w tym momencie wydawać się niejasna, pojęcie "funkcja wyższego rzędu" będzie używane zarówno dla metod jak i funkcji przyjmujących jako parametry funkcje lub zwracających inne funkcje.
19
+
20
+
Jednym z najczęściej spotykanych przykładów funkcji wyższego rzędu jest funkcja `map`, która dostępna jest dla kolekcji w Scali.
16
21
17
22
```tut
18
-
def apply(f: Int => String, v: Int) = f(v)
23
+
val salaries = Seq(20000, 70000, 40000)
24
+
val doubleSalary = (x: Int) => x * 2
25
+
val newSalaries = salaries.map(doubleSalary) // List(40000, 140000, 80000)
19
26
```
20
27
21
-
_Uwaga: metody są automatycznie zamieniane na funkcje, jeżeli wymaga tego kontekst_
28
+
Funkcja `doubleSalary` przyjmuje jako parametr wartość `x` typu `Int` i zwraca `x * 2`.
29
+
Ogólnie mówiąc, krotka po lewej stronie strzałki `=>` jest listą parametrów, a wartość wyrażenia po prawej stronie jest tym, co zostanie zwrócone.
30
+
W trzecim wierszu funkcja `doubleSalary` zostaje zastosowana na każdym elemencie listy `salaries`.
31
+
32
+
Aby zredukować trochę kod, możemy dodatkowo użyć funkcji anonimowej i przekazać ją bezpośrednio jako argument do funkcji `map`:
33
+
34
+
```
35
+
val salaries = Seq(20000, 70000, 40000)
36
+
val newSalaries = salaries.map(x => x * 2) // List(40000, 140000, 80000)
37
+
```
22
38
23
-
Praktyczny przykład:
39
+
Zauważ, że w powyższym przykładzie `x` nie jest zadeklarowane jako typ `Int`.
40
+
Dzieje się tak, ponieważ kompilator może wywnioskować typ, bazując na typie funkcji oczekiwanej przez `map`.
41
+
Poniżej jeszcze bardziej idiomatyczny sposób napisania tego kodu:
24
42
25
43
```tut
26
-
class Decorator(left: String, right: String) {
27
-
def layout[A](x: A) = left + x.toString() + right
44
+
val salaries = Seq(20000, 70000, 40000)
45
+
val newSalaries = salaries.map(_ * 2)
46
+
```
47
+
48
+
Ponieważ kompilator Scali zna typ parametru (pojedynczy Int), wystarczy jedynie dostarczyć prawą stronę funkcji.
49
+
Jedyne zastrzeżenie jest takie, że należy użyć `_` zamiast nazwy parametru (w poprzednim przykładzie było to `x`).
50
+
51
+
## Konwertowanie metod w funkcje
52
+
Możliwe jest, aby przekazać metody jako argumenty do funkcji wyższego rzędu.
53
+
Kompilator automatycznie przekonwertuje metodę w funkcję.
54
+
55
+
```
56
+
case class WeeklyWeatherForecast(temperatures: Seq[Double]) {
def forecastInFahrenheit: Seq[Double] = temperatures.map(convertCtoF) // <-- przekazanie metody convertCtoF
28
61
}
62
+
```
29
63
30
-
object FunTest extends App {
31
-
def apply(f: Int => String, v: Int) = f(v)
32
-
val decorator = new Decorator("[", "]")
33
-
println(apply(decorator.layout, 7))
64
+
W tym przykładzie metoda `convertCtoF` jest przekazana do funkcji `forecastInFahrenheit`.
65
+
Jest to możliwe, ponieważ kompilator konwertuje metodę `convertCtoF` w funkcję `x => convertCtoF(x)` (uwaga: `x` będzie tutaj wygenerowaną nazwą, która na pewno będzie unikalna w swoim zakresie).
66
+
67
+
## Funkcje przyjmujące inne funkcje
68
+
69
+
Jednym z powodów użycia funkcji wyższego rzędu jest zredukowanie nadmiarowego kodu.
70
+
Powiedzmy, że chcemy stworzyć metody, które potrafią zwiększyć czyjeś wynagrodzenie wg. jakiegoś współczynnika.
71
+
Bez użycia funkcji wyższego rzędu mogłoby to wyglądać w następujący sposób:
Nowa metoda, `promotion`, przyjmuje jako parametr listę wynagrodzeń oraz funkcję typu `Double => Double` (funkcję, która przyjmuje jako parametr Double i zwraca Double) oraz zwraca produkt.
108
+
109
+
## Funkcje zwracające inne funkcje
110
+
111
+
Istnieją pewne sytuacje, kiedy chcemy wygenerować jakieś funkcje.
val url = getURL(endpoint, query) // "https://www.example.com/users?id=1": String
41
125
```
42
126
43
-
W tym przykładzie metoda `decorator.layout` jest automatycznie konwertowana do funkcji typu `Int => String`, czego wymaga funkcja `apply`. Warto dodać, że metoda `decorator.layout` jest polimorficzna, co oznacza, że jej sygnatura jest odpowiednio dopasowana przez kompilator, dzięki czemu, gdy jest przekazana do funkcji `apply`, jest ona traktowana jako `Int => String`.
127
+
Zwróć uwagę na typ zwracany funkcji `urlBuilder` - jest to `(String, String) => String`.
128
+
Oznacza to, że `urlBuilder` zwraca funkcję anonimową biorącą jako parametry dwie wartości typu String i zwracającą String.
129
+
W tym wypadku zwracaną funkcją jest `(endpoint: String, query: String) => s"https://www.example.com/$endpoint?$query"`.
0 commit comments