Go:匿名函数

标签 go

这是我一直试图理解的代码:

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

我的问题是:为什么 fmt.Println(squares()()) 中的 x 的值保持不变?

最佳答案

精简版

每次调用 squares 时,您都在构建一个新的闭包。

这就好像您用面向对象的语言构建了一个新的 Counter 对象:

new Counter().increment(); // 4
new Counter().increment(); // 4

...相对于:

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)寻址。当您返回一个函数时,您的代码范围内当前可见的任何绑定(bind)都会与您的函数一起保存,这称为闭包。

闭包可以保持状态,就像对象一样。因此,您的闭包可以引用创建时可见的局部变量,即使您转义了引入局部变量的 block (谢天谢地,GC 在这里跟踪与这些变量关联的内存)。

当您定义f 时,您创建了一个新的闭包。每次调用它时,都会修改内部 x 变量引用的相同位置。但是,如果您创建新的闭包并分别调用它们一次,那么您将不会看到相同的副作用,因为每个 x 在内存中命名不同的位置。

关于Go:匿名函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37509981/

相关文章:

使用路径参数 Swagger

testing - 如何测试 Golang channel /go-routines

azure - 多个具有随机ip的云之间的TLS证书

go - BufferedWriter 在同时使用时不起作用

mongodb - 是否可以使用 mgo 驱动程序运行 mongo replicaset 命令?

go - standard_init_linux.go :207: exec user process caused "no such file or directory" while trying to statically link c libs

database - MongoDB 获取集合中的所有文档使用 mgo 给出一个空的结构片段

go - gRPC 和 HTTP 1 在单个端口上调用,不在 goLang 中使用 TLS

csv - 在两个微服务之间发送文件

time - 为什么 sleep 似乎在 goroutine 中不起作用