For loop Go中捕获的闭包(用于循环变量)

For loop Go中捕获的闭包(用于循环变量),for-loop,go,closures,For Loop,Go,Closures,不应该将编译器捕获for…range循环变量作为本地指定的闭包变量 长版本: 这也给我带来了一些困惑,我试图理解它;这就是为什么它在C#5.0foreach(原因:循环变量不能在循环体内部更改)中被固定的原因,以及不在C#for循环中固定它的原因(原因:循环变量可以在循环体内部更改) 现在(对我来说)for…range在Go中的循环看起来很像foreach在C#中的循环,但是尽管我们不能改变这些变量(比如k和v在for k中,v:=range m{…);但是,我们必须首先将它们复制到一些本地闭包

不应该将编译器捕获
for…range
循环变量作为本地指定的闭包变量

长版本:

这也给我带来了一些困惑,我试图理解它;这就是为什么它在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, 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)
    }
}
func main(){
lab1()//捕获的闭包不是预期的闭包
格式打印项次(“”)
lab2()//捕获的闭包不是预期的闭包
格式打印项次(“”)
lab3()//捕获的闭包行为正常
格式打印项次(“”)
}
func lab3(){
m:=make(映射[int32]int32)
var i int32

对于i=1;i您希望对变量或值进行闭包吗?例如

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()
}
主程序包
输入“fmt”
func VariableLoop(){
f:=make([]func(),3)
对于i:=0;i<3;i++{
//变量i上的闭包
f[i]=func(){
fmt.Println(一)
}
}
fmt.Println(“可变循环”)
对于u,f:=范围f{
f()
}
}
func ValueLoop(){
f:=make([]func(),3)
对于i:=0;i<3;i++{
i:=i
//i值上的闭包
f[i]=func(){
fmt.Println(一)
}
}
格式打印项次(“ValueLoop”)
对于u,f:=范围f{
f()
}
}
func VariableRange(){
f:=make([]func(),3)
对于i:=范围f{
//变量i上的闭包
f[i]=func(){
fmt.Println(一)
}
}
fmt.Println(“可变范围”)
对于u,f:=范围f{
f()
}
}
func ValueRange(){
f:=make([]func(),3)
对于i:=范围f{
i:=i
//i值上的闭包
f[i]=func(){
fmt.Println(一)
}
}
格式打印项次(“值范围”)
对于u,f:=范围f{
f()
}
}
func main(){
VariableLoop()
ValueLoop()
可变范围()
ValueRange()
}
输出:

VariableLoop 3 3 3 ValueLoop 0 1 2 VariableRange 2 2 2 ValueRange 0 1 2 可变回路 3. 3. 3. ValueLoop 0 1. 2. 可变范围 2. 2. 2. 值范围 0 1. 2. 参考资料:

函数文字是闭包:它们可能引用在中定义的变量 一个周围的函数。然后这些变量在 围绕函数和函数文字,它们作为 只要它们是可访问的

要在每个闭包启动时将v的当前值绑定到它,一个 每次迭代都必须修改内部循环以创建新变量。 一种方法是将变量作为参数传递给闭包

更简单的方法是使用声明创建一个新变量 看起来很奇怪,但在围棋中效果很好的风格


你能把你的例子写得更短更清楚吗?也许是因为现在已经是半夜了,但我看不出你在期待什么,也看不到你的例子中发生了什么。我已经描述了代码和目的,我以这种方式写了它,并且遇到了这个问题。这是一个常见问题解答:@dyoo谢谢;但这正是我觉得不对的关于
for…range
循环。除了@dyoo的链接,在
for
循环中有变量的闭包;在
for…range
循环中有值的闭包。对于
i:=i
我看到围棋人使用的另一种方法是让内部函数以
i
作为参数。至于它是否应该像所有人一样以这种方式工作各种各样的设计问题,这是有争议的,但当你进入一个语言生态系统时,你必须使用你所拥有的。@KavehShahbazian:你的选择。见我修改后的答案。在你的例子中,使用
k,v:=k,v
表示
range
。@TwoTwo-Two-Two你对生态系统的看法绝对正确。但在C#an中,这显然不是一个好的选择d他们已经修复了。我只是想知道原因(比如,由于Go编译器内部或类似的原因,这甚至是不可能的),正如我从谷歌搜索中了解到的那样,它已经存在了一段时间。啊,有趣的问题。也许设计师只是觉得它更容易(或更干净?)所有循环变量都被视为一个在范围外声明的单个变量,用于捕获。Python是这样(
[lambda:i for i in range(10)][0]()
prints
9
);令人惊讶的是,Perl不是(
my@subs;对于my$i(1..10){push@subs,sub{print“$i”;};$subs[0]>)
prints
1
)。