流程控制

流程控制,用于控制代码的执行流程。典型的流程:

  • 顺序,代码从头至尾执行
  • 分支,选择某段代码执行
  • 循环,重复执行某段代码
  • 强制跳转,提前终止等

流程图

通过图例的方式,来描述程序的流程,这种图例,称之为流程图。典型的流程图如下所示:

  • 椭圆形,表示开始或结束
  • 矩形,某一个进程,步骤,操作
  • 菱形,表示条件判断
  • 平行四边形,表示数据

演示:

image-20200410091220954

if 条件分支

if 语句根据条件表达式的判定结果执行对应的语句块,条件表达式是布尔表达式。支持 else 和 else if 结构,表示否者和继续判定条件。同时支持条件初始化语句。

语法一,单 if 分支:

// 语法1,一旦条件满足,则执行分支语句块,否则分支语句块不执行
if 条件表达式 {
	分支语句块
}
1
2
3
4

语法1

语法二,else 分支:

// 语法2
if 条件表达式 { // 如果条件满足,执行 条件成立语句块
    条件成立语句块
} else { // 否则,执行 条件不满足语句块
    条件不满足语句块
}
1
2
3
4
5
6

image-20200410101545028

语法三,else if 分支:

// 语法3
// 多个条件表达式,会依次判断,第一个满足的条件,执行对应的语句块,整条 if 结束。
// 即使存在多个条件表达式,满足条件,则也不会执行多个条件成立语句块。只有一个分支语句块会执行。
// 若存在 else 子句,当全部条件都不满足时执行。
if 条件表达式1 {
    条件1成立语句块
} else if 条件表达式2 {
    条件2成立语句块
} [可选任意的 else if] [可选 else 语句]
1
2
3
4
5
6
7
8
9

image-20200410102009264

语法四,预执行语句:

