Go语言流程控制

codex Aug 03, 2020

switch 语句

switch var1 {
   case val1:
       ...
   case val2:
       ...
   default:
       ...
}switch {
   case condition1:
       ...
   case condition2:
       ...
   default:
       ...
}

switch 语句的第二种形式是不提供任何被判断的值(实际上默认为判断是否为 true),然后在每个 case 分支中进行测试不同的条件。当任一分支的测试结果为 true 时,该分支的代码会被执行。

switch 语句的第三种形式是包含一个初始化语句:

switch initialization {
   case val1:
       ...
   case val2:
       ...
   default:
       ...
}switch result := calculate(); {
   case result < 0:
       ...
   case result > 0:
       ...
   default:
       // 0
}

变量 var1 可以是任何类型,而 val1 和 val2 则可以是同类型的任意值。类型不被局限于常量或整数,但必须是相同的类型;或者最终结果为相同类型的表达式。前花括号 { 必须和 switch 关键字在同一行。

您可以同时测试多个可能符合条件的值,使用逗号分割它们,例如:case val1,val2,val3。一旦成功地匹配到某个分支,在执行完相应代码后就会退出整个 switch 代码块,也就是说您不需要特别使用 break 语句来表示结束。

如果在执行完每个分支的代码后,还希望继续执行后续分支的代码,可以使用 fallthrough 关键字来达到目的。

fallthrough强制执行后面的case代码,fallthrough不会判断下一条case的expr结果是否为true。

package main

import "fmt"

func main() {

   switch a := 1; {
   case a == 1:
       fmt.Println("The integer was == 1")
       fallthrough
   case a == 2:
       fmt.Println("The integer was == 2")
   case a == 3:
       fmt.Println("The integer was == 3")
       fallthrough
   case a == 4:
       fmt.Println("The integer was == 4")
   case a == 5:
       fmt.Println("The integer was == 5")
       fallthrough
   default:
       fmt.Println("default case")
   }
}程序输出:
The integer was == 1
The integer was == 2

select控制

select是Go语言中的一个控制结构,类似于switch语句,主要用于处理异步通道操作,所有情况都会涉及通信操作。因此select会监听分支语句中通道的读写操作,当分支中的通道读写操作为非阻塞状态(即能读写)时,将会触发相应的动作。select语句会选择一组可以发送或接收操作中的一个分支继续执行。select没有条件表达式,一直在等待分支进入可运行状态。

select中的case语句必须是一个channel操作
select中的default子句总是可运行的。
  • 如果有多个分支都可以运行,select会伪随机公平地选出一个执行,其他分支不会执行。
  • 如果没有可运行的分支,且有default语句,那么就会执行default的动作。
  • 如果没有可运行的分支,且没有default语句,select将阻塞,直到某个分支可以运行。

package main

import (
   "fmt"
   "time"
)

func main() {
   var c1, c2, c3 chan int
   var i1, i2 int
   select {
   case i1 = <-c1:
       fmt.Printf("received ", i1, " from c1\n")
   case c2 <- i2:
       fmt.Printf("sent ", i2, " to c2\n")
   case i3, ok := (<-c3):
       if ok {
           fmt.Printf("received ", i3, " from c3\n")
       } else {
           fmt.Printf("c3 is closed\n")
       }
   case <-time.After(time.Second * 3): //超时退出
       fmt.Println("request time out")
   }
}

// 输出:request time out

for循环

最简单的基于计数器的迭代,基本形式为:

for  初始化语句; 条件语句; 修饰语句 {}

这三部分组成的循环的头部,它们之间使用分号 ; 相隔,但并不需要括号 () 将它们括起来。

您还可以在循环中同时使用多个计数器:

for i, j := 0, N; i < j; i, j = i+1, j-1 {}

这得益于 Go 语言具有的平行赋值的特性,for 结构的第二种形式是没有头部的条件判断迭代(类似其它语言中的 while 循环),基本形式为:for 条件语句 {}。

您也可以认为这是没有初始化语句和修饰语句的 for 结构,因此 ;; 便是多余的了

条件语句是可以被省略的,如 i:=0; ; i++ 或 for { } 或 for ;; { }(;; 会在使用 Gofmt 时被移除):这些循环的本质就是无限循环。最后一个形式也可以被改写为 for true { },但一般情况下都会直接写 for { }。

如果 for 循环的头部没有条件语句,那么就会认为条件永远为 true,因此循环体内必须有相关的条件判断以确保会在某个时刻退出循环。

package main

import (
   "fmt"
)

func main() {
   a := []int{1, 2, 3, 4, 5, 6}
   for i, j := 0, len(a)-1; i < j; i, j = i+1, j-1 {
       a[i], a[j] = a[j], a[i]
   }

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

for-range 结构

for-range 结构是 Go 语言特有的一种迭代结构,它在许多情况下都非常有用。它可以迭代任何一个集合,包括数组(array)和字典(map),同时可以获得每次迭代所对应的索引和值。一般形式为:

for ix, val := range coll { }

要注意的是,val 始终为集合中对应索引的值的副本,因此它一般只具有只读性质,对它所做的任何修改都不会影响到集合中原有的值(注:如果 val 为指针,则会产生指针的副本,依旧可以修改集合中的原值)。

package main

import (
   "fmt"
   "time"
)

type field struct {
   name string
}

func (p *field) print() {
   fmt.Println(p.name)
}

func main() {
   data := []field{ {"one"}, {"two"}, {"three"} }

   for _, v := range data {
       go v.print()
   }
   time.Sleep(3 * time.Second)
   // goroutines (可能)显示: three, three, three
}

当前的迭代变量作为匿名goroutine的参数。

package main

import (  
   "fmt"
   "time"
)

func main() {  
   data := []string{"one", "two", "three"}

   for _, v := range data {
       go func(in string) {
           fmt.Println(in)
       }(v)
   }

   time.Sleep(3 * time.Second)
   // goroutines输出: one, two, three
}

一个字符串是 Unicode 编码的字符(或称之为 rune)集合,因此您也可以用它迭代字符串:

for pos, char := range str {
...
}

if

If语句由布尔表达式后紧跟一个或多个语句组成,注意布尔表达式不用()

if 布尔表达式 {
  /* 在布尔表达式为 true 时执行 */
}

break

一个 break 的作用范围为该语句出现后的最内部的结构,它可以被用于任何形式的 for 循环(计数器、条件判断等)。但在 switch 或 select 语句中,break 语句的作用结果是跳过整个代码块,执行后续的代码。

continue

关键字 continue 忽略剩余的循环体而直接进入下一次循环的过程,但不是无条件执行下一次循环,执行之前依旧需要满足循环的判断条件。关键字 continue 只能被用于 for 循环中。

label

for、switch 或 select 语句都可以配合标签(label)形式的标识符使用,即某一行第一个以冒号(:)结尾的单词(Gofmt 会将后续代码自动移至下一行)(标签的名称是大小写敏感的,为了提升可读性,一般建议使用全部大写字母)continue 语句指向 LABEL1,当执行到该语句的时候,就会跳转到 LABEL1 标签的位置。

使用标签和 Goto 语句是不被鼓励的:它们会很快导致非常糟糕的程序设计,而且总有更加可读的替代方案来实现相同的需求。

Tags

Great! You've successfully subscribed.
Great! Next, complete checkout for full access.
Welcome back! You've successfully signed in.
Success! Your account is fully activated, you now have access to all content.