栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > .Net

Golang——流程控制语句、跳转控制语句细节

.Net 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Golang——流程控制语句、跳转控制语句细节

流程控制语句分类:
  1. 顺序结构:普通的代码,由上而下依次执行
  2. 分支结构(if, switch)
  3. 循环结构(for)
if语句:

格式1:

if (关系表达式) {
    语句体;	
}

执行流程:

  1. 首先计算关系表达式的值
  2. 如果关系表达式的值为true就执行语句体
  3. 如果关系表达式的值为false就不执行语句体
  4. 继续执行后面的语句内容

if语句格式2

if (关系表达式) {
    语句体1;	
} else {
    语句体2;	
}

执行流程:

  1. 首先计算关系表达式的值
  2. 如果关系表达式的值为true就执行语句体1
  3. 如果关系表达式的值为false就执行语句体2
  4. 继续执行后面的语句内容

if语句格式3

if (关系表达式1) {
    语句体1;	
} else if (关系表达式2) {
    语句体2;	
} 
…
else {
    语句体n+1;
}
  1. 首先计算关系表达式1的值
  2. 如果值为true就执行语句体1;如果值为false就计算关系表达式2的值
  3. 如果值为true就执行语句体2;如果值为false就计算关系表达式3的值
  4. …
  5. 如果没有任何关系表达式为true,就执行语句体n+1。

演示:

定义一个在0~100之间的变量a, 判断分数在什么范围,90 ~ 100优秀,80 ~ 89良好,70 ~ 79中等,60 ~ 69及格,0 ~ 59请努力加油!

func main() {
	// 定义一个在0~100之间的变量a, 判断分数在什么范围,90 ~ 100优秀,80 ~ 89良好,70 ~ 79中等,60 ~ 69及格,0 ~ 59请努力加油!
	var a float64
	fmt.Println("请输入成绩:")
	fmt.Scan(&a)

	if a >= 90 && a <= 100 {
		fmt.Println("优秀")
	} else if a >= 80 && a <= 89 {
		fmt.Println("良好")
	} else if a >= 70 && a <= 79 {
		fmt.Println("中等")
	} else if a >= 60 && a <= 69 {
		fmt.Println("及格")
	} else if a >= 0 && a <= 59 {
		fmt.Println("有脸吃饭?")
	} else {
		fmt.Println("成绩需在0~100之间")
	}
}
if使用细节:
  • 条件表达式值必须是bool类型,可省略括号,且左大括号不能另起一行
  • 小心因条件匹配顺序写出死代码

死亡代码是指永远不会被执行的代码,通常用代码覆盖率测试进行检查。

func main() {
	x := 8
	if x > 5 { 			//优先判断,条件表达式结果为true。
		println("a")
	} else if x > 7 { 	// 永远不会执行
		println("b")
	}
}

尽可能减少代码嵌套,让正常逻辑处于相同层次。以常见的有数错误返回值处理为例

func check(x int) error {
	if x <= 0 {
		return errors.New("x <= 0")
	}
	return nil
}

func main() {
	x := 10
	if err := check(x); err == nil {
		x++
		println(x)
	} else {
		log.Fatalln(err)
	}
}

很显然,if块承担了两种逻辑:错误处理和后续正常操作。基于重构原则,我们应该保持代码块功能的单一性。如此,if块仅完成条件检查和错误处理,相关正常逻辑保持在同一层次。当有人试图通过 阅读这段代码来获知逻辑流程时,完全可忽略if块细节。同时,单一功能可提升代码可维 护性,更利于拆分重构

func main() {
	x := 10
	if err := check(x); err != nil {
		log.Fatalln(err)
	}
	x++
	println(x)
}

对于某些过于复杂的组合条件,建议将其重构为函数

func main() {
	s := "9"
	if n, err := strconv.ParseInt(s, 10, 64); err != nil || n < 0 || n > 10 || n%2 != 0 {
		log.Fatalln("invalid number")
	}
	println("ok")
}

函数调用虽然有一些性能损失,可却让主流程变得更加清爽。况且,条件语句独立之后, 更易于测试,同样会改善代码可维护性

func check(s string) error {
	n, err := strconv.ParseInt(s, 10, 64)
	if err != nil || n < 0 || n > 10 || n%2 != 0 {
		return errors.New("invalid number")
	}
	return nil
}
func main() {
	s := "9"
	if err := check(s); err != nil {
		log.Fatalln(err)
	}
	println("ok")
}

switch语句:
  • go语言中的switch无须执行break,但是默认在每个case最后都有break,case执行完毕后自动跳出Switch。如需要贯通后续的case须使用fallthrough强制执行后面的case代码
  • 相邻的空case不构成多条件匹配
  • 不能出现重复的case常量值

