CC BY 4.0 (除特别声明或转载文章外)
Go语言能将函数作为函数的参数和返回值很了不起。
作为参数
教程给了如下例子:
package main
import (
"fmt"
"math"
)
func compute(fn func(float64, float64) float64) float64 {
return fn(3, 4)
}
func main() {
hypot := func(x, y float64) float64 {
return math.Sqrt(x*x + y*y)
}
fmt.Println(hypot(5, 12))
fmt.Println(compute(hypot))
fmt.Println(compute(math.Pow))
}
输出如下:
13
5
81
Program exited.
hypot
是求平方根的函数。
对于后两个输出,是把3,4分别传给了hytop
和math.Pow
作为参数。
重点是接下来的函数作为返回值时独特的闭包用法。
作为返回值
典型的闭包用法是将函数B作为函数A的返回值。此时,函数B可以访问到A函数的实参、局部变量。
形式上,函数A的大括号包裹住函数B,仿佛给函数B、A函数的实参、A函数的局部变量开辟了一个封闭空间,他们互相之间“绑定”且并列。但另一次调用函数A返回的函数B又在另一个封闭空间里了,和之前那个互不搭界。
例如教程提供的题目“用函数闭包实现输出斐波那契数列的功能”,我的思路是这样的:
package main
import "fmt"
// 返回一个“返回int的函数”
func fibonacci() func() int {
count := 0 // 将count作为跟底下那个函数绑定在一起的计数器,以便输出0,1这两个基础
// 要完成Fibonacci数列的计算,需要存储上一个和上上个
prev := 0
next := 1
return func() int {
if count == 0 || count == 1 {
count++
return count - 1
}
temp := next
next = prev + next
prev = temp
return next
}
}
func main() {
f := fibonacci()
for i := 0; i < 10; i++ {
fmt.Println(f())
}
}
输出如下:
0
1
1
2
3
5
8
13
21
34
解这道题,我发现函数闭包的一大作用是可以查看某个函数的调用次数。在本例中,我将count计数器和计算数列下一项的函数绑定在一起,能追踪函数的执行次数。以往,一个函数被调用一次,又恢复如初,执行次数等信息不会被保存。但有了函数闭包,每一次调用函数,和他绑定的东西仍从上一次的值开始。
再看个例子:
package main
import "fmt"
func adder(g int) func(int) int {
sum := 0
return func(x int) int {
sum += x
g++
sum += g
return sum
}
}
func main() {
pos, neg := adder(6), adder(8)
for i := 0; i < 12; i++ {
fmt.Println(
pos(i),
neg(-2*i),
)
}
}
输出如下:
7 9
16 17
27 24
40 30
55 35
72 39
91 42
112 44
135 45
160 45
187 44
216 42
细细品究上述代码,就能发现这个输出结果的正确。而且这很能体现函数A的参数和局部变量都会和函数B绑定并且它们的变化被保存了。