日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 人文社科 > 生活经验 >内容正文

生活经验

Go 学习笔记(29)— range 作用于字符串、数组、切片、字典、通道

發布時間:2023/11/27 生活经验 50 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Go 学习笔记(29)— range 作用于字符串、数组、切片、字典、通道 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

1. 使用說明

range 應用于不同數據類型時,類似迭代器操作,返回 (索引, 值) 或 (鍵, 值)。 下表是對應的結構:

type1st value2nd value
stringindexs[index]
array/sliceindexs[index]
mapkeym[key]
channelelement

2. 使用示例

如果想忽略不想要的值時,可以使用 _ 這個特殊變量。

package mainfunc main() {s := "abc"for i := range s { // 忽略 2nd value,支持 string/array/slice/map。println(s[i])}println("***************")for _, c := range s { // 忽略 index。println(c)}println("***************")for range s { // 忽略全部返回值,僅迭代。println("*")}println("***************")m := map[string]int{"a": 1, "b": 2}for k, v := range m { // 返回 (key, value)。println(k, v)}}

map 類型變量作為 range 表達式時,我們得到的 map 變量的副本與原變量指向同一個 map,如果我們在循環的過程中,對 map 進行了修改,那么這樣修改的結果是否會影響后續迭代呢?這個結果和我們遍歷 map 一樣,具有隨機性。

3. 常見的坑

3.1 循環變量的重用

func main() {var m = []int{1, 2, 3, 4, 5}  for i, v := range m {go func() {time.Sleep(time.Second * 3)fmt.Println(i, v)}()}time.Sleep(time.Second * 10)
}

輸出結果:

4 5
4 5
4 5
4 5
4 5

預期結果

0 1
1 2
2 3
3 4
4 5

這是因為我們最初的“預期”本身就是錯的。這里,初學者很可能會被 for range 語句中的短聲明變量形式“迷惑”,簡單地認為每次迭代都會重新聲明兩個新的變量 iv。但事實上,這些循環變量在 for range 語句中僅會被聲明一次,且在每次迭代中都會被重用。

上面代碼等價于下面

func main() {var m = []int{1, 2, 3, 4, 5}         {i, v := 0, 0for i, v = range m {go func() {time.Sleep(time.Second * 3)fmt.Println(i, v)}()}}time.Sleep(time.Second * 10)
}

通過等價轉換后的代碼,我們可以清晰地看到循環變量 iv 在每次迭代時的重用。而 Goroutine 執行的閉包函數引用了它的外層包裹函數中的變量 iv,這樣,變量 iv 在主 Goroutine 和新啟動的 Goroutine 之間實現了共享,而 i, v 值在整個循環過程中是重用的,僅有一份。在 for range 循環結束后,i = 4, v = 5,因此各個 Goroutine 在等待 3 秒后進行輸出的時候,輸出的是 i,v 的最終值。

修改代碼

func main() {var m = []int{1, 2, 3, 4, 5}for i, v := range m {go func(i, v int) {time.Sleep(time.Second * 3)fmt.Println(i, v)}(i, v)}time.Sleep(time.Second * 10)
}

3.2 參與循環的是 range 表達式的副本

數組是內置(build-in)類型,是一組同類型數據的集合,它是值類型,通過從 0 開始的下標索引訪問元素值。在初始化后長度是固定的,無法修改其長度。

當作為方法的參數傳入時將復制一份數組而不是引用同一指針。數組的長度也是其類型的一部分,通過內置函數 len(array) 獲取其長度。

注意,和 C 中的數組相比,又是有一些不同的

  1. Go 中的數組是值類型,換句話說,如果你將一個數組賦值給另外一個數組,那么,實際上就是將整個數組拷貝一份;
  2. 如果 Go 中的數組作為函數的參數,那么實際傳遞的參數是一份數組的拷貝,而不是數組的指針。這個和 C 要區分開。因此,在 Go 中如果將數組作為函數的參數傳遞的話,那效率就肯定沒有傳遞指針高了;
  3. array 的長度也是 Type 的一部分,這樣就說明 [10]int[20]int 是不一樣的;

內置類型切片(“動態數組"),與數組相比切片的長度是不固定的,可以追加元素,在追加時可能使切片的容量增大。

切片中有兩個概念:一是 len 長度,二是 cap 容量,長度是指已經被賦過值的最大下標+1,可通過內置函數 len() 獲得。容量是指切片目前可容納的最多元素個數,可通過內置函數 cap() 獲得。

切片是引用類型,因此在當傳遞切片時將引用同一指針,修改值將會影響其他的對象。

需要強調的是, range 會復制對象, range 返回的是每個元素的副本,而不是直接返回對該元素的引用

package mainimport "fmt"func main() {a := [3]int{0, 1, 2}for i, v := range a { // index、value 都是從復制品中取出。if i == 0 { // 在修改前,我們先修改原數組。a[1], a[2] = 999, 999fmt.Println(a) // 確認修改有效,輸出 [0, 999, 999]。}a[i] = v + 100 // 使用復制品中取出的 value 修改原數組。}fmt.Println(a) // 輸出 [100, 101, 102]。}

建議改用引用類型,其底層數據不會被復制。

package mainfunc main() {s := []int{1, 2, 3, 4, 5}for i, v := range s { // 復制 struct slice { pointer, len, cap }。if i == 0 {s = s[:3]  // 對 slice 的修改,不會影響 range。s[2] = 100 // 對底層數據的修改。}println(i, v)}}

輸出:

0 1
1 2
2 100
3 4
4 5

其它示例


func main() {var a = [5]int{1, 2, 3, 4, 5}var r [5]intfmt.Println("original a =", a)for i, v := range a {if i == 0 {a[1] = 12a[2] = 13}r[i] = v}fmt.Println("after for range loop, r =", r)fmt.Println("after for range loop, a =", a)
}

期望輸出結果:

original a = [1 2 3 4 5]
after for range loop, r = [1 12 13 4 5]
after for range loop, a = [1 12 13 4 5]

實際輸出結果:

original a = [1 2 3 4 5]
after for range loop, r = [1 2 3 4 5]
after for range loop, a = [1 12 13 4 5]

修改循環迭代行代碼為下面即可達到預期效果:

for i, v := range a[:] {# orfor i, v := range &a

總結

以上是生活随笔為你收集整理的Go 学习笔记(29)— range 作用于字符串、数组、切片、字典、通道的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。