在不少实际问题中有许多具有规律性的重复操作,因此在程序中就需要重复执行某些语句。以下为大多编程语言循环程序的流程图:

Go语言 循环语句 - 图1

Go 语言提供了以下几种类型循环处理语句:

循环类型 描述
for 循环 重复执行语句块
循环嵌套 在 for 循环中嵌套一个或多个 for 循环

循环控制语句可以控制循环体内语句的执行过程。GO 语言支持以下几种循环控制语句:

控制语句 描述
break 语句 经常用于中断当前 for 循环或跳出 switch 语句
continue 语句 跳过当前循环的剩余语句,然后继续进行下一轮循环
goto 语句 将控制转移到被标记的语句

for 循环

for 循环是一个循环控制结构,可以执行指定次数的循环 Go 语言的 For 循环有 3 种形式,只有其中的一种使用分号

for 循环的几种形式

和 C 语言、Java、JavaScript的 for 一样

  1. for init; condition; post { }

先对表达式 1 赋初值,判别赋值表达式 init 是否满足给定条件,若其值为真,满足循环条件,则执行循环体内语句,然后执行 post,进入第二次循环,再判别 condition;否则判断 condition 的值为假,不满足条件,就终止for循环,执行循环体外语句

  • init 一般为赋值表达式,给控制变量赋初值
  • condition 关系表达式或逻辑表达式,循环控制条件
  • post 一般为赋值表达式,给控制变量增量(increment)或减量(decrement)

image.png

和 C 的 while 一样:

  1. for condition { }

和 C 的 for(;;) 一样:

  1. for { }

for 循环的 range 格式可以对 slice、map、数组、字符串等进行迭代循环

  1. for key, value := range oldMap {
  2. newMap[key] = value
  3. }
  4. //和JavaScript中for中操作对象,数组的对比
  5. var obj = { a: 1, b: 2, c:3 }
  6. for (const [k,v] of Object.entries(obj)) {
  7. console.log(k,v)
  8. }
  9. var obj = { a: 1, b: 2, c:3 }
  10. for (const k in obj) {
  11. console.log(k, obj[k])
  12. }
  13. var obj = [1,2,3]
  14. for (const k in obj) {
  15. console.log(k, obj[k])
  16. }

计算 1 到 10 的数字之和

  1. package main
  2. import "fmt"
  3. func main() {
  4. sum := 0
  5. for i := 0; i <= 10; i++ {
  6. sum += i
  7. }
  8. fmt.Println(sum)
  9. }
  10. // 输出结果:55


sum 小于 10 的时候计算 sum 自相加后的值

  1. package main
  2. import "fmt"
  3. func main() {
  4. sum := 1
  5. for ;sum <= 10; {
  6. sum += sum
  7. }
  8. fmt.Println(sum)
  9. // 这样写也可以,更像 While 语句形式
  10. for sum <= 10{
  11. sum += sum
  12. }
  13. fmt.Println(sum)
  14. }

无限循环

如果循环中条件语句永远不为 false 则会进行无限循环,我们可以通过 for 循环语句中只设置一个条件表达式来执行无限循环:

  1. package main
  2. import "fmt"
  3. func main() {
  4. sum := 0
  5. for;;{ fmt.Printf(";;\n"); }
  6. for true { fmt.Printf("这是条件为true的无限循环\n"); }
  7. for {
  8. sum++ // 无限循环下去
  9. fmt.Printf("无参数的无限循环。\n");
  10. }
  11. fmt.Println(sum) // 无法输出
  12. }

⚠️ 要停止无限循环,可以在命令窗口按下ctrl+c

循环嵌套

Go 语言允许用户在循环内使用循环, 以下为 Go 语言嵌套循环的格式:

  1. for [(init; condition; increment) | condition | Range] {
  2. for [(init; condition; increment) | condition | Range]{
  3. statement(s);
  4. }
  5. statement(s);
  6. }

使用循环嵌套来输出 2 到 100 间的素数

  1. package main
  2. import "fmt"
  3. func main() {
  4. // 定义局部变量
  5. var i, j int
  6. for i=2; i < 100; i++ {
  7. for j=2; j*j <= i; j++ {
  8. if(i%j==0) {
  9. break; // 如果发现因子,则不是素数
  10. }
  11. }
  12. if(j*j > i) {
  13. fmt.Printf("%d 是素数\n", i);
  14. }
  15. }
  16. }
  17. /*
  18. 2 是素数
  19. 3 是素数
  20. 5 是素数
  21. 7 是素数
  22. 11 是素数
  23. 13 是素数
  24. 17 是素数
  25. 19 是素数
  26. 23 是素数
  27. 29 是素数
  28. 31 是素数
  29. */

