如何在 Go 中構造 for 循環


介紹

在計算機編程中,循環 是在滿足某些條件之前循環重複執行一段代碼的代碼結構。在計算機編程中使用循環可以讓您自動並重復地執行類似的任務。想象一下,如果您有一個需要處理的文件列表,或者您想計算一篇文章的行數。您就可以在代碼中使用循環來解決這樣的問題。

在 Go 中,for 循環是基於循環計數器或循環變量實現代碼的重複執行。與其他具有多個循環結構(例如 whiledo 等 )的編程語言不同,Go 只有 for 循環。這有助於使您的代碼更清晰和更具可讀性,因爲您不必擔心會有多種策略來實現相同的循環結構。在開發過程中,這種強可讀性和低認知負擔也將使您的代碼比其他語言更不容易出錯。

在本教程中,您將瞭解 Go 中 for 循環是如何工作的,包括其使用的三個主要變體。我們將首先展示如何創建不同類型的 for 循環,然後介紹如何《在 Go 中遍歷順序數據類型》(點擊跳轉查看)。最後,我們將解釋如何使用嵌套循環。

聲明 ForClause 和 Condition 循環

爲了適應各種用例,在 Go 中創建 for 循環有三種不同的方法,每種方法都有自己的功能。這些是使用 ConditionForClauseRangeClause 創建 for 循環。在本節中,我們將解釋如何聲明和使用 ForClause 和 Condition 變體。

讓我們先看看如何在 ForClause 中使用 for 循環。

ForClause 循環 被定義爲有一個 初始語句,後跟一個 條件,然後是一個 後置語句。它們按以下語法排列:

for [ Initial Statement ] ; [ Condition ] ; [ Post Statement ] {
    [Action]
}

爲了解釋前面組成元素的作用,讓我們看一個使用 ForClause 語法在指定值範圍內遞增的 for 循環:

for i := 0; i < 5; i++ {
 fmt.Println(i)
}

讓我們分解這個循環並識別每個部分

循環的第一部分是 i := 0 ,這是初始語句:

for i := 0; i < 5; i++ {
 fmt.Println(i)
}

它表明我們正在聲明一個名爲 i 的變量,並將初始值設置爲 0

接下來是條件:

for i := 0; i < 5; i++ {
 fmt.Println(i)
}

在這種情況下,我們聲明當 i 小於 5 時,循環應該繼續。

最後,是一個後置語句:

for i := 0; i < 5; i++ {
 fmt.Println(i)
}

在後置語句中,循環遍歷 ii++ 增量 [1] 運算符進行迭代加一。

當我們運行這個程序時,輸出如下所示:

Output
0
1
2
3
4

循環運行了 5 次。最初,設置 i0,然後檢查是否 i 小於5。由於 i 的值小於 5 ,因此執行了循環並執行了 fmt.Println(i) 的動作。循環結束時, 調用 i++ 語句,使 i 的值加 1。

注意: 請記住,在編程中我們傾向於從索引 0 開始,這就是爲什麼雖然打印了 5 個數字,但它們的範圍是 0-4。

我們不會限制必須從 0 開始或者以某個特定值結束。我們可以爲我們的初始語句分配任何值,也可以在我們的後置語句中賦予任何值。這允許我們創建任何所需的範圍來循環:

for i := 20; i < 25; i++ {
 fmt.Println(i)
}

在這裏,迭代會從 20(包括)到 25(不包括),所以輸出如下所示:

Output
20
21
22
23
24

我們還可以使用我們的後置語句以不同的值遞增。這類似於其他語言中的 step

首先,讓我們使用正值遞增的後置語句:

for i := 0; i < 15; i += 3 {
 fmt.Println(i)
}

在這種情況下,設置 for 循環以便打印出從 0 到 15 的數字,但增量爲 3,因此每三個數字打印一次,如下所示:

Output
0
3
6
9
12

我們也可以爲我們的後置語句使用負值來向後迭代,但我們必須相應地調整我們的初始語句和條件參數:

for i := 100; i > 0; i -= 10 {
 fmt.Println(i)
}

在這裏,我們將 i 初始值設置爲100,使用 i < 0 的條件在處停止,並且後置語句使用 -= 運算符將值減 10。循環開始於 100 並結束於 0,每次迭代減少 10。我們可以在輸出中看到這種情況:

Output
100
90
80
70
60
50
40
30
20
10

您也可以從語法中不使用初始語句和後置語句,而只使用條件。這就是所謂的 Condition 循環

i := 0
for i < 5 {
 fmt.Println(i)
 i++
}

這一次,我們在前面的代碼行中將變量聲明爲 i 與循環分開。該循環只有一個條件子句,用於檢查 i 是否小於5。只要條件爲 true,循環就會繼續迭代。

有時您可能不知道完成某項任務所需的迭代次數。在這種情況下,您可以省略所有語句,並使用 break 關鍵字退出執行:

for {
 if someCondition {
  break
 }
 // do action here
}

這方面的一個例子可能是,如果我們正在從一個不確定大小的結構(如 緩衝區 [2] )中讀取,並且我們不知道何時完成讀取:

