Go
Domknięcia
Szukaj…
Podstawy zamknięcia
Zamknięcie to funkcja rozpatrywana łącznie z otoczeniem. Ta funkcja jest zazwyczaj funkcją anonimową zdefiniowaną w innej funkcji. Środowisko to zakres leksykalny funkcji zamykającej (bardzo podstawową ideą zakresu leksykalnego funkcji byłby zakres istniejący między nawiasami klamrowymi funkcji).
func g() {
i := 0
f := func() { // anonymous function
fmt.Println("f called")
}
}
W treści funkcji anonimowej (powiedzmy f
) zdefiniowanej w innej funkcji (powiedzmy g
) dostępne są zmienne obecne w zakresach zarówno f
jak i g
. Jednak to zakres g
tworzy część środowiskową zamknięcia (część funkcyjna to f
), w wyniku czego zmiany dokonane w zmiennych w zakresie g
zachowują swoje wartości (tzn. Środowisko utrzymuje się między wywołaniami f
) .
Rozważ poniższą funkcję:
func NaturalNumbers() func() int {
i := 0
f:= func() int { // f is the function part of closure
i++
return i
}
return f
}
W powyższej definicji NaturalNumbers
ma wewnętrzną funkcji f
które NaturalNumbers
powraca. Wewnątrz f
zmienna i
zdefiniowana w zakresie NaturalNumbers
.
Otrzymujemy nową funkcję od NaturalNumbers
taką jak:
n := NaturalNumbers()
Teraz n
jest zamknięciem. Jest to funkcja (zdefiniowana przez f
), która ma również powiązane środowisko (zakres NaturalNumbers
).
W przypadku n
część środowiska zawiera tylko jedną zmienną: i
Ponieważ n
jest funkcją, można ją wywołać:
fmt.Println(n()) // 1
fmt.Println(n()) // 2
fmt.Println(n()) // 3
Jak widać z powyższej wartości wyjściowej, za każdym razem, gdy wywoływane jest n
, inkrementuje i
. i
zaczyna się od 0, a każde wywołanie n
wykonuje i++
.
Wartość i
jest zachowywana między połączeniami. Oznacza to, że środowisko, będące częścią zamknięcia, trwa.
NaturalNumbers
wywołanie NaturalNumbers
stworzy i zwróci nową funkcję. To zainicjuje nowe i
w NaturalNumbers
. Co oznacza, że nowo zwrócona funkcja stanowi kolejne zamknięcie mające tę samą część dla funkcji (wciąż f
), ale zupełnie nowe środowisko (nowo zainicjowane i
).
o := NaturalNumbers()
fmt.Println(n()) // 4
fmt.Println(o()) // 1
fmt.Println(o()) // 2
fmt.Println(n()) // 5
Zarówno n
i o
są zamknięcia zawierające tę samą funkcję części (co daje im takie samo zachowanie), ale różnych środowiskach. Zatem użycie zamknięć pozwala funkcjom na dostęp do trwałego środowiska, które może być użyte do przechowywania informacji między rozmowami.
Inny przykład:
func multiples(i int) func() int {
var x int = 0
return func() int {
x++
// paramenter to multiples (here it is i) also forms
// a part of the environment, and is retained
return x * i
}
}
two := multiples(2)
fmt.Println(two(), two(), two()) // 2 4 6
fortyTwo := multiples(42)
fmt.Println(fortyTwo(), fortyTwo(), fortyTwo()) // 42 84 126