Lekcje modułu (4/5)
Domknięcia i funkcje jako wartości
W Go funkcje są wartościami pierwszej klasy: można je do nich przypisać zmienne, przekazywane do innych funkcji i zwracane. Zamknięcie to a literał funkcyjny (anonimowy), który przechwytuje zmienne pliku środowisko, w którym jest zdefiniowane.
Dosłowność funkcji
Funkcja bez nazwy, zapisana w linii. Zadzwoń natychmiast lub przypisz to:
greet := func(name string) {
fmt.Println("ciao", name)
}
greet("Anna")
// invocata immediatamente:
func() { fmt.Println("subito") }()Typ greet to func(string).
Przechwytywanie środowiska: zamknięcie
Literał funkcyjny może odczytywać i modyfikować zmienne widoczne w jego zakres leksykalny, nawet po zwróceniu funkcji zewnętrznej:
func counter() func() int {
n := 0
return func() int {
n++
return n
}
}
c := counter()
fmt.Println(c(), c(), c()) // 1 2 3n „żyje” tak długo, jak przynajmniej jedno zamknięcie odwołuje się do niego. Każde wezwanie do
counter() tworzy nowy, niezależny plik n.
Zamknięcie jako wywołanie zwrotne
Bardzo popularny wzór:
nums := []int{3, 1, 2}
sort.Slice(nums, func(i, j int) bool {
return nums[i] < nums[j]
})Zamknięcie przechwytuje nums i definiuje kolejność.
Funkcje jako parametry
Aby zaakceptować funkcję jako parametr, należy wpisać jej typ:
func apply(nums []int, f func(int) int) []int {
out := make([]int, len(nums))
for i, n := range nums {
out[i] = f(n)
}
return out
}
double := func(x int) int { return x * 2 }
apply([]int{1, 2, 3}, double) // [2 4 6]Jeśli podpis staje się trudny do odczytania, zdefiniuj alias typu:
type Transform func(int) int
func apply(nums []int, f Transform) []int { ... }Spróbuj
Zdefiniuj licznik(), który zwraca zamknięcie: każde wywołanie zamknięcia zwiększa się i zwraca wewnętrzny licznik.
Pokaż wskazówkę
`n` należy zadeklarować PRZED zwrotem; zamknięcie to oddaje.
Rozwiązanie dostępne po 3 próbach
Przypisz do f literał funkcji, który wypisuje „ciao”, a następnie wywołaj go.
Pokaż wskazówkę
Składnia: `f := func() { ... }`, następnie `f()`.
Rozwiązanie dostępne po 3 próbach
Co to drukuje? (uważaj na zakres zamknięcia)
mk := func() func() int {
x := 0
return func() int { x++; return x }
}
a := mk()
b := mk()
fmt.Println(a(), a(), b())Podsumowanie
- Funkcje są pierwszej klasy: można je przypisać, przekazać, zwrócić.
- Literał funkcyjny:
func(params) ret { ... }, anonimowy. - Zamknięcie: przechwytuje zmienne zakresu leksykalnego, utrzymuje je przy życiu.
- Jawny typ funkcji w parametrach:
func(int) int. - Od wersji 1.22 każda iteracja for ma własną kopię zmiennej pętli.