Chuyển đến nội dung chính
Mô-đun 3 · Bài học 4 trong tổng số 514/50 trong khóa học~12 min
Bài học theo mô-đun (4/5)

Đóng cửa và chức năng như giá trị

Trong Go, các hàm là giá trị hạng nhất: chúng có thể được gán cho các biến, được chuyển đến các hàm khác và được trả về. đóng cửa là một hàm theo nghĩa đen (ẩn danh) nắm bắt các biến của môi trường mà nó được xác định.

Chức năng theo nghĩa đen

Một hàm không có tên, được viết nội tuyến. Bạn gọi nó ngay lập tức hoặc chỉ định nó:

Go
greet := func(name string) {
    fmt.Println("ciao", name)
}
greet("Anna")

// invocata immediatamente:
func() { fmt.Println("subito") }()

Loại greet là func(string).

Nắm bắt môi trường: đóng cửa

Hàm đen có thể đọc và sửa đổi các biến hiển thị trong phạm vi từ vựng của nó, ngay cả sau khi hàm bên ngoài đã trả về:

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

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

n "tồn tại" miễn là có ít nhất một lần đóng tham chiếu đến nó. Mỗi cuộc gọi đến counter() tạo n mới, độc lập.

Đóng cửa dưới dạng gọi lại

Một mô hình rất phổ biến:

Go
nums := []int{3, 1, 2}
sort.Slice(nums, func(i, j int) bool {
    return nums[i] < nums[j]
})

Việc đóng sẽ ghi lại nums và xác định thứ tự.

Chức năng như tham số

Để chấp nhận một hàm làm tham số, hãy viết ra kiểu của nó:

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]

Nếu chữ ký trở nên khó đọc, hãy xác định loại bí danh:

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

Hãy thử nó

tập thể dục#go.m3.l4.e1
Nỗ lực: 0Đang tải…

Xác định counter() trả về một bao đóng: mỗi lệnh gọi đến bao đóng sẽ tăng dần và trả về một bộ đếm nội bộ.

Đang tải trình chỉnh sửa…
Hiển thị gợi ý

`n` phải được khai báo TRƯỚC KHI trả về; việc đóng cửa nắm bắt nó.

Giải pháp khả dụng sau 3 lần thử

tập thể dục#go.m3.l4.e2
Nỗ lực: 0Đang tải…

Gán cho f một chữ hàm in 'ciao', sau đó gọi nó.

Đang tải trình chỉnh sửa…
Hiển thị gợi ý

Cú pháp: `f := func() { ... }` rồi `f()`.

Giải pháp khả dụng sau 3 lần thử

Quiz#go.m3.l4.e3
Sẵn sàng

Cái này in cái gì? (coi chừng phạm vi đóng cửa)

Go
mk := func() func() int {
    x := 0
    return func() int { x++; return x }
}
a := mk()
b := mk()
fmt.Println(a(), a(), b())
Tùy chọn trả lời

Tóm tắt lại

  • Chức năng hạng nhất: có thể gán, có thể vượt qua, có thể trả về.
  • Hàm ý: func(params) ret { ... }, ẩn danh.
  • Closure: nắm bắt các biến thuộc phạm vi từ vựng, giữ cho chúng tồn tại.
  • Kiểu hàm rõ ràng trong tham số: func(int) int.
  • Kể từ Go 1.22, mỗi lần lặp for đều có bản sao của biến vòng lặp.