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

时间:2013-10-08 22:58:39

标签: go

我是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序列。

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

由于

3 个答案:

答案 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等。您可以看到abi的不同实例,否则调用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()) } } 关闭(不是生成器),初始环境fprevious := 0:= 1) ,循环中的每次迭代调用current都会返回f()并修改环境,因此下次调用时,current将为previous并且1 current