package main

import (
 "bytes"
 "fmt"
 "io"
)

func main() {
 buf := bytes.NewBufferString("one\ntwo\nthree\nfour\n")

 for {
  line, err := buf.ReadString('\n')
  if err != nil {
   if err == io.EOF {

    fmt.Print(line)
    break
   }
   fmt.Println(err)
   break
  }
  fmt.Print(line)
 }
}

在前面的代碼中,buf :=bytes.NewBufferString("one\ntwo\nthree\nfour\n") 聲明瞭一個包含一些數據的緩衝區。因爲我們不知道緩衝區何時會完成讀取,所以我們創建了一個沒有子句的 for 循環。在 for 循環內部,我們使用 line, err := buf.ReadString('\n') 從緩衝區讀取一行並檢查從緩衝區讀取是否有錯誤。如果有,我們解決錯誤,並 [使用 break 關鍵字退出 for 循環](下週_我們會講到哦_)。有了break,您就不需要使用停止循環的條件。

在本節中,我們學習瞭如何聲明 ForClause 循環並使用它來迭代已知範圍的值。我們還學習瞭如何使用 Condition 循環進行迭代,直到滿足特定條件。接下來,我們將瞭解 RangeClause 如何用於迭代順序數據類型。

使用 RangeClause 循環遍歷順序數據類型

