当前位置: 首页 > 工具软件 > golang-101 > 使用案例 >

《10节课学会Golang-04-流程控制》

韦锦程
2023-12-01

微信关注【面试情报局】我们一起干翻面试官, 回复golang获取本文源码
视频地址:b站 10节课学会Golang,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 < 10 条件表达式:在每次迭代前求值
	// i++    后置语句:在每次迭代的结尾执行
	for i := 0; i < 10; i++ {
		sum += i
	}
	fmt.Printf("\tsum: %d\n", 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: %d\n", 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:%c\n", i, v)
	}
}

func main() {
	fmt.Println("Steps3():")
	Steps3()
}

Rangefor遍历的区别

package main

import "fmt"

// Steps4 range和for遍历的区别
func Steps4() {
	str := "Golang 教程"
	for i := 0; i < len(str); i++ {
		fmt.Printf("\ti:%d,v:%c\n", i, str[i])
	}

	for i, v := range str { // 遍历字符串
		fmt.Printf("\ti:%d,v:%c\n", i, v)
	}
}

func main() {
	fmt.Println("Steps4():")
	Steps4()
}

For循环中的breakcontinue

package main

import "fmt"

// Steps5 for 循环中的 break 和 continue
func Steps5() {
	for i := 0; i < 10; i++ {
		if i == 5 { // 下一小节介绍
			fmt.Printf("\ti:%d, continue\n", i)
			continue
		}

		if i == 6 {
			fmt.Printf("\ti:%d, break\n", i)
			break
		}
	}
}

func main() {
	fmt.Println("Steps5():")
	Steps5()
}
  • Goto实现循环
package main

import "fmt"

// Steps6 goto 实现循环
func Steps6() {
	i := 0

Next: // 跳转标签声明
	fmt.Printf("\ti:%d\n", i)
	i++
	if i < 5 {
		goto Next // 跳转
	}
}

func main() {
	fmt.Println("Steps6():")
	Steps6()
}

goto 语句用于无条件跳转到程序的另一个位置。其中,Next 是一个标识符,用于指定要跳转到的位置。注意,Next 必须在当前函数内部定义。

If判断

GolangIf语句和其它语言语义相同

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 < 2 flag:", flag)
	case flag < 4:
		fmt.Println("\tcase flag < 4, flag:", flag)
	case flag > 6, flag < 10: // case 可以设置多个条件。flag 大于6或小于10都能执行当前case流程
		fmt.Println("\tcase flag > 6 || flag < 10 flag:", flag)
	case flag > 6 && flag < 10: // case 可以设置组合条件。flag 大于6并且小于10都才能执行当前case流程
		fmt.Println("\tcase flag > 6 || flag < 10 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 < 2 flag:", flag)
		case flag < 4:
			fmt.Println("\tcase flag < 4, flag:", flag)
		case flag > 6, flag < 8: // case 可以设置多个条件。flag 大于6或小于10都能执行当前case流程
			fmt.Println("\tcase flag > 6 || flag < 8 flag:", flag)
		case flag > 6 && flag < 10: // case 可以设置组合条件。flag 大于6并且小于10都才能执行当前case流程
			fmt.Println("\tcase flag > 6 && flag < 10 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(" world\n")

	fmt.Printf("\thello")
}

func main() {
	fmt.Println("Steps1():")
	Steps1()
}

在上面的示例中,defer 语句都在函数中定义,它们会在函数返回之前执行。defer fmt.Printf(" world\n"),会在func Steps1()返回前执行。

通过defer延时打印数字:

package main

import "fmt"

// Steps2 推迟的函数调用会被压入一个栈中。当外层函数返回时,被推迟的函数会按照后进先出的顺序调用。
func Steps2() {
	fmt.Println("\tbegin")
	for i := 0; i < 3; i++ {
		defer fmt.Println("\t\ti:", i)
		fmt.Printf("\t\ti:%d\n", 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

recoverGo 语言中用于从 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

思考题

  1. 计算 100000 以内偶数,并且不是 4 的倍数外的所有数值和
  2. 定义函数Calculation通过Switch实现加减乘除
// 参考
func Calculation(option byte, a float64,b float64) float64{
	switch option {
	case '-':
	.......	
	}
}

  1. 通过For循环打印如下图形
*
**
***
****
*****
******

参考

https://gfw.go101.org/article/control-flows.html


系列文章

《10节课学会Golang-01-Package》
《10节课学会Golang-02-变量与常量》
《10节课学会Golang-03-函数》
《10节课学会Golang-04-流程控制》
《10节课学会Golang-05-结构体》
《10节课学会Golang-06-数组与切片》
《10节课学会Golang-07-Map》
《10节课学会Golang-08-Interface》
《10节课学会Golang-09-Goroutine》
《10节课学会Golang-10-Channel》

微信关注【面试情报局】我们一起干翻面试官, 回复golang获取本文源码

 类似资料: