本文介绍了为什么这两个 for 循环变体会给我不同的行为?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在我的程序中看到了与此特定循环相关的不同行为,但我不确定我是否理解为什么它的行为方式如此.

I'm seeing different behavior in my program that's tied to this particular loop in my program but I'm not sure I understand why it's behaving the way it is.

//global variable
var cmds = []string {
    "create",
    "delete",
    "update",
}

func loop1() {

    actions := make(map[string]func())

    for _, cmd := range cmds {
        actions[cmd] = func() {
            fmt.Println(cmd)
        }
    }
    for _, action := range actions {
        action()
    }
}
func loop2() {

    actions := make(map[string]func())

    for i, cmd := range cmds {
        command := cmds[i]
        actions[cmd] = func() {
            fmt.Println(command)
        }
    }
    for _, action := range actions {
        action()
    }
}

loop1() 的输出是

update
update
update

loop2() 的输出是

delete
update
create

我查看了互联网并阅读了以下内容

I went looking on the internet and read the following

当对一个切片进行测距时,每次迭代都会返回两个值.第一个是索引,第二个是该索引处元素的副本

它说的是一个副本,那么这是否意味着它返回了一个字符串的副本,但它实际上是一个指向变量 cmd 的指针?在这种情况下,对 cmd 的任何引用都将在循环结束时实际引用数组中的最后一个元素,例如更新?这是否意味着在使用 range 方法时,数组的元素应该总是由它们的索引引用,并且使用它返回的元素的用例是什么,因为它总是更新指针?

It says a copy, so does that mean it returns a copy of the string but it's really a pointer to variable cmd? In which case any references to cmd will by the end of the loop all actually reference the last element in the array, e.g. update? Does this mean that elements of an array should always be referenced by their index when using the range method, and what's the use case for using the element it returns since it's always updating the pointer?

推荐答案

loop1() 的问题是你在引用的 actions 映射中存储了一个函数字面量循环变量 cmd.这个循环变量只有一个实例,所以当你在循环之后调用存储在 actions 映射中的函数时,所有的函数都将引用这个单个循环变量(保留它是因为函数/闭包仍然有一个对它的引用),但它的值在执行时将是 for 循环设置的最后一个值,也就是 中的最后一个值cmds 切片(即 "update",所以你会看到 "update" 打印了 3 次).

The problem with loop1() is that you store a function literal in the actions map that references the loop variable cmd. There is only one instance of this loop variable, so when after the loop you call the functions stored in the actions map, all will refer to this single loop variable (which is kept because the functions / closures still have a reference to it), but its value at the time of execution will be the last value set by the for loop, which is the last value in the cmds slice (that is, "update", so you'll see "update" printed 3 times).

一个简单的解决方法是制作此循环变量的副本,因此每次迭代,每个函数文字都会有自己的副本,该副本与循环变量分离":

An easy workaround is to make a copy of this loop variable, so each iteration, each function literal will have its own copy, which is "detached" from the loop variable:

func loop1() {
    actions := make(map[string]func())

    for _, cmd := range cmds {
        cmd2 := cmd
        actions[cmd] = func() {
            fmt.Println(cmd2) // Refer to the detached, copy variable!
        }
    }
    for _, action := range actions {
        action()
    }
}

有了这个,loop1() 的输出(在 Go游乐场):

With this, output of loop1() (try it on the Go Playground):

update
create
delete

这不是 for ... range 的问题,这是因为闭包引用了同一个变量,并且您不会立即使用该变量的值,只有在环形.当你打印这个变量的值时,所有的都打印相同的,它的最后一个值.

This it's not an issue of the for ... range, it's because the closures refer to the same variable, and you don't use the value of the variable right away, only after the loop. And when you print the value of this variable, all print the same, last value of it.

另见这个可能的重复:Golang:使用范围为循环切片/地图注册多个路由

这篇关于为什么这两个 for 循环变体会给我不同的行为?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-11 19:32