本章主要介绍如何使用 Go 的流程控制语句,控制代码执行流程。流程控制语句包括 if、switch、for、defer。还会介绍如何使用常见运算符来构建程序逻辑。
5.1 if 语句
if 语句是构建计算机程序的重要组成部分,几乎所有编程语言都提供 if 语句。简而言之,if 用做条件判断,如果条件成立,则执行某些操作。“if this then that”范式允许程序以不同方式响应数据,并允许程序员建立可以响应不同条件的逻辑。从命令行工具到大型系统,都少不了 if 的支持。在 Go 中,if 语句也同样简单:
package main
import "fmt"
func main() {
b := true
if b {
fmt.Println("b is true")
}
}
代码解释如下:
短变量声明 b 的值为 true,并被自动推断为布尔类型
if 语句判断 b 是否为 true,如果为 true,执行大括号里的内容
打印 b is true。
将 b 的值改为 false,if 语句的结果将为 false,不会执行大括号内的代码。
如果有多条 if 语句,会按照顺序逐条执行。
if 语句是对布尔表达式求值,值为 true,则执行大括号内的代码;值为 false,则不执行。if 语句虽然简单,但功能强大,仅使用 if 语句就能构建出复杂的程序。
5.2 else 语句
else 是代码块中的最后一句,在它之前的任何语句为 true,则 else 语句不会被执行;如果它前面的所有语句都为 false,则执行 else 语句。在 Go 中, else 语句紧跟 if 或 else if 语句的右括号。
else 语句可用于补充它之前的 if 语句不为 true 的情况。下面这段代码是 else 语句的示例:
package main
import "fmt"
func main() {
b := false
if b {
fmt.Println("b is true")
} else {
fmt.Println("b is false")
}
}
5.3 else if 语句
在很多情况下,需要多次判断布尔表达式的值,这时可以使用 else if 语句。如果 else if 前的布尔表达式结果为 false,则判断随后的布尔表达式。也就是说,如果前一条 if 或 else if 语句为 false,会尝试执行下一条。else if 语句紧跟着 if 或 else if 语句的右括号,并需要提供一个布尔表达式:
package main
import "fmt"
func main() {
i := 2
if i > 2 {
fmt.Println("i > 2")
} else if i == 2 {
fmt.Println("i is 2")
} else {
fmt.Println("i < 2")
}
}
代码释义:
短变量声明 i 的值为2
第一条 if 语句判断 i 的值是否大于2,如果是,打印“i > 2”,并忽略以下判断
else if 语句判断 i 是否等于2,如果是,打印“i is 2”,并忽略以下判断
如果 if、else if 都为 false,则执行 else 语句,打印“i < 2”
else 和 else if 的根本区别是 else if 语句运行 bool 条件判断;else 只能执行代码。
5.4 比较运算符
布尔表达式仅能对 true、false 进行判断,如果进行更复杂的条件判断,可以使用比较运算符。将任何数据类型与另一个相同类型的数据进行比较,如果匹配则返回 true,否则返回 false。编程中一些常见的比较运算包括:
比较两个字符串是否相等
比较两个数字是否相等、大于或小于
下表是 Go 的比较运算符:
比较运算符 | 操作 |
---|---|
== | 等于 |
!= | 不等于 |
< | 小于 |
<= | 小于或等于 |
> | 大于 |
=> | 大于或等于 |
在 Go 语言中,比较运算符只能对相同类型的数据进行比较。例如,无法将字符串与整数相比较。
5.5 算术运算符
可以使用算术运算增强判断语句的逻辑,算术运算符经常与比较运算符结合使用,以创建布尔语句来管理控制流。布尔语句中经常使用的算术运算包括:
两个数字的和是否等于特定数字?
两个数字的差是否大于特定数字?
两个数字相除是否等于特定数字?
下表是 Go 的常用算术运算符:
算术运算符 | 操作 |
---|---|
+ | 总和(也称加法) |
- | 差异(也称减法) |
* | 乘积(也称乘法) |
/ | 商(也称为除法) |
% | 余数(也称取模) |
和比较运算符一样,算术运算符两边的类型也必须相同。
5.6 逻辑运算符
与比较和算术运算符一样,逻辑运算符也可用于管理流程控制。逻辑运算符支持三种类型的比较。布尔语句中经常使用的逻辑运算包括:
两个变量都正确吗?
两个变量中的任何一个正确吗?
变量等于某个数字吗?
下表是 Go 的常用逻辑运算符:
逻辑运算符 | 操作 |
---|---|
&& | AND-两个条件均为 true |
|| | OR-任何一个条件为 true |
! | NOT-用于翻转。如果条件为 true,则!运算符将为 false |
5.7 位运算符
位运算符的使用相比于前三种运算符,比较低频。Go 支持的位运算符如下表:
位运算符 | 操作 |
---|---|
& | 按位与运算符”&”是双目运算符。其功能是参与运算的两数各对应的二进位相与。 |
| | 按位或运算符”|”是双目运算符。其功能是参与运算的两数各对应的二进位相或。 |
^ | 按位异或运算符”^”是双目运算符。其功能是参与运算的两数各对应的二进位相异或,当两对应的二进位相异时,结果为1。 |
<< | 左移运算符”<<”是双目运算符。左移 n 位就是乘以2的 n 次方。其功能把”<<”左边的运算数的各二进位全部左移若干位,由”<<”右边的数指定移动的位数,高位丢弃,低位补0。 |
>> | 右移运算符”>>”是双目运算符。右移 n 位就是除以2的 n 次方。其功能是把”>>”左边的运算数的各二进位全部右移若干位,”>>”右边的数指定移动的位数。 |
&^ | 按位清零”&^”是双目运算符。将运算符左边数据相异的位保留,相同位清零。 |
着重讲一下按位清零运算符:
1 &^ 0 ----> 1
1 &^ 1 ----> 0
0 &^ 1 ----> 0
0 &^ 0 ----> 0
右侧数为1,不论左侧是0是1,均置为0;
右侧数为0,左侧数是什么,其值就是什么。
我们看一段使用按位清零运算符对文件权限的操作:
package main
import "fmt"
const (
Executable = 1 << iota
Writeable
Readable
)
func main() {
// 1 0001
// 2 0010
// 4 0100
// 7 0111
fmt.Println(Readable, Writeable, Executable)
chmod := 7
// 去掉可执行权限
chmod = chmod &^ Executable
fmt.Println(chmod)
}
5.8 switch 语句
如过 if else 语句过于冗长,可以改用 switch 语句,增加可读性。为了可读性,许多程序员喜欢使用 switch 语句,而不是 if else,并且在编译器级别,生成的代码也可能更高效。除了短小的 if else 语句块外,switch 语句更易读且更高效。
package main
import "fmt"
func main() {
i := 2
switch i {
case 2:
fmt.Println("Two")
case 3:
fmt.Println("Three")
case 4:
fmt.Println("Four")
}
}
代码释义:
switch 语句声明将判断变量i
一系列 case 表达式对变量进行判断
如果 case 表达式为 true,则执行该 case 中的代码
如果 case 表达式为 false,则执行下一个 case 语句
本示例将打印“Two”
switch 提供了一种更简洁的 else if 条件编写方法,如果 case 语句均不为 true,可以执行默认 case。这类似于 else 语句。在 switch 语句中,default 关键字用于执行在所有条件都不匹配时的代码块。尽管 default 语句通常为最后一条执行的语句,但它可以出现在 switch 语句块中的任何位置。
package main
import "fmt"
func main() {
s := "c"
switch s {
case "a":
fmt.Println("The letter a!")
case "b":
fmt.Println("The letter b!")
default:
fmt.Println("I don't know!")
}
}
5.9 for 循环语句
for 语句用来循环执行一段代码,直到某个条件不再成立为止。一个简单的示例是将一个数字加一,直到达到某个数字:
package main
import "fmt"
func main() {
i := 0
for i < 10 {
i++
fmt.Println(i)
}
}
代码释义:
短变量声明 i 等于0
for 语句判断 i 是否小于10
如果判断结果为 true,则执行 for 中的代码
i 使用++增量运算符对变量进行计算
i 值被打印到终端
执行返回到布尔表达式,继续判断 i 是否小于10
当 i 不小于10时,布尔表达式为 false,终止 for 循环
5.9.1 for 语句中的 init 和 post 声明
除了单个条件,for 语句还可以在第一次运行时指定初始化语句、要判断的条件以及 post 语句,这些用分号分隔。
init:仅在第一次迭代前执行
条件声明:这是一个布尔语句,每次迭代都会进行判断
post 声明:每次迭代后执行
示例:
package main
import "fmt"
func main() {
for i := 0; i < 10; i++ {
fmt.Println("i is", i)
}
}
代码释义:
初始化变量 i,并赋值为0
for 语句判断 i 是否小于10
如果判断结果为 true,则执行 for 中的代码
i 使用++增量运算符对变量进行计算
i 值被打印到终端
执行返回到判断语句,继续判断 i 是否小于10
当 i 不小于10时,布尔表达式为 false,终止 for 循环语句
5.9.2 for 循环中的 range
for 语句也可以对复合数据结构进行遍历,在下面的示例中,我们用 for 语句遍历一个数组:
package main
import "fmt"
func main() {
numbers := []int{1, 2, 3, 4, 5}
for i, n := range numbers {
fmt.Println("The index of the loop is", i)
fmt.Println("The value from the array is", n)
}
}
代码释义:
声明一个变量 numbers,值是包含5个整数的切片
range 语句会返回 numbers 的索引和对应的值
for 语句将迭代的索引值分配给变量i
for 语句将迭代的数组值分配给变量n
打印每个变量的值
带有 range 子句的 for 语句可用于遍历 Go 中的大多数数据结构,而无需了解数据的长度。
5.10 defer 语句
defer 语句的作用是把某个函数的执行放在最后,通常用于清理操作,比如关闭打开的文件,关闭连接的网络,或者与 recover 组合,收集 panic 日志。下面是一个 defer 示例,它将在其他函数返回时执行:
package main
import "fmt"
func main() {
defer fmt.Println("I am run after the function completes")
fmt.Println("https://www.aiops.red")
}
这段代码会先打印 "https://www.aiops.red"
的输出,再打印 defer 语句。
defer 语句后只能跟函数,多个 defer 语句的执行顺序是,最后出现的 defer 语句最先被执行。
5.11 总结
本章内容,介绍了如何使用控制流构造逻辑,学习了 if、else 和 else if 语句构造执行流。学习了比较、算术、逻辑、位运算符,并使用它们构造布尔表达式。学习了switch语句,以代替复杂的 if、else 和 else if。学习了单个条件的 for 循环语句以及 range 子句。了解了 defer 语句,defer 语句总是最后执行,并且 defer 语句后面只能跟函数。