中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Go語言基礎05-Go流程控制

發布時間:2020-08-06 14:17:19 來源:網絡 閱讀:311 作者:Leexide 欄目:編程語言

文章轉載請注明出處www.leexide.com
希望每一位尋求轉載的朋友都能夠按照要求進行,鼓勵原創,尊重原創。
微信公眾號:DevOps運維運營之家
QQ號碼:1045884038
E-mail:leexide@126.com
如有問題或建議,請關注微信公眾號
Go語言基礎05-Go流程控制

1 概述

流程控制是順序編程中必不可少的一部分,它是整個編程基礎的重要一環。在順序編程的流程控制部分,Go語言和其他主流語言有一些差別,主要體現在Go語言沒有do-while語句,因此for語句擁有更廣泛的含義與用途。另一方面switch語句也有一些擴展,例如支持類型判斷和初始化子語句等。

除了這些常見的流程控制語法的關鍵字,Go語言還有三個特殊的關鍵字,分別是:

  • defer:用于捕獲異常和資源回收等工作;
  • select:用于多分支選擇(配合通道使用);
  • go:用于異步啟動goroutine并執行特定函數。

2 條件語句

2.1 if判斷

例子:

package main

import "fmt"

func main() {
    a := 1
    if a < 20 {
        fmt.Printf("a小于20\n")
    }
    fmt.Printf("a的值是:%d\n", a)
}

2.2 if-else判斷

例子:

package main

import "fmt"

func main() {
    a := 100
    if a < 20 {
        fmt.Printf("a小于20\n")
    } else {
        fmt.Printf("a大于20\n")
    }
    fmt.Printf("a的值是:%d\n", a)
}

例子:

package main

import "fmt"

func main() {
    a := 100
    if a < 20 {
        fmt.Printf("a小于20\n")
        if a > 10 {
            fmt.Printf("a大于10\n")
        } else {
            fmt.Printf("a小于10\n")
        }
    } else {
        fmt.Printf("a大于20\n")
    }
    fmt.Printf("a的值是:%d\n", a)
} 
}

說明:
上面的代碼嵌套了一層if-else語句,這在編程規范中可以說大忌,特別是在邏輯復雜的情況下,嵌套if語句將非常影響性能,因此就有了else-if語句。

2.3 else-if判斷

else-if語句是在前面if-else語句之上再度擴展的,為了解決多重判斷的問題。例如:

package main

import "fmt"

func main() {
    a := 11
    if a > 20 {
        fmt.Printf("a大于20\n")
    } else if a < 10 {
        fmt.Printf("a小于10\n")
    } else {
        fmt.Printf("a大于10\n")
        fmt.Printf("a小于20\n")
    }
    fmt.Printf("a的值是:%d\n", a)
}

else-if語句可以連續使用多個else if關鍵字,例如判斷一個數字是否大于10小于20且不等于11:

package main

import "fmt"

func main() {
    a := 13
    if a > 20 {
        fmt.Printf("a大于20\n")
    } else if a < 10 {
        fmt.Printf("a小于10\n")
    } else if a == 11 {
        fmt.Printf("a等于11\n")
    } else {
        fmt.Printf("a大于10\n")
        fmt.Printf("a小于20\n")
        fmt.Printf("a不等于11\n")
    }
    fmt.Printf("a的值是:%d\n", a)
}

2.4 初始化子語句

if語句可以有一個子語句,用于初始化局部變量,例如:

package main

import "fmt"

func main() {
    if a := 10; a < 20 {
        fmt.Printf("a小于20\n")
    } else {
        fmt.Printf("a的值是:%d\n", a)
    }
}

注意1:
子語句只能有一個表達式,比如下面的例子是無法編譯的。

func main() {
    if b := 10; a := 10; a < 20 {  //編譯出錯,初始化子語句只能有一個表達式
        fmt.Printf("a小于20\n")
    } else {
        fmt.Printf("a的值是:%d\n", a)
    }
}

注意2:
a的值是在if代碼塊中定義的,所以不能在代碼塊之外調用,例如下面代碼也是無法編譯的:

