本章主要介绍如何使用 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 语句后面只能跟函数。

文档更新时间: 2021-11-08 09:52   作者:admin