4. Go 程序結(jié)構(gòu)控制

當(dāng)編寫一個(gè)有一丟丟難度的程序的時(shí)候,就一定會(huì)涉及到程序結(jié)構(gòu)(流向)的控制:需要一步一步順序執(zhí)行;需要根據(jù)不同情況進(jìn)行選擇,運(yùn)行不同的代碼;對(duì)相同的重復(fù)的事情,循環(huán)重復(fù)N次;兩段代碼同時(shí)運(yùn)行,要可能要修改某一個(gè)變量值,但到底誰(shuí)可以在什么時(shí)間、什么情況改需要精細(xì)控制。

在Go中,為了完成上述程序執(zhí)行邏輯的控制,需要用的if, for, switch, func, select等內(nèi)容,逐個(gè)來(lái)看。

順序結(jié)構(gòu)

Go 默認(rèn)會(huì)按照順序結(jié)構(gòu)執(zhí)行,即按照 main() 函數(shù)的語(yǔ)句的順序,一條一條執(zhí)行,直到執(zhí)行碰到return 正常退出程序,或遇到程序異常,異常退出程序。當(dāng)程序執(zhí)行到函數(shù)main() 中遇到另外一個(gè)函數(shù)時(shí),系統(tǒng)就會(huì)將main 函數(shù)的執(zhí)行狀態(tài)保存到一個(gè)叫”?!暗臄?shù)據(jù)結(jié)構(gòu)中,跳出main函數(shù),去執(zhí)行函數(shù)FuncA,在FuncA中遇到函數(shù) FuncB 也一樣。等函數(shù)FuncB 執(zhí)行完成后,會(huì)返回FuncA,系統(tǒng)會(huì)從棧中找到FuncA 的信息并恢復(fù),繼續(xù)執(zhí)行FuncA,知道FuncA返回,然后需要執(zhí)行 main() 函數(shù),直到 main() 完成,"交還"系統(tǒng)控制權(quán)。

函數(shù)調(diào)用結(jié)構(gòu).png

棧是一種數(shù)據(jù)結(jié)構(gòu),具有”先進(jìn)后出“(FILO)這樣的特性,更多細(xì)節(jié)可以參考維基百科 。

選擇結(jié)構(gòu)

當(dāng)任務(wù)遇到不同的情況需要不同處理時(shí),就需要用到 if 語(yǔ)句;當(dāng)然還有其一家兄弟if..else if...else

if condition{

    /* true logic code */

} // condition滿足就執(zhí)行,否則不執(zhí)行,執(zhí)行下面的。
//最為常見(jiàn)的是錯(cuò)誤處理,如果有錯(cuò)誤就進(jìn)行錯(cuò)誤處理,沒(méi)錯(cuò)誤就繼續(xù)

if condition{   

    /* true logic code */

}else {

    /* false logic code */

}// 條件滿足就執(zhí)行true邏輯,否則執(zhí)行false邏輯。

if conditionA{

    /* A condition */ 

}else if conditionB{

    /*B condition */    

}else if conditionC{

    /*C condition */    

}else{

    /* other condition*/

}// 多條件就從上往下逐個(gè)執(zhí)行,如果條件都不滿足,就執(zhí)行else。 
//還記得90-100評(píng)A,80-90評(píng)B,...嗎?

if else 除了上述用法以外,還可以嵌套執(zhí)行,就是如下的效果,用于復(fù)雜的條件判定。

if conditionA{
    if conditonA.1 {
        //A.1
    }else {
        //A.x(x!=1)
    }
}else{
    // !A
}

