直接调用函数和使用指针之间的行为不同

huangapple go评论86阅读模式
英文:

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 &quot;fmt&quot;

// 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 &lt; 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 &lt; 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 等变量。你可以看到 ab 有不同的 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(&quot;newClosure with &amp;i=&quot;, &amp;i)
	return func() {
		fmt.Println(i, &amp;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 &amp;i= 0xc010000000    // a := newClosure()
0 0xc010000000                      // a()
1 0xc010000000                      // a()
2 0xc010000000                      // a()
newClosure with &amp;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 &lt; 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 := 0current := 1),因此第二次调用返回的是始终等于1current

第二个main()方法

func main() {
    f := fibonacci()
    for i := 0; i < 10; i++ {
        fmt.Println(f())
    }
}

f 是具有初始环境previous := 0current := 1)的闭包(而不是生成器),循环中的每次迭代调用f()返回current并修改环境,因此在下一次调用中,previous将变为1current将变为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 &lt; 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 &lt; 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.

huangapple
  • 本文由 发表于 2013年10月9日 06:58:39
  • 转载请务必保留本文链接:https://go.coder-hub.com/19260006.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定