// 语法四,条件初始化语法
// 在判断条件时,可以将条件初始化的语句,写在if语句部分。
// 目的是在if语句的作用域范围内,初始化条件标识符,使得标识符作用域够用即可
// 条件成立与否,仅仅与条将表达式相关
if 条件初始化语句; 条件表达式 {
1
2
3
4
5

image-20200410102117157

演示:

// 条将表达式
num1, num2 := 4, 3
//condition := num1 > num2
//condition := num1 < num2 // false
condition := num1 < num2 || num1 > num2// 逻辑运算
// 语法1
if condition {
    fmt.Println("条件成立,执行此语句")
}

// 语法2
num1, num2 := 4, 3
//condition := num1 > num2
condition := num1 < num2
if condition {
    fmt.Println("条件成立,执行此语句")
} else {
    fmt.Println("条件不成立,执行此语句")
}

// 语法三
score := 98
if 100 == score {
    fmt.Println("满分,奖励一个赞!👍。")
} else if 90 <= score {
    fmt.Println("90分以上,优秀。奖励500元。")
} else if 60 <= score {
    fmt.Println("刚刚及格,good!奖励2000元。")
} else {
    fmt.Pintln("未及格,革命尚未成功,同志仍需努力!")
}

// 语法四
// 如果存在某个key,则对该key的值操作
m := map[string]int{
    "Jordan": 23,
    "Curry": 30,
}
// 先获取map的中的某个key,再做条件判断
if value, ok := m["Curry"]; ok {
    // 此 value 仅仅可以在 if 语句中使用
    fmt.Println("Curry:", value)
}
//fmt.Println(value) // undefined: value
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44

switch 状态分支

通过判断表达式是否与某个状态值相等来推定条件是否满足,进而执行语句块。

语法一,状态值判断:

// 语法一,状态值判断,是典型的switch应用场景
switch 判定表达式 {
case 状态值1:
    语句块1
case 状态值2:
    语句块2
// 可选的
case 状态值N:
    语句块N
// 可选的
default:
    语句块default
}
	
1
2
3
4
5
6
7
8
9
10
11
12
13
14

image-20200410105157860

语法二,初始化语句:

// 语法二,支持初始化表达式
switch 初始化语句;判定表达式 {

}
1
2
3
4

image-20200410105644320

语法三,条件判断:

// 语法一,多条件判断
// 与 if else if 的结构和功能一致,因此出镜率不高!
switch {
	case 条件1:
		语句块1
	case 条件2:
		语句块2
	// 可选的
    case 条件N:
		语句块N
    // 可选的
	default:
		语句块default
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

image-20200410103429208

演示:

// 语法一
score := 97
switch {
case 100 == score:
    fmt.Println("100分,因为只有100分.")
case 90 <= score:
    fmt.Println("优秀,少做一道题即可.")
case 60 <= score:
    fmt.Println("及格,呵呵!")
default:
    fmt.Println("同志仍要努力——孙文")
}
// 优秀,少做一道题即可.

//语法 二
rand.Seed(time.Now().UnixNano()) // 以时间戳作为随机数的种子
value := rand.Intn(6) + 1 // [0, 6) // 0, 1, 2, 3, 4, 5: + 1:1,2,3,4,5,6
fmt.Println("value:", value)
switch value {
case 1: // 1 == value
    fmt.Println("1点,小")
case 2: // 2 == value
    fmt.Println("2点,小")
case 3: // 3 == value
    fmt.Println("3点,小")
case 4: // 4 == value
    fmt.Println("4点,大")
case 5: // 5 == value
    fmt.Println("5点,大")
case 6: // 6 == value
    fmt.Println("6点,大")
default:
    fmt.Println("错误的点数,请重新投掷。")
}

// 语法三
m := map[string]int{
    "Jordan": 6,
    "Kobe": 4,
    "Curry": 5,
}
switch value := m["Curry"]; value {
case 4: // 4 == value
    fmt.Println("4点,大")
case 5: // 5 == value
    fmt.Println("5点,大")
case 6: // 6 == value
    fmt.Println("6点,大")
default:
    fmt.Println("错误的点数,请重新投掷。")
}
// 5点,大
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52

fallthrough 穿透

fallthrough 语句,可以作为switch语句块的最后一条语句出现。其作用,是穿透到下面的语句块继续执行,而不需要做case的判定。

如图所示:

image-20200410112723090

演示:

//fallthrough
rand.Seed(time.Now().UnixNano()) // 以时间戳作为随机数的种子
value := rand.Intn(6) + 1 // [0, 6) // 0, 1, 2, 3, 4, 5: + 1:1,2,3,4,5,6
fmt.Println("value:", value)

switch value {
case 1: // 1 == value
    fallthrough
case 2: // 2 == value
    fallthrough
case 3: // 3 == value
    fmt.Println("1,2,3点,小")
case 4: // 4 == value
    fallthrough // 向下穿透
case 5: // 5 ==
    fallthrough
case 6: // 6 == value
    fmt.Println("4,5,6点,大")
default:
    fmt.Println("错误的点数,请重新投掷。")
}
// 随机 1,2,3
123点,小

// 随机 4,5,6
456点,大

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

fallthrough 的语法目的,主要是为了让多个状态值,使用同一个语句块来处理。

for 循环

重复执行代码块的控制流程。

语法:

// 语法一,无限循环,循环体永远执行,直到被强制退出。
// 语法上终止循环的执行是break,for {} 通常会配合 break 使用。
for {
   // 循环体 
}

// 语法二,条件循环
// 条件为true,则执行循环体,条件为false,for语句结束。
// 条件表示是,就是结果为 bool 型的表达式。典型关系和逻辑运算。
for 条件表达式 {
    // 循环体
}

// 语法三,经典的 for 语句
for 初始化语句;条件表达式;条件变化语句 {
    // 循环体
}
// 其中 初始化语句,条件表达式和条件变化语句都是可以省略的,但结构不能改变,保留分号。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

流程图

语法一:

image-20200410114951613

语法二:

image-20200410120045584

语法三:

image-20200410144446419

演示

// 语法一
for {
    rand.Seed(time.Now().UnixNano()) // 以时间戳作为随机数的种子
    value := rand.Intn(100) // [0, 100)
    fmt.Println("value:", value)

    if 11 == value || 22 == value || 33 == value {
        break
    }
}

// 语法二:
value := 0// 初始化
for value<60 {
    fmt.Println("当前值:", value)
	// 生成随机数
    time.Sleep(time.Nanosecond) // 暂停 1 ns, 0.000000001 s
    rand.Seed(time.Now().UnixNano()) // 以时间戳作为随机数的种子
    value = rand.Intn(100) // [0, 100)
    fmt.Println("下一个值:", value)
}
当前值: 0
下一个值: 10
当前值: 10
下一个值: 15
当前值: 15
下一个值: 68

//语法三
for i:=0; i<5; i++ {
    fmt.Println(i)
}
0
1
2
3
4
// 省略各部分的演示:
i:=0
for ; i<5; i++ {
    fmt.Println(i)
}

for i:=0; i<5;  {
    fmt.Println(i)
    i++
}

for i:=0; ; i++ { // 无条件表达式,意味着条件判断永远成立。
    fmt.Println(i)
}
// 更极端的省略
i:=0
for ;i<5 ;  { // for i<5 {
    fmt.Println(i)
    i++
}
i:=0
for ;; { // for {
    fmt.Println(i)
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62

break 终止

break 语句用于终止 for、switch、select 语句的执行。使用标签语法可终止嵌套的外层语句。语法:

break [label]
label 必须为 for、switch、select 的标签
1
2

演示:

OuterLoop:
for i = 0; i < n; i++ {
    for j = 0; j < m; j++ {
        switch a[i][j] {
        case nil:
            state = Error
            break OuterLoop
        case item:
            state = Found
            break OuterLoop
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13

continue 继续

continue 语句会开启下次循环体执行,本次循环体内 continue 后边的语句不会再执行。支持使用标签进入外层循环的下一次执行。语法:

continue [label]
label 必须为 for 的标签
1
2

演示:

RowLoop:
for y, row := range rows {
    for x, data := range row {
        if data == endOfRow {
            continue RowLoop
        }
        row[x] = data + bias(x, y)
    }
}
1
2
3
4
5
6
7
8
9

goto 跳转

goto 语句可跳转到目标标签位置,语法:

goto label
1

注意:

  • label 与 goto 语句需要在同一个函数中。
  • 不能跳过声明语句。
  • 不能跳入内层语句块中。

演示:

// success
goto someLabel

someLabel:
	someStatement

// bad
goto L  // BAD
	v := 3 // 声明语句
L

goto La // BAD
if {
    la:	// 语句块内部标签
    	someStatement
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16