func main() {
    if a := 10; a < 20 {
        fmt.Printf("a小于20\n")
    } 
    //編譯出錯,a是在if代碼塊中定義的,所以不能再函數中調用
    fmt.Printf("a的值是:%d\n", a)
}

3 選擇語句

在上面使用if條件語句時,一般用于二元判斷,對于多元判斷使用條件語句就會顯得煩瑣,所以就有了選擇語句。例如判斷一個數字屬于哪個區間就可以用選擇語句輕松實現,避免條件語句煩瑣的嵌套過程。

3.1 switch語句

在Go語言中,switch表示選擇語句的關鍵字,switch語句會根據初始化表達式得出一個值,然后根據case語句的條件,執行相應的代碼塊,最終返回特定內容。每個case被稱為一種情況,只有當初始化語句的值符合case的條件語句時,case才會被執行。

如果沒有遇到符合的case,則可以使用默認的case(default case),如果己經遇到了
符合的case,那么后面的case都不會被執行。

與其他編程語言不同的是,在Go語言編程中,switch有兩種類型。

  • 表達式switch:在表達式switch中,case包含與switch表達式的值進行比較的表達式。
  • 類型switch:在類型switch中,case包含與特殊注釋的switch表達式的類型進行比較的類型。
3.1.1 表達式switch

例子:

package main

import "fmt"

func main() {
    grade := "B"
    marks := 90

    switch marks {
    case 90:
        grade = "A"
    case 80:
        grade = "B"
    case 60, 70:
        grade = "C"
    default:
        grade = "D"
    }
    fmt.Printf("你的成績為%s\n", grade)
}

這種方式雖然簡單,但一般不這樣寫,因為不容易擴展這個選擇語句,例如把分數改為100或者91時,也會返回“你的成績為D”,這明顯不符合實際情況。這是因為選擇語句中沒有考慮值的范圍的問題,現在假設90分到100分為A, 80分到89分為B, 60分到79分為C ,低于60分則為D 。這種情況下上面的單值判斷己經不能滿足我們此時的需求。

因此就需要完整的switch 表達式寫法了:

package main

import "fmt"

func main() {
    grade := "E"
    marks := 90

    switch {
    case marks >= 90:
        grade = "A"
    case marks >= 80:
        grade = "B"
    case marks >= 70:
        grade = "C"
    case marks >= 60:
        grade = "D"
    default:
        grade = "E"
    }

    switch {
    case grade == "A":
        fmt.Printf("你的成績優秀!\n")
    case grade == "B":
        fmt.Printf("表現良好!\n")
    case grade == "C", grade == "D": //case表達式可以有多個
        fmt.Printf("再接再厲!\n")
    default:
        fmt.Printf("成績不合格!\n")
    }
    fmt.Printf("你的成績為%s\n", grade)
}

上面例子中有兩個switch相互關聯,第一個switch語句判斷成績所屬區間,并得出grade的值, 然后第二個switch根據grade的值返回相應的話語。

注意,每一個case都可以擁有多個表達式,它的含義與fallthrough一樣,例如下面兩種寫法是一樣的意思:

    /*  多表達式寫法   */
    case grade == "C", grade == "D": //case表達式可以有多個
        fmt.Printf("再接再厲!\n")
    /*  fallthrough寫法    */
    case grade == "C":
        fallthrough
    case grade == "D":
        fmt.Printf("再接再厲!\n")

fallthrough關鍵詞可以把當前case控制權交給下一個case語句判斷。

3.1.2 類型switch

類型switch語句針對變量類型判斷執行哪個case代碼塊,下面是一個簡單的例子:

package main

import "fmt"

var x interface{} //空接口

func main() {
    x = 1
    switch i := x.(type) { //這里表達式只有一句初始化子語句
    case nil:
        fmt.Printf("這里是nil,x的類型是%T", i)
    case int:
        fmt.Printf("這里是int,x的類型是%T", i)
    case float64:
        fmt.Printf("這里是float64,x的類型是%T", i)
    case bool:
        fmt.Printf("這里是bool,x的類型是%T", i)
    case string:
        fmt.Printf("這里是string,x的類型是%T", i)
    default:
        fmt.Printf("未知類型")
    }
}

