drq1257 2017-06-23 07:48 采纳率: 0%
浏览 28
已采纳

为什么这两个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()
    }
}

The output for loop1() is

update
update
update

The output for loop2() is

delete
update
create

I went looking on the internet and read the following

When ranging over a slice, two values are returned for each iteration. The first is the index, and the second is a copy of the element at that index

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?

  • 写回答

1条回答 默认 最新

  • doubu7134 2017-06-23 07:57
    关注

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

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

    update
    create
    delete
    

    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.

    Also see this possible duplicate: Golang: Register multiple routes using range for loop slices/map

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥40 复杂的限制性的商函数处理
  • ¥15 程序不包含适用于入口点的静态Main方法
  • ¥15 素材场景中光线烘焙后灯光失效
  • ¥15 请教一下各位,为什么我这个没有实现模拟点击
  • ¥15 执行 virtuoso 命令后,界面没有,cadence 启动不起来
  • ¥50 comfyui下连接animatediff节点生成视频质量非常差的原因
  • ¥20 有关区间dp的问题求解
  • ¥15 多电路系统共用电源的串扰问题
  • ¥15 slam rangenet++配置
  • ¥15 有没有研究水声通信方面的帮我改俩matlab代码