捕获闭包(for循环变量)在Go [英] Captured Closure (for Loop Variable) in Go

查看:667
本文介绍了捕获闭包(for循环变量)在Go的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

不应该将编译器捕获 for ... range 循环变量作为本地分配的闭包变量?

Shouldn't Go compiler capture for...range loop variables as a locally assigned closure variable?

Long版本:

这导致我有些混乱在C#,我也试图理解它;为什么它被固定在C#5.0 foreach (原因:循环变量不能在循环体内改变)和不固定的推理它在C#中为循环(原因:循环变量可以在循环体内改变)。

This caused me some confusion in C# too and I was trying to understand it; that why it is fixed in C# 5.0 foreach (reason: the loop variable can not change inside the body of loop) and the reasoning for not fixing it in C# for loops (reason: the loop variable can change inside the body of loop).

现在(对我) for ... range 在Go看起来很像 foreach loops在C#中,但尽管我们不能改变这些变量(例如 k v c> for k,v:= range m {...} );仍然我们必须首先将它们复制到一些本地关闭,以便他们按照预期的行为。

Now (to me) for...range loops in Go seems pretty much like foreach loops in C#, but despite the fact that we can not alter those variables (like k and v in for k, v := range m { ... }); still we have to copy them to some local closures first, for them to behave as expected.

这是什么推理? (我怀疑这是因为Go以相同的方式处理循环;但我不知道)。

What is the reasoning behind this? (I suspect it's because Go treats any for loop the same way; but I'm not sure).

下面是一些代码来检查描述的行为:

Here is some code to examine described behavior:

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)
    }
}

lab1 中所示,注释 //(A)我们得到范围的最后一个值;输出类似于打印 9,9 十次,而不是显示预期结果,例如 1,1 2,2 ,...(和当然地图不一定在Go中排序,所以我们可以看到 3,3 十次作为最后一对值;而不是 10,10 十次作为最后一对值)。对于在 lab2 处的注释 //(B)的代码也是如此,因为我们试图捕获外部变量在内部范围(我把这个太简单的尝试)。在 lab3 在注释 //(C)的代码一切正常,你会看到十对数字 1,1 2,2 ,....

As it is shown in lab1, at the comment // (A) we get just the last values from the range; the output is like printing 9,9 ten times instead of showing expected result like 1,1, 2,2, ... (and of-course maps are not necessarily sorted in Go so we may see 3,3 ten times as the last pair of values; instead of 10,10 ten times as the last pair of values). The same goes for code at comment // (B) at lab2, which was expected because we are trying to capture outer variables inside the inner scope (I put this one too just to try that). In lab3 at code at comment // (C) everything works fine and you will see ten pairs of numbers there like 1,1, 2,2, ....

我在Go中尝试使用 closure + function 替换 tuple

I was trying to use closure+function as a replacement for tuples in Go.

推荐答案

你想要关闭变量还是值?例如,

Do you want the closure over the variable or the value? For example,

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()
}

输出:


VariableLoop
3
3
3
ValueLoop
0
1
2
VariableRange
2
2
2
ValueRange
0
1
2

参考文献:


Go编程语言规范

函数文字

函数字面量是闭包:它们可以引用
a周围函数中定义的变量。这些变量然后在
周围函数和函数文本之间共享,并且它们作为
长度存在,因为它们是可访问的。

Function literals are closures: they may refer to variables defined in a surrounding function. Those variables are then shared between the surrounding function and the function literal, and they survive as long as they are accessible.

常见问题:关闭作为goroutines运行会发生什么?

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

To bind the current value of v to each closure as it is launched, one must modify the inner loop to create a new variable each iteration. One way is to pass the variable as an argument to the closure.

更容易的是创建一个新的变量,使用声明
风格,看起来很奇怪,但在Go中工作正常。

Even easier is just to create a new variable, using a declaration style that may seem odd but works fine in Go.

这篇关于捕获闭包(for循环变量)在Go的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