Уроки модуля (4/5)
Замыкания и функции как значения
В Go функции являются первоклассными значениями: их можно присваивать переменные, передаваемые другим функциям и возвращаемые. Замыкание — это функциональный литерал (анонимный), который фиксирует переменные среду, в которой он определен.
Функциональный литерал
Функция без имени, написанная в строке. Вы позвоните ему немедленно или назначьте это:
greet := func(name string) {
fmt.Println("ciao", name)
}
greet("Anna")
// invocata immediatamente:
func() { fmt.Println("subito") }()Тип greet — func(string).
Захват окружающей среды: закрытие
Функциональный литерал может читать и изменять переменные, видимые в его лексическая область видимости, даже после того, как внешняя функция вернулась:
func counter() func() int {
n := 0
return func() int {
n++
return n
}
}
c := counter()
fmt.Println(c(), c(), c()) // 1 2 3n «живет» до тех пор, пока на него ссылается хотя бы одно замыкание. Каждый звонок в
counter() создает новый, независимый n.
Закрытие как обратный вызов
Очень распространенная закономерность:
nums := []int{3, 1, 2}
sort.Slice(nums, func(i, j int) bool {
return nums[i] < nums[j]
})Замыкание захватывает nums и определяет порядок.
Функции как параметры
Чтобы принять функцию в качестве параметра, запишите ее тип:
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]Если подпись становится трудно читать, определите псевдоним типа:
type Transform func(int) int
func apply(nums []int, f Transform) []int { ... }Попробуйте это
Определите counter(), который возвращает замыкание: каждый вызов замыкания увеличивается и возвращает внутренний счетчик.
Показать подсказку
`n` должен быть объявлен ПЕРЕД возвратом; закрытие фиксирует его.
Решение доступно после 3 попыток
Назначьте f литерал функции, который печатает «ciao», а затем вызовите его.
Показать подсказку
Синтаксис: `f := func() { ... }`, затем `f()`.
Решение доступно после 3 попыток
Что это печатает? (следите за областью закрытия)
mk := func() func() int {
x := 0
return func() int { x++; return x }
}
a := mk()
b := mk()
fmt.Println(a(), a(), b())Резюме
- Функции первоклассные: назначаемые, передаваемые, возвращаемые.
- Функциональный литерал:
func(params) ret { ... }, анонимный. - Замыкание: захватывает переменные лексической области видимости и сохраняет их в рабочем состоянии.
- Явный тип функции в параметрах:
func(int) int. — Начиная с Go 1.22, каждая итерация for имеет собственную копию переменной цикла.