4. 流程控制
4.1 条件判断(if)
在Go语言中,可以通过if关键字进行条件判断,格式如下:
if 表达式1 {
分支1
} else if 表达式2 {
分支2
} else {
分支3
}
当表达式 1结果为 rue 时,执行分支1,否则判断表达式2 ,如果满足则执行分支2,都不满足时,则执行分支3 。表达式2 、分支2 和分支3 都是可选的,可以根据实际需要进行选择
要进行选择。
Go 规定与 if 匹配的左括号“ {”必须与 if 和表达式放在同一行,如果尝试将“{”放在其 位置,将会触发编译错误。
同理,与 else 匹配的“{”也必须与 else 在同一行, else 也必须与上一个 if 或else if的右边的大括号在一行。
特殊写法:
可以在if表达式之前添加一个执行语句,再根据变量的值进行判断,代码如下:
if err := Connect(); err != nil {
fmt.Println(err)
return
}
这种写法可以将返回值与判断放在一行进行处理,而且返回值的作用范围被限制在if、else 语句组合中。
4.2 构建循环(for)
Go语言中的所有循环类型均可以使用for关键字来完成。
基于语句和表达式的基本for循环格式如下:
for 初始语句;条件表达式; 结束语句 {
循环体代码
}
循环体不停地进行循环,直到条件表达式返回false时自动退出循环,执行for循环体之后的语句。
for循环可以通过break、goto、return、panic语句强制退出循环。for的初始语句、条件表达式、结束语句的详细介绍如下。
4.2.1 for中的初始语句
初始语句是在第一次循环前执行的语句,一般使用初始语句执行变量初始化,如果变量在此处被声明,其作用域将被局限在这个for的范畴内。
初始语句可以被忽略,但是初始语句之后的分号必须要写,代码如下:
step := 2
for ; step > 0; step-- {
fmt.Println(step)
}
这段代码将step 放在 for 的前面进行初始化, for 中没有初始语句, 此时 step 的作用域就比在初始语句中声明 step 要大。
4.2.2 for中的条件表达式
对每次循环开始前计算的表达式,如果表达式为true,则循环继续,否则循环结束。条件表达式可以被忽略,被忽略条件的表达式默认形成无限循环。
-
结束循环时带可执行语句的无限循环
下面代码忽略条件表达式,但是保留结束语句,代码如下:
var i int for ; ; i++ { if i > 10 { break } }
-
无限循环
上面的代码还可以改写为更美观的写法,代码如下:
var i int for { if i > 10 { break } i++ }
-
只有一个循环条件的循环
var i int for i <= 10 { i++ }
4.2.3 for中结束语句
在结束每次循环前执行的语句,如果循环被break、goto、return、panic等语句强制退出,结束语句不会被执行。
4.3 键值循环(for range)
Go语言可以使用for range遍历数组、切片、字符串、map及通道(channel)。通过for range遍历的返回值有一定的规律:
- 数组、切片、字符串返回索引和值
- map返回键和值
- 通道(channel)只返回通道内的值。
4.3.1 遍历数组、切片
在遍历代码中,key和value分别代表切片的下标及下标对应的值。下面的代码展示如何遍历切片,数组也是类似的遍历方法:
for key, value := range []int{1,2,3,4} {
fmt.Printf("key:%d, value:%d", key, value)
}
4.3.2 遍历字符串
可以通过for range组合,对字符串进行遍历,遍历时,key和value分别代表字符串的索引(base0)和字符串中的每一个字符。
下面这段代码展示了如何遍历字符串:
var str = "hello 你好"
for key, value := range str {
fmt.Printf("key:%d, value:0x%x", key, value)
}
代码中的v变量,实际类型时rune,实际上就是int32, 以十六进制打印出来的就是字符的编码。
4.3.3 遍历map
对于map类型来说,for range遍历时,key和value分别代表map的索引键key和索引对应的值,一般称为map的键值对,因为它们总是一对一对的出现。下面的代码演示了如何遍历map.
m := map[string]int {
"hello":100,
"world":200,
}
for key, value := range m {
fmt.Printf("key:%d, value:%d", key, value)
}
4.3.4 遍历通道(channel)
for range可以遍历通道(channel),但是 通道在遍历时,只输出一个值,即管道内的类型对应的数据。
通道遍历如下:
c := make(chan int)
go func() {
c <- 1
c <- 2
c <- 3
c <- 4
colse(c)
}()
for v := range c {
fmt.Println(v)
}
4.4 分支选择(switch)
Go语言改进了switch的语法设计,避免人为造成失误。Go语言的switch中的每一个case间时独立的代码块,不需要同过break语句跳出当前代码以避免执行到下一行。示例代码如下:
var a = "hello"
switch a {
case "hello":
fmt.Println(1)
case "world":
fmt.Println(2)
default:
fmt.Println(0)
}
上面例子中,每 case 均是字符串格式,且使用了default 分支, Go规定每个switch 只能有一个 default 分支
-
一分支多值
当出现多个case要放在一起的时候,可以像下面的代码这样写:
var a = "mum" switch a { case "mum", "daddy": fmt.Println("family") }
-
分支表达式
case后不仅仅只是常量,还可以和if一样添加表达式,代码如下:
var r int = 11
switch {
case r > 10 && r < 20 :
fmt.Println(r)
}
注意: 这种情况的switch后面不再跟判断变量,连判断的目标都没有了。
4.4.2 跨越case的fallthrough
为了兼容一些移植代码,依然加入了fallthrough关键字来实现这一功能,代码如下:
var a = "hello"
switch {
case a == "hello":
fmt.Println(1)
fallthrough
case a != "world":
fmt.Println(2)
}
4.5 跳转到指定的代码标签(goto)
goto语句通过标签进行代码间的无条件跳转。goto语句可以在快速跳出循环、避免重复退出上有一定的帮助。Go语言中使用goto语句能简化一些代码的实现过程。
4.5.1 退出多层循环
退出多层的循环,示例代码如下:
//外循环
for x :=0; x < 10; x++ {
//内循环
for y := 0; y < 10; y++ {
//满足条件退出所有循环
if y == 2 {
goto OUT
}
}
}
//手动返回,避免执行标签
return
//标签
OUT:
fmt.Println("out loop")
4.5.2 统一错误处理
多处错误处理存在代码重复时非常棘手,后期陆续在这些代码中如果添加更多的判断,就需要在每块雷同代码中依次修改,极易造成疏忽和错误。
如果使用 goto 语句来实现同样的逻辑:
err := firstCheck()
if err != nil {
goto OnExit //发生错误时跳转错误标签OnExit
}
err = secondCheck()
if err != nil {
goto OnExit
}
fmt.Println("done")
return
OnExit: //汇总所有流程进行错误打印并退出进程
fmt.Println(err)
exitProcess()
4.6 跳出指定循环(break)
break语句可以结束for、switch和select的代码块。break语句还可以在语句后面添加标签,表示退出某个标签对应的代码块,标签要求必须定义在对应的for、switch和select的代码块上。
OuterLoop:
for i := 0; i < 2; i++ {
for j := 0; j < 5; j++ {
switch j {
case 2:
fmt.Println(i,j)
break OuterLoop
case 3:
fmt.Println(i,j)
break OuterLoop
}
}
}
4.7 继续下一次循环(continue)
continue语句可以结束当前循环,开始下一次的循环迭代过程,仅限在for循环内使用。在continue语句后添加标签时,表示开始标签对应的循环。例如:
OuterLoop:
for i := 0; i < 2; i++ {
for j := 0; j < 5; j++ {
switch j {
case 2:
fmt.Println(i,j)
continue OuterLoop
}
}
}