Go基础-010 流程控制

1.概述

代码的执行顺序,有 3 大类,为:

  • 顺序,从上到下依次执行,基础流程(核心流程)
  • 循环,某段代码重复执行。
  • 分支,选择性执行某段代码。

2.流程图回顾

使用如下的典型图例,描述流程节点:

  • 椭圆,表示开始和结束:


  • 矩形,表示某个具体的进程步骤:


  • 菱形,表示条件判断:


3. 分支if

1) 语法
  • 语法 1,只有 if 语句
// 语法 1,条件成立,执行语句块内容
if condition {
  // 语句块 
}
  • 语法 2,if + else
// 语法 2,条件成立,执行 if 语句块,否则执行 else 语句块 
if condition {
  // if 语句块 
} else {
  // else 语句块 
}
  • 语法 3,else if
// 语法 3,条件 1 成立,执行语句块 1 然后结束,若条件 1 不成立,继 续判断条件 2,若成立执行语句块 2 然后结束。直到全部条件判断完毕。
condition1, condition2, conditionN := true, true, true 
if condition1 {
/  / 语句块 1
} else if condition2 {
  // 语句块 2
} else if conditionN {
  // 语句块 N 
}
  • 语法 4,else if else
// 语法 4,与语法 3 一致,多一个 else 语句块,当全部条件不满足时,执行 else 语句块 
if condition1 {
  // 语句块 1
} else if condition2 {
  // 语句块 2
} else if conditionN {
  // 语句块 N 
} else {
  // else 语句块
 }
  • 条件表达式为布尔表达式
    条件表达式一定是可以返回布尔值的表达式。不能直接判断 0,”” 这种非布尔类型数
    据,没有自动向布尔值转换的过程,下面的语法是不对的:
if 0 { 
  // non-bool 0 (type int) used as if condition
  fmt.Println("if statement block") 
}

  • 支持条件初始化语句
    在 if 后,支持先完成条件初始化,再去完成条件表达式的语法,结构为:

if 条件初始化语句;条件表达式 {
}

if condition := 10>8; condition { 
  fmt.Println("使用条件初始化语句")
  fmt.Println("条件成立") 
}

注意:以上结构中,条件的是否成立,仅仅与条件表达式相关,与条件初始化语句无关。

if c:=10<8;true{ 
    fmt.Println(c)
    fmt.Println("使用条件初始化语句")
    fmt.Println("条件成立")
}

// false 
// 使用条件初始化语句 
// 条件成立

注意:使用条件初始化的意思,主要是为了将判断条件变量的作用域(有效区域)限定在 if 语句 范围内(缩小变量有效作用域策略), condition2,在 if 外无效。

if condition2 := 10>8; condition2 { 
 fmt.Println("使用条件初始化语句")
 fmt.Println("条件成立")
 fmt.Println(condition2)
}
fmt.Println(condition2) //undefined: condition2

4.分支 switch

1) if 和 switch 的差异?

if,条件分支,利用条件表达式确定条件是否成立。
switch,状态分支,利用状态值确定分支条件。

代码演示:

    // score >= 80 就是条件
    score := 98
    if score >= 90 {
        fmt.Println("A")
    } else if score >= 80 {
        fmt.Println("B")
    } else if score >=60 {
        fmt.Println("C")
    } else {
        fmt.Println("呵呵")
    }

      // int,string,bool,就是特定的状态!
    switch d := 10; d {
    case 9 :
        fmt.Println(9)
    case 10 :
        fmt.Println(10)
    case 11 :
        fmt.Println(11)
    default:
        fmt.Println("default")
    }
2) 语法,switch case default
value := 4 
switch value { 
default:
  fmt.Println("six") 
case 1:
  fmt.Println("one") 
case 2:
  fmt.Println("two")
//...
case 3, 4, 5: // value == 3 || value == 4 || value == 5
  fmt.Println("three, four, five") }

执行流程是,判断 switch 表达式的值与 case 给定的状态是否相等,相等则执行对应的语句 块,然后 switch 结束。
注意,多个状态值,可以在一个 case 中描述。
注意,若没有任何 case 匹配,则会执行 default 分支。前提是存在 default 分支。(default 分
支是可选的)。default 的位置无关。

