下面是我一直试图理解的代码:
package main
import (
"fmt"
)
func squares() func() int {
var x int
return func() int {
x = x + 2
return x * x
}
}
func main() {
f := squares()
fmt.Println(f())
fmt.Println(f())
fmt.Println(f())
fmt.Println(squares()())
fmt.Println(squares()())
fmt.Println(squares()())
}我们得到的结果:
4
16
36
4
4
4我的问题是:为什么x在fmt.Println(squares()())中的值保持不变?
发布于 2016-05-29 12:50:26
短版
每次调用squares时,您都要构建一个新的闭包。
这就像用面向对象的语言构建了一个新的计数器对象一样:
new Counter().increment(); // 4
new Counter().increment(); // 4...as反对:
c = new Counter();
c.increment(); // 4
c.increment(); // 16较长版本
在您的函数中,var x int声明一个局部变量x
func squares() func() int {
var x int
return func() int {
x = x + 2
return x * x
}
}对于任何函数,局部变量仅在函数中可见。如果在不同的上下文中调用函数,则每个调用都有一组可通过本地符号寻址的单独内存存储集(此处为x)。当您返回一个函数时所发生的情况是,代码范围中当前可见的任何绑定都与您的函数保持在一起,然后称为闭包。
闭包可以保存状态,就像对象一样。因此,您的闭包可以引用创建时可见的局部变量,甚至当您逃离引入局部变量的块时也是如此(谢天谢地,GC是在这里跟踪与这些变量关联的内存)。
定义f时,可以创建一个新的闭包。每次调用它时,都会修改内部x变量引用的相同位置。但是,如果您创建新的闭包并每一个调用它们一次,那么您将不会看到相同的副作用,因为每个x都会在内存中指定不同的位置。
发布于 2016-05-29 15:52:55
这里有一个不同的角度来回答你的问题。
首先,分解squares()函数,以了解发生了什么:
func squares() func() int {上面的函数定义了一个名为squares的函数,它返回func() int类型的另一个函数(并且返回一个int,但这不是这里的重点)。
先把它钻到你的脑子里:它会返回一个函数。
现在,让我们看看当我们调用squares()时会发生什么
var x int就这样。它定义了一个变量x,它默认为每个Go规范的值0。
好的,现在我们在作用域中有一个名为x的变量,它的值为0。现在,我们返回一个函数:
return func() int {
x = x + 2
return x * x
}如果我们之前没有定义x,这将是一个构建错误,因为必须定义x。但是,它是在前面的范围中定义的。
Go对闭包的使用允许定义另一个作用域,它使用前一个作用域中的变量。var x int在这种情况下。
此外,您还可以修改闭包(前一个作用域中的变量)。在本例中,我们正在修改在前一个作用域中定义的前一个var x int。
保持这些想法,让我们运行一些代码.
f := squares()在这里,我们运行squares(),它将var x int定义为零,并返回一个名为f()的func(),它可以在x上完成更多的工作。
fmt.Println(f())
fmt.Println(f())
fmt.Println(f())由于我们继续重用f(),这会将var x int的作用域保留在堆栈上,并且只要您有这个f()变量,该堆栈就会继续存在。因此,x继续保留其修改后的值并进行修改。
有了所有这些知识,您的问题的答案很简单:如果您不保留该范围(如上面所示的f()定义),那么您将根据squares()的每次邀请定义一个新的var x int = 0。
fmt.Println(squares()())这会在每次调用时调用一个新的squares()。因此,一个新的var x int = 0。
因此,使用squares()()调用的正方形返回函数的输出总是在每次调用时在var x int = 0上运行,因为每次调用都会调用一个新的方块。
发布于 2016-05-29 19:52:30
因为(1) x是匿名函数的捕获闭包,(2) int类型的默认值是0。因此,每次您调用它时,您都会看到相同的输出。
让我们像下面这样重写函数squares:
func squares(initialValue int) func() int {
var x int
x = initialValue
return func() int {
x = x + 2
return x * x
}
}现在要说的是:
func main() {
f := squares(0)
fmt.Println(f())
fmt.Println(f())
fmt.Println(f())
fmt.Println(squares(0)())
fmt.Println(squares(0)())
fmt.Println(squares(0)())
}我们会看到准确的输出!因为我们正在用x初始化0。如果我们使用1作为x的初始值
func main() {
f := squares(1)
fmt.Println(f())
fmt.Println(f())
fmt.Println(f())
fmt.Println(squares(1)())
fmt.Println(squares(1)())
fmt.Println(squares(1)())
}我们将看到这一结果:
9
25
49
9
9
9正如您所看到的,它只是x的初始值,当未显式初始化时,它的默认值为零。
https://stackoverflow.com/questions/37509981
复制相似问题