3

我是 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()())
    }
}

这段代码应该打印出斐波那契数列(前 10 个),但只打印出 10 次 1。但是如果我将代码更改为:

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

然后它工作正常。输出是斐波那契数列。

有人可以帮我解释一下吗?

谢谢

4

3 回答 3

4

fibonacci()创建一个新的斐波那契生成器函数。fibonacci()()做同样的事情,然后调用一次,返回结果并丢弃生成器,不再使用。如果你在一个循环中调用它,它只会继续创建新的生成器并且只使用它们的第一个值。

如果您想要的不仅仅是第一个值,您需要完全按照您在第二个示例中所做的那样做。将生成器本身存储在一个变量中,然后多次调用同一个生成器。

于 2013-10-08T23:03:37.633 回答
1

这与返回闭包后如何将变量封装在闭包中有关。考虑以下示例(播放中的实时代码):

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

而且您已经从输出中看到这不起作用。

于 2013-10-08T23:47:47.123 回答
1

func fibonacci() func() int返回一个函数文字(闭包),它返回一个int表示列表中最后生成的数字。

第一个 main() 方法

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

f生成器函数,循环调用f()()中的每次迭代都会生成一个带有新环境的新闭包 , previous := 0current := 1), so第二次调用返回current总是等于1.

第二个 main() 方法

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

f是具有初始环境(,:= 1)的闭包(不是生成器) ,循环中的每次迭代调用女巫返回并修改环境,因此下一次调用将是and 。previous := 0currentf()currentprevious1current 2

于 2013-10-08T23:59:52.403 回答