條件判斷結構:if else
分支選擇結構:switch case
迴圈結構:for
break:退出for或switch結構(以及select)
continue:進入下一次for迭代
雖然Go是類C的語言,但Go在這些流程控制語句中的條件表示式部分不使用括號。甚至有些時候使用括號會報錯,但有些複雜的條件判斷需要使用括號改變優先順序。
如:
if (name == "longshuai" && age > 23) || (name == "xiaofang" && age < 22) {
print("yeyeye!!!")
}
if語句
if condition1 {
// do something
} else if condition2 {
// do something else
} else {
// catch-all or default
}
注意,Go對語法要求很嚴格。左大括號{
必須和if、else或else if在同一行,右大括號}
必須換行,如果有else或else if,則必須緊跟這兩個關鍵字。也就是說,上面的程式碼結構中,大括號的使用位置是強制規範的,不能隨意換行放置。
在Go中,if語句的condition前面可以加上初始化語句,例如Go中很常見的:
if val := 10; val > max {
// do something
}
它在一定程度上等價於:
val := 10
if val > max {
// do something
}
但注意,前面簡寫的方式中,val
的作用域只在if範圍內,if外面無法訪問這個val。如果在if語句之前已經定義了一個val,那麼這個val將被if中的val掩蓋,直到if退出後才恢復。
func main() {
val := 20
if val := 10; val > 3 {
println("true")
}
println(val) // 輸出20
}
一種解決方式是if中的初始化語句不要使用:=
,而是直接使用=
,但這樣會修改原始的值。
func main() {
val := 20
if val = 10; val > 3 {
println("true")
}
println(val) // 輸出10
}
在Go中,經常使用兩個(或多個)返回值的函式,一個返回值作為值,另一個作為布林型別的判斷值,或者作為錯誤資訊。通常會使用if語句去檢測多個返回值的函式是否成功。
但注意,一般有兩種判斷返回值:一種是ok型別,一種是err型別的錯誤資訊。前者是布林值,後者是表明錯誤資訊的字串,如果沒錯誤,則err為nil。
value,ok := func_name()
if !ok {
// func_name執行錯誤
os.Exit(1)
}
value,err := func_name()
if err != nil {
// func_name執行錯誤
os.Exit(1)
// 或 return err
}
將上面的簡寫一下,得到更常見的判斷方式:
if value,ok := func_name();ok {
// ok為true,函式執行成功
} else {
// ok為false,函式執行失敗
os.Exit(1)
}
if value,err := func_name();err != nil {
// err不為nil,說明出現錯誤
return err
//或os.Exit(1)
} else {
// err為空,說明執行正確
}
switch語句
switch語句用於提供分支測試。有兩種swithc結構:expression switch和type switch,本文暫時只介紹expression switch,它用於判斷表示式是否為true。
對於expression switch,也有三種形式:等值比較、表示式比較、初始化表示式。
等值比較結構:當var1的值為val1時,執行statement1,當var1的值為val2時,執行statement2,都不滿足時,執行預設的語句statement。
switch var1 {
case val1:
statement1
case val2:
statement2
default:
statement
}
等值比較侷限性很大,只能將var1和case中的值比較是否相等。如果想比較不等,或者其它表示式型別,可以使用下面的表示式比較結構。
表示式比較結構:評估每個case結構中的condition,只要評估為真就執行,然後退出(預設情況下)。
switch {
case condition1:
statement1
case condition2:
statement2
default:
statement
}
初始化表示式:可以和if一樣為switch加上初始化表示式,同樣作用域只在switch可見。但注意,initialization後面記得加上分號”;”結尾。見下文示例。
switch initialization; { // 不要省略分號
case condition1:
statement1
case condition2:
statement2
defautl:
statement
}
default
是可選的,且可以寫在switch的任何位置。
如果case中有多個要執行的語句,可以加大括號,也可以不加大括號。當只有一個語句的時候,statement可以和case在同一行。
case中可以提供多個用於測試的值,使用逗號分隔,只要有一個符合,就滿足條件:
switch var1 {
case val1,val2,val3:
statement1
case val4,val5:
statement2
default:
statement
}
例如:
val := 20
switch val {
case 10, 11, 15:
println(11, 15)
case 16, 20, 22: // 命中
println(16, 20, 22)
default:
println("nothing")
}
即使是表示式比較結構,也一樣可以使用逗號分隔多個表示式,這時和使用邏輯或”||”是等價的:
func main() {
val := 21
switch {
case val % 4 == 0:
println(0)
case val % 4 == 1, val % 4 == 2: //命中
println(1, 2)
default:
println("3")
}
}
預設情況下case命中就結束,所以所有的case中只有一個會被執行。但如果想要執行多個,可以在執行完的某個case的最後一個語句上加上fallthrough
,它會無條件地直接跳轉到下一條case並執行,如果下一條case中還有fallthrough,則相同的邏輯。此外,fallthrough的後面必須只能是下一個case或default,不能是額外的任何語句,否則會報錯。
例如:
func main() {
val := 21
switch val % 4 {
case 0:
println(0)
case 1, 2: // 命中
println(1, 2) // 輸出
fallthrough // 執行下一條,無需條件評估
// println("sd") //不能加此行語句
case 3:
println(3) // 輸出
fallthrough // 執行下一條,無需條件評估
default:
println("end") // 輸出
}
}
執行結果為:
1 2
3
end
fallthrough
一般用於跳過某個case。例如:
swtich i {
case 0: fallthrough
case 1: statement1
default: statement
}
它表示等於0或等於1的時候都執行statement1。這和前面case中多個評估值的功能是一樣的。
以下是一個初始化表示式結構的switch示例:
func main() {
val := 21
switch val := 23; {
case val % 4 == 0:
println(0,val)
case val % 4 == 1 || val % 4 == 2:
println(1, 2,val)
default: // 命中
println(3,val) // 輸出"3 23"
}
println(val) // 輸出21
}
for語句
Go中只有一種迴圈結構:for。
普通格式的for
// 完整格式的for
for init; condition; modif { }
// 只有條件判斷的for,實現while的功能
// 要在迴圈體中加上退出條件,否則無限迴圈
for condition { }
例如:
// 完整格式
func main() {
for i := 0; i < 5; i++ {
fmt.Println(i)
}
}
// 只有條件的格式
func main() {
var i int = 5
for i >= 0 {
i = i - 1
fmt.Printf(i)
}
}
無限迴圈
好幾種方式實現for的無限迴圈。只要省略for的條件判斷部分就可以實現無限迴圈。
for i := 0;;i++
for { }
for ;; { }
for true { }
無限迴圈時,一般在迴圈體中加上退出語句,如break、os.Exit、return等。
for range遍歷
range關鍵字非常好用,可以用來迭代那些可迭代的物件。比如slice、map、array,還可以迭代字串,甚至是Unicode的字串。
for index,value := range XXX {}
但千萬注意,value是從XXX中拷貝的副本,所以通過value去修改XXX中的值是無效的,在迴圈體中應該總是讓value作為一個只讀變數。如果想要修改XXX中的值,應該通過index索引到源值去修改(不同型別修改的方式不一樣)。
以迭代字串為例。
func main() {
var a = "Xiaofang,你好"
for index,value := range a {
println(index,string(value))
}
}
輸出結果:
0 X
1 i
2 a
3 o
4 f
5 a
6 n
7 g
8 ,
9 你
12 好
可見,在迭代字串的時候,是按照字元而非位元組進行索引的。
下面通過value去修改slice將無效。
func main() {
s1 := []int{11,22,33}
for index,value := range s1 {
value += 1 // 只在for結構中有效
fmt.Println(index,value)
}
fmt.Println(s1) // for外面的結果仍然是[11 22 33]
}
要在迴圈結構中修改slice,應該通過index索引的方式:
func main() {
s1 := []int{11,22,33}
for index,value := range s1 {
value += 1
s1[index] = value
fmt.Println(index,value)
}
fmt.Println(s1) // [12 23 34]
}
break和continue
breake用於退出當前整個迴圈。如果是巢狀的迴圈,則退出它所在的那一層迴圈。break除了可以用在for迴圈中,還可以用在switch結構或select結構。
continue用於退出當前迭代,進入下一輪迭代。continue只能用於for迴圈中。
標籤和goto
當某一行中第一個單詞後面跟一個冒號的時候,Go就認為這是一個標籤。例如:
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
", i, j)
}
}
}
使用標籤能讓break、continue以及goto跳轉到指定的位置繼續往下執行。例如這裡的continue LABEL1
,當j == 4
的時候,就直接跳到外層迴圈進入下一輪迭代。而break LABEL
則指定直接退出LABEL所在的那一層迴圈。
goto懶得介紹了,反正沒人用,也強烈不建議使用,甚至標籤都建議不要使用。一般能使用LABEL或goto的結構,都能改寫成其它更好的語句。
空語句塊
Go中支援空block{}
,這個大括號有自己的作用域,裡面的程式碼只執行一次,退出大括號就退出作用域。
func main() {
{
v := 1
{
v := 2
fmt.Println(v) // 輸出2
}
fmt.Println(v) // 輸出1
}
}