for-range 范围(Range)

Go 语言中 range 关键字用于 for 循环中迭代数组(array)、切片(slice)、集合(map)、通道(channel)的元素。在数组和切片中它返回元素的索引和索引对应的值,在集合中返回 key-value 键值对

这种格式的循环可以对字符串、数组、切片等进行迭代输出元素

  1. package main
  2. import "fmt"
  3. func main() {
  4. strings := []string{"google", "runoob"}
  5. for i, s := range strings {
  6. fmt.Println(i, s)
  7. }
  8. numbers := [6]int{1, 2, 3, 5}
  9. for i,x:= range numbers {
  10. fmt.Printf("第%d位 x 的值 = %d\n", i,x)
  11. }
  12. }
  13. /*
  14. 0 google
  15. 1 runoob
  16. 第0位 x 的值 = 1
  17. 第1位 x 的值 = 2
  18. 第2位 x 的值 = 3
  19. 第3位 x 的值 = 5
  20. 第4位 x 的值 = 0
  21. 第5位 x 的值 = 0
  22. */
  1. package main
  2. import "fmt"
  3. func main() {
  4. //这是我们使用range去求一个slice的和。使用数组跟这个很类似
  5. nums := []int{2, 3, 4}
  6. sum := 0
  7. for _, num := range nums[1:] {
  8. sum += num
  9. }
  10. fmt.Println("sum:", sum)
  11. //在数组上使用range将传入index和值两个变量。上面那个例子我们不需要使用该元素的序号,所以我们使用空白符"_"省略了。有时侯我们确实需要知道它的索引。
  12. for i, num := range nums {
  13. if num == 3 {
  14. fmt.Println("index:", i)
  15. }
  16. }
  17. //range也可以用在map的键值对上
  18. kvs := map[string]string{"a": "apple", "b": "banana"}
  19. for k, v := range kvs {
  20. fmt.Printf("%s -> %s\n", k, v)
  21. }
  22. //range也可以用来枚举Unicode字符串。第一个参数是字符的索引,第二个是字符(Unicode的值)本身。
  23. for i, c := range "go" {
  24. fmt.Println(i, c)
  25. }
  26. c := make(chan int, 12)
  27. go fibonacci(cap(c), c)
  28. for i := range c { fmt.Println(i) }
  29. }
  30. func fibonacci(n int, c chan int) {
  31. x, y := 0, 1
  32. for i := 0; i <= n; i++ {
  33. c <- x
  34. x, y = y, x+y
  35. }
  36. close(c)
  37. }
  38. /* 以上实例运行输出结果为:
  39. sum: 7
  40. index: 1
  41. a -> apple
  42. b -> banana
  43. 0 103
  44. 1 111
  45. 0
  46. 1
  47. 1
  48. 2
  49. 3
  50. 5
  51. 8
  52. 13
  53. 21
  54. 34
  55. 55
  56. 89
  57. 144
  58. */

break 语句

Go 语言中 break 语句用于以下两方面:

  • 用于循环语句中跳出循环,并开始执行循环之后的语句。
  • break 在 switch(开关语句)中在执行一条 case 后跳出语句的作用。
  • 在多重循环中,可以用标号 label 标出想 break 的循环

break 语法格式与语句流程图

  1. break;

image.png
**

在变量 a 大于 15 的时候跳出循环

  1. package main
  2. import "fmt"
  3. func main() {
  4. // 定义局部变量
  5. var a int = 10
  6. // for 循环
  7. for a < 20 {
  8. fmt.Printf("a的值为 : %d\n", a);
  9. a++;
  10. if a > 15 {
  11. break; //使用 break 语句跳出循环
  12. }
  13. }
  14. }
  15. /*
  16. a的值为 : 10
  17. a的值为 : 11
  18. a的值为 : 12
  19. a的值为 : 13
  20. a的值为 : 14
  21. a的值为 : 15
  22. */

