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<l; i++ {
  fmt.Println(i, arr[i]) 
}
// 不建议的语法
for i := 0; i<len(arr); i++ {
  fmt.Println(i, 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 时,被视为循环结构。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,718评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,683评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,207评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,755评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,862评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,050评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,136评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,882评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,330评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,651评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,789评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,477评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,135评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,864评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,099评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,598评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,697评论 2 351

推荐阅读更多精彩内容