格式:

 switch (表达式) {
  	case 1:
  		语句体1
  	case 2:
  		语句体2
  	...
  	default:
  		语句体n+1
  }

执行流程:

  • 首先计算出表达式的值
  • 其次,和case依次比较,一旦有对应的值,就会执行相应的语句,在执行的过程中,遇到break就会结束。
  • 最后,如果所有的case都和表达式的值不匹配,就会执行default语句体部分,然后程序结束掉。 所以建议将default放在末尾

演示:

func switchDemo() {
	// 定义一个在0~100之间的变量a, 判断分数在什么范围,90 ~ 100优秀,80 ~ 89良好,70 ~ 79中等,60 ~ 69及格,0 ~ 59请努力加油!
	var score float64
	fmt.Println("请输入成绩:")
	fmt.Scan(&score)

	switch {
	case score >= 90 && score <= 100:
		fmt.Println("优秀")
	case score >= 80 && score <= 89:
		fmt.Println("良好")
	case score >= 70 && score <= 79:
		fmt.Println("中等")
	case score >= 60 && score <= 69:
		fmt.Println("及格")
	case score >= 0 && score <= 59:
		fmt.Println("有脸吃饭?")
	default:
		fmt.Println("成绩必须在0~100之间")
	}
}

fallthrough必须放在case块结尾

func main() {
	switch x := 5; x {
	default:
		println(x)
	case 5:
		x += 10
		println(x)
		fallthrough //继续执行下一case,但不再匹配条件表达式。
	case 6:
		x += 20
		println(x)
		//fallthrough 		//如果在此继续fallthrough,不会执行default,完全按源码顺序。
		//导致“cannot fallthrough final case in switch”错误。
	}
}

switch还被用来替换if语句。被省略的switch条件表达式默认值为true,继而与case比较表达式结果匹配

func main() {
	switch x := 5; {		// 相当于"switch x := 5; true { ... }"°
	case x > 5:
		println("a")
	case x > 0 && x <= 5: // 多条件是 OR 关系,不能写成“case x > 0, x <= 5”。
		println("b")
	default:
		println("z")
	}
}
for循环:

循环语句可以在满足循环条件的情况下,反复执行某一段代码,这段被重复执行的代码被称为循环体语句,当反复执行这个循环体时,需要在合适的时候把循环判断条件修改为false,从而结束循环,否则循环将一直执行下去,形成死循环。

  • range和fori循环中的局部变量都会被重复使用

格式

for (初始化语句;条件判断语句;条件控制语句) {
	循环体语句;
}

// 三种for定义格式:

func main() {
	for i := 0; i < 3; i++ {}//初始化表达式支持函数调用或定义局部变量。
	
	x := 3
	for x < 10 { // 相当于"while x > 0 {}” 或 “for ; x < 10 ; {}”。
		x++
	}
	
	for { 		// 死循环,相当于"while true {}” 或 “for true {}”。
		循环体语句
	}
}

格式解释:

  • 初始化语句: 用于表示循环开启时的起始状态,简单说就是循环开始的时候什么样
  • 条件判断语句:用于表示循环反复执行的条件,简单说就是判断循环是否能一直执行下去
  • 循环体语句: 用于表示循环反复执行的内容,简单说就是循环反复执行的事情
  • 条件控制语句:用于表示循环执行中每次变化的内容,简单说就是控制循环是否能执行下去

执行流程

  1. 执行初始化语句
  2. 执行条件判断语句,看其结果是true还是false=
    如果是false,循环结束
    如果是true,继续执行
  3. 执行循环体语句
  4. 执行条件控制语句
  5. 回到2继续

求1-100之间的偶数和,并把求和结果在控制台输出

func forDemo() {
	var sum int
	for i := 0; i <= 100; i++ {
		if i%2 == 0 {
			sum += i
		}
	}
	fmt.Println(sum)
}
// 这种方式就+1了,每次直接加2,不需要if判断
func forDemo2() {
	var sum int
	for i := 2; i <= 100; i += 2 {
		sum += i
	}
	fmt.Println(sum)
}
range
  • range本质是一个函数,在使用的时候可以加括号使用
  • 修改range得到后的value不影响原切片/数组的数据,其实就相当于修改的是个副本,和原来的是无关,但是在range循环里打印的话还是副本的内容
  • range和fori循环中的局部变量都会被重复使用

range 关键字用于

  • for循环中迭代数组(array)、切片(slice)、链表(channel)或集合(map)的元素
  • 在数组和切片中它返回元素的索引值
  • 在集合中返回 key-value 对的 key 值

演示:通过参数列表传递多个参数,使用range获取数据

