在Go语言中,函数是“一等公民”,可以像其他值一样被传递和赋值。匿名函数是没有名称的函数,而闭包则是指匿名函数捕获其外部作用域变量的能力。闭包使得函数可以携带状态,成为函数式编程风格的重要工具。
匿名函数没有名字,通常直接在需要的地方定义并调用,或者赋值给变量。
package main
import "fmt"
func main() {
// 立即执行的匿名函数
func() {
fmt.Println("匿名函数执行了")
}()
// 带参数的匿名函数
func(msg string) {
fmt.Println("消息:", msg)
}("Hello Go")
// 带返回值的匿名函数
sum := func(a, b int) int {
return a + b
}(10, 20)
fmt.Println("求和结果:", sum)
}
将匿名函数赋值给变量,就可以多次调用。
add := func(a, b int) int {
return a + b
}
result := add(5, 3)
fmt.Println(result) // 8
将匿名函数传递给其他函数,实现回调或定制行为。
// 接收一个函数作为参数,对切片每个元素应用该函数
func mapSlice(s []int, fn func(int) int) []int {
result := make([]int, len(s))
for i, v := range s {
result[i] = fn(v)
}
return result
}
func main() {
nums := []int{1, 2, 3, 4}
doubled := mapSlice(nums, func(x int) int {
return x * 2
})
fmt.Println(doubled) // [2 4 6 8]
}
当一个函数返回一个匿名函数,并且该匿名函数引用了外部函数的局部变量,就形成了闭包。闭包中的变量会“存活”在匿名函数中,即使外部函数已经退出。
func counter() func() int {
// 局部变量 count 被内部匿名函数捕获
count := 0
return func() int {
count++
return count
}
}
func main() {
c1 := counter()
fmt.Println(c1()) // 1
fmt.Println(c1()) // 2
fmt.Println(c1()) // 3
c2 := counter() // 新的闭包,拥有独立的 count
fmt.Println(c2()) // 1
}
counter()都会创建一个新的count变量,并返回一个引用了它的闭包。每个闭包都有自己的状态。闭包捕获的是外部变量的引用(而非副本),因此闭包内部可以修改变量,并且多个闭包可以共享同一个变量。
func accumulator() func(int) int {
total := 0
return func(inc int) int {
total += inc
return total
}
}
func main() {
acc := accumulator()
fmt.Println(acc(10)) // 10
fmt.Println(acc(20)) // 30
fmt.Println(acc(5)) // 35
}
在循环中定义闭包时,所有闭包捕获的是同一个循环变量的地址,导致预期外的结果。
func main() {
funcs := []func(){}
for i := 0; i < 3; i++ {
funcs = append(funcs, func() {
fmt.Println(i) // 捕获的是变量 i 的引用
})
}
for _, f := range funcs {
f() // 输出 3 3 3(而不是 0 1 2)
}
}
解决方法:在循环内创建一个新变量,或者通过参数传递。
for i := 0; i < 3; i++ {
i := i // 创建新变量,遮蔽外层 i
funcs = append(funcs, func() {
fmt.Println(i)
})
}
// 或通过参数传递
for i := 0; i < 3; i++ {
funcs = append(funcs, func(n int) func() {
return func() { fmt.Println(n) }
}(i))
}
for 循环语义进行了调整,每次迭代会创建新的循环变量实例,从而避免了此问题。但为了兼容旧版本,仍建议采用显式复制的方式。defer使用。func multiplyBy(factor int) func(int) int {
return func(x int) int {
return x * factor
}
}
double := multiplyBy(2)
triple := multiplyBy(3)
fmt.Println(double(5)) // 10
fmt.Println(triple(5)) // 15
匿名函数想要递归调用自身,需要先将其赋值给一个变量。
var fib func(n int) int
fib = func(n int) int {
if n < 2 {
return n
}
return fib(n-1) + fib(n-2)
}
fmt.Println(fib(10)) // 55
package main
import (
"fmt"
"sort"
)
type Person struct {
Name string
Age int
}
func main() {
people := []Person{
{"Alice", 30},
{"Bob", 25},
{"Charlie", 35},
}
// 按年龄升序排序的闭包
sort.Slice(people, func(i, j int) bool {
return people[i].Age < people[j].Age
})
fmt.Println("按年龄排序:", people)
// 按名字长度排序的闭包
sort.Slice(people, func(i, j int) bool {
return len(people[i].Name) < len(people[j].Name)
})
fmt.Println("按名字长度排序:", people)
}