Go 循環語句 For
---
基礎使用
for i := 0 ; i < 100 ; i++ {
fmt.Println(i)
}
· go語言的for,條件不需要括號。
· 可以省略初始條件,結束條件,遞增表達式。
· for 條件內可以定義變量。
· 全部省略就是一個死循環。
for {
...
}
· 當只有if條件的時候就是一個while
var i = 0
for i < 100 {
fmt.Println(i)
i++
}
語法糖 for-range
例子:下面代碼會不會停止?
func main() {
v := []int{3, 4, 5}
for i := range v {
v = append(v, i)
}
}
range用法
for i := range a {
fmt.Println(i)
}
range表達式左側
在range內可用兩種方式assign給左邊變量即(i)
- =
- := 這種方式,在go中每次循環都會複用左邊宣告的變量
range表達式右側類型
可以用於range的類型如下
array,pointer to an array,slice,string,map,可接受的channel
range 表達式會在開始循環前被 evaluated 一次,循環開始之前會先把 range 表達式複製給一個變量
evaluated我的理解是值賦予一個變量的動作,可用這個來理解,"append(slice1, 1, 2, 3, 4) evaluated but not used",相當於是算出一個值後沒有把對應的值賦給一個變量。
Go中,無論我們對什麼賦值,都會被複制。如果賦值了一個指針,那我們就複製了一個指針副本。如果賦值了一個結構體,那我們就複製了一個結構體副本。
range對語法糖的還原
- array
// The loop we generate:
// len_temp := len(range)
// range_temp := range
// for index_temp = 0; index_temp < len_temp; index_temp++ {
// value_temp = range_temp[index_temp]
// index = index_temp
// value = value_temp
// original body
// }
- slice
// for_temp := range
// len_temp := len(for_temp)
// for index_temp = 0; index_temp < len_temp; index_temp++ {
// value_temp = for_temp[index_temp]
// index = index_temp
// value = value_temp
// original body
// }
- channel
//for{
// v,ok=<-ch
// if!ok{
// break
// }
// original body
//}
回到例子
for_temp := v
len_temp := len(for_temp)
for index_temp = 0; index_temp < len_temp; index_temp++ {
value_temp = for_temp[index_temp]
index = index_temp
value = value_temp
v = append(v, index)
}
在循環開始前對這個結構體生成副本然後賦值給 for_temp,後面的循環實際上是在對 for_temp 進行迭代,及運算。任何對於原始變量 v本身(而非對其背後指向的數組)的更改都和生成的副本 for_temp沒有關係。但其背後指向的數組還是以指針的形式共享給 v和 for_temp,所以 v[i] = 1這樣的語句仍然可以工作。
延伸
range map會發生什麼?
- 在 range 循環裏對 maps 做添加或刪除元素的操作是安全的。
- 如果在循環中對 map 添加了一個元素,那麼這個元素並不一定會出現在後續的迭代中。
第一點,對map的複製,是複製map結構體的指針。所以range對指針副本做操作的內存地址和對maps指針指向的內存地址一樣,所以操作的內容會一致。
第二點,map本質是hash table,哈希表內部數組裏的元素並不是以特定順序存放。最後一個添加的元素有可能經過哈希後被放到了內部數組裏的第一個索引位,沒有辦法預測當前添加的元素是否會出現在後續的迭代中,畢竟在添加元素的時候很可能已經遍歷過了第一個索引位。
中文字符串的便利性
直接遍歷一箇中文字符串string,會出現亂碼,因爲一箇中文是4字節。如果需要通過遍歷string來打印中文字符串,需要先將string轉成[]rune。而for-range的用法可以直接避免。
var str string = "測試中文"
str2 := []rune(str) //轉爲切片類型
for i := 0; i < len(str2); i++ {
fmt.Printf("%c \n", str2[i])
}
var str string = "測試中文"
for index, val := range str {
fmt.Printf("index=%d , val=%c \n", index, val)
}