英文:
Go closure variable scope
问题
我正在阅读《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
}
}
// 这是它的调用方式
nextEven := makeEvenGenerator()
fmt.Println(nextEven())
fmt.Println(nextEven())
fmt.Println(nextEven())
1)为什么i
没有重置?
2)nextEven()
返回的是uint
类型,还是Println
聪明到可以处理任何类型?
英文:
I'm reading 'CreateSpace An Introduction to Programming in Go 2012'
and on page 86 I found this evil magic
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())
- Why is
i
not resetting ? - is
nextEven()
returning anduint
or isPrintln
so smart that it can work with everything ?
答案1
得分: 15
为了清晰起见,我将为这两个函数分配名称:
func makeEvenGenerator() func() uint { // 将其称为“工厂函数”
i := uint(0)
return func() (ret uint) { // 将其称为“闭包函数”
ret = i
i += 2
return
}
}
工厂函数返回闭包函数——在Go中,函数是一等公民,也就是说它们可以作为右值表达式,例如:
f := func() { fmt.Println("f was called") }
f() // 输出 "f was called"
在你的代码中,闭包函数包裹了工厂函数的上下文,这被称为“词法作用域”。这就是为什么变量 i
在闭包函数内部可用,它不是 i
的副本,而是对 i
本身的引用。
闭包函数使用了一个名为 ret
的“命名返回值”。这意味着在闭包函数内部,你将隐式地声明 ret
,并且在 return
的位置,ret
的值将被返回。
这行代码:
ret = i
将 i
的当前值赋给 ret
。它不会改变 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())
}
}
英文:
For the sake of clarity, I'll assign names to both functions:
func makeEvenGenerator() func() uint { // call this "the factory"
i := uint(0)
return func() (ret uint) { // call this "the closure"
ret = i
i += 2
return
}
}
The factory returns the closure – functions are first class citizens in Go i.e. they can be right-hand expressions, for example:
f := func() { fmt.Println("f was called"); }
f() // prints "f was called"
In your code, the closure wraps over the context of the factory, this is called lexical scoping. This is why the variable i
is available inside the closure, not as a copy but as a reference to i
itself.
The closure uses a named return value called ret
. What this means is that inside the closure you'll have implicitly declared ret
and at the point of return
, whatever value ret
has will be returned.
This line:
ret = i
will assign the current value of i
to ret
. It will not change i
. However, this line:
i += 2
will change the value of i
for the next time the closure is called.
<hr>
Here you'll find a little closure example I wrote together for you. It's not extremely useful but illustrates the scope, purpose and use of closures pretty well in my opinion:
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())
}
}
答案2
得分: 8
- 为什么i没有重置?
在Go语言中,闭包通过引用来捕获变量。这意味着内部函数持有对外部作用域中i
变量的引用,每次调用它都会访问同一个变量。
nextEven()
返回的是uint
类型,还是Println
聪明到可以处理任何类型?
fmt.Println()
(以及fmt.Print()
、fmt.Fprint()
等)可以处理大多数类型。它以"默认格式"打印其参数。这与使用fmt.Printf()
和%v
动词打印的内容相同。
英文:
> 1) Why is i not resetting ?
Closures in Go capture variables by reference. That means the inner function holds a reference to the i
variable in the outer scope, and each call of it accesses this same variable.
> 2) is nextEven() returning and uint or is Println so smart that it can
> work with everything ?
fmt.Println()
(along with fmt.Print()
, fmt.Fprint()
, etc.) can work most types. It prints its arguments in the "default format". It is the same thing that is printed using fmt.Printf()
using the %v
verb.
答案3
得分: -1
闭包中的变量既不受代码段的限制,也不受上下文的限制。
英文:
The variable in closure is free from neither code segment nor context.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论