第5章 CHAPTER 5 流 程 控 制 流程控制是Go语言中必不可少的一部分,也是整个编程基础的重要一环。Go语言的流程控制语句和其他编程语言的流程控制语句有些不同,主要体现在Go语言没有while和dowhile语句。 Go语言常用的流程控制包括if语句、switch语句、for语句及goto语句等,switch语句和goto语句主要是为了简化代码、降低代码重复率,属于扩展类的流程控制语句。 4min 5.1条件判断 在Go语言中,if语句主要用于条件判断。if语句还有两个分支结构: ifelse语句和elseif语句。 5.1.1if单分支 在Go语言中,关键字if是用于判断某个条件(布尔型或逻辑型)的语句,如果该条件成立,则会执行if后由花括号“{}”括起来的代码块,否则就忽略该代码块继续执行后续的代码,伪代码如下: if 条件表达式 { 代码块 } if语句的流程图如图51所示。 图51if语句的流程图 例如使用if语句判断一个变量的大小: var a int = 20 if a < 30 { fmt.Printf("a 小于 30\n") } fmt.Printf("a 的值为%d\n", a) 这时可以使用输入函数动态地进行判断,代码如下: //unit5/if语句/1.if单分支.go package main import "fmt" func main() { var a int fmt.Printf("输入一个数字:") _, err := fmt.Scanf("%d", &a) if err != nil { fmt.Println("输入的数字错误") return } fmt.Printf("你输入的值是 %d\n", a) if a < 30 { fmt.Println("a 小于 30") } } 5.1.2ifelse双分支 if语句后可以使用可选的else语句,else语句中的表达式在条件表达式为false时执行,if和else后的两个代码块是相互独立的分支,只能执行其中的一个,伪代码如下: if 条件表达式 { 代码块1 } else { 代码块2 } ifelse双分支的流程图如图52所示。 图52ifelse双分支的流程图 示例代码如下: //unit5/if语句/2.if-else双分支.go package main import "fmt" func main() { var day int fmt.Printf("几天周几:") _, err := fmt.Scanf("%d", &day) if err != nil { fmt.Println("输入的数字错误") return } if day >= 1 && day <= 5 { fmt.Println("今天是工作日") }else { fmt.Println("今天是休息日") } } 5.1.3ifelseif多分支 除了可以直接使用ifelse双分支语句,还可以在else后面继续增加if条件判断,语法如下: if 条件表达式1 { //当条件表达式1为true时执行 } else if 条件表达式2 { //当条件表达式2为true时执行 } else { //当条件表达式1和2都不为true时执行 } 示例代码如下: //unit5/if语句/3.if-else-if多分支.go package main import "fmt" func main() { score := 80 if score >= 90 { fmt.Println("优秀") } else if score >= 80 { fmt.Println("良好") } else { fmt.Println("一般") } } ifelseif多分支的流程图如图53所示。 图53ifelseif多分支的流程图 5.1.4if嵌套 if嵌套表示可以在if语句块中继续添加if判断,语法如下: if 条件表达式1 { if 条件表达式2 { //当条件表达式2为true时执行 } else { //当条件表达式2为false时执行 } } else { //当条件表达式1为false时执行 } 示例代码如下: //unit5/if语句/4.if嵌套.go package main import "fmt" func main() { score := 90 if score >= 90 { if score >= 95 { fmt.Println("非常优秀") } else { fmt.Println("优秀") } } else { fmt.Println("一般") } } if嵌套也可以无限制地嵌套,但是在实际开发中同样不建议嵌套太多层。 5.1.5知识扩展——卫语句 万能的ifelse语句,仿佛任何需求都能够满足,也是写代码常用的一种语句,有很多复杂的业务嵌套了无数个ifelse语句,例如图54中让人头疼的if语句,应该如何优化呢? 图54多层if嵌套语句 卫语句就是把复杂的条件表达式拆分成多个条件表达式,减少嵌套。将嵌套了好几层的ifelse语句,转换为多个if语句,实现它的逻辑,这多条的if语句就是卫语句。 卫语句将某些关键条件优先判断,简化程序流程走向。卫语句往往用于对if条件嵌套代码的优化。 在《阿里巴巴Java开发手册》中强制规定: 超过 3 层的ifelse的逻辑判断代码可以使用卫语句、策略模式、状态模式等来实现,其中卫语句即代码逻辑先考虑失败、异常、中断、退出等直接返回的情况,以方法多个出口的方式,解决代码中判断分支嵌套的问题,这是逆向思维的体现。 例如分数得分示例,不使用卫语句如下: //unit5/if语句/5.卫语句.go package main import "fmt" func main() { var scope int fmt.Printf("输入你的分数:") _, err := fmt.Scanf("%d", &scope) if err != nil { fmt.Println("输入的分数错误") return } if scope >= 80 { if scope >= 90 { fmt.Println("A") }else { fmt.Println("B") } }else { if scope >= 60 { fmt.Println("C") }else { fmt.Println("D") } } } 使用卫语句如下: //unit5/if语句/5.卫语句.go package main import "fmt" func main() { var scope int fmt.Printf("输入你的分数:") _, err := fmt.Scanf("%d", &scope) if err != nil { fmt.Println("输入的分数错误") return } if scope >= 90 { fmt.Println("A") return } if scope >= 80 { fmt.Println("B") return } if scope >= 60 { fmt.Println("C") return } fmt.Println("D") } 5.2switch语句 switch语句是根据变量的值执行不同的case,在执行的过程中会从第1个case开始判断,直到碰到一个符合条件的case为止,然后执行该case中的语句,语法如下: switch 变量 { case 变量1: //当变量和变量1相等时执行 case 变量2: //当变量和变量2相等时执行 default: //当没有符合的case时执行 } 示例代码如下: //unit5/switch语句/6.switch语句.go package main import "fmt" func main() { var day int fmt.Printf("几天周几:") _, err := fmt.Scanf("%d", &day) if err != nil { fmt.Println("输入的数字错误") return } switch day { case 1,2,3,4,5: fmt.Println("工作日") case 6,7: fmt.Println("周末") default: fmt.Println("错误的时间") } } switch语句的主要特点如下: (1) switch和if语句一样,switch后面可以带一个可选的简单的初始化语句。 (2) switch后面的表达式也是可选的,如果没有表达式,则case子句是一个布尔表达式,而不是一个值,此时就相当于多重ifelse语句。 (3) switch条件表达式的值不像C语言那样必须限制为整数,可以是任意支持相等比较运算的类型变量。 (4) 通过fallthough语句来强制执行下一个case子句(不再判断下一个case子句的条件是否满足)。 (5) switch支持default语句,当所有的case分支都不符合时,执行default语句,并且default语句可以放到任意位置,并不影响switch的判断逻辑。 (6) switch和.(type)结合可以进行类型的查询。 在默认情况下,匹配到一个case之后整个switch语句就会结束,其他的case条件就不会参与下次匹配,如果需要执行后面的case,则可以使用fallthrough关键字,使用fallthrough之后switch便不会退出,代码如下: //unit5/switch语句/7.fallthrough.go package main import "fmt" func main() { a := 11 //没有fallthrough switch { case a > 5: fmt.Println("a大于5") case a > 10: fmt.Println("a大于10") } fmt.Println("=======") //有fallthrough switch { case a > 5: fmt.Println("a大于5") fallthrough case a > 10: fmt.Println("a大于10") } } 7min 5.3循环语句 在不少实际问题中有许多具有规律性的重复操作,因此在程序中就需要重复执行某些语句。 for循环是一个循环控制结构,可以执行指定次数的循环。 Go语言的for循环有4种模式,分别是标准for循环模式、while模式、dowhile模式、for range模式。 5min 5.3.1标准for循环 for循环的语法如下: for init; condition; post { } 其中,init表示初始化条件,condition表示条件,post表示赋值表达式。 例如,以从1加到5为例,使用标准for循环实现,代码如下: //unit5/for循环/标准for循环.go package main import "fmt" func main() { var sum = 0 //定义一个用于求和的变量 for i := 0; i <= 5; i++ { sum += i } fmt.Println(sum) //15 } 以下是详细执行过程分析: (1) 定义一个sum变量,用于把每次循环的结果相加。 (2) 定义一个变量i并赋值为0,进入循环将i的值追加到sum变量中,然后i自增1。 (3) 此时i等于1,判断i的值是否小于或等于5,如果条件成立,则继续进入循环,循环往复直到i等于6; 如果条件不成立,则结束循环,输出sum变量。 3min 5.3.2while模式的for循环 while模式下的for循环,只需一个条件,如果条件成立,则执行循环体; 如果条件不成立,则结束循环,语法如下: for condition { } 以1加到100为例,使用while模式的for循环,代码如下: //unit5/for循环/while型for循环.go package main import "fmt" func main() { var sum = 0 //定义一个用于求和的变量 var i = 0 for i <= 100 { sum += i i++ } fmt.Println(sum) //5050 } 1min 5.3.3dowhile模式的for循环 dowhile模式下的for循环没有条件,结束循环的条件在循环体中编写,语法如下: for { } 以1加到100为例,使用dowhile模式的for循环,代码如下: //unit5/for循环/do while型for循环.go package main import "fmt" func main() { var sum = 0 //定义一个用于求和的变量 var i = 0 for { if i > 100 { //当i大于100时结束循环 break } sum += i i++ } fmt.Println(sum) //5050 } 当循环体内没有条件结束循环时程序就会不停地执行循环体中的代码,在计算机中称为“死循环”。 下例是不断打印当前时间的死循环,代码如下: //unit5/for循环/死循环.go package main import ( "fmt" "time" ) func main() { for { time.Sleep(1 * time.Second) //程序等待1s fmt.Println(time.Now().Format("2006-01-02 15:04:05"))//年月日时分秒格式的时间 } } 3min 5.3.4for range模式的for循环 用于遍历slice(切片)、map、数组、字符串等数据类型,格式如下: for key, value := range iteration {} 当循环slice、数组、字符串等数据类型时,key为当前元素的索引,代码如下: var slice = []string{"A", "B", "C"} for index, item := range slice { fmt.Printf("元素值 %s, 索引:%d\n", item, index) } 当循环map类型时,代码如下: var maps = map[string]any{"name": "枫枫", "age": 25} for key, value := range maps { fmt.Printf("元素值 %v, key:%s\n", value, key) } 3min 5.3.5break语句 在Go语言中,break语句用于终止当前循环或者switch语句的执行,并跳出该循环或者switch语句的代码块。 例如循环10个数字,但是只需前3个数,代码如下: //unit5/for循环/break.go package main import "fmt" func main() { for i := 1; i < 11; i++ { fmt.Println(i) if i == 3 { break } } } 5.3.6continue语句 Go语言的continue语句有点像break语句,但是continue不是跳出循环,而是跳过当前循环执行下一条循环语句。 例如循环10个数字,只需其中的偶数,代码如下: //unit5/for循环/continue.go package main import "fmt" func main() { for i := 1; i < 11; i++ { //任何数对2取余 //如果结果为0,则表示是2的倍数 if i%2 != 0 { continue } fmt.Println(i) } } 5.3.7多重循环 Go语言允许用户在循环内使用循环,需要注意的是,break只能跳出当前for循环,不能跳出多个for循环。 例如打印九九乘法表,代码如下: //unit5/for循环/九九乘法表.go package main import "fmt" func main() { for m := 1; m < 10; m++ { for n := 1; n <= m; n++ { fmt.Printf("%dx%d=%d ", n, m, m*n) } fmt.Println() } } 输出结果如图55所示。 图55九九乘法表