1.概述
代碼的執(zhí)行順序,有 3 大類,為:
- 順序,從上到下依次執(zhí)行,基礎流程(核心流程)
- 循環(huán),某段代碼重復執(zhí)行。
- 分支,選擇性執(zhí)行某段代碼。
2.流程圖回顧
使用如下的典型圖例,描述流程節(jié)點:
-
橢圓,表示開始和結束:
-
矩形,表示某個具體的進程步驟:
-
菱形,表示條件判斷:
3. 分支if
1) 語法
- 語法 1,只有 if 語句
// 語法 1,條件成立,執(zhí)行語句塊內(nèi)容
if condition {
// 語句塊
}
- 語法 2,if + else
// 語法 2,條件成立,執(zhí)行 if 語句塊,否則執(zhí)行 else 語句塊
if condition {
// if 語句塊
} else {
// else 語句塊
}
- 語法 3,else if
// 語法 3,條件 1 成立,執(zhí)行語句塊 1 然后結束,若條件 1 不成立,繼 續(xù)判斷條件 2,若成立執(zhí)行語句塊 2 然后結束。直到全部條件判斷完畢。
condition1, condition2, conditionN := true, true, true
if condition1 {
/ / 語句塊 1
} else if condition2 {
// 語句塊 2
} else if conditionN {
// 語句塊 N
}
- 語法 4,else if else
// 語法 4,與語法 3 一致,多一個 else 語句塊,當全部條件不滿足時,執(zhí)行 else 語句塊
if condition1 {
// 語句塊 1
} else if condition2 {
// 語句塊 2
} else if conditionN {
// 語句塊 N
} else {
// else 語句塊
}
- 條件表達式為布爾表達式
條件表達式一定是可以返回布爾值的表達式。不能直接判斷 0,”” 這種非布爾類型數(shù)
據(jù),沒有自動向布爾值轉(zhuǎn)換的過程,下面的語法是不對的:
if 0 {
// non-bool 0 (type int) used as if condition
fmt.Println("if statement block")
}
- 支持條件初始化語句
在 if 后,支持先完成條件初始化,再去完成條件表達式的語法,結構為:
if 條件初始化語句;條件表達式 {
}
if condition := 10>8; condition {
fmt.Println("使用條件初始化語句")
fmt.Println("條件成立")
}
注意:以上結構中,條件的是否成立,僅僅與條件表達式相關,與條件初始化語句無關。
if c:=10<8;true{
fmt.Println(c)
fmt.Println("使用條件初始化語句")
fmt.Println("條件成立")
}
// false
// 使用條件初始化語句
// 條件成立
注意:使用條件初始化的意思,主要是為了將判斷條件變量的作用域(有效區(qū)域)限定在 if 語句 范圍內(nèi)(縮小變量有效作用域策略), condition2,在 if 外無效。
if condition2 := 10>8; condition2 {
fmt.Println("使用條件初始化語句")
fmt.Println("條件成立")
fmt.Println(condition2)
}
fmt.Println(condition2) //undefined: condition2
4.分支 switch
1) if 和 switch 的差異?
if,條件分支,利用條件表達式確定條件是否成立。
switch,狀態(tài)分支,利用狀態(tài)值確定分支條件。
代碼演示:
// score >= 80 就是條件
score := 98
if score >= 90 {
fmt.Println("A")
} else if score >= 80 {
fmt.Println("B")
} else if score >=60 {
fmt.Println("C")
} else {
fmt.Println("呵呵")
}
// int,string,bool,就是特定的狀態(tài)!
switch d := 10; d {
case 9 :
fmt.Println(9)
case 10 :
fmt.Println(10)
case 11 :
fmt.Println(11)
default:
fmt.Println("default")
}
2) 語法,switch case default
value := 4
switch value {
default:
fmt.Println("six")
case 1:
fmt.Println("one")
case 2:
fmt.Println("two")
//...
case 3, 4, 5: // value == 3 || value == 4 || value == 5
fmt.Println("three, four, five") }
執(zhí)行流程是,判斷 switch 表達式的值與 case 給定的狀態(tài)是否相等,相等則執(zhí)行對應的語句 塊,然后 switch 結束。
注意,多個狀態(tài)值,可以在一個 case 中描述。
注意,若沒有任何 case 匹配,則會執(zhí)行 default 分支。前提是存在 default 分支。(default 分
支是可選的)。default 的位置無關。
在 switch 表達式中,同樣支持初始化表達式。 支持:
switch 初始化表達式; switch 表達式 {
}
目的是將條件分支值變量的有效作用域控制在 switch 語句的范圍內(nèi),避免全局污染!
代碼演示:
switch value := 4; value {
default:
fmt.Println("six")
case 1:
fmt.Println("one")
case 2:
fmt.Println("two")
//...
case 3, 4, 5: // value == 3 || value == 4 || value == 5
fmt.Println("three, four, five")
}
fmt.Println(value) //undefined: value
格外注意的是,switch 的 switch 表達式是也可以省略的。(if 的條件表達式是不能省略的。) 省略后,case 應該使用條件語句完成分支判斷,不能再使用狀態(tài)值了,因為沒有值可以用
于判定了。
switch value := 4; {
default:
fmt.Println(value)
fmt.Println("six")
case value == 1:
fmt.Println("one")
case value == 2:
fmt.Println("two")
case value == 3 || value == 4 || value == 5:
fmt.Println("three, four, five")
}
3) fallthrough,穿越到下個 case 塊
go 的 switch case 在滿足了一個 case 后,執(zhí)行對應的語句塊,之后 swtich 直接結束。
與很多其他語言不通的是,很多其他語句的 swtich 當 case 滿足后會依次執(zhí)行余下的 全部語句塊。
為了在語法上與其他語言類似,go 提供了 fallthrough 語句,用于在 case 語句塊間穿越。 當 case 語句塊的最后的語句為 fallthrought 時,會執(zhí)行下一個 case 語句塊:
switch value := 2; value {
default:
fmt.Println("six")
case 1:
fmt.Println("one")
case 2:
fmt.Println("two")
fallthrough //
//...
case 3, 4, 5: // value == 3 || value == 4 || value == 5
fmt.Println("three, four, five")
//fallthrough // 最后一個語句塊中不能存在 fallthrough
}
// 輸出 two
// three, four, five
注意:
fallthrough 只能是語句塊的最后一條語句。
而且最后一個語句塊中不能存在 fallthrough。
若執(zhí)行的語句塊不存在 fallthrough,會停止穿越!
4) switch interface.(type) 結構,type switch
該結構可以完成類型檢測,執(zhí)行對應的代碼。
// 類型檢測
var x interface{}
//x = 42
x = "hank"
//x = false
//
switch d := x.(type) {
case int:
fmt.Println(d, d+42)
case string:
fmt.Println(d, d + " GoLang")
//fallthrough //cannot fallthrough in type switch
case bool:
fmt.Println(d, !d)
}
該結構也是 swtich 的語法。
特殊性在于: 不支持 falltrhrough。
5. 循環(huán) for
1) 概述
基于循環(huán)條件,重復執(zhí)行代碼塊的結構。 流程圖上,通常是一個閉合的環(huán)。
語法上使用 for 完成循環(huán)結構。(go 語言中沒有 while 結構循環(huán))
2) 基本語法
for 條件初始化語句;條件表達式;條件更新語句{
//循環(huán)體
}
for i:=0; i<10; i++ {
fmt.Println(i)
}
for 存在三個循環(huán)條件部分,初始化,條件判斷,條件變化。
若 循環(huán)體執(zhí)行了 N 次,那么:
- 條件初始化執(zhí)行 1 次。
- 條件判斷執(zhí)行 N+1 次。
- 條件變化執(zhí)行 N 次。
可見,應該將條件數(shù)據(jù)放在初始化中完成,避免重復執(zhí)行,例如,當使用 i 作為索引, 遍歷數(shù)組或切片的時候,語法為:
arr := [...]int{42, 1024, 365, 2048, 1, 36, 88, 66}
for i, l := 0, len(arr); i<l; i++ {
fmt.Println(i, arr[i])
}
// 不建議的語法
for i := 0; i<len(arr); i++ {
fmt.Println(i, arr[i])
}
3) 條件循環(huán)語法(其他語言的 while 語法)
for 條件表達式 {
}
result, n := 0, 0
for result <= 100 {
n ++
result += n
}
fmt.Println(result)
條件的數(shù)據(jù)依賴于外部數(shù)據(jù)完成。
通常循環(huán)體內(nèi)部,要完成循環(huán)條件的更新。
4) 無限循環(huán)語法
for {
}
i := 0
for { // for true {
fmt.Println(i) i ++
if i >= 10 {
break // 強制 for 終止
}
}
不需要 循環(huán)條件,表示條件永遠為 true。 意味著,循環(huán)體會無限次數(shù)的循環(huán)執(zhí)行下去。
通常,需要配合循環(huán)體內(nèi)部的條件變化以及在循環(huán)體內(nèi)部強制終止來實現(xiàn)。上面的 break
結構。
該結構通常用于實現(xiàn)守護進程。需要一直運行的程序。
6. 循環(huán)遍歷,for range
適用于遍歷:array,slice,map 復合結構。
語法一致:
for i, v := range data {
}
for i := range data {
}
for _, v := range data {
}
7. 注意,語句塊的左大括號,與語句標簽在一行
for ;; {
if ; {
swtich ; {
for := range {
\\ 不能是:
for ;;
{
if ;
{
switch ;
{
// 包括函數(shù)的大括號,也需要與函數(shù)聲明在一行。
func funcName() {
8. goto label
label: 語句標簽。類似于路標,用于標識代碼位置。
使用 goto 語句可以跳轉(zhuǎn)到特定的 label 位置。
演示,例如在處理錯誤時:
// 錯誤處理
// 可能的錯誤有很多種,逐一檢測。
// 一旦檢測到任意的錯誤,則立即處理錯誤,不再檢測后續(xù)的錯誤,邏輯如
下:
if error1 {
goto errorLabel
}
if error2 {
goto errorLabel
}
if error3 { //標簽跳轉(zhuǎn)
goto errorLabel
}
errorLabel: // 標簽定義
fmt.Println("error processing")
上面的語法中,涉及到了:
- 定義標簽: 為標簽設置特定的名字,滿足標識符定義即可。
- 使用標簽: 完成。 跳轉(zhuǎn)標簽:使用 goto 完成跳轉(zhuǎn)。
注意: 跳轉(zhuǎn) goto,不是任意跳轉(zhuǎn)。跳轉(zhuǎn)的目標不是任何位置都可達到,例如: 不能跨函數(shù)跳轉(zhuǎn)。
不能跳轉(zhuǎn)到內(nèi)部的語句塊中。
總結下來:標識符必須可見,才可以 goto 成功。
標識符可見的語法: 語句塊嵌套的可見性。內(nèi)層可以看見外層,但是外層看不到內(nèi)層。
不同的語句塊也不可以跳轉(zhuǎn)。
func main() {
goto labelName
// 內(nèi)層可以看見外層,但是外層看不到內(nèi)層。
for i:=0;i<10;i++ {
labelName: // XXX 不可
fmt.Println()
}
}
// 不同的語句塊也不可以跳轉(zhuǎn)。
func F() {
labelName: // XXX 不可
fmt.Println()
}
9. break,強行終止循環(huán)執(zhí)行
break 用于循環(huán)內(nèi)部,當執(zhí)行到 break 時,會終止 break 所在的循環(huán)的執(zhí)行。
代碼演示
for i:=0; i<10; i++ {
if i == 6 {
break
}
fmt.Println(i)
}
以上結果,6 及后續(xù)數(shù)字沒有輸出。執(zhí)行到了 break,會立即終止。
10.continue,強行終止本次循環(huán)體執(zhí)行,循環(huán)繼續(xù)
continue,僅僅終止本次循環(huán)體執(zhí)行。但是 for 的整體循環(huán)并沒有結束,會繼續(xù)修改循
環(huán)條件,執(zhí)行下一次循環(huán)體。
代碼演示:
for i:=0; i<10; i++ {
if i == 6 {
continue
}
fmt.Println(i)
}
以上的結果,沒有 6 的輸出。continue 之后的代碼,不會繼續(xù)執(zhí)行,而是指向下一次 i== 7 的循環(huán)體。
11.break 或 continue 多重循環(huán)
當循環(huán)語句出現(xiàn)嵌套時,若需要在內(nèi)部循環(huán)體中,直接終止外部的循環(huán)執(zhí)行,則需要配
合 標簽 語法完成終止。 演示:
outerLabel:
for i:=0; i<3; i++ {
for j:=0; j<3; j++ {
if i + j > 2{
break outerLabel
}
fmt.Println(i, j)
}
}
此時,break outerLabel, 會導致 outerLabel 對應的 for 全部終止! 否則,break,僅僅會終止 所在的 for j 的執(zhí)行,而不會終止 for i 的執(zhí)行。
12.switch 中的 break 一定要使用標簽
for 中使用 switch 結構判斷執(zhí)行 break,需要使用標簽語法。
代碼演示:
forLabel:
for i:=0; i<10; i++ {
switch i {
case 6:
break forLabel
}
// if i == 6 {
// break
// }
fmt.Println(i)
}
若不使用標簽語法,switch 中的 break 不會終止循環(huán)。
原因,switch 中的 break,是終止 switch 的作用。在實現(xiàn) switch 時,被視為循環(huán)結構。