golang 第五章 程序流程控制

流程控制主要分为三大类:顺序控制、分支控制、循环控制

顺序控制

  1. 程序从上到下逐行的执行,中间没有任何判断和跳转
  2. golang中定义变量时采用合法的前向引用,即需要先声明后引用

分支控制

if分支控制

单分支

if 条件表达式 {
    执行代码块
}

双分支

if 条件表达式 {
    执行代码块1
} else {
    执行代码块2
}

多分支

if 条件表达式1 {
    执行代码1
} else if 条件表达式2 {
    执行代码2
} ... {

} else {
    执行代码n
}

嵌套分支建议控制在3层以内

if 条件表达式x {
    if 条件表达式y {
        执行代码1
    } else {
        执行代码2
    }
}

switch分支控制

  1. switch语句用于基于不同的条件执行不同的动作,每一个case分支都是唯一的,从上到下逐一测试,直到匹配为止
  2. 先执行表达式得到值,然后和case的表达式进行比较。如果相等则匹配成功,执行对应的语句块,然后退出switch控制;如果没有匹配成功,则执行default下的语句块,然后退出switch控制。
  3. case后表达式可以有多个,使用逗号间隔
  4. case语句块后不需要break,默认会有
  5. 基本语法:
switch 表达式 {
    case 表达式1,表达式2,...:
        语句块1
    case 表达式3,表达式4,...:
        语句块2
    // 可以多个case语句
    default:
        默认语句块
}

注意细节

  1. switch/case后是一个表达式(即常量值变量、一个有返回值的函数都可以)
  2. case:
    1. case后的各个表达式的值的数据类型,必须和switch的表达式数据类型一致
    2. case后面可以带多个表达式,使用逗号间隔,比如case 表达式1,表达式2,...
    3. case后面的表达式为常量值时,不能出现重复
    4. case后面不需要带break,程序匹配到一个case后就会执行对应的代码块,然后退出switch,如果一个也匹配不到则执行default,因此default语句时必须的
    5. 穿透fallthrough,即在case语句块后面增加fallthrough,则会继续执行下一个case,这种做法也叫switch穿透
  3. switch
    1. switch后可以不带表达式,类似if else分支来使用
    2. switch后可以直接声明或者定义一个变量,分号结束,例如switch grade :=90; {},但此种方法不推荐使用
    3. switch可以用于type-switch来判断某个interface变量中实际指向的变量类型
      type-switch
package main
import (
	"fmt"
)

func main() {

	// 	案例:
	// 请编写一个程序,该程序可以接收一个字符,比如: a,b,c,d,e,f,g
	// a表示星期一,b表示星期二,c表示星期三,根据用户的输入显示相应的信息
	// 要求使用 switch 语句完成
	var key byte
	fmt.Println("请输入一个字符 a,b,c:")
	fmt.Scanf("%c", &key)
	switch key { //将语法现象
		case 'a':
			fmt.Println("今天是周一")
		case 'b':
			fmt.Println("今天是周二")
		case 'c':
			fmt.Println("今天是周三")
		default:
			fmt.Println("输入有误...")
	}

	// case后可以有多个表达式
	var n1 int32 = 51
	var n2 int32 = 20
	switch n1 {
		case n2,10,5 :
			fmt.Println("ok1")
		case 90,100,51 :  // 此处不能再写10、5,否则和上面重复
			fmt.Println("ok2~")
	}


	//switch 后也可以不带表达式,类似 if --else分支来使用
	var age int = 10
	switch {
		case age == 10 :
			fmt.Println("age 为 10")
		case age == 20 :
			fmt.Println("age 为 20")
		default :
			fmt.Println("没有匹配到")
	}

	//case 中也可以对 范围进行判断
	var score int = 90
	switch {
		case score > 90 :
			fmt.Println("成绩优秀..")
		case score >=70 && score <= 90 :
			fmt.Println("成绩优良...")
		case score >= 60 && score < 70 :
			fmt.Println("成绩及格...")
		default :
			fmt.Println("不及格")
	}


	//switch 后也可以直接声明/定义一个变量,分号结束,不推荐
	switch grade := 90; { // 在golang中,可以这样写
		case grade > 90 :
			fmt.Println("成绩优秀~..")
		case grade >=70 && grade <= 90 :
			fmt.Println("成绩优良~...")
		case grade >= 60 && grade < 70 :
			fmt.Println("成绩及格~...")
		default :
			fmt.Println("不及格~")
	}

	//switch 的穿透 fallthrought
	var num int = 10
	switch num {
		case 10:
			fmt.Println("ok1")
			fallthrough //默认只能穿透一层
		case 20:
			fmt.Println("ok2")
			fallthrough
		case 30:
			fmt.Println("ok3")
		default:
			fmt.Println("没有匹配到..")
	}
}

switch和if的比较

  1. 判断的具体数值不多,而且符合整数、浮点数、字符、字符串这几种类型,建议使用switch
  2. 对区间的判断或结果为bool型,建议使用ifif使用范围更广

