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

3 回复

@lutzhorn,感谢你清晰的解释!当你这样详细阐述时,我完全明白了。我真的很感激你能这样分解说明。

更多关于Golang闭包问题求助(Go语言与编程新手)的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


你好 @cozco,欢迎!

这与闭包无关,而与 ab 的赋值有关。

a, b = b, a+b

b 值赋给 a,并将 ab 值之和赋给 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

可以看到,这实际上生成了一个等比数列(每个数是前一个数的两倍),而不是斐波那契数列。

闭包的关键点在于:

  1. 闭包函数捕获了外部函数的局部变量ab
  2. 这些变量在闭包的多次调用之间保持状态
  3. 对变量的修改会影响到后续的调用

要验证这一点,可以添加调试输出:

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
    }
}

这就是为什么第一个版本能正确生成斐波那契数列,而第二个版本生成了等比数列。

回到顶部