Golang流程控制

546 阅读4分钟

Go里的流程控制有以下几种:

  • if - else 条件语句
  • switch - case 选择语句
  • for - range 循环语句
  • goto 无条件跳转语句
  • defer 延迟执行

if-else

基本用法

func TestIfElse(condition string) {

  if condition == "1" {
    fmt.Println("ONE")
  } else if condition == "2" {
    fmt.Println("TWO")
  } else if condition == "3" {
    fmt.Println("THREE")
  } else {
    fmt.Println("OTHER")
  }
}

func Execute() {
  TestIfElse("1")
}

高级写法

在 if 里可以允许先运行一个表达式,取得变量后,再对其进行判断,比如第一个例子里代码也可以写成这样

func TestIfElseHight() {
  if a := GetCondition("ONE"); a == 1 {
    fmt.Println("TEST")
  }
}

func GetCondition(condition string) int {
  if condition == "ONE" {
    return 1
  }
  return 0
}

Go语言明确不支持三元表达式

switch-case

简单用法

拿 switch 后的表达式分别和 case 后的表达式进行对比,只要有一个 case 满足条件,就会执行对应的代码块,然后直接退出 switch - case ,如果 一个都没有满足,才会执行 default 的代码块。


func TestSwitchCase() {
  condition := "CASE1"

  switch condition {
  case "CASE1":
    fmt.Println("CASE1 PRINT")
  case "CASE2":
    fmt.Println("CASE2 PRINT")
  default:
    fmt.Println("DEFAULT PRINT")
  }
}

一个case多个条件

case后接多个条件

func TestSwitchCase() {
  condition := "CASE2"

  switch condition {
  case "CASE1", "CASE3", "CASE5":
    fmt.Println("ODD PRINT")
  case "CASE2", "CASE4", "CASE6":
    fmt.Println("EVEN PRINT")
  default:
    fmt.Println("DEFAULT PRINT")
  }
}

case后的常量不能重复,不然会报错duplicate case “XXX” in switch

switch接函数

switch 后面可以接一个函数,只要保证 case 后的值类型与函数的返回值 一致即可

func GetCondition(condition string) int {
  if condition == "ONE" {
    return 1
  }
  return 0
}


func TestSwitchCaseFunc() {
  switch GetCondition("ONE") {
  case 1:
    fmt.Print("ONE PRINT")
  case 0:
    fmt.Print("TWO PRINT")
  default:
    fmt.Print("DEFAULT")
  }
}

switch可以不接表达式,不接任何东西时相当于if-elseif-else

score := 30

switch {
    case score >= 95 && score <= 100:
        fmt.Println("优秀")
    case score >= 80:
        fmt.Println("良好")
    case score >= 60:
        fmt.Println("合格")
    case score >= 0:
        fmt.Println("不合格")
    default:
        fmt.Println("输入有误...")
}

switch的穿透能力

正常情况下 switch - case 的执行顺序是:只要有一个 case 满足条件,就会直接退出 switch - case ,如果 一个都没有满足,才会执行 default 的代码块。

但是有一种情况是例外。

那就是当 case 使用关键字 fallthrough 开启穿透能力的时候。


func TestSwitchFallThrough() {
  condition := "CASE1"

  switch condition {
  case "CASE1", "CASE3", "CASE5":
    fmt.Println("ODD PRINT")
    fallthrough
  case "CASE2", "CASE4", "CASE6":
    fmt.Println("EVEN PRINT")
  default:
    fmt.Println("DEFAULT PRINT")
  }
}


// 输出结果为
ODD PRINT
EVEN PRINT

fallthrough只能穿透一层

for循环

语法规则

for [condition |  ( init; condition; increment ) | Range]
{
   statement(s);
}

for后面可以接:

  1. condition[一个条件表达式]
  2. ( init; condition; increment ) [最常见的]
  3. Range range表达式
  4. 什么都不接

样例


func TestFor() {
  a := 1
  for a <= 5 {
    fmt.Printf("a: %v\n", a)
    a++
  }

  for i := 1; i <= 5; i++ {
    fmt.Printf("i: %v\n", i)
  }

  rangeArr := []string{"C1", "C2", "C3"}
  for _, item := range rangeArr {
    fmt.Printf("item: %v\n", item)
  }

  number := 0
  for {
    if number >= 5 {
      break
    }
    fmt.Printf("number: %v\n", number)
    number++
  }

}

输出

a: 1
a: 2
a: 3
a: 4
a: 5
i: 1
i: 2
i: 3
i: 4
i: 5
item: C1
item: C2
item: C3
number: 0
number: 1
number: 2
number: 3
number: 4

go无条件跳转

语法规则

goto 后接一个标签,这个标签的意义是告诉 Go程序下一步要执行哪里的代码。

所以这个标签如何放置,放置在哪里,是 goto 里最需要注意的。

goto 标签;
...
...
标签: 表达式;

简单例子

func TestGoto() {
  goto printB
  fmt.Println("AAAAA")

printB:
  fmt.Println("BBBBB")
}

结果是BBBBB,不会输出A,直接就跳过去了。

注意事项

goto语句和标签之前不能有变量申明

func TestGoto() {
  goto printB
  fmt.Println("AAAAA")

  var testP = "CCC"
printB:
  fmt.Println("BBBBB")
}

这样会报错processcontrol\processcontrol.go:10:7: goto printB jumps over declaration of testP at processcontrol\processcontrol.go:13:6

常见用法

func TestGotoNormal() {
  i := 1
loopPrint:
  if i <= 5 {
    fmt.Printf("i: %v\n", i)
    i++
    goto loopPrint
  }

  j := 1
  for {
    if j >= 5 {
      goto loopBreak
    }
    fmt.Printf("j: %v\n", j)
    j++
  }
loopBreak:

  k := 1
loopContinue:
  for k <= 5 {
    if k%2 == 1 {
      k++
      goto loopContinue
    }
    fmt.Printf("k: %v\n", k)
    k++
  }
}

输出

i: 1
i: 2
i: 3
i: 4
i: 5
j: 1
j: 2
j: 3
j: 4
k: 2
k: 4

defer延迟语句

延迟调用

defer 的用法很简单,只要在后面跟一个函数的调用,就能实现将这个 xxx 函数的调用延迟到当前函数执行完后(return后)再执行。

func TestDefer() {
  defer fmt.Println("BBBBBB")
  fmt.Println("AAAAAA")
}

输出:

AAAAAA
BBBBBB

及时求值的变量快照

使用 defer 只是延时调用函数,此时传递给函数里的变量,不应该受到后续程序的影响。

如下:

func TestDefer() {
  str := "BBBBB"
  defer fmt.Println(str)
  str = "AAAAA"
  fmt.Println(str)
}

闭包

这样写会产生闭包问题,str变量在外部。

func TestDeferFunc() {

  str := "AAAA"

  defer func() {
    fmt.Printf("str: %v\n", str)
  }()

  str = "BBBBB"
  fmt.Printf("str: %v\n", str)
}

输结果

str: BBBBB
str: BBBBB

str这个变量,在defer打印出来之后,还是输出的BBBBB

换种方式:

func TestDeferFunc() {

  str := "AAAA"

  defer func(s string) {
    fmt.Printf("s: %v\n", s)
  }(str)

  str = "BBBBB"
  fmt.Printf("str: %v\n", str)
}

输出

str: BBBBB
s: AAAA

执行顺序问题

defer执行语句像一个栈一样,先入后出,先写后执行

func TestDeferOrder() {
  defer println("AAAAA")
  defer println("BBBBB")
  defer println("CCCCC")
}

结果:

CCCCC
BBBBB
AAAAA