- 顺序结构:普通的代码,由上而下依次执行
- 分支结构(if, switch)
- 循环结构(for)
格式1:
if (关系表达式) {
语句体;
}
执行流程:
- 首先计算关系表达式的值
- 如果关系表达式的值为true就执行语句体
- 如果关系表达式的值为false就不执行语句体
- 继续执行后面的语句内容
if语句格式2
if (关系表达式) {
语句体1;
} else {
语句体2;
}
执行流程:
- 首先计算关系表达式的值
- 如果关系表达式的值为true就执行语句体1
- 如果关系表达式的值为false就执行语句体2
- 继续执行后面的语句内容
if语句格式3
if (关系表达式1) {
语句体1;
} else if (关系表达式2) {
语句体2;
}
…
else {
语句体n+1;
}
- 首先计算关系表达式1的值
- 如果值为true就执行语句体1;如果值为false就计算关系表达式2的值
- 如果值为true就执行语句体2;如果值为false就计算关系表达式3的值
- …
- 如果没有任何关系表达式为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 {}”。
循环体语句
}
}
格式解释:
- 初始化语句: 用于表示循环开启时的起始状态,简单说就是循环开始的时候什么样
- 条件判断语句:用于表示循环反复执行的条件,简单说就是判断循环是否能一直执行下去
- 循环体语句: 用于表示循环反复执行的内容,简单说就是循环反复执行的事情
- 条件控制语句:用于表示循环执行中每次变化的内容,简单说就是控制循环是否能执行下去
执行流程
- 执行初始化语句
- 执行条件判断语句,看其结果是true还是false=
如果是false,循环结束
如果是true,继续执行- 执行循环体语句
- 执行条件控制语句
- 回到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



