Skip to content

Commit d8bcd81

Browse files
authored
Merge pull request #1239 from kamilduda/pl/tour/higher-order-functions
Polish translation of Tour of Scala: Higher - order functions
2 parents a940aa2 + 38ebf57 commit d8bcd81

File tree

1 file changed

+100
-14
lines changed

1 file changed

+100
-14
lines changed

_pl/tour/higher-order-functions.md

Lines changed: 100 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,38 +6,124 @@ discourse: false
66

77
partof: scala-tour
88

9-
num: 7
9+
num: 8
1010
language: pl
1111
next-page: nested-functions
1212
previous-page: mixin-class-composition
1313
---
1414

15-
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.
1621

1722
```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)
1926
```
2027

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+
```
2238

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:
2442

2543
```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]) {
57+
58+
private def convertCtoF(temp: Double) = temp * 1.8 + 32
59+
60+
def forecastInFahrenheit: Seq[Double] = temperatures.map(convertCtoF) // <-- przekazanie metody convertCtoF
2861
}
62+
```
2963

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:
72+
73+
```tut
74+
object SalaryRaiser {
75+
76+
def smallPromotion(salaries: List[Double]): List[Double] =
77+
salaries.map(salary => salary * 1.1)
78+
79+
def greatPromotion(salaries: List[Double]): List[Double] =
80+
salaries.map(salary => salary * math.log(salary))
81+
82+
def hugePromotion(salaries: List[Double]): List[Double] =
83+
salaries.map(salary => salary * salary)
3484
}
3585
```
3686

37-
Wykonanie zwraca poniższy wynik:
87+
Zauważ, że każda z trzech metod różni się jedynie współczynnikiem z jakim zmienia wynagrodzenie.
88+
Aby to uprościć, możemy wydzielić powtórzony kod do funkcji wyższego rzędu:
89+
90+
```tut
91+
object SalaryRaiser {
92+
93+
private def promotion(salaries: List[Double], promotionFunction: Double => Double): List[Double] =
94+
salaries.map(promotionFunction)
95+
96+
def smallPromotion(salaries: List[Double]): List[Double] =
97+
promotion(salaries, salary => salary * 1.1)
98+
99+
def bigPromotion(salaries: List[Double]): List[Double] =
100+
promotion(salaries, salary => salary * math.log(salary))
38101
102+
def hugePromotion(salaries: List[Double]): List[Double] =
103+
promotion(salaries, salary => salary * salary)
104+
}
39105
```
40-
[7]
106+
107+
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.
112+
Oto przykład funkcji zwracającej inną funkcję.
113+
114+
```tut
115+
def urlBuilder(ssl: Boolean, domainName: String): (String, String) => String = {
116+
val schema = if (ssl) "https://" else "http://"
117+
(endpoint: String, query: String) => s"$schema$domainName/$endpoint?$query"
118+
}
119+
120+
val domainName = "www.example.com"
121+
def getURL = urlBuilder(ssl=true, domainName)
122+
val endpoint = "users"
123+
val query = "id=1"
124+
val url = getURL(endpoint, query) // "https://www.example.com/users?id=1": String
41125
```
42126

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

Comments
 (0)