有多重循环,使用标记和不使用标记的区别

  1. package main
  2. import "fmt"
  3. func main() {
  4. // 不使用标记
  5. fmt.Println("---- break ----")
  6. for i := 1; i <= 3; i++ {
  7. fmt.Printf("i: %d\n", i)
  8. for i2 := 11; i2 <= 13; i2++ {
  9. fmt.Printf("i2: %d\n", i2)
  10. break
  11. }
  12. }
  13. // 使用标记
  14. fmt.Println("---- break label ----")
  15. re:
  16. for i := 1; i <= 3; i++ {
  17. fmt.Printf("i: %d\n", i)
  18. for i2 := 11; i2 <= 13; i2++ {
  19. fmt.Printf("i2: %d\n", i2)
  20. break re
  21. }
  22. }
  23. }
  24. /*
  25. ---- break ----
  26. i: 1
  27. i2: 11
  28. i: 2
  29. i2: 11
  30. i: 3
  31. i2: 11
  32. ---- break label ----
  33. i: 1
  34. i2: 11
  35. */

continue 语句

Go 语言的 continue 语句 有点像 break 语句。但是 continue 不是跳出循环,而是跳过当前循环执行下一次循环语句 for 循环中,执行 continue 语句会触发 for 增量语句的执行 在多重循环中,可以用标号 label 标出想 continue 的循环

continue 语法格式与语句流程图

  1. continue;

image.png
**

在a=15 的时候跳过本次循环执行下一次循环

  1. package main
  2. import "fmt"
  3. // func main() {
  4. // var a int = 10 // 定义局部变量
  5. // for a < 20 {
  6. // if a == 15 {
  7. // a = a + 1;
  8. // continue; // 跳过此次循环,即使跳过一次下面的打印
  9. // }
  10. // fmt.Printf("a 的值为 : %d\n", a);
  11. // a++;
  12. // }
  13. // }
  14. func main() {
  15. for a :=10 ;a < 20;a++ {
  16. if a == 15 {
  17. continue; // 跳过此次循环,即使跳过一次下面的打印
  18. }
  19. fmt.Printf("a 的值为 : %d\n", a);
  20. }
  21. }
  22. /*
  23. a 的值为 : 10
  24. a 的值为 : 11
  25. a 的值为 : 12
  26. a 的值为 : 13
  27. a 的值为 : 14
  28. a 的值为 : 16
  29. a 的值为 : 17
  30. a 的值为 : 18
  31. a 的值为 : 19
  32. */

多重循环,使用标记和不使用标记的区别

  1. package main
  2. import "fmt"
  3. func main() {
  4. // 不使用标记
  5. fmt.Println("---- continue ---- ")
  6. for i := 1; i <= 3; i++ {
  7. fmt.Printf("i: %d\n", i)
  8. for i2 := 11; i2 <= 13; i2++ {
  9. fmt.Printf("i2: %d\n", i2)
  10. continue
  11. }
  12. }
  13. // 使用标记
  14. fmt.Println("---- continue label ----")
  15. re:
  16. for i := 1; i <= 3; i++ {
  17. fmt.Printf("i: %d\n", i)
  18. for i2 := 11; i2 <= 13; i2++ {
  19. fmt.Printf("i2: %d\n", i2)
  20. continue re
  21. }
  22. }
  23. }
  24. /* ---- continue ----
  25. i: 1
  26. i2: 11
  27. i2: 12
  28. i2: 13
  29. i: 2
  30. i2: 11
  31. i2: 12
  32. i2: 13
  33. i: 3
  34. i2: 11
  35. i2: 12
  36. i2: 13
  37. ---- continue label ----
  38. i: 1
  39. i2: 11
  40. i: 2
  41. i2: 11
  42. i: 3
  43. i2: 11
  44. */

goto 语句

Go 语言的 goto 语句可以无条件地转移到过程中指定的行 goto 语句通常与条件语句配合使用。可用来实现条件转移, 构成循环,跳出循环体等功能 但是,在结构化程序设计中一般不主张使用 goto 语句, 以免造成程序流程的混乱,使理解和调试程序都产生困难

goto 语法格式与语句流程图

  1. goto label;
  2. ..
  3. .
  4. label: statement;

image.png

