英文:
Different behavior between calling function directly and using pointer
问题
我是你的中文翻译助手,以下是代码的翻译:
package main
import "fmt"
// fibonacci 是一个返回一个返回 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()())
}
}
这段代码的目的是打印出斐波那契数列的前10个数字,但实际上只打印了10次1。
但是,如果将代码改为:
func main() {
f := fibonacci()
for i := 0; i < 10; i++ {
fmt.Println(f())
}
}
那么它就能正常工作了,输出的是斐波那契数列。
有人可以帮我解释一下吗?
谢谢!
英文:
I am new to Go language and got confused with the following code
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()())
}
}
This code is supposed to print out the Fibonacci Sequence (first 10), but only print out 10 times 1.
but if I change the code to:
func main() {
f := fibonacci()
for i := 0; i < 10; i++ {
fmt.Println(f())
}
}
Then it is working fine. The output is the Fibonacci sequence.
Could any one help me explain this?
Thanks
答案1
得分: 4
fibonacci()
创建一个新的斐波那契生成器函数。fibonacci()()
也是如此,然后调用它一次,返回结果并丢弃生成器,以后不再使用。如果你在循环中调用它,它只会不断创建新的生成器并且只使用它们的第一个值。
如果你想要多个值,你需要像你在第二个例子中所做的那样。将生成器本身存储在一个变量中,然后多次调用同一个生成器。
英文:
fibonacci()
creates a new fibonacci generator function. fibonacci()()
does the same, and then calls it once, returns the result and discards the generator, never to be used again. If you call that in a loop, it'll just keep creating new generators and only using their first value.
If you want more than just the first value, you need to do exactly what you did in your second example. Store the generator itself in a variable and then call the same generator multiple times.
答案2
得分: 1
这与闭包中变量的封装方式有关,闭包在返回后会封装变量。考虑以下示例(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()()
}
你已经通过输出看到这样做不起作用。
英文:
This has to do with how variables are encapsulated in closures after returning the closure.
Consider the following example (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()
}
Running this code will yield something like the following output. I annotated
which line comes from which statement:
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()
In the example, the closure returned by newClosure
encapsulates the local variable i
.
This corresponds to current
and the like in your code. You can see that a
and b
have different instances of i
, or else the call b()
would've printed 3
instead.
You can also see that the i
variables have different addresses. (The variable is already
on the heap as go does not have a separate stack memory, so using it in the closure is
no problem at all.)
So, by generating a new closure you're automatically creating a new context for the
closure and the local variables are not shared between the closures. This is the reason
why creating a new closure in the loop does not get you further.
The equivalent in for your code in terms of this example would be:
for i:=0; i < 10; i++ {
newClosure()()
}
And you've already seen by the output that this will not work.
答案3
得分: 1
func fibonacci() func() int
返回一个函数字面量(闭包),该闭包返回一个表示列表中最后生成的数字的int
。
第一个main()方法
f := fibonacci
for i := 0; i < 10; i++ {
fmt.Println(f()())
}
f
是生成器函数,循环中的每次迭代调用f()()
生成一个新的闭包,该闭包具有一个新的环境(previous := 0
,current := 1
),因此第二次调用返回的是始终等于1
的current
。
第二个main()方法
func main() {
f := fibonacci()
for i := 0; i < 10; i++ {
fmt.Println(f())
}
}
f
是具有初始环境(previous := 0
,current := 1
)的闭包(而不是生成器),循环中的每次迭代调用f()
返回current
并修改环境,因此在下一次调用中,previous
将变为1
,current
将变为2
。
英文:
func fibonacci() func() int
return a function literal (closure) that returns an int
representing the last generated number in the list.
The first main() method
f := fibonacci
for i := 0; i < 10; i++ {
fmt.Println(f()())
}
f
is the generator function, every iteration in the loop invoke f()()
that generates a new closure with a new envirement ( previous := 0
, current := 1), so
, the second invocation returns current
which is always equal to 1
.
The second main() method
func main() {
f := fibonacci()
for i := 0; i < 10; i++ {
fmt.Println(f())
}
}
f
is the closure (not the generator) with an initial envirement ( previous := 0
, current
:= 1), every iteration in the loop invoke f()
witch returns current
and modify the envirement, so whith the next call, previous
will be 1
and current
2
.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论