耐心和持久胜过激烈和狂热。
前言
我是陈明勇,本文分享的知识是 Go 的循环结构。如果本文对你有帮助,不妨点个赞,如果你是 Go 语言初学者,不妨点个关注,一起成长一起进步,如果本文有错误的地方,欢迎指出!
循环结构
循环结构是指在程序中需要反复执行某个功能而设置的一种程序结构。有的编程语言,包含两种循环结构,一种是 for
循环,另一种是 while
循环,而在 Go
里面,有且只有一种循环 —— for
循环。接下来看一个示例:
func main() { sum := 0 for num := 1; num <= 10; num++ { sum += num } println(sum) // 55 } 复制代码
上述代码实现的功能是在 1-10
之中累加求和,最后的结果为 55
。
- 上图所示,
for
循环分为四个部分,第一部分num := 1
为循环前置语句,在这一部分,我们一般都会定义一些变量,这些变量被使用于第二部分和第三部分里。 - 第二部分是条件判断表达式,也就是布尔表达式,多条件可以使用逻辑操作符进行连接。此部分的作用是判定循环是否继续下去,图中循环不终止的条件为
num <= 10
。只要条件成立,就会去执行第三部分. - 第三部分为循环体,只要循环不终止,程序就会重复执行循环体里面的代码。上述例子中,循环体所做的事情就是累加
num
变量的值。 - 第四部分为循环后置语句,这一部分通常会对第一部分所定义的变量进行更新,例如上述例子中,对
num
进行自增。
for
循环执行顺序是这样的:
- 第一部分(只会执行一次)
- 第二部分(若布尔表达式的值为 false 则终止循环,不进行第三第四部分)
- 第三部分
- 第四部分,然后返回第二部分继续执行。 对于上述四个部分,除了第三部分以外,其他部分都可以省略。如果只留第三部分,那么就形成死循环,以下为示例:
func main() { for { println("糟糕,死循环!") } } 复制代码
- 在一些场景下,我们会利用死循环去做一些特定的事,但是最终还是要跳出死循环的。如何跳出死循环,就涉及到接下来要讲的关键字
break
。
for-range
除了上面所讲的普通 for
循环的形式,Go
里面还支持一种 for
循环,为 for-range
循环,这是一种什么循环形式呢?我们来看看例子:
import "fmt" func main() { nums := [4]int{1, 2, 3, 4} for i := 0; i < len(nums); i++ { fmt.Printf("下标:%d,元素:%d\n", i, nums[i]) } } 复制代码
上述代码,在循环前置语句中,声明数组的下标,然后循环体通过下标值打印数组的元素,我们来看看使用 for-range
的代码实现是怎么样的:
import "fmt" func main() { nums := [4]int{1, 2, 3, 4} for i, num := range nums { fmt.Printf("下标:%d,元素:%d\n", i, num) } } 复制代码
与普通 for
循环相比,for-range
的形式代码量少了很多,除了循环体保留了下来,其余部分都融入到了 for-range
的语义里。上述代码中,变量 i
为数组的下标索引,num
为数组中的元素值。如果我们所关注的只是数组的下标索引或者元素值,可以进行以下改造:
- 只关注下标索引
import "fmt" func main() { nums := [4]int{1, 2, 3, 4} for i := range nums { fmt.Printf("下标:%d\n", i) } } 复制代码
- 仅仅定义一个
i
变量。 - 只关注元素值
import "fmt" func main() { nums := [4]int{1, 2, 3, 4} for _, num := range nums { fmt.Printf("元素值:%d\n", num) } } 复制代码
- 索引位置使用
_
代替,表示忽略下标索引的接收。 - 下标索引和元素值都不关注
package main func main() { nums := [4]int{1, 2, 3, 4} for range nums { } } 复制代码
break 和 continue 关键字
break
和 continue
关键字用于控制 for
循环的代码流程,且只对最近的 for
循环有效(多层循环的情况下)。
- break
退出for
循环,循环体后续代码不再执行。 - continue
终止本轮循环,循环体后续代码不再执行,进入下一轮循环。
示例
- 循环遍历数组,如果在数组内找到元素值
6
,则退出循环。
func main() { nums := [5]int{1, 2, 6, 3, 4} for _, num := range nums { if num == 6 { break } println("元素:", num) } } 复制代码
- 执行结果:
元素: 1 元素: 2 复制代码
- 根据执行结果可知,遍历数组到元素
6
的时候,使用break
关键字,循环就终止了,后面的元素3
和4
没有被打印出来。前面所提到的死循环也可以使用break
关键字跳出循环。 - 循环遍历数组,只打印奇数,忽略偶数。
func main() { nums := [5]int{1, 2, 6, 3, 4} for _, num := range nums { if num%2 == 0 { continue } println("元素:", num) } } 复制代码
- 执行结果:
元素: 1 元素: 3 复制代码
- 遇到偶数元素,就使用关键字
continue
结束本轮循环,开始下一轮循环。
label
在 Go
语言中,label
语句的作用是标记跳转的目标。示例说明: 遍历二维数组,找到元素 3
后结束跳出整个循环。
func main() { nums := [][]int{ {1, 2}, {3, 4}, {5, 6}, } for i := 0; i < len(nums); i++ { println("第", i+1, "轮:") for j := 0; j < len(nums[i]); j++ { if nums[i][j] == 3 { break } println("元素值:", nums[i][j]) } } } 复制代码
执行结果:
第 1 轮: 元素值: 1 元素值: 2 第 2 轮: 第 3 轮: 元素值: 5 元素值: 6 复制代码
外层循环一共要循环三轮,根据结果可知,使用 break
关键字,并没有跳出整个循环。在第二轮循环之后,进入内层循环,找到元素 4
,break
关键字只终止了内层循环,外层循环的第三轮还会继续执行,这并不是我们想要的结果。要想达到目标结果,需要结合 label
语句实现:
func main() { nums := [][]int{ {1, 2}, {3, 4}, {5, 6}, } outerLoop: for i := 0; i < len(nums); i++ { println("第", i+1, "轮:") for j := 0; j < len(nums[i]); j++ { if nums[i][j] == 3 { break outerLoop } println("元素值:", nums[i][j]) } } } 复制代码
执行结果:
第 1 轮: 元素值: 1 元素值: 2 第 2 轮: 复制代码
在第一层循环前面,使用 label
语句,用 outerLoop
进行标记,然后在 break
关键字后面加上这个标记,就能实现跳出整个循环。
小结
本文首先介绍了 Go
里面的普通 for
循环,然后由普通 for
循环引出了 for-range
循环,对于数组、切片、Map
等复合数据结构,遍历方式使用 for-range
的形式会更好,特殊的复合数据类型如 Map
,遍历方式也只能用 for-range
的形式。本文还提到了 break
、continue
和 label
关键字,通过案例介绍了它们的使用场景。