我在程序中看到的不同行为与我程序中的这个特定循环有关,但我不确定我理解为什么它的行为方式如此。
//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
我去查看internet并阅读以下内容
在切片上进行测距时,每次迭代都会返回两个值。第一个是索引,第二个是索引
的元素副本
它表示副本,这是否意味着它返回字符串的副本,但它实际上是变量cmd
的指针?在这种情况下,对cmd
的任何引用都将在循环结束时实际引用数组中的最后一个元素,例如update
?这是否意味着在使用range
方法时,数组的元素应始终由其索引引用,以及使用它返回的元素的用例是什么,因为它始终更新指针?
答案 0 :(得分:4)
loop1()
的问题在于,您在actions
地图中存储了一个引用循环变量 cmd
的函数文字。这个循环变量只有一个实例,所以当你在循环之后调用存储在actions
映射中的函数时,所有这些都将引用这个单循环变量(由于函数/闭包仍有引用而保留它但是它的值在执行时将是for
循环设置的最后一个值,这是cmds
切片中的最后一个值(即,"update"
,所以你会看到"update"
打印3次。)
一个简单的解决方法是复制这个循环变量,所以每次迭代时,每个函数文本都有自己的副本,它与循环变量“分离”:
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 Playground上尝试):
update
create
delete
这不是for ... range
的问题,因为闭包引用相同的变量,并且您不会立即使用变量的值,仅在循环之后。当你打印这个变量的值时,所有都打印相同的,最后一个值。
另见{x 3}}