我是Go语言的新手并且对以下代码感到困惑
package main
import "fmt"
// fibonacci is a function that returns
// a function that returns an int.
func fibonacci() func() int {
previous := 0
current := 1
return func () int{
current = current+previous
previous = current-previous
return current
}
}
func main() {
f := fibonacci
for i := 0; i < 10; i++ {
fmt.Println(f()())
}
}
这段代码应打印出Fibonacci序列(前10个),但只打印出10次1。 但如果我将代码更改为:
func main() {
f := fibonacci()
for i := 0; i < 10; i++ {
fmt.Println(f())
}
}
然后它工作正常。输出是Fibonacci序列。
有人可以帮我解释一下吗?
由于
答案 0 :(得分:4)
fibonacci()
创建了一个新的斐波纳契生成器函数。 fibonacci()()
执行相同操作,然后调用一次,返回结果并丢弃生成器,永远不会再次使用。如果你在循环中调用它,它将继续创建新的生成器并仅使用它们的第一个值。
如果您想要的不仅仅是第一个值,您需要完成您在第二个示例中所做的操作。将生成器本身存储在变量中,然后多次调用同一个生成器。
答案 1 :(得分:1)
这与返回闭包后变量如何封装在闭包中有关。 请考虑以下示例(live code on play):
func newClosure() func() {
i := 0
fmt.Println("newClosure with &i=", &i)
return func() {
fmt.Println(i, &i)
i++
}
}
func main() {
a := newClosure()
a()
a()
a()
b := newClosure()
b()
a()
}
运行此代码将产生类似于以下输出的内容。我注释了 哪一行来自哪个陈述:
newClosure with &i= 0xc010000000 // a := newClosure()
0 0xc010000000 // a()
1 0xc010000000 // a()
2 0xc010000000 // a()
newClosure with &i= 0xc010000008 // b := newClosure()
0 0xc010000008 // b()
3 0xc010000000 // a()
在示例中,newClosure
返回的闭包封装了局部变量i
。
这对应于代码中的current
等。您可以看到a
和b
有i
的不同实例,否则调用b()
会打印3
。
您还可以看到i
变量具有不同的地址。 (变量已经存在
在堆上,因为go没有单独的堆栈内存,所以在闭包中使用它是
没问题。)
因此,通过生成一个新的闭包,您将自动为该创建一个新的上下文 闭包和闭包之间不共享局部变量。这就是原因 为什么在循环中创建一个新的闭包 不会让你更进一步。
根据此示例,代码的等效内容为:
for i:=0; i < 10; i++ {
newClosure()()
}
你已经在输出中看到这不起作用。
答案 2 :(得分:1)
func fibonacci() func() int
返回function literal(闭包),返回表示列表中最后生成的数字的int
。
第一个main()方法
f := fibonacci
for i := 0; i < 10; i++ {
fmt.Println(f()())
}
f
是生成器函数,循环中的每次迭代调用f()()
,生成一个带有新环境的新闭包({{ 1}},previous := 0
,第二次调用返回current := 1), so
,它总是等于current
。
第二个main()方法
1
func main() {
f := fibonacci()
for i := 0; i < 10; i++ {
fmt.Println(f())
}
}
是关闭(不是生成器),初始环境(f
,previous := 0
:= 1) ,循环中的每次迭代调用current
都会返回f()
并修改环境,因此下次调用时,current
将为previous
并且1
current
。