10

我正在阅读 'CreateSpace An Introduction to Programming in Go 2012'

在第 86 页我发现了这个邪恶的魔法

func makeEvenGenerator() func() uint {
    i := uint(0)

    return func() (ret uint) {
        ret = i
        i += 2
        return
    }
}

// here's how it's called
nextEven := makeEvenGenerator()
fmt.Println(nextEven())
fmt.Println(nextEven())
fmt.Println(nextEven())

1)为什么i不重置?2)正在nextEven()返回,uint或者很Println聪明,可以处理所有事情?

4

3 回答 3

13

为了清楚起见,我将为这两个函数指定名称:

func makeEvenGenerator() func() uint { // call this "the factory"
    i := uint(0)

    return func() (ret uint) { // call this "the closure"
        ret = i
        i += 2
        return
    }
}

工厂返回闭包——函数是 Go 中的一等公民,即它们可以是右手表达式,例如:

f := func() { fmt.Println("f was called"); }

f() // prints "f was called"

在您的代码中,闭包覆盖了工厂的上下文,这称为词法作用域。这就是为什么变量i在闭包内可用的原因,不是作为副本,而是作为对i自身的引用。

闭包使用一个命名的返回值,称为ret. 这意味着在闭包内,您将隐式声明ret,并且在 点处,将返回return任何值。ret

这一行:

ret = i

i将分配给的当前值ref。它不会改变i。但是,这一行:

i += 2

i将在下次调用闭包时更改 的值。


在这里,您会找到我一起为您编写的一个小闭包示例。它不是非常有用,但在我看来,它很好地说明了闭包的范围、目的和使用:

package main

import "fmt"

func makeIterator(s []string) func() func() string {
    i := 0
    return func() func() string {
        if i == len(s) {
            return nil
        }
        j := i
        i++
        return func() string {
            return s[j]
        }
    }
}

func main() {

    i := makeIterator([]string{"hello", "world", "this", "is", "dog"})

    for c := i(); c != nil; c = i() {
        fmt.Println(c())
    }

}
于 2013-11-13T15:36:13.117 回答
8

1)为什么我不重置?

Go 中的闭包通过引用捕获变量。这意味着内部函数持有i对外部范围内变量的引用,并且每次调用它都会访问同一个变量。

2) nextEven() 是返回和 uint 还是 Println 如此聪明以至于它可以处理所有事情?

fmt.Println()(连同fmt.Print(),fmt.Fprint()等)可以使用大多数类型。它以“默认格式”打印其参数。fmt.Printf()这与使用%v动词打印的内容相同。

于 2013-11-14T03:55:31.843 回答
-1

闭包中的变量既没有代码段也没有上下文。

于 2016-05-10T10:09:20.663 回答