Przejdź do głównej treści
eLearner.app
Moduł 3 · Lekcja 4 z 514/50 w kursie~12 min
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:

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

Go
func counter() func() int {
    n := 0
    return func() int {
        n++
        return n
    }
}

c := counter()
fmt.Println(c(), c(), c())  // 1 2 3

n „ż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:

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

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

Go
type Transform func(int) int
func apply(nums []int, f Transform) []int { ... }

Spróbuj

Ćwiczenie#go.m3.l4.e1
Próby: 0Ładowanie...

Zdefiniuj licznik(), który zwraca zamknięcie: każde wywołanie zamknięcia zwiększa się i zwraca wewnętrzny licznik.

Ładowanie edytora...
Pokaż wskazówkę

`n` należy zadeklarować PRZED zwrotem; zamknięcie to oddaje.

Rozwiązanie dostępne po 3 próbach

Ćwiczenie#go.m3.l4.e2
Próby: 0Ładowanie...

Przypisz do f literał funkcji, który wypisuje „ciao”, a następnie wywołaj go.

Ładowanie edytora...
Pokaż wskazówkę

Składnia: `f := func() { ... }`, następnie `f()`.

Rozwiązanie dostępne po 3 próbach

Quiz#go.m3.l4.e3
Gotowe

Co to drukuje? (uważaj na zakres zamknięcia)

Go
mk := func() func() int {
    x := 0
    return func() int { x++; return x }
}
a := mk()
b := mk()
fmt.Println(a(), a(), b())
Opcje odpowiedzi

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.