在a=15的时候跳过本次循环并回到循环的开始语句LOOP处

  1. package main
  2. import "fmt"
  3. func main() {
  4. // 定义局部变量
  5. var a int = 10
  6. // 循环
  7. LOOP: for a < 20 {
  8. if a == 15 {
  9. // 跳过迭代
  10. a = a + 1
  11. goto LOOP
  12. }
  13. fmt.Printf("a的值为 : %d\n", a)
  14. a++
  15. }
  16. }
  17. /*
  18. a的值为 : 10
  19. a的值为 : 11
  20. a的值为 : 12
  21. a的值为 : 13
  22. a的值为 : 14
  23. a的值为 : 16
  24. a的值为 : 17
  25. a的值为 : 18
  26. a的值为 : 19
  27. */

打印九九乘法表

  1. package main
  2. import "fmt"
  3. func main() {
  4. //print9x()
  5. gotoTag()
  6. }
  7. //嵌套for循环打印九九乘法表
  8. func print9x() {
  9. for m := 1; m < 10; m++ {
  10. for n := 1; n <= m; n++ {
  11. fmt.Printf("%dx%d=%d ",n,m,m*n)
  12. }
  13. fmt.Println("")
  14. }
  15. }
  16. //for循环配合goto打印九九乘法表
  17. func gotoTag() {
  18. for m := 1; m < 10; m++ {
  19. n := 1
  20. LOOP: if n <= m {
  21. fmt.Printf("%dx%d=%d ",n,m,m*n)
  22. n++
  23. goto LOOP
  24. }
  25. fmt.Println("")
  26. n++
  27. }
  28. }
  29. /*
  30. 1x1=1
  31. 1x2=2 2x2=4
  32. 1x3=3 2x3=6 3x3=9
  33. 1x4=4 2x4=8 3x4=12 4x4=16
  34. 1x5=5 2x5=10 3x5=15 4x5=20 5x5=25
  35. 1x6=6 2x6=12 3x6=18 4x6=24 5x6=30 6x6=36
  36. 1x7=7 2x7=14 3x7=21 4x7=28 5x7=35 6x7=42 7x7=49
  37. 1x8=8 2x8=16 3x8=24 4x8=32 5x8=40 6x8=48 7x8=56 8x8=64
  38. 1x9=9 2x9=18 3x9=27 4x9=36 5x9=45 6x9=54 7x9=63 8x9=72 9x9=81
  39. */

使用 goto 退出多层循环

下面这段代码在满足条件时,需要连续退出两层循环,使用传统的编码方式如下

  1. package main
  2. import "fmt"
  3. func main() {
  4. var breakAgain bool
  5. for x := 0; x < 10; x++ {
  6. for y := 0; y < 10; y++ {
  7. // 满足某个条件时, 退出循环
  8. if y == 2 {
  9. breakAgain = true // 设置退出标记
  10. break // 退出本次循环
  11. }
  12. }
  13. // 根据标记, 还需要退出一次循环
  14. if breakAgain { break }
  15. }
  16. fmt.Println("done")
  17. }

将上面的代码使用Go语言的 goto 语句进行优化

  1. package main
  2. import "fmt"
  3. func main() {
  4. for x := 0; x < 10; x++ {
  5. for y := 0; y < 10; y++ {
  6. if y == 2 {
  7. // 跳转到标签
  8. goto breakHere
  9. }
  10. }
  11. }
  12. return // 手动返回, 避免执行进入标签
  13. breakHere: // 标签
  14. fmt.Println("done")
  15. }

使用 goto 集中处理错误

多处错误处理存在代码重复时是非常棘手的,例如:

  1. package main
  2. import "fmt"
  3. func main() {
  4. err := firstCheckError() //执行某逻辑,返回错误
  5. //如果发生错误,打印错误退出进程
  6. if err != nil {
  7. fmt.Println(err)
  8. exitProcess()
  9. return
  10. }
  11. err = secondCheckError()
  12. if err != nil {
  13. fmt.Println(err)
  14. exitProcess()
  15. return
  16. }
  17. fmt.Println("done")
  18. }

在上面代码中,有一部分都是重复的错误处理代码,如果后期在这些代码中添加更多的判断,就需要在这些雷同的代码中依次修改,极易造成疏忽和错误,使用 goto 语句来实现同样的逻辑:

  1. package main
  2. import "fmt"
  3. func main() {
  4. err := firstCheckError()
  5. if err != nil {
  6. goto onExit
  7. }
  8. err = secondCheckError()
  9. if err != nil {
  10. goto onExit
  11. }
  12. fmt.Println("done")
  13. return //阻止正常顺序读取到onExit
  14. onExit:
  15. fmt.Println(err)
  16. exitProcess()
  17. }