如何解决Go中捕获的闭包用于循环变量
您是否希望对变量或值进行闭包?例如,
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()
}
输出:
可变回路
3
3
3
价值循环
0
1个
2
可变范围
2
2
2
价值范围
0
1个
2
参考文献:
函数文字是闭包:它们可以引用周围函数中定义的变量。然后,这些变量在周围的函数和函数文字之间共享,并且只要可以访问它们就可以保留。
常见问题解答:以goroutines身份运行闭包会发生什么?
要将v的当前值绑定到每个闭包启动时,必须修改内部循环以在每次迭代时创建一个新变量。一种方法是将变量作为参数传递给闭包。
甚至更容易的是使用声明样式创建一个新变量,该声明样式可能看起来很奇怪,但在Go中可以正常工作。
解决方法
Go编译器不应该将for...range
循环变量捕获为本地分配的闭包变量吗?
长版:
这也引起了我对C#的困惑,并且我试图理解它。这就是为什么它在C#5.0中已得到修复foreach
(原因:循环变量
不能 在循环体内改变)以及未在C#for
循环中对其进行修复的原因(原因:循环变量 可以 在循环体内改变)。
现在(对我而言)for...range
,Go中的循环看起来很像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,v := range m {
l = append(l,int32) {
kLocal,v // (B) captures just the last values assigned to k and v from the range
return kLocal,v)
}
}
func lab1() {
m := make(map[int32]int32)
var i int32
for i = 1; i <= 10; i++ {
m[i] = i
}
l := [](func() (int32,int32) { return k,v }) // (A) captures just the last values assigned to k and v from the range
}
for _,v)
}
}
如图所示lab1
,在注释中,//
(A)
我们仅从中获得了最后一个值range
。输出就像打印9,9
十次,而不是显示预期的结果,如1,1
,,2,2
…(当然,路线图不一定要在Go中排序,因此我们可能将3,3
十次视为最后一对值;而不是将10,10
十次视为最后一个值。对值)。在处的注释//
(B)
处的代码也是如此lab2
,这是预期的,因为我们正在尝试捕获内部作用域内的外部变量(我也尝试这样做是为了放置它)。在lab3
在在注释代码//
(C)
一切正常,你会十岁了数对有喜欢的1,1
,2,2
....
我试图用 闭包+函数 代替Go中的 tuple 。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。