在 Go 中,使用 for 循環來迭代連續或集合數據類型(如 [切片、數組]_(點擊跳轉查看)[字符串](點擊跳轉查看)_的元素是很常見的。爲了更容易做到這一點,我們可以使用帶有 RangeClause 語法的 for 循環。雖然您可以使用 ForClause 語法遍歷順序數據類型,但 RangeClause 更簡潔且更易於閱讀。

在我們研究使用 RangeClause 之前,讓我們看看如何使用 ForClause 語法遍歷切片:

package main

import "fmt"

func main() {
 sharks := []string{"hammerhead""great white",   "dogfish""frilled""bullhead""requiem"}

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

運行它將給出以下輸出,打印出切片的每個元素:

Output
hammerhead
great white
dogfish
frilled
bullhead
requiem

現在,讓我們使用 RangeClause 執行相同的操作:

package main

import "fmt"

func main() {
 sharks := []string{"hammerhead""great white",   "dogfish""frilled""bullhead""requiem"}

 for i, shark := range sharks {
  fmt.Println(i, shark)
 }
}

在這種情況下,我們打印出列表中的每個項。雖然我們使用了變量 ishark ,但我們可以將變量稱爲任何其他 有效的變量名 [3],我們會得到相同的輸出:

Output
0 hammerhead
1 great white
2 dogfish
3 frilled
4 bullhead
5 requiem

在切片上使用 range 時,它將始終返回兩個值。第一個值將是當前迭代所在的索引,第二個是該索引處的值。在這個示例中,對於第一次迭代,索引是 0,值是 hammerhead

有時,我們只想要切片元素內的值,而不是索引。但是,如果我們將前面的代碼更改爲僅打印值,我們將收到編譯時錯誤:

package main

import "fmt"

func main() {
 sharks := []string{"hammerhead""great white""dogfish""frilled""bullhead""requiem"}

 for i, shark := range sharks {
  fmt.Println(shark)
 }
}
Output
src/range-error.go:8:6: i declared and not used

因爲 ifor 循環中聲明瞭,但從未使用過,編譯器會報告 i declared and not used。每當您聲明一個變量並且不使用它時,您都會在 Go 中收到相同的錯誤。

因此,Go 具有 空白標識符 [4],即下劃線 ( _ )。在 for 循環中,您可以使用空白標識符來忽略從 range 關鍵字返回的任何值。在這種情況下,我們要忽略索引,它是返回的第一個參數。

package main

import "fmt"

func main() {
 sharks := []string{"hammerhead""great white",   "dogfish""frilled""bullhead""requiem"}

 for _, shark := range sharks {
  fmt.Println(shark)
 }
}
Output
hammerhead
great white
dogfish
frilled
bullhead
requiem

輸出顯示 for 循環遍歷字符串切片,並打印切片中每個項,且不會打印索引。

您也可以使用 range 將項目添加到列表中:

package main

import "fmt"

func main() {
 sharks := []string{"hammerhead""great white",   "dogfish""frilled""bullhead""requiem"}

 for range sharks {
  sharks = append(sharks, "shark")
 }

 fmt.Printf("%q\n", sharks)
}
Output
['hammerhead''great white''dogfish''frilled''bullhead''requiem''shark''shark''shark''shark''shark''shark']

在這裏,我們爲切片 "shark" 添加了切片長度的佔位符字符串 sharks

請注意,我們不必使用空白標識符 _ 來忽略 range 運算符的任何返回值。如果我們不需要使用任何一個 range  的返回值,Go 允許我們省略語句的整個聲明部分。

我們也可以使用 range 運算符來填充切片的值

package main

import "fmt"

func main() {
 integers := make([]int, 10)
 fmt.Println(integers)

 for i := range integers {
  integers[i] = i
 }

 fmt.Println(integers)
}

在這個例子中,切片 integers 初始化了十個空值,但 for 循環會設置列表中的所有值,如下所示:

Output
[0 0 0 0 0 0 0 0 0 0]
[0 1 2 3 4 5 6 7 8 9]

第一次打印切片 integers 的值時,我們看到全爲零。然後我們遍歷每個索引並將值設置爲當前索引。然後當我們第二次打印值integers 時,顯示它們現在都具有09 的值。

我們也可以使用 range 運算符來遍歷字符串中的每個字符:

package main

import "fmt"

func main() {
 sammy := "Sammy"

 for _, letter := range sammy {
  fmt.Printf("%c\n", letter)
 }
}
Output
S
a
m
m
y

當遍歷 [map] _(點擊跳轉) _時,range將返回

package main

import "fmt"

func main() {
 sammyShark := map[string]string{"name""Sammy""animal""shark""color""blue""location""ocean"}

 for key, value := range sammyShark {
  fmt.Println(key + ": " + value)
 }
}
Output
color: blue
location: ocean
name: Sammy
animal: shark

注意:map 返回的順序是隨機的。每次運行此程序時,您可能會得到不同的結果。

現在我們已經學會了如何使用 range for 循環來遍歷數據,下面讓我們看看如何在循環中使用循環。

嵌套 For 循環

就像其他編程語言一樣,在 Go 中循環也是可以嵌套的。嵌套 是當我們在一個結構內又使用了一個結構。在這種情況下,嵌套循環是發生在另一個循環中的循環。當您希望對數據集的每個元素執行循環操作時,這些可能很有用。

嵌套循環在結構上類似於 嵌套if語句 [5]。它的構造如下:

for {
    [Action]
    for {
        [Action]  
    }
}

程序首先遇到外循環,執行它的第一次迭代。第一次迭代觸發內部嵌套循環,然後運行完成。然後程序返回到外部循環的頂部,完成第二次迭代並再次觸發嵌套循環。同樣,嵌套循環運行到完成,程序返回到外部循環的頂部,直到序列完成或中斷或其他語句中斷該過程。

讓我們實現一個嵌套 for 循環,以便我們仔細看看。在這個例子中,外層循環將遍歷一個名爲 numList 的整數切片,而內層循環將遍歷一個名爲 alphaList 的字符串切片。

package main

import "fmt"

func main() {
 numList := []int{1, 2, 3}
 alphaList := []string{"a""b""c"}

 for _, i := range numList {
  fmt.Println(i)
  for _, letter := range alphaList {
   fmt.Println(letter)
  }
 }
}

當我們運行這個程序時,我們將得到以下輸出:

Output
1
a
b
c
2
a
b
c
3
a
b
c

輸出說明程序通過打印 1 完成了外循環的第一次迭代,然後連續觸發內循環打印的完成。內循環完成後,程序返回到外循環的頂部,打印 2,然後再次完整打印內循環(a, b, c)等。

嵌套 for 循環可用於遍歷由切片組成的切片中的項。在由切片組成的切片中,如果我們只使用一個 for 循環,程序會將每個內部列表作爲一項輸出:

package main

import "fmt"

func main() {
 ints := [][]int{
  []int{0, 1, 2},
  []int{-1, -2, -3},
  []int{9, 8, 7},
 }

 for _, i := range ints {
  fmt.Println(i)
 }
}
Output
[0 1 2]
[-1 -2 -3]
[9 8 7]

爲了訪問內部切片的每個單獨項,我們將實現一個嵌套for循環:

package main

import "fmt"

func main() {
 ints := [][]int{
  []int{0, 1, 2},
  []int{-1, -2, -3},
  []int{9, 8, 7},
 }

 for _, i := range ints {
  for _, j := range i {
   fmt.Println(j)
  }
 }
}
Output
0
1
2
-1
-2
-3
9
8
7

當我們在這裏使用嵌套 for 循環時,我們能夠迭代切片中包含的各個項。

結論

在本教程中,我們學習瞭如何聲明和使用 for 循環來解決 Go 中的重複任務。我們還學習了 for 循環的三種不同變體以及何時使用它們。要了解有關 for 循環以及如何控制它們的流程的更多信息,請閱讀 [在 Go 中使用循環時的 Break 和 Continue 語句](下週我們會講到哦)

相關鏈接:

[1] https://golang.org/ref/spec#IncDec_statements

[2] https://golang.org/pkg/bytes/#Buffer

[3] https://gocn.github.io/How-To-Code-in-Go/docs/11-How_To_Use_Variables_and_Constants_in_Go/#%E5%91%BD%E5%90%8D%E5%8F%98%E9%87%8F%E8%A7%84%E5%88%99%E5%92%8C%E6%A0%B7%E5%BC%8F

[4] https://golang.org/ref/spec#Blank_identifier

[5] https://gocn.github.io/How-To-Code-in-Go/docs/23-How_To_Write_Conditional_Statements_in_Go/#%E5%B5%8C%E5%A5%97%E7%9A%84if%E8%AF%AD%E5%8F%A5

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/GCT15zGT3fjxK9CNEWcPmg