當(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ù)據(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ǔ)句中,如果statement 在if中,則statement中變量作用范圍是該 if 塊中,一旦離開(kāi)if,就無(wú)法訪問(wèn)。而 statement 在if 外,則上下文對(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)。