類型switch的初始化子語句中需要判斷的變量必須是具有接口類型的變量(如果是固定類型的變量就沒有判斷的意義了)。在語法上類型switch與表達式switch沒有太大區別。

3.2 switch初始化語句

switch語句同樣擁有初始化子語句,和if一樣均是寫在關鍵字后面,只能有一句語句,例如:

    /* 單值判斷寫法    */
    switch marks := 90; marks {
    case 90:
        grade = "A"
    case 80:
        grade = "B"
    case 70:
        grade = "C"
    case 60:
        grade = "D"
    default:
        grade = "E"
    }

    /* 范圍表達式寫法   */
    switch marks := 90; { //這里的分號不能省略
    case marks >= 90:
        grade = "A"
    case marks >= 80:
        grade = "B"
    case marks >= 70:
        grade = "C"
    case marks >= 60:
        grade = "D"
    default:
        grade = "E"
    }

3.3 select語句

在Go語言中,除了switch語句,還有一種選擇語句一-select,這種選擇語句用于配合通道(channel)的讀寫操作,用于多個channel的并發讀寫操作。

switch是按順序從上到下依次執行的,而select是隨機選擇一個case來判斷,直到匹配其中的一個case,舉個例子:

package main

import "fmt"

func main() {
    a := make(chan int, 1024)
    b := make(chan int, 1024)

    for i := 0; i < 10; i++ {
        fmt.Printf("第%d次", i)
        a <- 1
        b <- 1

        select {
        case <-a:
            fmt.Println("from a")
        case <-b:
            fmt.Println("from b")
        }
    }
}

上述代碼的意思是,同時在ab中選擇,哪個有內容就從哪個讀,由于channel的讀寫操作是阻塞操作,使用select語句可以避免單個channel的阻塞。此外select同樣可以使用default代碼塊,用于避免所有channel同時阻塞。

4 循環語句

循環語句是編程中常使用的流程控制語句之一,在Go語言中,循環語句的關鍵字是for ,沒有while關鍵字。for語句可以根據指定的條件重復執行其內部的代碼塊,這個判斷條件一般是由for關鍵字后面的子語句給出的。

例如一個簡單的for循環例子:

package main

import "fmt"

func main() {
    for a := 0; a < 5; a++ {
        fmt.Printf("a的值是:%d\n", a)
    }
}

上面for關鍵字后面有三個子語句,初始化變量a0,并判斷當a小于5時執行下面代碼塊的內容,每次判斷a的值都加l,直到不符合初始化語句的判斷條件,進而退出循環。

4.1 for的子語句

for語句后面的三個子語句我們稱為:

  • 初始化子語句
  • 條件子語句
  • 后置子語句

這三者不能顛倒順序,其中條件子語句是必需的,條件子語句會返回一個布爾型,true 則執行代碼塊,false則跳出循環。

例如以下示例代碼中就省略了初始化子語句和后置子語句:

package main

import "fmt"

func main() {
    a := 0
    b := 5
    for a < b {
        a++
        fmt.Printf("a的值是:%d\n", a)
    }
}

在上面的例子中,for關鍵字后面只有一個a&lt; b的判斷語句,這是典型的條件判斷語
句,它實際上是三個子語句的簡寫:

for ; a < b ;
//Go語言編譯器會自動判斷三個子語句中是否存在條件子語句

//例如寫成這樣就會報錯
for ; ; a < b

后置子語句的意思就是先進行條件子語句判斷,for代碼塊執行之后再對條件變量操作的語句進行判斷,上面例子中的a++就是一個后置子語句。

4.2 range子語句

每一個for語句都可以使用一個特殊的range子語句,其作用類似于迭代器,用于輪詢數組或者切片值中的每一個元素,也可以用于輪詢字符串的每一個字符,以及字典值中的每個鍵值對,甚至還可以持續讀取一個通道類型值中的元素。

例子:

package main

import "fmt"

func main() {
    str := "abcz"

    for i, char := range str {
        fmt.Printf("字符串第%d個字符的值為%d\n", i, char)
    }
    for _, char := range str { //忽略第一個值(忽略index)
        println(char)
    }

    for i := range str { //忽略第二個值
        fmt.Println(i)
    }

    for range str { //忽略全部返回值,只執行下面代碼塊
        println("執行成功")
    }
}

