Kav*_*ian 4 closures for-loop go
Go编译器不应该将for...range循环变量捕获为本地分配的闭包变量吗?
长版:
这也使我对C#产生了一些困惑,并且我试图理解它。这就是为什么它在C#5.0中已得到修复foreach(原因:循环变量不能在循环体内改变)以及未在C#for循环中对其进行修复的原因(原因:循环变量可以在循环体内改变)。
现在,(我)for...range在围棋圈看起来很像foreach在C#中循环,但尽管我们不能改变这些变量(如k和v中for k, v := range m { ... }); 仍然我们必须首先将它们复制到某些本地闭包中,以使其表现出预期的效果。
这背后的原因是什么?(我怀疑这是因为Go用for相同的方式对待任何循环;但我不确定)。
这是一些代码来检查描述的行为:
func main() {
lab1() // captured closure is not what is expected
fmt.Println(" ")
lab2() // captured closure is not what is expected
fmt.Println(" ")
lab3() // captured closure behaves ok
fmt.Println(" ")
}
func lab3() {
m := make(map[int32]int32)
var i int32
for i = 1; i <= 10; i++ {
m[i] = i
}
l := [](func() (int32, int32)){}
for k, v := range m {
kLocal, vLocal := k, v // (C) captures just the right values assigned to k and v
l = append(l, func() (int32, int32) {
return kLocal, vLocal
})
}
for _, x := range l {
k, v := x()
fmt.Println(k, v)
}
}
func lab2() {
m := make(map[int32]int32)
var i int32
for i = 1; i <= 10; i++ {
m[i] = i
}
l := [](func() (int32, int32)){}
for k, v := range m {
l = append(l, func() (int32, int32) {
kLocal, vLocal := k, v // (B) captures just the last values assigned to k and v from the range
return kLocal, vLocal
})
}
for _, x := range l {
k, v := x()
fmt.Println(k, v)
}
}
func lab1() {
m := make(map[int32]int32)
var i int32
for i = 1; i <= 10; i++ {
m[i] = i
}
l := [](func() (int32, int32)){}
for k, v := range m {
l = append(l, func() (int32, int32) { return k, v }) // (A) captures just the last values assigned to k and v from the range
}
for _, x := range l {
k, v := x()
fmt.Println(k, v)
}
}
Run Code Online (Sandbox Code Playgroud)
如图所示lab1,在注释中,// (A)我们仅从中获得了最后一个值range。输出就像打印9,9十次,而不是显示预期的结果,如1,1,,2,2...(当然,路线图不一定要在Go中排序,因此我们可能将3,3十次视为最后一对值;而不是将10,10十次视为最后一个值。对值)。在处的注释// (B)处的代码也是如此lab2,这是预期的,因为我们正在尝试捕获内部作用域内的外部变量(我也尝试这么做)。在lab3在在注释代码// (C)一切正常,你会十岁了数对有喜欢的1,1,2,2....
我正在尝试使用闭包+函数代替Go中的tuple。
您是否希望对变量或值进行闭包?例如,
package main
import "fmt"
func VariableLoop() {
f := make([]func(), 3)
for i := 0; i < 3; i++ {
// closure over variable i
f[i] = func() {
fmt.Println(i)
}
}
fmt.Println("VariableLoop")
for _, f := range f {
f()
}
}
func ValueLoop() {
f := make([]func(), 3)
for i := 0; i < 3; i++ {
i := i
// closure over value of i
f[i] = func() {
fmt.Println(i)
}
}
fmt.Println("ValueLoop")
for _, f := range f {
f()
}
}
func VariableRange() {
f := make([]func(), 3)
for i := range f {
// closure over variable i
f[i] = func() {
fmt.Println(i)
}
}
fmt.Println("VariableRange")
for _, f := range f {
f()
}
}
func ValueRange() {
f := make([]func(), 3)
for i := range f {
i := i
// closure over value of i
f[i] = func() {
fmt.Println(i)
}
}
fmt.Println("ValueRange")
for _, f := range f {
f()
}
}
func main() {
VariableLoop()
ValueLoop()
VariableRange()
ValueRange()
}
Run Code Online (Sandbox Code Playgroud)
输出:
可变回路 3 3 3 价值循环 0 1个 2 可变范围 2 2 2 价值范围 0 1个 2
参考文献:
函数文字是闭包:它们可以引用周围函数中定义的变量。然后,这些变量在周围的函数和函数文字之间共享,并且只要可以访问它们就可以保留。
常见问题解答:以goroutines身份运行闭包会发生什么?
要将v的当前值绑定到每个闭包启动时,必须修改内部循环以在每次迭代时创建一个新变量。一种方法是将变量作为参数传递给闭包。
甚至更容易的是使用声明样式创建一个新变量,该声明样式可能看起来很奇怪,但在Go中可以正常工作。