for-range造就回圈永動機?快來看看go中for-range的那些事!

2020-09-29 11:01:15

來自公眾號:新世界雜貨鋪

迴圈永動機

Q1: 如果我們在遍歷陣列的同時修改陣列元素,能否得到一個永遠都不會停止的迴圈呢?

func main() {
 arr := []int{1, 2, 3}
 for _, v := range arr {
     arr = append(arr, v)
 }
 fmt.Println(arr)
}
// 輸出: 1 2 3 1 2 3

上述程式碼的輸出意味著迴圈只遍歷了原始切片中的三個元素,我們遍歷切片時追加的元素不會增加回圈的執行次數, 所以迴圈最終還是停了下來

答: 對於所有的range迴圈, Go語言會在編譯期間將原切片或者陣列賦值給一個新的變數ha, 在賦值的過程中就發生了拷貝, 所以我們遍歷的切片已經不是原始的切片變數了

神奇的指標

Q2: 我們遍歷一個陣列時,如果獲取range返回變數的地址並儲存到另一個陣列或雜湊時, 就會遇到令人困惑的現象

func main() {
    arr := []int{1, 2, 3}
    newArr := []*int{}
    for _, v := range arr {
        newArr = append(newArr, &v)
    }
    for _, v := range newArr {
        fmt.Println(*v)
    }
}
// 輸出: 3 3 3

答: 遇到這種同時遍歷索引和元素的range迴圈時, go語言會額外建立一個新的v2變數儲存切片中的元素, 迴圈中使用的這個變數v2會在每一次迭代被重新賦值而覆蓋, 在賦值時也發生了拷貝. 因為在迴圈中返回的變數的地址都完全相同, 所以才會出現神奇的指標的現象

map的隨機遍歷

Q3: go語言中使用range遍歷雜湊表時, 往往都會得到不同的結果?

答: 但這並不是說明雜湊表不穩定, 這是go語言故意這樣設計的, 他在執行時為雜湊表遍歷引入不確定性, 也是告訴所有使用go語言的使用者, 程式不要依賴於雜湊表的穩定遍歷。

參考

https://draveness.me/golang/docs/part2-foundation/ch05-keyword/golang-for-range/