本文是全系列中第4 / 10篇:10节课学会Go
流程控制
循环语句
Golang
中有三种类型的循环语句:for
循环、range
循环和 goto
语句。
- For循环
Golang
中通过For
关键字来定义一个循环并且只有For
关键字(Golang
中没有while
关键字),格式
for initialization; condition; post { // do something }
其中,initialization
是循环开始前的初始化语句,condition
是循环条件,post
是每次循环结束后执行的语句。这些语句都是可选的,如果不需要可以省略。
package main
import "fmt"
// Steps1 通过 for 循环累加 0-9
func Steps1() {
sum := 0
// for 循环
// i := 0 初始化语句:在第一次迭代前执行
// i // i++ 后置语句:在每次迭代的结尾执行
for i := 0; i 10; i++ {
sum += i
}
fmt.Printf("tsum: %dn", sum)
}
func main() {
fmt.Println("Steps1():")
Steps1()
}
通过For
实现类似while
的语义
package main
import "fmt"
// Steps2 for循环初始化语句和后置语句不是必须的
func Steps2() {
sum := 0
// 初始化语句和后置语句是可选的
for sum 5 {
sum++
}
fmt.Printf("tsum: %dn", sum)
}
func main() {
fmt.Println("Steps2():")
Steps2()
}
- Range
通过Range
关键字来遍历字符串,数组,切片或映射
package main
import "fmt"
// Steps3 range形式的循环遍历
func Steps3() {
str := "Golang Tutorial"
for i, v := range str { // 遍历字符串
fmt.Printf("ti:%d,v:%cn", i, v)
}
}
func main() {
fmt.Println("Steps3():")
Steps3()
}
Range
和for
遍历的区别
package main
import "fmt"
// Steps4 range和for遍历的区别
func Steps4() {
str := "Golang 教程"
for i := 0; i len(str); i++ {
fmt.Printf("ti:%d,v:%cn", i, str[i])
}
for i, v := range str { // 遍历字符串
fmt.Printf("ti:%d,v:%cn", i, v)
}
}
func main() {
fmt.Println("Steps4():")
Steps4()
}
For
循环中的break
和continue
package main
import "fmt"
// Steps5 for 循环中的 break 和 continue
func Steps5() {
for i := 0; i 10; i++ {
if i == 5 { // 下一小节介绍
fmt.Printf("ti:%d, continuen", i)
continue
}
if i == 6 {
fmt.Printf("ti:%d, breakn", i)
break
}
}
}
func main() {
fmt.Println("Steps5():")
Steps5()
}
- Goto实现循环
package main
import "fmt"
// Steps6 goto 实现循环
func Steps6() {
i := 0
Next: // 跳转标签声明
fmt.Printf("ti:%dn", i)
i++
if i 5 {
goto Next // 跳转
}
}
func main() {
fmt.Println("Steps6():")
Steps6()
}
goto
语句用于无条件跳转到程序的另一个位置。其中,Next
是一个标识符,用于指定要跳转到的位置。注意,Next
必须在当前函数内部定义。
If判断
Golang
中If
语句和其它语言语义相同
package main
import "fmt"
// if 分支打印不同字符
func main() {
flag := 10
if flag > 5 { // 判断表达式
fmt.Println("flag:", flag)
}
flag = 14
//flag = 16
//flag = 21
if flag > 20 {
fmt.Println("flag:", flag)
} else if flag 15 {
fmt.Println("flag:", flag)
} else {
fmt.Println("flag:", flag)
}
}
Switch选择
Golang
中可以通过switch-case
来实现分支选择, 每一个case
分支都是唯一的,从上往下逐一判断,直到匹配为止,如果某些case
分支条件重复了,编译会报错。
每个case
分支最后自带break
效果,匹配成功就不会执行其它case
; 如果所有分支都没有匹配成功并且又定义了default
分支, 那最终会走default
分支。
case
后面的值可以是任何常量表达式,例如字符串、数字、布尔值等等。
示例一:
package main
import "fmt"
// Steps1 基础用法
func Steps1() {
flag := 1
//flag = 2
//flag = 3
//flag = 4
//flag = 5
switch flag { // flag 待判断条件
case 1: // 条件 flag 是否等于 1。是:执行该case下的流程,否:选择其它满足条件的 case
fmt.Println("tcase:", flag)
// Golang 中每个 case 后面不需要 break 语句。当然 return 是可选的
case 2:
fmt.Println("tcase:", flag)
case 3, 4: // case 可以设置多个条件。只要 flag 等于3或4都能执行当前case流程
fmt.Println("tcase:", flag)
case 5:
fmt.Println("tcase:", flag)
return
default: // 当所有case都无法满足, 会执行 default 的流程。如果没有 default 那当前 switch 执行完成
fmt.Println("tdefault:", flag)
}
}
// Steps2 switch 条件可以是任何支持判断的类型
func Steps2() {
flag := "Hello"
flag = "World"
flag = "Golang"
flag = "Tutorial"
flag = "Process"
switch flag { // flag 待判断条件
case "Hello": // 条件 flag 是否等于 "Hello"。是:执行该case下的流程,否:选择其它满足条件的 case
fmt.Println("tcase:", flag)
case "World":
fmt.Println("tcase:", flag)
case "Golang", "Tutorial": // case 可以设置多个条件。只要 flag 等于"Golang"或"tutorial"都能执行当前case流程
fmt.Println("tcase:", flag)
default: // 当所有case都无法满足, 会执行 default 的流程。如果没有 default 那当前 switch 执行完成
fmt.Println("tdefault:", flag)
}
}
// switch 是编写一连串 if - else 语句的简便方法
func main() {
fmt.Println("Steps1():")
Steps1()
fmt.Println("Steps2():")
Steps2()
}
示例二:
package main
import "fmt"
// Steps3 switch true 可以将一长串 if-then-else 写得更加清晰
func Steps3() {
flag := 1
//flag = 2
//flag = 3
//flag = 4
//flag = 5
//flag = 7
switch { // flag 待判断条件
case flag 2: // 条件 flag 是否小于 2。是:执行该case下的流程,否:选择其它满足条件的 case
fmt.Println("tcase flag , flag)
case flag 4:
fmt.Println("tcase flag , flag)
case flag > 6, flag 10: // case 可以设置多个条件。flag 大于6或小于10都能执行当前case流程
fmt.Println("tcase flag > 6 || flag , flag)
case flag > 6 && flag 10: // case 可以设置组合条件。flag 大于6并且小于10都才能执行当前case流程
fmt.Println("tcase flag > 6 || flag , flag)
}
}
// Steps4 for + switch 的使用
func Steps4() {
for flag := 0; flag 11; flag++ {
switch { // flag 待判断条件
case flag 2: // 条件 flag 是否小于 2。是:执行该case下的流程,否:选择其它满足条件的 case
fmt.Println("tcase flag , flag)
case flag 4:
fmt.Println("tcase flag , flag)
case flag > 6, flag 8: // case 可以设置多个条件。flag 大于6或小于10都能执行当前case流程
fmt.Println("tcase flag > 6 || flag , flag)
case flag > 6 && flag 10: // case 可以设置组合条件。flag 大于6并且小于10都才能执行当前case流程
fmt.Println("tcase flag > 6 && flag , flag)
}
}
}
// switch 是编写一连串 if - else 语句的简便方法
func main() {
fmt.Println("Steps3():")
Steps3()
fmt.Println("Steps4():")
Steps4()
}
Defer
Golang
中通过defer
来实现延时调用, 用于指定一个函数调用在函数返回之前执行。常用来做一些收尾工作: 关闭连接,清理资源
package main
import "fmt"
// defer作用:
// 释放占用的资源
// 捕捉处理异常 recover
// Steps1 defer 语句会将函数推迟到外层函数返回之后执行。
// 推迟调用的函数其参数会立即求值,但直到外层函数返回前该函数都不会被调用。
func Steps1() {
defer fmt.Printf(" worldn")
fmt.Printf("thello")
}
func main() {
fmt.Println("Steps1():")
Steps1()
}
在上面的示例中,defer
语句都在函数中定义,它们会在函数返回之前执行。defer fmt.Printf(" worldn")
,会在func Steps1()
返回前执行。
通过defer延时打印数字:
package main
import "fmt"
// Steps2 推迟的函数调用会被压入一个栈中。当外层函数返回时,被推迟的函数会按照后进先出的顺序调用。
func Steps2() {
fmt.Println("tbegin")
for i := 0; i 3; i++ {
defer fmt.Println("tti:", i)
fmt.Printf("tti:%dn", i)
}
fmt.Println("tend")
}
func main() {
fmt.Println("Steps2():")
Steps2()
}
/*
----- -----
| | | |
| | V | | |
| | | V
| ... |
| 3 |
| 2 |
| 1 |
| 0 |
—————
*/
/* 执行结果
begin
i:0
i:1
i:2
end
i: 2
i: 1
i: 0
*/
defer
语句中引用函数中的变量,会在函数调用是根据最新的值计算(Steps3
); defer
语句中的函数参数会在 defer
语句定义时计算,而不是在函数调用时计算(Steps4
)。
package main
import "fmt"
func Steps3() {
fmt.Println("tbegin")
x := 2
defer func() {
x = x * x
fmt.Println("tx =", x) // x = 9
}()
fmt.Println("tend")
x = 3
}
func Steps4() {
fmt.Println("tbegin")
x := 2
defer func(x int) {
x = x * x
fmt.Println("tx =", x) // x = 4
}(x)
fmt.Println("tend")
x = 3
}
func main() {
fmt.Println("Steps3():")
Steps3()
fmt.Println("Steps4():")
Steps4()
}
recover
recover
是 Go
语言中用于从 panic
恢复的内置函数。
当函数中发生 panic
时,程序会停止执行当前函数的代码,但是会继续执行当前函数的 defer
语句,直到所有的 defer
语句都执行完毕。如果其中某个 defer
语句调用了 recover
,则程序会停止向上传递 panic
,并在调用 recover
的地方继续执行代码,而不是终止程序的运行。
package main
import "fmt"
// 捕捉处理异常 recover
func main() {
defer func() {
if err := recover(); err != nil {
// 捕捉错误 run err: runtime error: integer divide by zero
fmt.Println("run err:", err)
}
}()
a := 10
b := 0
_ = a / b
fmt.Println("return")
}
在这个示例中,我们通过a/b
,因为b
是 0 所以会导致程序 panic
。但是,我们在 defer
语句中使用了 recover
,当程序 panic
时,会执行 defer
语句中的匿名函数。这个匿名函数调用 recover
函数,如果有错误信息,则输出错误信息,并恢复程序的正常执行。
需要注意的是,recover
函数只能在 defer
函数中使用,否则会引发运行时错误。此外,recover
函数只会在发生 panic
时返回错误信息,如果没有 panic
,则会返回 nil
。
思考题
- 计算 100000 以内偶数,并且不是 4 的倍数外的所有数值和
2. 定义函数Calculation
通过Switch
实现加减乘除
// 参考
func Calculation(option byte, a float64,b float64) float64{
switch option {
case '-':
.......
}
}
3. 通过For
循环打印如下图形
*
**
***
****
*****
******
参考
https://gfw.go101.org/article/control-flows.html