关闭变量范围

时间:2013-11-13 15:12:28

标签: go closures

我正在阅读'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()返回uintPrintln如此聪明以至于它可以与所有内容一起使用吗?

3 个答案:

答案 0 :(得分:11)

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

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的值。


Here你会找到我为你一起写的一个小例子。它并不是非常有用,但在我看来很清楚地说明了闭包的范围,目的和用途:

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())
    }

}

答案 1 :(得分:6)

  

1)为什么我没有重置?

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

  

2)nextEven()返回和uint或Println如此聪明,它可以   与一切合作?

fmt.Println()(以及fmt.Print()fmt.Fprint()等)可以使用大多数类型。它以“默认格式”打印其参数。使用fmt.Printf()动词使用%v打印时也是如此。

答案 2 :(得分:0)

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