func main() {
	//Demo03(1, 2, 3, 4, 5)
	//Demo04(1, 2, 3, 4, 5)
	Demo05(1, 2, 3, 4, 5)
}

// a代表索引    v代表数据

func Demo03(num ...int) {
	for a, v := range num {
		fmt.Print("a = ", a, "    ")
		fmt.Println("v = ", v)
	}
}

// 如果只写一个变量,取出的是索引

func Demo04(num ...int) {
	for v := range num {
		//fmt.Print("a = ", a, "    ")
		fmt.Println("v = ", v)
	}
}

// 可以把a使用_代替,表示是一个匿名变量,匿名变量不会保存具体的数据,就可以用v取出数据

func Demo05(num ...int) {
	for _, v := range num {
		//fmt.Print("a = ", a, "    ")
		fmt.Println("v = ", v)
	}
}

range会复制目标数据,受直接影响的是array,可改用*array或slice类型,相关数据类型中,string、slice基本结构是个很小的struct,而map、channel是指针封装,复制成本都很小,不需专门优化

func main() {
	data := [3]int{10, 20, 30}
	for i, x := range data { // 从data复制品中取值
		if i == 0 {
			data[0] += 100
			data[1] += 200
			data[2] += 300
		}
		fmt.Printf("x: %d, data: %dn", x, data[i])
	}
	for i, x := range data[:] { // 仅复制slice,不包括底层array
		if i == 0 {
			data[0] += 100
			data[1] += 200
			data[2] += 300
		}
		fmt.Printf("x: %d, data: %dn", x, data[i])
	}
}

输出:

x: 10, data: 110
x: 20, data: 220
x: 30, data: 330
x: 110, data: 210
x: 420, data: 420
x: 630, data: 630
跳转控制语句:
  • 跳转控制语句(break):满足条件后,终止整个语句开,break语句可以用在switch、for中进行使用
  • 跳转控制语句(continue):满足条件后,跳过本次循环,继续下次循环,基于条件控制, 在循环内部使用,for可以用
    注意: continue只能在循环中进行使用!
  • 跳转控制语句(return):表示终止函数
  • 跳转控制语句(goto):转移到指定的行,goto一般配合条件判断使用,可以用来做条件转移,跳出循环等操作

标号方式跳出:

func forDemo() {
	goto lable // 要跳过的代码前
	要跳过的代码
	lable://要跳过的代码后的位
}

// 循环嵌套就可以用了
func forDemo() {
label:
	for i := 0; i < 3; i++ {
		for j := 0; j < 2; j++ {
			if j == 1 {
				break label
			}
			fmt.Println(i)
		}
	}
	fmt.Println("跳出来了吗")
}

演示:

func forDemo3() {
	for i := 1; i <= 5; i++ {
		if i == 3 {
			break
		}
		fmt.Println(i) // 结果是1、2   break会直接跳出for循环
	}
}


func forDemo4() {
	for i := 1; i <= 5; i++ {
		if i == 2 {
			continue
		}
		fmt.Println(i)  // 结果是1、3、4、5     continue会跳过2,执行后面的
	}
}

func forDemo5() {
	return // 结束函数
	for i := 1; i <= 5; i++ {
		if i == 3 {
			break
		}
		return // 结束循环
		fmt.Println("走我了吗")
		fmt.Println(i)
	}
}

func forDemo6() {
	goto label // 跳过123,执行45
	fmt.Println(1)
	fmt.Println(2)
	fmt.Println(3)
label:
	fmt.Println(4)
	fmt.Println(5)
}
循环嵌套:

循环嵌套就是在一个循环体里面嵌入另一个循环。

打印乘法口诀表案例:

func forDemo6() {
	for i := 1; i <= 9; i++ { // 外循环控制行
		for j := 1; j <= i; j++ { // 内循环控制列
			fmt.Printf("%d*%d=%d ", j, i, j*1)
		}
		fmt.Print("n")
	}
}

输出结果:

1*1=1 
1*2=1 2*2=2 
1*3=1 2*3=2 3*3=3 
1*4=1 2*4=2 3*4=3 4*4=4 
1*5=1 2*5=2 3*5=3 4*5=4 5*5=5 
1*6=1 2*6=2 3*6=3 4*6=4 5*6=5 6*6=6 
1*7=1 2*7=2 3*7=3 4*7=4 5*7=5 6*7=6 7*7=7 
1*8=1 2*8=2 3*8=3 4*8=4 5*8=5 6*8=6 7*8=7 8*8=8 
1*9=1 2*9=2 3*9=3 4*9=4 5*9=5 6*9=6 7*9=7 8*9=8 9*9=9 
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1023455.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号