在 switch 表达式中,同样支持初始化表达式。 支持:
switch 初始化表达式; switch 表达式 {
}
目的是将条件分支值变量的有效作用域控制在 switch 语句的范围内,避免全局污染!
代码演示:

switch value := 4; value { 
default:
  fmt.Println("six") 
case 1:
  fmt.Println("one") 
case 2:
  fmt.Println("two") 
//...
case 3, 4, 5:  // value == 3 || value == 4 || value == 5
  fmt.Println("three, four, five") 
}

fmt.Println(value) //undefined: value

格外注意的是,switch 的 switch 表达式是也可以省略的。(if 的条件表达式是不能省略的。) 省略后,case 应该使用条件语句完成分支判断,不能再使用状态值了,因为没有值可以用
于判定了。

switch value := 4; { 
default:
  fmt.Println(value)
  fmt.Println("six") 
case value == 1:
  fmt.Println("one") 
case value == 2:
  fmt.Println("two")
case value == 3 || value == 4 || value == 5: 
  fmt.Println("three, four, five")
}
3) fallthrough,穿越到下个 case 块

go 的 switch case 在满足了一个 case 后,执行对应的语句块,之后 swtich 直接结束。
与很多其他语言不通的是,很多其他语句的 swtich 当 case 满足后会依次执行余下的 全部语句块。
为了在语法上与其他语言类似,go 提供了 fallthrough 语句,用于在 case 语句块间穿越。 当 case 语句块的最后的语句为 fallthrought 时,会执行下一个 case 语句块:

switch value := 2; value { 
default:
  fmt.Println("six") 
case 1:
  fmt.Println("one") 
case 2:
  fmt.Println("two") 
  fallthrough // 
//...
case 3, 4, 5: // value == 3 || value == 4 || value == 5
  fmt.Println("three, four, five") 
  //fallthrough // 最后一个语句块中不能存在 fallthrough
}

// 输出 two  
// three, four, five

注意:
fallthrough 只能是语句块的最后一条语句。
而且最后一个语句块中不能存在 fallthrough。
若执行的语句块不存在 fallthrough,会停止穿越!

4) switch interface.(type) 结构,type switch

该结构可以完成类型检测,执行对应的代码。

// 类型检测
var x interface{} 
//x = 42
x = "hank"
//x = false
//
switch d := x.(type) { 
case int:
  fmt.Println(d, d+42) 
case string:
  fmt.Println(d, d + " GoLang")
  //fallthrough //cannot fallthrough in type switch
case bool:
  fmt.Println(d, !d) 
}

该结构也是 swtich 的语法。
特殊性在于: 不支持 falltrhrough。

5. 循环 for

1) 概述

基于循环条件,重复执行代码块的结构。 流程图上,通常是一个闭合的环。
语法上使用 for 完成循环结构。(go 语言中没有 while 结构循环)

2) 基本语法
for 条件初始化语句;条件表达式;条件更新语句{
  //循环体
}

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

for 存在三个循环条件部分,初始化,条件判断,条件变化。

若 循环体执行了 N 次,那么:

  • 条件初始化执行 1 次。
  • 条件判断执行 N+1 次。
  • 条件变化执行 N 次。
    可见,应该将条件数据放在初始化中完成,避免重复执行,例如,当使用 i 作为索引, 遍历数组或切片的时候,语法为:
arr := [...]int{42, 1024, 365, 2048, 1, 36, 88, 66} 
for i, l := 0, len(arr); i
3) 条件循环语法(其他语言的 while 语法)
for 条件表达式 {
}
result, n := 0, 0 
for result <= 100 {
  n ++
  result += n 
}
fmt.Println(result)

条件的数据依赖于外部数据完成。
通常循环体内部,要完成循环条件的更新。

4) 无限循环语法
for {
}
i := 0
for { // for true {
  fmt.Println(i) i ++
  if i >= 10 {
    break // 强制 for 终止 
  }
}

