一尘不染

去闭包变量范围

go

我正在阅读“ CreateSpace 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如此聪明以至于它可以与所有东西配合使用?


阅读 196

收藏
2020-07-02

共1个答案

一尘不染

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

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,在时returnret将返回任何值。

这行:

ret = i

会将的当前值分配iref。它不会改变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())
    }

}
2020-07-02