Golang闭包问题求助(Go语言与编程新手)
Golang闭包问题求助(Go语言与编程新手) 大家好,
我是Go语言和编程的新手,目前正在学习Todd Mcleod在Udemy上的Go课程。我找到了这个用于生成斐波那契数列的函数:https://golang.org/doc/play/fib.go,并稍微修改了一下。
有人能解释一下为什么下面的代码:
package main
import "fmt"
func main() {
n := fib()
for i := 0; i < 10; i++ {
fmt.Println(n())
}
}
func fib() func() int {
a, b := 0, 1
return func() int {
a, b = b, a+b
return a
}
}
会产生下面的结果,这符合我的预期。
fibonacci % go run main.go
1
1
2
3
5
8
13
21
34
55
但是这段代码:
package main
import "fmt"
func main() {
n := fib()
for i := 0; i < 10; i++ {
fmt.Println(n())
}
}
func fib() func() int {
a, b := 0, 1
return func() int {
a = b
b = a + b
return a
}
}
却产生了:
fibonacci % go run main.go
1
2
4
8
16
32
64
128
256
512
我猜这可能是非常明显的原因,但我还是新手,所以不太确定我漏掉了什么。感谢您的建议。
更多关于Golang闭包问题求助(Go语言与编程新手)的实战教程也可以访问 https://www.itying.com/category-94-b0.html
嗨 @lutzhorn,感谢你清晰的解释!当你这样详细阐述时,我完全明白了。我真的很感激你能这样分解说明。
更多关于Golang闭包问题求助(Go语言与编程新手)的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
你好 @cozco,欢迎!
这与闭包无关,而与 a 和 b 的赋值有关。
a, b = b, a+b
将 b 的 旧 值赋给 a,并将 a 和 b 的 旧 值之和赋给 b。
a = b
b = a + b
同样将 b 的 旧 值赋给 a,但它将 a 的 新 值和 b 的 旧 值之和赋给 b。这基本上忽略了 a,并将 b 与自身相加,如同 b * 2。这就是你得到的结果。
这是一个典型的闭包变量捕获问题。在Go语言中,闭包会捕获其外部作用域的变量,并持续引用这些变量。
第一个版本使用并行赋值是正确的:
a, b = b, a+b
这行代码会同时计算右侧的值,然后同时赋值给左侧。等价于:
tempA := b
tempB := a + b
a = tempA
b = tempB
第二个版本使用顺序赋值有问题:
a = b // 第一步:a被赋值为b的值
b = a + b // 第二步:此时a已经是新的b值,所以b = b + b = 2b
让我们跟踪一下第二个版本的执行过程:
初始: a=0, b=1
第一次调用:
a = b // a=1, b=1
b = a + b // b=1+1=2
返回a=1
第二次调用:
a = b // a=2, b=2
b = a + b // b=2+2=4
返回a=2
第三次调用:
a = b // a=4, b=4
b = a + b // b=4+4=8
返回a=4
可以看到,这实际上生成了一个等比数列(每个数是前一个数的两倍),而不是斐波那契数列。
闭包的关键点在于:
- 闭包函数捕获了外部函数的局部变量
a和b - 这些变量在闭包的多次调用之间保持状态
- 对变量的修改会影响到后续的调用
要验证这一点,可以添加调试输出:
func fib() func() int {
a, b := 0, 1
return func() int {
fmt.Printf("Before: a=%d, b=%d\n", a, b)
a, b = b, a+b
fmt.Printf("After: a=%d, b=%d\n", a, b)
return a
}
}
这就是为什么第一个版本能正确生成斐波那契数列,而第二个版本生成了等比数列。

