主頁 > 後端開發 > 深入探究for...range陳述句

深入探究for...range陳述句

2023-06-14 07:46:18 後端開發

1. 引言

在Go語言中,我們經常需要對資料集合進行遍歷操作,對于陣列來說,使用for陳述句可以很方便地完成遍歷,然而,當我們面對其他資料型別,如map、string 和 channel 時,使用普通的for回圈無法直接完成遍歷,為了更加便捷地遍歷這些資料型別,Go語言引入了for...range陳述句,本文將以陣列遍歷為起點,逐步介紹for...range陳述句在不同資料型別中的應用,

2. 問題引入

假設我們有一個整數陣列,我們想要遍歷陣列中的每個元素并對其進行處理,在這種情況下,我們可以使用for陳述句結合陣列的長度來實作遍歷,例如:

package main

import "fmt"

func main() {
    numbers := [5]int{1, 2, 3, 4, 5}

    for i := 0; i < len(numbers); i++ {
        fmt.Println(numbers[i])
    }
}

在上述代碼中,我們定義了一個整數陣列numbers,通過普通的for回圈遍歷了陣列并列印了每個元素,然而,當我們遇到其他資料型別時,如mapstring 或者channel時,此時使用for陳述句將無法簡單對其進行遍歷,那有什么方式能夠方便完成對mapstring等型別的遍歷呢?

事實上,go語言中存在for....range陳述句,能夠實作對這些型別的遍歷,下面我們來仔細介紹下for...range

3. 基本介紹

在Go語言中,for...range陳述句為遍歷陣列、切片、映射和通道等資料結構提供了一種便捷的方式,它隱藏了底層的索引或迭代器等細節,是Go語言為遍歷各種資料結構提供的一種優雅而簡潔的語法糖,使得遍歷操作更加方便和直觀,下面仔細簡介使用for...range完成對切片, map, channel的遍歷操作,

3.1 遍歷切片

當使用for...range陳述句遍歷切片時,它會逐個迭代切片中的元素,并將索引和對應的值賦值給指定的變數,示例代碼如下:

numbers := [5]int{1, 2, 3, 4, 5}

for index, value := range numbers {
    // 在這里處理 index 和 value
}

其中numbers 是我們要遍歷的切片,index 是一個變數,它在每次迭代中都會被賦值為當前元素的索引(從0開始),value 是一個變數,它在每次迭代中都會被賦值為當前元素的值,

如果只關注切片中的值而不需要索引,可以使用下劃線 _ 替代索引變數名,以忽略它:

numbers := []int{1, 2, 3, 4, 5}

for _, value := range numbers {
    fmt.Println("Value:", value)
}

這樣,回圈體只會列印出切片中的值而不顯示索引,

通過for...range陳述句遍歷切片,我們可以簡潔而直觀地訪問切片中的每個元素,無需手動管理索引,使得代碼更加簡潔和易讀,

3.2 遍歷map

當使用for...range陳述句遍歷map時,它會迭代映射中的每個鍵值對,并將鍵和對應的值賦值給指定的變數,示例代碼如下:

students := map[string]int{
    "Alice":   25,
    "Bob":     27,
    "Charlie": 23,
}

for key, value := range students {
    // 在這里處理 key 和 value
}

這里for...range會遍歷所有的鍵值對,無需我們去手動處理迭代器的邏輯,即可完成對map的遍歷操作,

3.3 遍歷string

當使用for...range陳述句遍歷字串時,它會逐個迭代字串中的字符,并將每個字符的索引和值賦值給指定的變數,以下是遍歷字串的示例代碼:

text := "Hello, 世界!"

for index, character := range text {
    fmt.Printf("Index: %d, Character: %c\n", index, character)
}

輸出結果為:

Index: 0, Character: H
Index: 1, Character: e
Index: 2, Character: l
Index: 3, Character: l
Index: 4, Character: o
Index: 5, Character: ,
Index: 6, Character:  
Index: 7, Character: 世
Index: 10, Character: 界

