Go语言函数闭包

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分别传给了hytopmath.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绑定并且它们的变化被保存了。