为什么这两个for循环变量会给我不同的行为?

我在程序中看到了与该程序中特定循环相关的不同行为,但我不确定 我了解为什么它的行为方式如此。 </ p>

  //全局变量
var cmds = [] string {
“ create”,
“ delete”,
“ update”,
}

func loop1(){

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

,cmd:= range cmds {
actions [cmd ] = func(){
fmt.Println(cmd)
}
}
表示
,动作:=范围动作{
action()
}
}
func loop2(){

动作:= make(map [string] func())

for i,cmd:= range cmds {
命令:= cmds [i]
动作[cmd] = func(){
fmt.Println(command)
}
}
表示_,操作:=范围操作{
action()
}
}
</ code> </ pre>

loop1()</ code>的输出为</ p>

  update 
update
update
</ code> </ pre>

loop2()</ code>的输出为</ p>

  delete 
update
create
</ code> </ pre>

我去找互联网并重新 </ p>


在切片上移动时,每次迭代都会返回两个值。 第一个是索引,第二个是该索引处的元素的副本</ p>
</ blockquote>

它表示一个副本,所以这意味着它返回了该元素的副本。 字符串,但实际上是指向变量 cmd </ code>的指针吗? 在这种情况下,对 cmd </ code>的任何引用都将在循环结束时全部实际引用数组中的最后一个元素,例如 <代码>更新</代码>? 这是否意味着在使用 range </ code>方法时,数组的元素应该始终由其索引引用,并且由于它总是在更新指针,因此使用它返回的元素的用例是什么?</ p>

</ div>

展开原文

原文

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?

duaner1634
duaner1634 Go中捕获的闭包(对于循环变量)的可能重复项
大约 3 年之前 回复

1个回答



loop1()</ code>的问题是您将函数文字存储在 actions </ 引用循环变量</ strong> cmd </ code>的code>映射。 该循环变量只有一个实例,因此在循环后调用 actions </ code>映射中存储的函数时,所有实例都将引用此单个循环变量(之所以保留,是因为函数/闭包仍然存在 (对此有一个引用),但其执行时的值 </ em>将是 for </ code>循环设置的最后一个值,即中的最后一个值 cmds </ code>切片(即“ update” </ code>,因此您会看到“ update” </ code>打印了3次)。</ p>

< p>一个简单的解决方法是为此循环变量制作一个副本,因此每次迭代,每个函数文字都将有其自己的副本,该副本与循环变量“分离”:</ p>

 < 代码> func loop1(){
动作:= make(map [string] func())

表示_,cmd:=范围cmds {
cmd2:= cmd
动作[cmd] = func( ){
fmt.Println(cmd2)//请参考分离的复制变量!
}
}
中的_,action:=范围操作{
action()
}
}
</ code> </ pre>

使用此命令,输出 loop1()</ code>(在游乐场):</ p>

  update 
create
delete
</ code> </ pre> >

这不是 for ... range </ code>的问题,这是因为闭包引用了相同的变量,并且您没有使用正确的变量值 离开,只有在循环之后。 并且当您打印此变量的值时,所有变量都将打印相同的最后一个值。</ p>

也请参见以下可能的重复项: Golang:使用范围为循环切片/ map注册多个路由 </ p> \ n </ div>

展开原文

原文

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

duanken7168
duanken7168 通常,是的,但是在这种情况下,命令是没有参数的函数类型,因此以这种方式使用它更加不便。
大约 3 年之前 回复
draxq02664
draxq02664 或者,对于更简洁的代码,只需将变量作为参数传递给闭包:actions [cmd] = func(cmd string){fmt.Println(cmd)}(cmd)
大约 3 年之前 回复
dongwei2882
dongwei2882 是的,这不是for ...范围的问题,这是因为闭包引用的是同一变量,并且当您打印此变量的值时,所有变量都将打印相同的最后一个值。
大约 3 年之前 回复
doushaqing7080
doushaqing7080 好的,这与以下事实有关:我们使用的是闭包,而与范围操作无关
大约 3 年之前 回复
Csdn user default icon
上传中...
上传图片
插入图片
抄袭、复制答案,以达到刷声望分或其他目的的行为,在CSDN问答是严格禁止的,一经发现立刻封号。是时候展现真正的技术了!
立即提问