需要注意的是,Go語言中的字串是以UTF-8編碼存盤的,UTF-8是一種變長編碼,不同的Unicode字符可能會占用不同數量的位元組,而index的值表示每個字符在字串中的位元組索引位置,所以字符的索引位置并不一定是連續的,

這里通過for...range陳述句遍歷字串,我們可以方便地處理每個字符,無需手動管理索引和字符編碼問題,使得處理字串的邏輯更加簡潔和易讀,

3.4 遍歷channel

當使用for...range陳述句遍歷channel時,它會迭代通道中的每個值,直到通道關閉為止,下面是一個示例代碼:

ch := make(chan int)

// 向通道寫入資料的例子
go func() {
    ch <- 1
    ch <- 2
    ch <- 3
    close(ch)
}()

// 將輸出 1 2 3
for value := range ch {
    fmt.Println("Value:", value)
}

在示例中,我們向通道寫入了3個整數值,然后,使用for...range陳述句遍歷通道,從中獲取每個值并進行處理,

需要注意的是,如果通道中沒有資料可用,for...range陳述句會阻塞,直到有資料可用或通道被關閉,因此,當通道中沒有資料時,它會等待資料的到達,

通過for...range陳述句遍歷通道,可以非常方便得不斷從channel中取出資料,然后對其進行處理,

4. 注意事項

for...range陳述句可以認為是go語言的一個語法糖,簡化了我們對不同資料結構的遍歷操作,但是使用for...range陳述句還是存在一些注意事項的,充分了解這些注意事項,能夠讓我們更好得使用該特性,下面我們將對其來進行敘述,

4.1 迭代變數是會被復用的

當使用for...range回圈時,迭代變數是會被復用的,這意味著在每次回圈迭代中,迭代變數都將被重用,而不是在每次迭代中創建一個新的迭代變數,

下面是一個簡單的示例代碼,演示了迭代變數被復用的情況:

package main

import "fmt"

func main() {
        numbers := []int{1, 2, 3, 4, 5}

        for _, value := range numbers {
           go func() {
              fmt.Print(strconv.Itoa(value) + " ")
           }()
        }
}

在上述代碼中,我們使用for...range回圈遍歷切片numbers,并在每次回圈迭代中創建一個匿名函式并啟動一個goroutine,該匿名函式列印當前迭代的value變數,下面是一個可能的結果:

4 5 5 5 5

出現這個結果的原因,就是由于迭代變數被復用,所有的goroutine都會共享相同的value變數,當goroutine開始執行時,它們可能會讀取到最后一次迭代的結果,而不是預期的迭代順序,這會導致輸出結果可能是重復的數字或者不按照預期的順序輸出,

如果不清楚迭代變數會被復用的特點,這個在某些場景下可能會導致意料之外結果的出現,因此,如果for...range回圈中存在并發操作,延遲函式等操作時,同時也依賴于迭代變數的值,這個時候需要確保在回圈迭代中創建新的副本,以避免意外的結果,

4.2 參與迭代的為range運算式的副本資料

對于for...range回圈,是使用range運算式的副本資料進行迭代,這意味著迭代程序中對原始資料的修改,并不會對迭代的結果造成影響,一個簡單的代碼示例如下:

package main

import "fmt"

func main() {
        numbers := [5]int{1, 2, 3, 4, 5}
        for i, v := range numbers {
           if i == 0 {
              numbers[1] = 100 // 修改原始資料的值
              numbers[2] = 200
           }
           fmt.Println("Index:", i, "Value:", v)
        }
}

在上述代碼中,我們使用for...range回圈遍歷陣列numbers, 然后在回圈體內修改了陣列中元素的值,遍歷結果如下:

Index: 0 Value: 1
Index: 1 Value: 2
Index: 2 Value: 3
Index: 3 Value: 4
Index: 4 Value: 5

可以看到,雖然在迭代程序中,對numbers進行遍歷,但是并沒有影響到遍歷的結果,從這里也可以證明,參與迭代的為range運算式的副本資料,而不是副本資料,