循环控制

for循环控制

// 语法1:
for 循环变量初始化;循环条件;循环变量迭代 {
    循环操作
}
// 其中 循环变量初始化 和 循环变量迭代 可以不在for之后,这两个可以写在其他地方,例如语法2

// 语法2:
循环变量初始化
for 循环条件 {
    循环操作
    循环变量迭代
}

// 语法3:无限循环,必须配合break语句使用
for {
    循环操作
}
// 等价于 for ;; {}

// 语法4:for-range方式,遍历字符串和数组
for index, val := range 变量 {
    fmt.Printf("索引%d,值%c", index, val)
}


// 举例:

// 语法1:
for i := 1; i <= 10; i++{
    fmt.PrintIn("这是循环体for")
}

// 语法2:
i := 1
for i <= 10 {
    fmt.PrintIn("这是循环体for")
     i++
}

//  语法3:无限循环
i := 1
for {
    if i <= 10 {
        fmt.PrintIn("这是循环体for",i)
    } else {
        break
    }
     i++
}

// 语法4:遍历字符串
//   传统方式遍历字符串:
var str string = "hello,world!"
for i := 0; i < len(str); i++ {
    fmt.Printf("%c \n",str[i]) //需要使用下标
}
//   for-range方式遍历字符串:
var str string = "hello,world!"
for index, value := range str {
    fmt.Printf("索引位置%d\t值为%c\n",index,value)
}
//      如果字符串含有中文,就会出现乱码,原因是对字符串的遍历是按照字节来遍历的,而一个汉字对应的是3个字节,解决方法就是将string转为切片rune
var str string = "hello,world!我是老北京人儿"
str2 := []rune(str)
for index, value := range str2 {
    fmt.Printf("索引位置 %d \t 值为 %c\n",index,value)
}

for循环的4个要素,其执行顺序如下:

  1. 循环变量初始化i := 1
  2. 循环条件i <= 10,是返回一个布尔值的表达式
  3. 循环操作(即循环体)fmt.PrintIn("这是循环体for")
  4. 循环变量迭代i++

while和do...while的实现

golang中没有while循环,其实现方式为for循环中的语法3--无限循环

// while实现
循环变量初始化
for {
    if 循环条件 {
        break // 跳出for循环
    }
    循环操作
    循环变量迭代
}

// do...while实现
循环变量初始化
for {
    循环操作
    循环变量迭代
    if 循环条件 {
        break // 跳出for循环
    }
}

嵌套循环一般使用两层,最多不要超过三层

跳转控制语句

break

break语句用于终止某个语句块的执行,用于中断当前或者指定标签的for循环或switch语句,其基本语法如下:

{
    ......
    break
    ......
}

break语句出现在多层嵌套的语句中时,可以通过标签来指明要终止哪一层语句块,例如:

lable2:  // 跳出标签2
for i:=0; i<4;i++ {
    // lable1:  // 跳出标签1
    for j:=0;j<10;j++ {
        if j==2 {
            // break  // 默认会跳出最近的for循环,即lable1的位置,等同于下面语句:
            // break lable1
            break lable2 // j=0 j=1,跳出最外层的for循环
        }
        fmt.Println("j=",j)
    }
}

continue

continue语句用于结束本次循环,继续执行下一次循环,当出现在多层嵌套的循环语句体时,可以通过标签指明要跳过的是哪一层循环,其基本语法如下:

{
    ......
    continue
    ......
}

例如:

lable2:  // 跳出标签2
for i:=0; i<4;i++ {
    // lable1:  // 跳出标签1
    for j:=0;j<10;j++ {
        if j==2 {
            // continue  // 默认会跳过最近的for循环,即lable1的位置,等同于下面语句:
            // continue lable1
            continue lable2 // j=0 j=1 j=0 j1...,没有j=2
        }
        fmt.Println("j=",j)
    }
}

goto

goto语句可以无条件的转移到程序中指定的行,通常与条件语句配合使用,用来实现条件转移、跳出循环体等功能,但是一般不主张用goto语句,以免造成流程的混乱,其基本语法如下:

goto lable
...
label:statement

例如:

var n int = 30
fmt.Println("ok1")
if n > 20 {
    goto label1
}
fmt.Println("ok2")
fmt.Println("ok3")
fmt.Println("ok4")
label1:
fmt.Println("ok5")
fmt.Println("ok6")
fmt.Println("ok7")

//ok1
//ok5
//ok6
//ok7

return

return使用在方法或者函数中,表四跳出所在的方法或者函数。如果是在普通函数中,则表示跳出该函数,即不再执行return后面的代码(终止函数);如果是在main函数中,表示终止main函数,即终止程序执行
例如:

var n int = 30
fmt.Println("ok1")
if n > 20 {
    return
}
fmt.Println("ok2")
fmt.Println("ok3")
fmt.Println("ok4")
fmt.Println("ok5")
fmt.Println("ok6")
fmt.Println("ok7")

//ok1