不需要 循环条件,表示条件永远为 true。 意味着,循环体会无限次数的循环执行下去。
通常,需要配合循环体内部的条件变化以及在循环体内部强制终止来实现。上面的 break
结构。
该结构通常用于实现守护进程。需要一直运行的程序。

6. 循环遍历,for range

适用于遍历:array,slice,map 复合结构。
语法一致:

for i, v := range data { 
}
for i := range data { 
}
for _, v := range data {
}

7. 注意,语句块的左大括号,与语句标签在一行

for ;; {
if ; {
swtich ; {
for := range {

\\ 不能是:
for ;; 
{
if ; 
{
switch ; 
{
// 包括函数的大括号,也需要与函数声明在一行。
func funcName() {

8. goto label

label: 语句标签。类似于路标,用于标识代码位置。
使用 goto 语句可以跳转到特定的 label 位置。

演示,例如在处理错误时:

// 错误处理
// 可能的错误有很多种,逐一检测。
// 一旦检测到任意的错误,则立即处理错误,不再检测后续的错误,逻辑如
下:
if error1 {
   goto errorLabel
}
if error2 {
   goto errorLabel
}
if error3 { //标签跳转
   goto errorLabel
}
errorLabel: // 标签定义 
  fmt.Println("error processing")

上面的语法中,涉及到了:

  • 定义标签: 为标签设置特定的名字,满足标识符定义即可。
  • 使用标签: 完成。 跳转标签:使用 goto 完成跳转。

注意: 跳转 goto,不是任意跳转。跳转的目标不是任何位置都可达到,例如: 不能跨函数跳转。
不能跳转到内部的语句块中。

总结下来:标识符必须可见,才可以 goto 成功。
标识符可见的语法: 语句块嵌套的可见性。内层可以看见外层,但是外层看不到内层。
不同的语句块也不可以跳转。

func main() {
  goto labelName
  // 内层可以看见外层,但是外层看不到内层。
  for i:=0;i<10;i++ {
    labelName:  // XXX 不可
      fmt.Println()
  }
}
// 不同的语句块也不可以跳转。
func F() {
   labelName:  // XXX 不可
      fmt.Println()
}

9. break,强行终止循环执行

break 用于循环内部,当执行到 break 时,会终止 break 所在的循环的执行。
代码演示

for i:=0; i<10; i++ { 
  if i == 6 {
    break
  }
  fmt.Println(i) 
}

以上结果,6 及后续数字没有输出。执行到了 break,会立即终止。

10.continue,强行终止本次循环体执行,循环继续

continue,仅仅终止本次循环体执行。但是 for 的整体循环并没有结束,会继续修改循
环条件,执行下一次循环体。
代码演示:

for i:=0; i<10; i++ { 
  if i == 6 {
    continue
  }
  fmt.Println(i) 
}

以上的结果,没有 6 的输出。continue 之后的代码,不会继续执行,而是指向下一次 i== 7 的循环体。

11.break 或 continue 多重循环

当循环语句出现嵌套时,若需要在内部循环体中,直接终止外部的循环执行,则需要配
合 标签 语法完成终止。 演示:

outerLabel:
for i:=0; i<3; i++ {
  for j:=0; j<3; j++ {
      if i + j > 2{
        break outerLabel
     }
     fmt.Println(i, j) 
  }
}

此时,break outerLabel, 会导致 outerLabel 对应的 for 全部终止! 否则,break,仅仅会终止 所在的 for j 的执行,而不会终止 for i 的执行。

12.switch 中的 break 一定要使用标签

for 中使用 switch 结构判断执行 break,需要使用标签语法。

代码演示:

forLabel:
  for i:=0; i<10; i++ {
   switch i {
   case 6:
     break forLabel
   }
   // if i == 6 {
   // break
   // } 
  fmt.Println(i)
}

若不使用标签语法,switch 中的 break 不会终止循环。
原因,switch 中的 break,是终止 switch 的作用。在实现 switch 时,被视为循环结构。

你可能感兴趣的:(Go基础-010 流程控制)