golang的for range原理以及引致的一些奇怪問題
阿新 • • 發佈:2018-12-01
基本用法
下述兩個函式test1與test2執行結果有何區別?
func test1() {
intArray := []int{1, 2, 3, 4}
for _, val := range intArray {
val++
}
// 仍然為[1, 2, 3, 4]
fmt.Println(intArray)
}
func test2() {
intArray := []int{1, 2, 3, 4}
for i := 0; i < len(intArray); i++ {
intArray[i]++
}
// 改變為[2, 3, 4, 5]
fmt. Println(intArray)
}
原理是這樣的,對於for i, val := range(intArray)
來說,val是intArray[i]的副本,對val的改變不會導致intArray內元素的改變。
另一方面,i和val在迴圈內都是同一個變數,只在迴圈頭宣告一次,即兩者的地址不變。這是另一個易錯的地方。下述兩個函式test3與test4執行結果有何區別?
package main
import "fmt"
func test3() {
slice := []int{0, 1, 2, 3}
myMap := make(map[int]*int)
for index , value := range slice {
myMap[index] = &value
}
// map[0]=3, map[1]=3, map[2]=3, map[3]=3
prtMap(myMap)
}
func test4() {
slice := []int{0, 1, 2, 3}
myMap := make(map[int]*int)
for index , value := range slice {
// 每次進入迴圈體,宣告一個新變數valueCopy,並把value賦值給它
valueCopy := value
myMap[index] = &valueCopy
}
// map[0]=0, map[1]=1, map[2]=2, map[3]=3
prtMap(myMap)
}
func prtMap(myMap map[int]*int) {
for key, value := range myMap {
fmt.Printf("map[%v]=%v\n", key, *value)
}
}
再舉一個閉包的例子
package main
import (
"fmt"
"time"
)
func main() {
str := []string{"I","am","Jason"}
for _,v := range str{
// 每個goroutine的v的地址相同,同時為外部v的地址
go func() {
// 這裡的v是引用外部變數v的地址
fmt.Println(v)
}()
}
time.Sleep(3 * time.Second)
}
/*
v的地址儲存的值為Jason
輸出結果:
Jason
Jason
Jason
*/
修改方法,利用函式的值傳遞性質:
package main
import (
"fmt"
"time"
)
func main() {
str := []string{"I","am","Jason"}
for _,v := range str{
// 把外部的v值拷貝給函式內部的v
// 每個goroutine的v的地址不同,同時不同於外部v的地址
go func(v string) {
fmt.Println(v)
}(v)
}
time.Sleep(3 * time.Second)
}
/*
輸出結果: {"I", "am", "Jason"}的排列組合
*/
Slice
字串
在 Go 中,字串是以 UTF-8 為格式進行儲存的,在字串上呼叫 len 函式,取得的是字串包含的 byte 的個數。
func test5() {
str := "beijing,北京"
// 14
fmt.Println(len(str))
// 10
fmt.Println(utf8.RuneCountInString(str))
// 10
fmt.Println(len([]rune(str)))
// 10
fmt.Println(utf8.RuneCount([]byte(str)))
}
再看下面例子,在go語言中支援兩種方式遍歷字串。
1.以位元組陣列的方式遍歷。
str := "beijing,北京"
for i := 0; i < len(str); i++{
fmt.Printf("%d: %v : %T\n", i, str[i], str[i])
}
輸出結果為:
0: 98 : uint8
1: 101 : uint8
2: 105 : uint8
3: 106 : uint8
...
11: 228 : uint8
12: 186 : uint8
13: 172 : uint8
可以看出,這個字串長度為14,儘管從直觀上來說,這個字串應該只有10個字元,這是因為每個中文字元在UTF-8中佔3個位元組,而不是1個位元組。
2.以Unicode字元遍歷
str := "beijing,北京"
for i, val := range str {
fmt.Printf("%d: %v : %T\n", i, val, val)
}
輸出結果為:
0: 98 : int32
1: 101 : int32
2: 105 : int32
3: 106 : int32
4: 105 : int32
5: 110 : int32
6: 103 : int32
7: 44 : int32
8: 21271 : int32
9: 20140 : int32
以Unicode字元方式遍歷時,每個字元的型別是rune,而不是byte。rune型別在go語言中佔用四個位元組。