range關鍵宇右邊是range表達式,表達式一般寫在for語句前面,以便提高代碼易讀性。像上面的例子可以寫成:

for i := range "abcz"
//把原來的str := "abcz"表達式寫到range關鍵字后面

這樣不僅降低了可讀性,也不容易管理后續的循環代碼塊。

range關鍵宇左邊表示的是一對索引-值對,根據不同的表達式返回不同的結果,詳見下表。

右邊表達式返回的類型 第一個值 第二個值
string index str[index],返回類型為rune
array/slice index str[index]
map key m[key]
channel element

從上表可以看出,除了輪詢字符串,還支持其他類型,例如數組,切片,字典甚至通道等等。

例子:

package main

import "fmt"

func main() {
    m := map[string]int{"a": 1, "b": 2}
    for k, v := range m {
        println(k, v)
    }

    numbers := []int{1, 2, 3, 4}
    for i, x := range numbers {
        fmt.Printf("第%d次,x的值為%d。\n", i, x)
    }
}

返回第一個值為索引值(鍵值),有時候并不是我們所需要的,因此可以使用“_”空標識符表示忽略第一個返回值。對于空字典或切片、空數組、空字符串等情況,for語句會直接結束,不會循環。

但如果需要指定for執行循環的次數,例如需要獲取數組(或者其他支持的類型)里面的值,而數組中有一個值是空字符串,則可以指定數組(或其他支持類型)長度,強制讓for循環執行相應次數,例如將上面的例子稍微改一下:

package main

import "fmt"

func main() {
    numbers := [5]int{1, 2, 3, 4}
    for i, x := range numbers {
        fmt.Printf("第%d次,x的值為%d。\n", i, x)
    }
}

由于定義了numbers長度為5 ,但numbers中只有4個值,因此最后一個為空值,從for循環返回的信息可以看到第5x的值為0,代碼塊的確執行了5次。

5 延遲語句

defer用于延遲調用指定函數,defer關鍵字只能出現在函數內部。
例子:

package main

import "fmt"

func main() {
    defer fmt.Println("World")
    fmt.Println("Hello")
}

上面例子會首先打印Hello,然后再打印World,因為第一句使用了defer關鍵字, defer語句會在函數最后執行,被延遲的操作是defer后面的內容。

defer后面的表達式必須是外部函數的調用,上面的例子就是針對fmt.Println 函數的延遲調用。defer有兩大特點:

  • 只有當defer語句全部執行,defer所在函數才算真正結束執行。
  • 當函數中有defer語句時,需要等待所有defer語句執行完畢,才會執行return語句。

因為defer的延遲特點,可以把defer語句用于回收資源、清理收尾等工作。使用defer語句之后,不用糾結回收代碼放在哪里,反正都是最后執行。

這里需要注意defer的執行時機,例如下面的例子:

package main

import "fmt"

var i = 0

func print() {
    fmt.Println(i)
}

func main() {
    for ; i < 5; i++ {
        defer print()
    }
}

上面例子返回的是55,這是因為每個defer都是在函數輪詢之后,最后才執行,此時i的值當然就是5了。如果要正確反向打印數字則應該這樣寫:

package main

import "fmt"

var i = 0

func print(i int) {
    fmt.Println(i)
}

func main() {
    for ; i < 5; i++ {
        defer print(i)
    }
}

上面例子引入了函數參數,雖然還沒介紹函數的概念,但是不妨礙理解這里面的defer關鍵知識。這里之所以是一個反序的數字列表,是因為defer其實是一個棧,遵循先入后出,或者理解為后進先出。

i等于0 時,defer語句第一次被壓棧,此時defer后面的函數返回0; i不斷自增,一直到i等于4時,defer語句第5次入棧,defer后的函數返回4;此時i的自增不再滿足for條件,于是跳出循環,在結束之前,Go語言會根據defer后進先出原則逐條打印棧內的數值,于是就出現現在看到的結果了。

簡單來說就是當一個函數內部有多個defer語句時,最后面的defer語句最先執行(當然是指在所有defer語句中) 。

