go学习笔记(17)for的四种写法

IMG_20211225_151523.jpg

go语言的循环语句都是通过for来实现的,简洁优雅。它支持四种写法:

  • 完整形式
  • 类似于while的条件循环
  • 死循环
  • for-range循环

下面来简单说一下吧,有过其他编程语言经验的话看一眼就明白了。

完整形式

这就是我们最熟悉的for语句了。

1
2
3
for i := 0; i < 10; i++ {
fmt.Println(i)
}

输出:

1
2
3
4
5
6
7
8
9
10
0
1
2
3
4
5
6
7
8
9

和if语句一样,for语句也不需要()。完整形式的for语句主要包含三个部分。第一部分是循环开始前循环变量的声明,这里只支持通过:=来声明,不能用var。第二部分是必须是一个结果为布尔类型的语句,在执行循环主体前判断循环是否应结束。最后一部分可以对循环变量进行赋值,它在每次循环结束后执行。

条件循环

其实就是c/c++、java、js等语言中的while循环,只保留完整形式中的第二部分语句就成了条件循环。

1
2
3
4
5
i := 0
for i < 10 {
fmt.Println(i)
i++
}

输出:

1
2
3
4
5
6
7
8
9
10
0
1
2
3
4
5
6
7
8
9

无限循环

把完整形式下的三个语句都省略就是无限循环了。

1
2
3
for {
fmt.Println("哈哈")
}

输出:

1
2
3
4
5
6
7
8
哈哈
哈哈
哈哈
哈哈
哈哈
哈哈
哈哈
...

break 和 continue

和其他语言类似,go语言也通过break和continue来中断或跳过本轮循环。

break

go语言中的break除了能退出单层循环外,也支持退出多层循环,有点类似于c/c++中goto语句:

1
2
3
4
5
6
7
8
9
START:
for i := 0; i < 5; i++ {
for j := 0; j < 5; j++ {
fmt.Println(i, j)
if i == 1 && j == 2 {
break START
}
}
}

如果要跳出多层循环,首先需要在要退出的那一层作一个标签(label),上面这段中我写了一个START标签,不要忘记最后的:。然后在要跳出循环的地方break 标签就能直接退出标记对应的for循环了。

go语言中如果想实现其他语言中do while循环的功能,可以通过ifbreak来实现:

1
2
3
do {
// 循环中要执行的语句
} while (CONDITION)

等同于:

1
2
3
4
5
6
for {
// 循环中要执行的语句
if !CONDITION {
break
}
}

比如,c++下用do while输出1到5:

1
2
3
4
5
int i=0;
do {
i++;
cout << i << endl;
} while (i<5)

用go来写:

1
2
3
4
5
6
7
8
i := 0
for {
i++
fmt.Println(i)
if !(i < 5) {
break
}
}

输出:

1
2
3
4
5
1
2
3
4
5

continue

continue语句可以跳过其后的语句,直接执行下一轮循环。

1
2
3
4
5
6
for i := 0; i < 5; i++ {
if i == 2 {
continue
}
fmt.Println(i)
}

输出:

1
2
3
4
0
1
3
4

continue也支持通过标签(label)来跳过嵌套循环:

1
2
3
4
5
6
7
8
9
START:
for i := 0; i < 3; i++ {
for j := 0; j < 3; j++ {
if i == 1 && j == 2 {
continue START
}
fmt.Println(i, j)
}
}

输出:

1
2
3
4
5
6
7
8
0 0
0 1
0 2
1 0
1 1
2 0
2 1
2 2

for-range 循环

for-range 循环可以很方便地对一组数据进行遍历,相比其他语言,go语言的 for-range 循环可以同时得到数据的索引和值。

1
2
3
4
arr := []int{1, 2, 3, 4, 5}
for i, num := range arr {
fmt.Println("index:", i, "num:", num)
}

输出:

1
2
3
4
5
index: 0 num: 1
index: 1 num: 2
index: 2 num: 3
index: 3 num: 4
index: 4 num: 5

for-range 循环的写法是这样的,for 索引,值:=range 可遍历的数据 {} ,range关键字会返回数组切片等对应的索引和值,对于map会返回键值对。

在遍历时,如果我们只需要索引或值其中某一个的时候可以用下划线_来占位:

1
2
3
4
arr := []int{1, 2, 3, 4, 5}
for _, num := range arr {
fmt.Println("num:", num)
}

输出:

1
2
3
4
5
num: 1
num: 2
num: 3
num: 4
num: 5

遍历map

在遍历map时要注意,go语言中map的键值对的顺序是随机的,也就是多次遍历map时其结果的顺序是不固定的。

1
2
3
4
5
6
7
8
9
10
11
12
girls := map[string]float64{
"wcy": 1.7,
"xmy": 1.6,
"llj": 1.6,
}
for i := 0; i < 3; i++ {
fmt.Println("loop:", i)
for k, v := range girls {
fmt.Println(k, v)
}
fmt.Println()
}

可能的输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
loop: 0
wcy 1.7
xmy 1.6
llj 1.6

loop: 1
llj 1.6
wcy 1.7
xmy 1.6

loop: 2
xmy 1.6
llj 1.6
wcy 1.7

遍历string

虽然string是由byte组成的,但在for-range遍历时是以rune为单位进行值的遍历,其索引为对应byte的起始索引。

1
2
3
4
5
6
strs := []string{"hi", "你好"}
for _, str := range strs {
for i, r := range str {
fmt.Println(i, r, string(r))
}
}

输出:

1
2
3
4
0 104 h
1 105 i
0 20320
3 22909

for-range 的值是拷贝

在使用for-range循环时,每次得到的值都是一个拷贝,我们不能直接修改其原始值。

1
2
3
4
5
nums := []int{1, 2, 3, 4, 5}
for _, num := range nums {
num = num * 2
}
fmt.Println(nums)

输出:

1
[1 2 3 4 5]

这篇笔记梳理了go语言的四种for的写法,在编程时合理的选用可以简化代码,提高可读性,减少bug。

江达小记