如果回圈中的操作,需要依賴中間修改后的資料結果,此時最好分成兩個遍歷,首先遍歷資料,修改其中的資料,之后再遍歷修改后的資料,對上述代碼改進如下:

numbers := [5]int{1, 2, 3, 4, 5}
// 1. 第一個遍歷修改資料
for i, _ := range numbers {
   if i == 0 {
      numbers[1] = 100 // 修改原始資料的值
      numbers[2] = 200
   }

}
// 2. 第二個遍歷輸出資料
for i, v := range numbers {
   fmt.Println("Index:", i, "Value:", v)
}

這次遍歷的結果,就是修改后的資料,如下:

Index: 0 Value: 1
Index: 1 Value: 100
Index: 2 Value: 200
Index: 3 Value: 4
Index: 4 Value: 5

4.3 map遍歷順序是不確定的

對于Go語言中的map型別,遍歷其鍵值對時的順序是不確定的,下面是一個簡單代碼的示例:

package main

import "fmt"

func main() {
        data := map[string]int{
                "apple":  1,
                "banana": 2,
                "cherry": 3,
        }

        for key, value := range data {
                fmt.Println(key, value)
        }
}

運行上述代碼,每次輸出的結果可能是不同的,即鍵值對的順序是不確定的,有可能第一次運行的結果為:

banana 2
cherry 3
apple 1

然后第二次運行的結果又與第一次運行的結果不同,可能為:

apple 1
banana 2
cherry 3

從這個例子可以證明,對map進行遍歷,其遍歷順序是不固定的,所以我們需要注意,不能依賴map的遍歷順序,

如果需要每次map中的資料按照某個順序輸出,此時可以先把key保存到切片中,對切片按照指定的順序進行排序,之后遍歷排序后的切片,并使用切片中的key來訪問map中的value,此時map中的資料便能夠按照指定的順序來輸出,下面是一個簡單的代碼代碼示例:

package main

import (
        "fmt"
        "sort"
)

func main() {
        data := map[string]int{
                "apple":  1,
                "banana": 2,
                "cherry": 3,
        }

        // 創建保存鍵的切片
        keys := make([]string, 0, len(data))
        for key := range data {
                keys = append(keys, key)
        }

        // 對切片進行排序
        sort.Strings(keys)

        // 按照排序后的鍵遍歷map
        for _, key := range keys {
                value := data[key]
                fmt.Println(key, value)
        }
}

5. 總結

本文對Go語言中的for...range進行了基本介紹,首先從一個簡單遍歷問題出發,發現基本的for陳述句似乎無法簡單實作對stringmap等型別的遍歷操作,從而引出了for...range陳述句,

接著我們仔細介紹了,如何使用for...rangestring,map,channel等型別的遍歷操作,然后我們再仔細介紹了使用for...range的三個注意事項,如參與迭代的為range運算式的副本資料,通過對這些注意事項的了解,我們能夠更好得使用for...range陳述句,避免出現預料之外的情況,

基于以上內容,完成了對for...range的介紹,希望能幫助你更好地理解和使用這個重要的Go語言特性,

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/555046.html

標籤:Go

上一篇:vs2022 wxWidgets

下一篇:返回列表

