首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Go:匿名函数

Go:匿名函数
EN

Stack Overflow用户
提问于 2016-05-29 12:37:22
回答 3查看 4.2K关注 0票数 2

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

代码语言:javascript
复制
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()())
}

我们得到的结果:

代码语言:javascript
复制
4
16
36
4
4
4

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

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2016-05-29 12:50:26

短版

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

这就像用面向对象的语言构建了一个新的计数器对象一样:

代码语言:javascript
复制
new Counter().increment(); // 4
new Counter().increment(); // 4

...as反对:

代码语言:javascript
复制
c = new Counter();
c.increment(); // 4
c.increment(); // 16

较长版本

在您的函数中,var x int声明一个局部变量x

代码语言:javascript
复制
func squares() func() int {
    var x int
    return func() int {
        x = x + 2
        return x * x
    }
}

对于任何函数,局部变量仅在函数中可见。如果在不同的上下文中调用函数,则每个调用都有一组可通过本地符号寻址的单独内存存储集(此处为x)。当您返回一个函数时所发生的情况是,代码范围中当前可见的任何绑定都与您的函数保持在一起,然后称为闭包。

闭包可以保存状态,就像对象一样。因此,您的闭包可以引用创建时可见的局部变量,甚至当您逃离引入局部变量的块时也是如此(谢天谢地,GC是在这里跟踪与这些变量关联的内存)。

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

票数 7
EN

Stack Overflow用户

发布于 2016-05-29 15:52:55

这里有一个不同的角度来回答你的问题。

首先,分解squares()函数,以了解发生了什么:

代码语言:javascript
复制
func squares() func() int {

上面的函数定义了一个名为squares的函数,它返回func() int类型的另一个函数(并且返回一个int,但这不是这里的重点)。

先把它钻到你的脑子里:它会返回一个函数。

现在,让我们看看当我们调用squares()时会发生什么

代码语言:javascript
复制
    var x int

就这样。它定义了一个变量x,它默认为每个Go规范的值0。

好的,现在我们在作用域中有一个名为x的变量,它的值为0。现在,我们返回一个函数:

代码语言:javascript
复制
    return func() int {
         x = x + 2
         return x * x
    }

如果我们之前没有定义x,这将是一个构建错误,因为必须定义x。但是,它是在前面的范围中定义的。

Go对闭包的使用允许定义另一个作用域,它使用前一个作用域中的变量。var x int在这种情况下。

此外,您还可以修改闭包(前一个作用域中的变量)。在本例中,我们正在修改在前一个作用域中定义的前一个var x int

保持这些想法,让我们运行一些代码.

代码语言:javascript
复制
f := squares()

在这里,我们运行squares(),它将var x int定义为零,并返回一个名为f()func(),它可以在x上完成更多的工作。

代码语言:javascript
复制
fmt.Println(f())
fmt.Println(f())
fmt.Println(f())

由于我们继续重用f(),这会将var x int的作用域保留在堆栈上,并且只要您有这个f()变量,该堆栈就会继续存在。因此,x继续保留其修改后的值并进行修改。

有了所有这些知识,您的问题的答案很简单:如果您不保留该范围(如上面所示的f()定义),那么您将根据squares()的每次邀请定义一个新的var x int = 0

代码语言:javascript
复制
fmt.Println(squares()())

这会在每次调用时调用一个新的squares()。因此,一个新的var x int = 0

因此,使用squares()()调用的正方形返回函数的输出总是在每次调用时在var x int = 0上运行,因为每次调用都会调用一个新的方块。

票数 2
EN

Stack Overflow用户

发布于 2016-05-29 19:52:30

因为(1) x是匿名函数的捕获闭包,(2) int类型的默认值是0。因此,每次您调用它时,您都会看到相同的输出。

让我们像下面这样重写函数squares

代码语言:javascript
复制
func squares(initialValue int) func() int {
    var x int
    x = initialValue
    return func() int {
        x = x + 2
        return x * x
    }
}

现在要说的是:

代码语言:javascript
复制
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的初始值

代码语言:javascript
复制
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)())
}

我们将看到这一结果:

代码语言:javascript
复制
9
25
49
9
9
9

正如您所看到的,它只是x的初始值,当未显式初始化时,它的默认值为零。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/37509981

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档