我正在阅读'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
如此聪明以至于它可以与所有内容一起使用吗?
答案 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)
闭包中的变量既没有代码段也没有上下文。