控制結構:
一、if-else結構:
if 是用于測試某個條件(布爾型或邏輯型)的語句,如果該條件成立,則會執行 if 後由大括号括起來的代碼塊,否則就忽略該代碼塊繼續執行後續的代碼。
if condition {
// do something
}
如果存在第二個分支,則可以在上面代碼的基礎上添加 else 關鍵字以及另一代碼塊,這個代碼塊中的代碼隻有在條件不滿足時才會執行。if 和 else 後的兩個代碼塊是互相獨立的分支,隻可能執行其中一個。
if condition {
// do something
} else {
// do something
}
如果存在第三個分支,則可以使用下面這種三個獨立分支的形式:
if condition1 {
// do something
} else if condition2 {
// do something else
} else {
// catch-all or default
}
else-if 分支的數量是沒有限制的,但是為了代碼的可讀性,還是不要在 if 後面加入太多的 else-if 結構。如果你必須使用這種形式,則把盡可能先滿足的條件放在前面。
即使當代碼塊之間隻有一條語句時,大括号也不可被省略(盡管有些人并不贊成,但這還是符合了軟體工程原則的主流做法)。
關鍵字 if 和 else 之後的左大括号 { 必須和關鍵字在同一行,如果你使用了 else-if 結構,則前段代碼塊的右大括号 } 必須和 else-if 關鍵字在同一行。這兩條規則都是被編譯器強制規定的。
非法的 Go 代碼:
if x{
}
else { // 無效的
}
要注意的是,在你使用 gofmt 格式化代碼之後,每個分支内的代碼都會縮進 4 個或 8 個空格,或者是 1 個 tab,并且右大括号與對應的 if 關鍵字垂直對齊。
在有些情況下,條件語句兩側的括号是可以被省略的;當條件比較複雜時,則可以使用括号讓代碼更易讀。條件允許是符合條件,需使用 &&、|| 或 !,你可以使用括号來提升某個表達式的運算優先級,并提高代碼的可讀性。
一種可能用到條件語句的場景是測試變量的值,在不同的情況執行不同的語句,不過将在第 5.3 節講到的 switch 結構會更适合這種情況。
示例 booleans.go
package main
import "fmt"
func main() {
bool1 := true
if bool1 {
fmt.Printf("The value is true\n")
} else {
fmt.Printf("The value is false\n")
}
}
輸出:
The value is true
注意事項:這裡不需要使用 if bool1 == true 來判斷,因為 bool1 本身已經是一個布爾類型的值。
這種做法一般都用在測試 true 或者有利條件時,但你也可以使用取反 ! 來判斷值的相反結果,如:if !bool1 或者 if !(condition)。後者的括号大多數情況下是必須的,如這種情況:if !(var1 == var2)。
當 if 結構内有 break、continue、goto 或者 return 語句時,Go 代碼的常見寫法是省略 else 部分(另見第 5.2 節)。無論滿足哪個條件都會傳回 x 或者 y 時,一般使用以下寫法:
if condition {
return x
}
return y
注意事項 不要同時在 if-else 結構的兩個分支裡都使用 return 語句,這将導緻編譯報錯 function ends without a return statement(你可以認為這是一個編譯器的 Bug 或者特性)。
if 可以包含一個初始化語句(如:給一個變量指派)。這種寫法具有固定的格式(在初始化語句後方必須加上分号):
if initialization; condition {
// do something
}
示例:
val := 10
if val > max {
// do something
}
二、switch 結構
相比較 C 和 Java 等其它語言而言,Go 語言中的 switch 結構使用上更加靈活。它接受任意形式的表達式:
switch var1 {
case val1:
...
case val2:
...
default:
...
}
變量 var1 可以是任何類型,而 val1 和 val2 則可以是同類型的任意值。類型不被局限于常量或整數,但必須是相同的類型;或者最終結果為相同類型的表達式。前花括号 { 必須和 switch 關鍵字在同一行。
您可以同時測試多個可能符合條件的值,使用逗号分割它們,例如:case val1, val2, val3。
每一個 case 分支都是唯一的,從上至下逐一測試,直到比對為止。( Go 語言使用快速的查找算法來測試 switch 條件與 case 分支的比對情況,直到算法比對到某個 case 或者進入 default 條件為止。)
一旦成功地比對到某個分支,在執行完相應代碼後就會退出整個 switch 代碼塊,也就是說您不需要特别使用 break 語句來表示結束。
是以,程式也不會自動地去執行下一個分支的代碼。如果在執行完每個分支的代碼後,還希望繼續執行後續分支的代碼,可以使用 fallthrough 關鍵字來達到目的。
是以:
switch i {
case 0: // 空分支,隻有當 i == 0 時才會進入分支
case 1:
f() // 當 i == 0 時函數不會被調用
}
并且:
switch i {
case 0: fallthrough
case 1:
f() // 當 i == 0 時函數也會被調用
}
在 case …: 語句之後,您不需要使用花括号将多行語句括起來,但您可以在分支中進行任意形式的編碼。當代碼塊隻有一行時,可以直接放置在 case 語句之後。
您同樣可以使用 return 語句來提前結束代碼塊的執行。當您在 switch 語句塊中使用 return 語句,并且您的函數是有傳回值的,您還需要在 switch 之後添加相應的 return 語句以確定函數始終會傳回。
可選的 default 分支可以出現在任何順序,但最好将它放在最後。它的作用類似與 if-else 語句中的 else,表示不符合任何已給出條件時,執行相關語句。
示例 switch1.go:
package main
import "fmt"
func main() {
var num1 int = 100
switch num1 {
case 98, 99:
fmt.Println("It's equal to 98")
case 100:
fmt.Println("It's equal to 100")
default:
fmt.Println("It's not equal to 98 or 100")
}
}
輸出:
It's equal to 100
我們也可以使用 switch 語句判斷從鍵盤輸入的字元(詳見第 12.2 節的 switch.go)。switch 語句的第二種形式是不提供任何被判斷的值(實際上預設為判斷是否為 true),然後在每個 case 分支中進行測試不同的條件。當任一分支的測試結果為 true 時,該分支的代碼會被執行。這看起來非常像鍊式的 if-else 語句,但是在測試條件非常多的情況下,提供了可讀性更好的書寫方式。
switch {
case condition1:
...
case condition2:
...
default:
...
}
例如:
switch {
case i < 0:
f1()
case i == 0:
f2()
case i > 0:
f3()
}
任何支援進行相等判斷的類型都可以作為測試表達式的條件,包括 int、string、指針等。
示例 switch2.go:
package main
import "fmt"
func main() {
var num1 int = 7
switch {
case num1 < 0:
fmt.Println("Number is negative")
case num1 > 0 && num1 < 10:
fmt.Println("Number is between 0 and 10")
default:
fmt.Println("Number is 10 or greater")
}
}
輸出:
Number is between 0 and 10
switch 語句的第三種形式是包含一個初始化語句:
switch initialization {
case val1:
...
case val2:
...
default:
...
}
這種形式可以非常優雅地進行條件判斷:
switch result := calculate(); {
case result < 0:
...
case result > 0:
...
default:
// 0
}
在下面這個代碼片段中,變量 a 和 b 被平行初始化,然後作為判斷條件:
switch a, b := x[i], y[j]; {
case a < b: t = -1
case a == b: t = 0
case a > b: t = 1
}
三、for 結構
檔案 for1.go 中示範了最簡單的基于計數器的疊代,基本形式為:
for 初始化語句; 條件語句; 修飾語句 {}
示例 for1.go:
package main
import "fmt"
func main() {
for i := 0; i < 5; i++ {
fmt.Printf("This is the %d iteration\n", i)
}
}
輸出:
This is the 0 iteration
This is the 1 iteration
This is the 2 iteration
This is the 3 iteration
This is the 4 iteration
由花括号括起來的代碼塊會被重複執行已知次數,該次數是根據計數器(此例為 i)決定的。循環開始前,會執行且僅會執行一次初始化語句
i := 0
;這比在循環之前聲明更為簡短。緊接着的是條件語句
i < 5
,在每次循環開始前都會進行判斷,一旦判斷結果為 false,則退出循環體。最後一部分為修飾語句
i++
,一般用于增加或減少計數器。
for 結構的第二種形式是沒有頭部的條件判斷疊代(類似其它語言中的 while 循環),基本形式為:for 條件語句 {}。您也可以認為這是沒有初始化語句和修飾語句的 for 結構,是以
;;
便是多餘的了。
package main
import "fmt"
func main() {
var i int = 5
for i >= 0 {
i = i - 1
fmt.Printf("The variable i is now: %d\n", i)
}
}
for-range 結構
這是 Go 特有的一種的疊代結構,您會發現它在許多情況下都非常有用。它可以疊代任何一個集合(包括數組和 map,詳見第 7 和 8 章)。文法上很類似其它語言中 foreach 語句,但您依舊可以獲得每次疊代所對應的索引。
要注意的是,val 始終為集合中對應索引的值拷貝,是以它一般隻具有隻讀性質,對它所做的任何修改都不會影響到集合中原有的值(譯者注:如果 val 為指針,則會産生指針的拷貝,依舊可以修改集合中的原值)。一個字元串是 Unicode 編碼的字元(或稱之為 rune)集合,是以您也可以用它疊代字元串:
for pos, char := range str {
...
}
四、Break 與 continue
for {
i = i - 1
fmt.Printf("The variable i is now: %d\n", i)
if i < 0 {
break
}
}
多層循環也可以
break+number
進行多層break
關鍵字 continue 忽略剩餘的循環體而直接進入下一次循環的過程,但不是無條件執行下一次循環,執行之前依舊需要滿足循環的判斷條件。
五、标簽與 goto
for、switch 或 select 語句都可以配合标簽(label)形式的辨別符使用,即某一行第一個以冒号(:)結尾的單詞(gofmt 會将後續代碼自動移至下一行)。
package main
import "fmt"
func main() {
LABEL1:
for i := 0; i <= 5; i++ {
for j := 0; j <= 5; j++ {
if j == 4 {
continue LABEL1
}
fmt.Printf("i is: %d, and j is: %d\n", i, j)
}
}
}
fcontinue 語句指向 LABEL1,當執行到該語句的時候,就會跳轉到 LABEL1 标簽的位置。
您可以看到當 j4 和 j5 的時候,沒有任何輸出:标簽的作用對象為外部循環,是以 i 會直接變成下一個循環的值,而此時 j 的值就被重設為 0,即它的初始值。如果将 continue 改為 break,則不會隻退出内層循環,而是直接退出外層循環了。另外,還可以使用 goto 語句和标簽配合使用來模拟循環。
package main
func main() {
i:=0
HERE:
print(i)
i++
if i==5 {
return
}
goto HERE
}
輸出:
01234
特别注意 使用标簽和 goto 語句是不被鼓勵的:它們會很快導緻非常糟糕的程式設計,而且總有更加可讀的替代方案來實作相同的需求。
如果您必須使用 goto,應當隻使用正序的标簽(标簽位于 goto 語句之後),但注意标簽和 goto 語句之間不能出現定義新變量的語句,否則會導緻編譯失敗。