標籤雲
其他(160896) Python(38222) JavaScript(25493) Java(18225) C(15237) 區塊鏈(8270) C#(7972) AI(7469) 爪哇(7425) MySQL(7248) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5874) 数组(5741) R(5409) Linux(5347) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4591) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2435) ASP.NET(2404) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) .NET技术(1984) 功能(1967) HtmlCss(1964) Web開發(1951) C++(1939) python-3.x(1918) 弹簧靴(1913) xml(1889) PostgreSQL(1881) .NETCore(1863) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • 深入探究for...range陳述句

    # 1. 引言 在Go語言中,我們經常需要對資料集合進行遍歷操作。對于陣列來說,使用for陳述句可以很方便地完成遍歷。然而,當我們面對其他資料型別,如map、string 和 channel 時,使用普通的for回圈無法直接完成遍歷。為了更加便捷地遍歷這些資料型別,Go語言引入了for...range ......

    uj5u.com 2023-06-14 07:46:18 more
  • vs2022 wxWidgets

    # 下載 https://www.wxwidgets.org/downloads/ 下載壓縮包即可 ![image](https://img2023.cnblogs.com/blog/916065/202306/916065-20230614040303993-2082032985.png) # 編 ......

    uj5u.com 2023-06-14 07:45:22 more
  • OpenFoam——自定義求解器

    ## 1、求解器 ### 1.1 復制原始碼 本案例以icoFoam為例,復制【openFOAM/OpenFOAM-9/applications/solvers/incompressible/icoFoam】檔案夾至run檔案夾下(我的是【openFOAM/mtl-9/run/solvers/inco ......

    uj5u.com 2023-06-14 07:44:36 more
  • C++面試八股文:什么是RAII?

    某日二師兄參加XXX科技公司的C++工程師開發崗位第13面: > 面試官:什么是`RAII`? > > 二師兄:`RAII`是`Resource Acquisition Is Initialization`的縮寫。翻譯成中文是資源獲取即初始化。 > > 面試官:`RAII`有什么特點和優勢? > > ......

    uj5u.com 2023-06-14 07:44:13 more
  • 現代C++學習指南-方向篇

    C++是一門有著四十年歷史的語言,先后經歷過四次版本大升級(誕生、98、11、17(20),14算小升級)。每次升級都是很多問題和解決方案的取舍。了解這些歷史,能更好地幫助我們理清語言的發展脈絡。所以接下來我將借它的發展歷程,談一談我對它的理解,最后給出我認為比較合理的學習路線指南。 ### C++ ......

    uj5u.com 2023-06-14 07:44:09 more
  • 現代C++學習指南-型別系統

    > 在前一篇,我們提供了一個方向性的指南,但是學什么,怎么學卻沒有詳細展開。本篇將在前文的基礎上,著重介紹下怎樣學習C++的型別系統。 ### 寫在前面 在進入型別系統之前,我們應該先達成一項共識——盡可能使用C++的現代語法。眾所周知,出于兼容性的考慮,C++中很多語法都是合法的。但是隨著新版本的 ......

    uj5u.com 2023-06-14 07:44:04 more
  • 主席樹學習筆記

    # 什么是主席樹 主席樹這個名字看上去很高級,其實不然,它還有另一個名字——可持久化線段樹。 ## 什么是可持久化 可持久化顧名思義就是它可以變得~~**持久**~~,就是我們對他不斷進行單點修改后,突然查詢它的某一個歷史版本,這就叫可持久化。 # 引入例題 [洛谷3919:可持久化陣列](http ......

    uj5u.com 2023-06-14 07:43:57 more
  • CoaXpress downlink資料決議方法

    ## 什么是downlink資料 downlink指的是相機傳輸到host采集卡的高速鏈路,其中包含了如下型別的資料: 1、Stream Data 2、Trigger Ack, Trigger; 3、Ack (reply data); 4、Event, Heartbeat ![](https://i ......

    uj5u.com 2023-06-13 07:48:48 more
  • Servlet p1 Servlet的實作

    學習課程: 【這可能是B站講的最好的Servlet教程,5小時打通Servlet全套教程丨2022最新版,輕松掌握servlet基礎+案例實操】 https://www.bilibili.com/video/BV1Kr4y1V7ZE/?share_source=copy_web&vd_source= ......

    uj5u.com 2023-06-13 07:48:35 more
  • C++面試八股文:了解位運算嗎?

    某日二師兄參加XXX科技公司的C++工程師開發崗位第12面: > 面試官:了解位運算嗎? > > 二師兄:了解一些。(我很熟悉) > > 面試官:請列舉以下有哪些位運算? > > 二師兄:按位與(`&`)、按位或(`|`)、按位異或(`^`),按位取反(`~`)、左移(`>`)。 > > 面試官:好 ......

    uj5u.com 2023-06-13 07:48:29 more