在 Go 中,匿名函数(Anonymous Function),也称为函数字面量,是指在代码中直接定义的、没有函数名的函数。它们是一等公民,意味着可以像任何其他类型(如 int、string)的值一样被使用。
基本语法
func(参数列表) (返回值列表) {
// 函数体
}
常见用法
a) 直接调用(立即执行函数 IIFE)
定义后立即用 () 调用,通常用于创建一个临时的作用域。
func main() {
// 定义并立即调用
func() {
fmt.Println("这是一个直接调用的匿名函数!")
}()
}
b) 赋值给变量
可以将匿名函数赋值给一个变量,然后通过这个变量来调用它。
func main() {
// 将匿名函数赋值给变量 add
add := func(a, b int) int {
return a + b
}
// 通过变量调用函数
sum := add(10, 20)
fmt.Println("Sum:", sum) // 输出: Sum: 30
}
c) 作为参数传递给其他函数(高阶函数)
当一个函数需要另一个函数作为其行为的一部分时,可以传递一个匿名函数。
func calculate(a, b int, operation func(int, int) int) int {
return operation(a, b)
}
func main() {
// 传递一个匿名函数作为参数
result := calculate(10, 5, func(x, y int) int {
return x * y
})
fmt.Println("Result:", result) // 输出: Result: 50
}
d) 作为函数的返回值(闭包)
一个函数可以返回另一个函数。返回的匿名函数可以“记住”并访问其创建时所在作用域的变量,即闭包。
// makeAdder 返回一个“加法器”函数
func makeAdder(x int) func(int) int {
// 返回的匿名函数“记住”了变量 x
return func(y int) int {
return x + y
}
}
func main() {
add5 := makeAdder(5) // 创建一个“加5”的函数
add10 := makeAdder(10) // 创建一个“加10”的函数
fmt.Println(add5(2)) // 输出: 7
fmt.Println(add10(2)) // 输出: 12
}
e) 在 Goroutine 中使用
这是 Go 并发编程中的核心模式,可以直接将一个匿名函数交给新的 Goroutine 去执行。
func main() {
// 在一个新的 Goroutine 中执行匿名函数
go func() {
fmt.Println("我在一个新的 Goroutine 中运行!")
}()
// 等待一下,不然 main 函数可能在 Goroutine 运行前就退出了
time.Sleep(100 * time.Millisecond)
}
与C++的区别
从核心概念上讲,Go的匿名函数和C++的lambda函数非常相似。它们都是:
- 没有名字的函数。
- 可以被赋值给变量、作为参数传递、作为返回值。
- 都是闭包,可以捕获其定义时所在作用域的变量。
但是变量捕获机制上它们存在着一个至关重要的区别。
在 Go 中,匿名函数总是通过“引用”的方式来捕获外部变量。这意味着,如果在匿名函数内部修改了捕获的变量,外部的原始变量也会被修改。
var factor = 2
multiply := func(n int) int {
factor = 3 // 修改了外部的 factor
return n * factor
}
fmt.Println(multiply(10)) // 输出: 30
fmt.Println(factor) // 输出: 3 (原始变量被修改了)
这也是为什么在 for 循环中使用 Goroutine 时,必须通过参数传递循环变量的原因,因为所有 Goroutine 默认会共享同一个按引用捕获的变量。
[=]: 按值捕获 (Capture by Value)。Lambda 内部拥有外部变量的一份副本,修改它不会影响外部。[&]: 按引用捕获 (Capture by Reference)。与 Go 的行为类似,修改会影响外部。[x, &y]: 精确指定x按值捕获,y按引用捕获。
C++ 的 Lambda 提供了强大的捕获列表 [],让程序员可以精确控制如何捕获外部变量:
int factor = 2;
// 按值捕获 factor
auto multiply = [=](int n) mutable -> int {
factor = 3; // 修改的是内部的副本
return n * factor;
};
std::cout << multiply(10) << std::endl; // 输出: 30
std::cout << factor << std::endl; // 输出: 2 (原始变量未被修改)