当前位置: 首页 > 面试题库 >

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

文德曜
2023-03-14
问题内容

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

长版:

这也引起了我对C#的困惑,并且我试图理解它。这就是为什么它在C#5.0中已得到修复foreach(原因:循环变量
不能 在循环体内改变)以及未在C#for循环中对其进行修复的原因(原因:循环变量 可以 在循环体内改变)。

现在(对我而言)for...range,Go中的循环看起来很像foreachC#中的循环,但是尽管事实是我们无法更改这些变量(例如kvfor 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)
    }
}

如图所示lab1,在注释中,// (A)我们仅从中获得了最后一个值range。输出就像打印9,9十次,而不是显示预期的结果,如1,1,,2,2…(当然,路线图不一定要在Go中排序,因此我们可能将3,3十次视为最后一对值;而不是将10,10十次视为最后一个值。对值)。在处的注释// (B)处的代码也是如此lab2,这是预期的,因为我们正在尝试捕获内部作用域内的外部变量(我也尝试这样做是为了放置它)。在lab3在在注释代码// (C)一切正常,你会十岁了数对有喜欢的1,12,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()
}

输出:

可变回路
3
3
3
价值循环
0
1个
2
可变范围
2
2
2
价值范围
0
1个
2

参考文献:

Go编程语言规范

函数文字

函数文字是闭包:它们可以引用周围函数中定义的变量。然后,这些变量在周围的函数和函数文字之间共享,并且只要可以访问它们就可以保留。

常见问题解答:以goroutines身份运行闭包会发生什么?

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

甚至更容易的是使用声明样式创建一个新变量,该声明样式可能看起来很奇怪,但在Go中可以正常工作。



 类似资料:
  • 问题内容: 这里发生了什么?我正在尝试创建功能列表: 这没有达到我的期望。我希望列表像这样: 但是列表中的所有功能似乎都是相同的,并且将固定值设置为9: 有任何想法吗? 问题答案: python中的lambda是闭包…。在计算lambda之前,不会对您提供的参数进行求值。那时,i = 9,因为您的迭代已完成。 您正在寻找的行为可以通过functools.partial实现

  • 闭包本身是相当灵活的,可以实现所需功能来让闭包运行而不用类型标注(原文:Closures are inherently flexible and will do what the functionality requires to make the closure work without annotation)。这允许变量捕获灵活地适应使用 情况,有时是移动(moving)有时是借用(borro

  • 问题内容: 我试图用多个变量在Go中编写一个for循环。 来自javascript世界,我想实现以下目标: 我已经尝试过像这样的“原始翻译”: 但这是行不通的。正确的语法是什么? 非常感谢! 问题答案: 在Go中,您可以像这样循环执行多个变量分配。

  • 问题内容: 不允许在Go中使用以下for循环, 下面两个变量的for循环的正确等效项是什么? 问题答案: 您没有逗号运算符来连接多个语句,但是您确实有多个分配,因此这可行:

  • 问题内容: 目前,我正在执行在循环内执行闭包的代码。我想知道所有闭包何时完成执行。目前,我正在使用一个计数器,并在关闭完成时将其递增,然后将其与要执行的时间进行比较。代码如下 有没有计数器的更好的方法。谢谢。 问题答案: 您可以为此使用。它的工作就像计数信号灯一样。您可以通过调用来增加计数,并通过来减少计数。您可以使用以下命令安排闭包在计数达到0时执行:

  • 问题内容: 我已经阅读了许多有关闭包和循环内闭包的解释。我很难理解这个概念。我有以下代码:有没有办法尽可能减少代码,以便可以使闭包的概念更清晰。我很难理解其中两个括号内的部分。谢谢 问题答案: 警告:长(ish)答案 这是直接从我在公司内部Wiki中写的文章中复制的: 问题:如何在循环中正确使用闭包?快速解答:使用功能工厂。 或更易读的版本: 这经常使刚接触javascript或函数式编程的人感到