2017-06-23 105 views
1

我在我的程序中看到了與我的程序中的特定循環綁定的不同行爲,但我不確定我是否理解爲什麼它的行爲方式如此。爲什麼這兩個for循環變體給我不同的行爲?

//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和閱讀下列

當rangi在切片上,每次迭代返回兩個值。第一個是指數,第二個是指數

在元素的副本,它說的副本,這是否意味着它返回字符串的副本,但它的確是一個指針變量cmd?在這種情況下,在循環結束時對cmd的任何引用都實際上引用數組中的最後一個元素,例如, update?這是否意味着在使用range方法時應始終使用索引引用數組的元素,以及使用返回的元素的用例是什麼,因爲它始終更新指針?

+0

的可能的複製[捕獲閉幕(用於循環變量)在圍棋](https://stackoverflow.com/questions/26692844/captured-closure-for-loop-variable -in-go) – Adrian

回答

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的問題,這是因爲封鎖指的是同一個變量,並且您不使用變量的值就,只有在l之後接力。而當你打印這個變量的值時,所有打印的都是相同的最後一個值。

也看到這個可能重複:Golang: Register multiple routes using range for loop slices/map

+0

好的,所以它與我們使用閉包不是必然與'range'操作有關的事實 – sreya

+0

@sreya是的,這不是'for ...的問題...範圍「,這是因爲閉包指向同一個變量,並且當您打印此變量的值時,所有打印都會輸出相同的最後一個值。 – icza

+0

或者對於更乾淨的代碼,只需將該變量作爲參數傳遞給閉包即可:'actions [cmd] = func(cmd string){fmt.Println(cmd)}(cmd)' – Adrian