上述特性,其他語(yǔ)言一般都會(huì)提供,Go中還提供一種方式是if statement;conditon{} 其中 statement 是一個(gè)簡(jiǎn)短的聲明或賦值,用于獲取condition變量的值。與statement; if conditon{} 區(qū)別是什么呢?……作用域。作用域是一種表明變量或函數(shù)等可見(jiàn)范圍與時(shí)間的描述。剛剛if語(yǔ)句中,如果statementif中,則statement中變量作用范圍是該 if 塊中,一旦離開(kāi)if,就無(wú)法訪問(wèn)。而 statementif 外,則上下文對(duì)其變量都可以看到或修改。Go 中有一個(gè)原則,與含義不相關(guān)的簡(jiǎn)單變量使用簡(jiǎn)短名稱定義,并且其作用域最小,這樣就可以多處重復(fù)利用這些簡(jiǎn)短名稱,且不會(huì)影響程序執(zhí)行。最為常見(jiàn)到例子就是錯(cuò)誤狀態(tài)判斷,在數(shù)據(jù)類型中map一節(jié)中,我們知道查詢一個(gè)key會(huì)返回兩個(gè)值,一個(gè)值本身,另外一個(gè)是值的狀態(tài)。一個(gè)大的程序中有很多錯(cuò)誤情況要判斷,此時(shí)都可以通過(guò) if value, ok := mapName[key];!ok{/*do something*/}

多選擇結(jié)構(gòu)

除了if...else外,還有一種選擇結(jié)構(gòu)叫 switch ,它通過(guò)用于很多個(gè)情況的選擇,用來(lái)替代復(fù)雜的if...else if...elseif...else 。switch 一般情況下,是后面跟有一個(gè)變量,case condition 對(duì)變量取值進(jìn)行判斷,滿足則執(zhí)行,執(zhí)行完當(dāng)前條件,退出 switch; 如果除 default 外的條件都不滿足,那就執(zhí)行defalut語(yǔ)句,如果沒(méi)有defalut語(yǔ)句,那就直接跳過(guò) switch。如果一個(gè)語(yǔ)句可以應(yīng)對(duì)多個(gè)condition,則可以使用逗號(hào),將所有滿足條件的羅列出來(lái)。

switch V{
    case conditonA:
        //statement
    case conditonB, conditonC, conditionD:
        //statemtnt
    case conditionE:
        // statement
    default:
        // statement
}

switch 還有一種特殊情況,就是沒(méi)有變量 v ,那么變量可以直接在condition中表達(dá),就更像if else。

如果執(zhí)行完第一個(gè)case,還想繼續(xù)向下判斷,那么可以通過(guò) fallthrough 來(lái)繼續(xù)測(cè)試后面的case條件,滿足則再次執(zhí)行

// 成績(jī)score
switch {
    case score < 60 && score >= 60:
        fmt.Printf("不及格,D\n")
    case score >= 60:
        fmt.Printf("及格,")
        fallthrough
    case score >=70 && score < 80  :
        fmt.Println("C\n")
    case score >=80 && score < 90:
        fmt.Println("B\n")
    case score >=90 && score <= 100:
        fmt.Println("A\n")
    default:
        fmt.Println("錄錯(cuò)成績(jī)了,別激動(dòng)!")
}

循環(huán)結(jié)構(gòu)

循環(huán),就是重復(fù)執(zhí)行某一個(gè)需要重復(fù)的工作,例如一堆數(shù)據(jù)中找最大,或者單純的想看看這個(gè)數(shù)據(jù)集。如果循環(huán)沒(méi)沒(méi)有退出條件的限制,最簡(jiǎn)單后果是CPU使用率一直很高,或者內(nèi)存被消耗盡,也或者你設(shè)計(jì)良好,它默默的一直干活,總之設(shè)計(jì)循環(huán)退出條件是一件聰明的事情,可以在任意情況下選擇退出,盡管你可以把退出條件設(shè)置的比較隱蔽。

Go 循環(huán)只有一種叫做for, 沒(méi)有其他。for 結(jié)構(gòu):

for init;condition;post{
    /* do something */ 
}