6 標簽

在Go語言中,有一個特殊的概念就是標簽,可以給forswitchselect等流程控制代碼塊打上一個標簽,配合標簽標識符可以方便跳轉到某一個地方繼續執行,有助于提高編程效率。標簽格式如下:

L1:
    for i := 0; i <= 5; i++ {
        //代碼塊
    }

//下面寫法也可以,不過Go語言編譯器會自動格式化為上面的格式
L2:switch i {
    //代碼塊
}

標簽的名稱是區分大小寫的,為了提高代碼易讀性,建議標簽名稱使用大寫字母和數字。標簽可以標記任何語句, 并不限定于流程控制語句,未使用的標簽會引發錯誤。

6.1 break

break語句意思是打斷,這個語句表示打斷當前流程控制。
例子:

package main

import "fmt"

func main() {
    for i := 0; i < 10; i++ {
        fmt.Printf("i的值是:%d\n", i)
        if i > 4 {
            break
        }
    }
}

上面例子中break的用法與主流編程語言并無差別,當i &gt;4時,break語句打斷了后續的循環操作,跳出循環。

但是對于嵌套流程控制語句,例如下面這種情況:

package main

import "fmt"

func main() {
    for {
        x := 1
        switch {
        case x > 0:
            fmt.Println("A")
            break
        case x == 1:
            fmt.Println("B")
        default:
            fmt.Println("C")
        }
    }
}

上面的例子會一直返回A,無限循環。上面代碼出現的break語句只是跳出了switch流程,并沒有跳出for循環,所以這個程序會一直執行下去。

為了跳出指定的流程控制代碼塊,就需要標簽出場了:

package main

import "fmt"

func main() {
LOOP1:
    for {
        x := 1
        switch {
        case x > 0:
            fmt.Println("A")
            break LOOP1
        case x == 1:
            fmt.Println("B")
        default:
            fmt.Println("C")
        }
    }
}

上面代碼中的break LOOPl表示跳出到標簽為LOOPl的代碼塊之外。

6.2 continue

break相反,continue用于跳轉到指定代碼塊位置繼續執行任務,continue 僅能用于for循環。例如與上面幾乎一樣的代碼,只是改動了一個關鍵宇:

package main

import "fmt"

func main() {
LOOP1:
    for i := 0; i <= 5; i++ {
        switch {
        case i > 0:
            fmt.Println("A")
            continue LOOP1
        case i == 1:
            fmt.Println("B")
        default:
            fmt.Println("C")
        }
        fmt.Printf("i is:%d\n", i)
    }
}

上面代碼中因為使用了continue語句跳轉到了外圍for循環中,與break不同的是,continue表示跳轉后繼續執行操作。

6.3 goto

Go語言中的goto語句可以無條件跳轉到相同函數中的帶標簽語句。標簽、goto等關鍵字都并非Go語言獨創,Go語言可以說是一門大量參考了其他語言優點的編程語言,在流程控制上做了一些擴增(如類型switch),同時也減少了一些關鍵宇(如while) 。

package main

func main() {
    var i int
    for {
        println(i)
        i++
        if i > 2 {
            goto BREAK
        }
    }
BREAK:
    println("break")
}

上面代碼中,goto語句指向了BREAK標簽,所以當循環中遇到i&gt;2時,直接跳轉打印breakgoto只能在同一個函數中跳轉。

下面的是我的公眾號二維碼,歡迎關注。文章轉載請注明出處www.leexide.com
Go語言基礎05-Go流程控制

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

长治市| 偃师市| 大埔县| 安徽省| 无锡市| 镇安县| 竹北市| 广丰县| 健康| 陕西省| 九龙坡区| 南丰县| 大名县| 道真| 东兴市| 民丰县| 苍山县| 鄯善县| 汉阴县| 夏邑县| 沧州市| 建始县| 广昌县| 贵德县| 鄯善县| 丽水市| 北安市| 兴国县| 汶上县| 平果县| 兖州市| 丁青县| 揭阳市| 原阳县| 四平市| 宝清县| 太谷县| 当雄县| 凤台县| 吉木乃县| 蕲春县|