Go基礎系列:流程控制結構

條件判斷結構:if else
分支選擇結構:switch case
循環結構:for
break:退出for或switch結構(以及select)
continue:進入下一次for迭代express

雖然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,則必須緊跟這兩個關鍵字。也就是說,上面的代碼結構中,大括號的使用位置是強制規範的,不能隨意換行放置。code

在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語句去檢測多個返回值的函數是否成功。string

但注意,通常有兩種判斷返回值:一種是ok類型,一種是err類型的錯誤信息。前者是布爾值,後者是代表錯誤信息的字符串,若是沒錯誤,則err爲nil。it

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\n", 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
    }
}
相關文章
相關標籤/搜索