循环语句
Go语言中只有for循环,Go中的for 循环可以实现其他语言中的 for ,while 和遍历迭代
for循环
for 循环是一个循环控制结构,可以执行指定次数的循环。
语法
Go 语言中的 For 循环有四种形式,只有其中的一种使用分号
第一种和普通的for 循环一样
for init; condition; post { }
//init 一般为赋值表达式,给控制变量赋初值
//condition 关系表达式或者或者逻辑表达式,循环控制条件
// post 一般为赋值表达式,给给控制变量增量或者减量
第二种和 while 类似的for循环
for condition { }
第三中无限循环
//无限循环 相当于 for true {}
for { }
for 循环中的 `range`格式可以对 slice、map 、数组、字符串 等进行迭代循环。格式如下:
for key, value := range oldMap {
newMap[key] = value
}
for 语句执行的过程
- 先对表达式init 赋初值;
- 判别赋值表达式 init 是否满足给定条件condition 若其值为真,满足循环条件,则执行循环体内语句,然后执行post, 进入第二次循环,判别condition 的值为假,不满足条件就终止for循环。
循环嵌套
Go 语言中允许循环嵌套:
for [condition | ( init; condition; increment ) | Range]
{
for [condition | ( init; condition; increment ) | Range]
{
statement(s);
}
statement(s);
}
循环控制
循环控制语句可以控制循环体内语句的执行过程。
| 控制语句 | 描述 |
|---|---|
| break | 经常用于中断当前for 循环或者跳出 switch 语句 |
| continue | 跳过当前循环的剩余语句,然后继续进行下一轮循环 |
| goto | 将控制转移到被标记的语句 |
- 这三个语句都可以配合标签label 使用
- 标签区分大小写,定义以后若不使用会造成编译错误
- continue 、break 配合标签 label 可以用于多层循环跳出
- goto 是调整执行位置,于 continue、break 配合标签 label 的结果并不相同
break
- 用于循环语句中跳出循环,并开始执行循环之后的语句
- break 在 switch (开关语句) 中在执行一条case 后跳出语句的作用
- 在多重循环中可以用标号 label 标出像break 的循环。
例子:
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: //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
}
}
}
continue
- continue 语句有点像 break 语句。但是continue 不是跳出循环,而是跳过当前循环执行的下一个循环语句。
- for 循环中,执行 continue 语句会触发 for 增量语句increment 的执行。
- 在多重循环中,可以用标号 label 标出想 continue 的循环。
goto
- go 语言中的 goto 语句 可以无条件地转移到过程中指定的行。
- goro 语句通常与条件语句配合使用。可以用来实现条件转移,构成循环,跳出循环等功能
但是在结构化程序设计中一般不主张使用go 语句,以免造成流程的混乱,使理解和调试程序都产生困难。
goto 的语法格式:
goto label;
..
.
label: statement;
循环语句range
语法
Golang range 类似迭代器操作,返回(索引,值)(键,值) for 循环的 range 格式可以对slice、map、数组、字符串 等进行迭代循环:
for key, value := range oldMap {
newMap[key] = value
}
选择接收值
可只设置一个接收器忽略 range 返回的第二个值,或使用 “_” 这个特殊变量忽略第一个返回值。没有接收器就是忽略全部返回值。
package main
func main() {
s := "abc"
// 忽略 2nd value,支持 string/array/slice/map。
for i := range s {
println(s[i])
}
// 忽略 index。
for _, c := range s {
println(c)
}
// 忽略全部返回值,仅迭代。
for range s {
}
m := map[string]int{"a": 1, "b": 2}
// 返回 (key, value)。
for k, v := range m {
println(k, v)
}
}
range 会复制值对象
package main
import "fmt"
func main() {
a := [3]int{0, 1, 2}
for i, v := range a { // range复制数组a。index、value 都是从复制品中取出。
if i == 0 { // 我们先修改原数组。
a[1], a[2] = 999, 999
fmt.Println(a) // 确认修改有效,输出 [0, 999, 999]。
}
a[i] = v + 100 // range 使用复制品中取出的 value 修改原数组。
}
fmt.Println(a)
}
输出结果:
[0 999 999]
[100 101 102] //而不是[100 1099 1099]
建议改成引用数据类型,其底层数组不会被复制
package main
func main() {
s := []int{1, 2, 3, 4, 5}
for i, v := range s { // s是切片,一种引用类型。range复制slice的struct:{ pointer, len, cap }。
if i == 0 {
s = s[:3] // 对 slice 的修改,不会影响 range。
s[2] = 100 // 对底层数据的修改。
}
println(i, v)
}
}
输出结果:
0 1
1 2
2 100
3 4
4 5
另外两种引用类型 map、channel 是指针包装,而不像 slice 是 struct。
for 和 for range 的区别
它们的区别主要是使用场景不同。
for可以:
- 遍历array和slice
- 遍历key为整型递增的map
- 遍历string
for range可以完成所有for可以做的事情,却能做到for不能做的,包括
- 遍历key为string类型的map并同时获取key和value
- 遍历channel