其中 init為變量初始化條件,如果if 一樣,在for 結(jié)構(gòu)聲明的變量,只能在該塊中調(diào)用,初始化條件只執(zhí)行一次;在初始化完成后,for 開(kāi)始判斷condition 是否滿足,如果條件滿足,則執(zhí)行循環(huán)塊;執(zhí)行完循環(huán)后執(zhí)行post 語(yǔ)句。然后繼續(xù)檢查條件是否滿足,重復(fù)下去,直到條件不滿足,退出循環(huán)條件。如果初始化條件聲明在for 之外,初始化條件可以跳過(guò);如果post語(yǔ)句在循環(huán)中執(zhí)行,那么post 也可以省略;如果看到下面的循環(huán)中斷關(guān)鍵字 break,可以用來(lái)手動(dòng)判斷是否退出,那么condition 也可以省略。

中斷循環(huán)的關(guān)鍵字 break ,當(dāng)程序執(zhí)行到循環(huán)中的break 語(yǔ)句時(shí),循環(huán)退出

跳出當(dāng)前循環(huán)的關(guān)鍵字 continue, 當(dāng)程序執(zhí)行到循環(huán)中的 continue 時(shí),循環(huán)剩下的語(yǔ)句將被跳過(guò),執(zhí)行post,開(kāi)始下一次循環(huán),計(jì)算循環(huán)條件。

package main

import  "fmt"

func main() {
    for i := 1; i <= 5; i++ {
        for j := 1; j <= i; j++ {
            if i == 4 && j == 2 {
                fmt.Printf("\t\t") //當(dāng)碰到i=4,j=2的時(shí)候,則打印兩個(gè)制表符
                continue // 跳出本地循環(huán),即不打印下面 2x4=8這一項(xiàng)
            }
            fmt.Printf("%d x %d = %d\t", j, i, i*j)
        }
        fmt.Println()
    }
}
/* another
內(nèi)部for循環(huán)還可以寫為
--------------version 1-------------------
for i := 1; i <= 5; i++ {
        j := 1
        for ; j <= i; j++ { // 雖然省略了init,但是分號(hào)不可省,注意
            if i == 4 && j == 2 {
                fmt.Printf("\t\t")
                continue
            }
            fmt.Printf("%d x %d = %d\t", j, i, i*j)
        }
        fmt.Println()
    }
---------------version 2 -------------------
for i := 1; i <= 5; i++ {
        j := 1
        for j <= i {// 支持只有條件的for
            if i == 4 && j == 2 {
                fmt.Printf("\t\t")
                continue
            }
            fmt.Printf("%d x %d = %d\t", j, i, i*j)
            j++ // 還是要更新條件變量,否則真的時(shí)一直循環(huán),直到死機(jī)或被系統(tǒng)強(qiáng)制結(jié)束
        }
        fmt.Println()
    }
--------------version 3 ------------------------
for i := 1; i <= 5; i++ {
        j := 1
        for { // 如果沒(méi)有條件就一直循環(huán),直到
            if j > i {
                break // 直到碰到break,break 可以通過(guò)if設(shè)置,如果沒(méi)有if,那就沒(méi)有循環(huán)
            }
            if i == 4 && j == 2 {
                fmt.Printf("\t\t")
                continue
            }
            fmt.Printf("%d x %d = %d\t", j, i, i*j)
            j++
        }
        fmt.Println()
    }
/* Result
1 x 1 = 1
1 x 2 = 2       2 x 2 = 4
1 x 3 = 3       2 x 3 = 6       3 x 3 = 9
1 x 4 = 4                       3 x 4 = 12      4 x 4 = 16
1 x 5 = 5       2 x 5 = 10      3 x 5 = 15      4 x 5 = 20      5 x 5 = 25
*/

需要注意的是,break只能退出當(dāng)前循環(huán),對(duì)于如上面嵌套循環(huán),如果全部退出循環(huán),則需要與嵌套循環(huán)數(shù)量完全一致的break語(yǔ)句。

這是一般程序結(jié)構(gòu)控制,對(duì)于并發(fā)或者說(shuō)多線程程序控制,更加復(fù)雜且難以預(yù)測(cè)。好消息是,Go 有原生并發(fā)支持,但不會(huì)再這一節(jié)展開(kāi)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容