4.3. 匿名函数及闭包
上一节介绍了函数值的基本用法,既然函数可被视为值,那么,在凹语言函数内部,是否可以像声明基本类型字面量那样,声明函数字面量?答案是肯定的,例如:
// 版权 @2023 凹语言 作者。保留所有权利。
func useFunc(i: i32, f: func(i32) => i32) {
if f == nil {
println("f == nil")
return
}
println(f(i))
}
func main {
f := func(i: i32) => i32 { return i * i } // 声明匿名函数并赋值给 f
useFunc(3, f) // 9
}
其中快捷声明的函数值 f
,它的初始值是字面量 func(i: i32) => i32 { return i * i }
,既一个没有名字的函数。在凹语言中,这种没有名字的函数字面量被称为匿名函数。在访问者模式、自定义快速排序等应用场景中,经常需要传入一些函数值参数,而这些函数可能仅在当前上下文环境出现一次,为此额外定义模块级的全局函数有诸多不便,这时即可使用匿名函数。
在函数A内部声明的匿名函数B,可以访问A内部的局部变量,例如:
// 版权 @2023 凹语言 作者。保留所有权利。
func useFunc(i: i32, f: func(i32) => i32) {
if f == nil {
println("f == nil")
return
}
println(f(i))
}
func main {
n: i32 = 0
f := func(i: i32) => i32 {
n = i * i
return n
}
useFunc(3, f)
println(n) // 9
}
可见函数值 f
可以读写外层的局部变量 n
。再来看一个更加复杂的例子:
// 版权 @2023 凹语言 作者。保留所有权利。
func genClosure(i: i32) => func() => i32 {
n := i
return func() => i32 {
n = n + 1
return n
}
}
func main {
c := genClosure(0)
d := genClosure(99)
println(c()) // 1
println(d()) // 100
println(c()) // 2
println(d()) // 101
}
每次调用 genFunc
都将生成一个函数值,这个函数值捕获了局部变量 n
,函数值每次执行会对捕获的 n
执行加1,多次执行 genFunc
所获得的函数值,它们捕获的 n
是不同的,每执行一次,捕获一个新的实例。
在函数内声明的匿名函数值,携带了本次运行时捕获的局部变量的状态。显然,这种函数值实质上就是闭包。