在不少实际问题中有许多具有规律性的重复操作,因此在程序中就需要重复执行某些语句。以下为大多编程语言循环程序的流程图:
Go 语言提供了以下几种类型循环处理语句:
循环类型 | 描述 |
---|---|
for 循环 | 重复执行语句块 |
循环嵌套 | 在 for 循环中嵌套一个或多个 for 循环 |
循环控制语句可以控制循环体内语句的执行过程。GO 语言支持以下几种循环控制语句:
控制语句 | 描述 |
---|---|
break 语句 | 经常用于中断当前 for 循环或跳出 switch 语句 |
continue 语句 | 跳过当前循环的剩余语句,然后继续进行下一轮循环 |
goto 语句 | 将控制转移到被标记的语句 |
for 循环
for 循环是一个循环控制结构,可以执行指定次数的循环 Go 语言的 For 循环有 3 种形式,只有其中的一种使用分号
for 循环的几种形式
和 C 语言、Java、JavaScript的 for 一样
for init; condition; post { }
先对表达式 1 赋初值,判别赋值表达式 init 是否满足给定条件,若其值为真,满足循环条件,则执行循环体内语句,然后执行 post,进入第二次循环,再判别 condition;否则判断 condition 的值为假,不满足条件,就终止for循环,执行循环体外语句
- init 一般为赋值表达式,给控制变量赋初值
- condition 关系表达式或逻辑表达式,循环控制条件
- post 一般为赋值表达式,给控制变量增量(increment)或减量(decrement)
和 C 的 while 一样:
for condition { }
和 C 的 for(;;) 一样:
for { }
for 循环的 range 格式可以对 slice、map、数组、字符串等进行迭代循环
for key, value := range oldMap {
newMap[key] = value
}
//和JavaScript中for中操作对象,数组的对比
var obj = { a: 1, b: 2, c:3 }
for (const [k,v] of Object.entries(obj)) {
console.log(k,v)
}
var obj = { a: 1, b: 2, c:3 }
for (const k in obj) {
console.log(k, obj[k])
}
var obj = [1,2,3]
for (const k in obj) {
console.log(k, obj[k])
}
计算 1 到 10 的数字之和
package main
import "fmt"
func main() {
sum := 0
for i := 0; i <= 10; i++ {
sum += i
}
fmt.Println(sum)
}
// 输出结果:55
sum 小于 10 的时候计算 sum 自相加后的值
package main
import "fmt"
func main() {
sum := 1
for ;sum <= 10; {
sum += sum
}
fmt.Println(sum)
// 这样写也可以,更像 While 语句形式
for sum <= 10{
sum += sum
}
fmt.Println(sum)
}
无限循环
如果循环中条件语句永远不为 false 则会进行无限循环,我们可以通过 for 循环语句中只设置一个条件表达式来执行无限循环:
package main
import "fmt"
func main() {
sum := 0
for;;{ fmt.Printf(";;\n"); }
for true { fmt.Printf("这是条件为true的无限循环\n"); }
for {
sum++ // 无限循环下去
fmt.Printf("无参数的无限循环。\n");
}
fmt.Println(sum) // 无法输出
}
⚠️ 要停止无限循环,可以在命令窗口按下ctrl+c
循环嵌套
Go 语言允许用户在循环内使用循环, 以下为 Go 语言嵌套循环的格式:
for [(init; condition; increment) | condition | Range] {
for [(init; condition; increment) | condition | Range]{
statement(s);
}
statement(s);
}
使用循环嵌套来输出 2 到 100 间的素数
package main
import "fmt"
func main() {
// 定义局部变量
var i, j int
for i=2; i < 100; i++ {
for j=2; j*j <= i; j++ {
if(i%j==0) {
break; // 如果发现因子,则不是素数
}
}
if(j*j > i) {
fmt.Printf("%d 是素数\n", i);
}
}
}
/*
2 是素数
3 是素数
5 是素数
7 是素数
11 是素数
13 是素数
17 是素数
19 是素数
23 是素数
29 是素数
31 是素数
*/
for-range 范围(Range)
Go 语言中 range 关键字用于 for 循环中迭代数组(array)、切片(slice)、集合(map)、通道(channel)的元素。在数组和切片中它返回元素的索引和索引对应的值,在集合中返回 key-value 键值对
这种格式的循环可以对字符串、数组、切片等进行迭代输出元素
package main
import "fmt"
func main() {
strings := []string{"google", "runoob"}
for i, s := range strings {
fmt.Println(i, s)
}
numbers := [6]int{1, 2, 3, 5}
for i,x:= range numbers {
fmt.Printf("第%d位 x 的值 = %d\n", i,x)
}
}
/*
0 google
1 runoob
第0位 x 的值 = 1
第1位 x 的值 = 2
第2位 x 的值 = 3
第3位 x 的值 = 5
第4位 x 的值 = 0
第5位 x 的值 = 0
*/
package main
import "fmt"
func main() {
//这是我们使用range去求一个slice的和。使用数组跟这个很类似
nums := []int{2, 3, 4}
sum := 0
for _, num := range nums[1:] {
sum += num
}
fmt.Println("sum:", sum)
//在数组上使用range将传入index和值两个变量。上面那个例子我们不需要使用该元素的序号,所以我们使用空白符"_"省略了。有时侯我们确实需要知道它的索引。
for i, num := range nums {
if num == 3 {
fmt.Println("index:", i)
}
}
//range也可以用在map的键值对上
kvs := map[string]string{"a": "apple", "b": "banana"}
for k, v := range kvs {
fmt.Printf("%s -> %s\n", k, v)
}
//range也可以用来枚举Unicode字符串。第一个参数是字符的索引,第二个是字符(Unicode的值)本身。
for i, c := range "go" {
fmt.Println(i, c)
}
c := make(chan int, 12)
go fibonacci(cap(c), c)
for i := range c { fmt.Println(i) }
}
func fibonacci(n int, c chan int) {
x, y := 0, 1
for i := 0; i <= n; i++ {
c <- x
x, y = y, x+y
}
close(c)
}
/* 以上实例运行输出结果为:
sum: 7
index: 1
a -> apple
b -> banana
0 103
1 111
0
1
1
2
3
5
8
13
21
34
55
89
144
*/
break 语句
Go 语言中 break 语句用于以下两方面:
- 用于循环语句中跳出循环,并开始执行循环之后的语句。
- break 在 switch(开关语句)中在执行一条 case 后跳出语句的作用。
- 在多重循环中,可以用标号 label 标出想 break 的循环
break 语法格式与语句流程图
break;
在变量 a 大于 15 的时候跳出循环
package main
import "fmt"
func main() {
// 定义局部变量
var a int = 10
// for 循环
for a < 20 {
fmt.Printf("a的值为 : %d\n", a);
a++;
if a > 15 {
break; //使用 break 语句跳出循环
}
}
}
/*
a的值为 : 10
a的值为 : 11
a的值为 : 12
a的值为 : 13
a的值为 : 14
a的值为 : 15
*/
有多重循环,使用标记和不使用标记的区别
package main
import "fmt"
func main() {
// 不使用标记
fmt.Println("---- break ----")
for i := 1; i <= 3; i++ {
fmt.Printf("i: %d\n", i)
for i2 := 11; i2 <= 13; i2++ {
fmt.Printf("i2: %d\n", i2)
break
}
}
// 使用标记
fmt.Println("---- break label ----")
re:
for i := 1; i <= 3; i++ {
fmt.Printf("i: %d\n", i)
for i2 := 11; i2 <= 13; i2++ {
fmt.Printf("i2: %d\n", i2)
break re
}
}
}
/*
---- break ----
i: 1
i2: 11
i: 2
i2: 11
i: 3
i2: 11
---- break label ----
i: 1
i2: 11
*/
continue 语句
Go 语言的 continue 语句 有点像 break 语句。但是 continue 不是跳出循环,而是跳过当前循环执行下一次循环语句 for 循环中,执行 continue 语句会触发 for 增量语句的执行 在多重循环中,可以用标号 label 标出想 continue 的循环
continue 语法格式与语句流程图
continue;
在a=15 的时候跳过本次循环执行下一次循环
package main
import "fmt"
// func main() {
// var a int = 10 // 定义局部变量
// for a < 20 {
// if a == 15 {
// a = a + 1;
// continue; // 跳过此次循环,即使跳过一次下面的打印
// }
// fmt.Printf("a 的值为 : %d\n", a);
// a++;
// }
// }
func main() {
for a :=10 ;a < 20;a++ {
if a == 15 {
continue; // 跳过此次循环,即使跳过一次下面的打印
}
fmt.Printf("a 的值为 : %d\n", a);
}
}
/*
a 的值为 : 10
a 的值为 : 11
a 的值为 : 12
a 的值为 : 13
a 的值为 : 14
a 的值为 : 16
a 的值为 : 17
a 的值为 : 18
a 的值为 : 19
*/
多重循环,使用标记和不使用标记的区别
package main
import "fmt"
func main() {
// 不使用标记
fmt.Println("---- continue ---- ")
for i := 1; i <= 3; i++ {
fmt.Printf("i: %d\n", i)
for i2 := 11; i2 <= 13; i2++ {
fmt.Printf("i2: %d\n", i2)
continue
}
}
// 使用标记
fmt.Println("---- continue label ----")
re:
for i := 1; i <= 3; i++ {
fmt.Printf("i: %d\n", i)
for i2 := 11; i2 <= 13; i2++ {
fmt.Printf("i2: %d\n", i2)
continue re
}
}
}
/* ---- continue ----
i: 1
i2: 11
i2: 12
i2: 13
i: 2
i2: 11
i2: 12
i2: 13
i: 3
i2: 11
i2: 12
i2: 13
---- continue label ----
i: 1
i2: 11
i: 2
i2: 11
i: 3
i2: 11
*/
goto 语句
Go 语言的 goto 语句可以无条件地转移到过程中指定的行 goto 语句通常与条件语句配合使用。可用来实现条件转移, 构成循环,跳出循环体等功能 但是,在结构化程序设计中一般不主张使用 goto 语句, 以免造成程序流程的混乱,使理解和调试程序都产生困难
goto 语法格式与语句流程图
goto label;
..
.
label: statement;
在a=15的时候跳过本次循环并回到循环的开始语句LOOP处
package main
import "fmt"
func main() {
// 定义局部变量
var a int = 10
// 循环
LOOP: for a < 20 {
if a == 15 {
// 跳过迭代
a = a + 1
goto LOOP
}
fmt.Printf("a的值为 : %d\n", a)
a++
}
}
/*
a的值为 : 10
a的值为 : 11
a的值为 : 12
a的值为 : 13
a的值为 : 14
a的值为 : 16
a的值为 : 17
a的值为 : 18
a的值为 : 19
*/
打印九九乘法表
package main
import "fmt"
func main() {
//print9x()
gotoTag()
}
//嵌套for循环打印九九乘法表
func print9x() {
for m := 1; m < 10; m++ {
for n := 1; n <= m; n++ {
fmt.Printf("%dx%d=%d ",n,m,m*n)
}
fmt.Println("")
}
}
//for循环配合goto打印九九乘法表
func gotoTag() {
for m := 1; m < 10; m++ {
n := 1
LOOP: if n <= m {
fmt.Printf("%dx%d=%d ",n,m,m*n)
n++
goto LOOP
}
fmt.Println("")
n++
}
}
/*
1x1=1
1x2=2 2x2=4
1x3=3 2x3=6 3x3=9
1x4=4 2x4=8 3x4=12 4x4=16
1x5=5 2x5=10 3x5=15 4x5=20 5x5=25
1x6=6 2x6=12 3x6=18 4x6=24 5x6=30 6x6=36
1x7=7 2x7=14 3x7=21 4x7=28 5x7=35 6x7=42 7x7=49
1x8=8 2x8=16 3x8=24 4x8=32 5x8=40 6x8=48 7x8=56 8x8=64
1x9=9 2x9=18 3x9=27 4x9=36 5x9=45 6x9=54 7x9=63 8x9=72 9x9=81
*/
使用 goto 退出多层循环
下面这段代码在满足条件时,需要连续退出两层循环,使用传统的编码方式如下
package main
import "fmt"
func main() {
var breakAgain bool
for x := 0; x < 10; x++ {
for y := 0; y < 10; y++ {
// 满足某个条件时, 退出循环
if y == 2 {
breakAgain = true // 设置退出标记
break // 退出本次循环
}
}
// 根据标记, 还需要退出一次循环
if breakAgain { break }
}
fmt.Println("done")
}
将上面的代码使用Go语言的 goto 语句进行优化
package main
import "fmt"
func main() {
for x := 0; x < 10; x++ {
for y := 0; y < 10; y++ {
if y == 2 {
// 跳转到标签
goto breakHere
}
}
}
return // 手动返回, 避免执行进入标签
breakHere: // 标签
fmt.Println("done")
}
使用 goto 集中处理错误
多处错误处理存在代码重复时是非常棘手的,例如:
package main
import "fmt"
func main() {
err := firstCheckError() //执行某逻辑,返回错误
//如果发生错误,打印错误退出进程
if err != nil {
fmt.Println(err)
exitProcess()
return
}
err = secondCheckError()
if err != nil {
fmt.Println(err)
exitProcess()
return
}
fmt.Println("done")
}
在上面代码中,有一部分都是重复的错误处理代码,如果后期在这些代码中添加更多的判断,就需要在这些雷同的代码中依次修改,极易造成疏忽和错误,使用 goto 语句来实现同样的逻辑:
package main
import "fmt"
func main() {
err := firstCheckError()
if err != nil {
goto onExit
}
err = secondCheckError()
if err != nil {
goto onExit
}
fmt.Println("done")
return //阻止正常顺序读取到onExit
onExit:
fmt.Println(err)
exitProcess()
}