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