5.6. 控制流

Go语言的控制结构与C的基本相同但是有些地方还是不同。Go中没有do, while这样的循环,for与switch 也非常的灵活。if和switch可以有一个初始化语句 就像for一样。还增加了一个type switch(类型选择)和多通道复用(multiway communications multiplexer)的select. 语法有一点点区别,圆括号大部分是 不需要的但是大括号必须始终括号分隔.

5.6.1. If

Go中简单的if实例:

  if x > 0 {
      return y
  }

建议在写if语句的时候采用多行。这是一种非常好的编程风格, 特别是在控制结构体里面有return或break的时候.

if和switch允许初始化声明,就可以使用本地变量(local variable).

  if err := file.Chmod(0664); err != nil {
      log.Stderr(err)
      return err
  }

在Go的库文件中,你可能经常看到if语句不进入下一条语句是因为函数在 break,continue,goto或reurn结束, else可以省略。

  f, err := os.Open(name, os.O_RDONLY, 0)
  if err != nil {
      return err
  }
  codeUsing(f)

一般的代码都会考虑错误的处理,如果没有出错的情况就继续运行但是在出错的时候 函数就会返回,所以在这里不需要else语句。

  f, err := os.Open(name, os.O_RDONLY, 0)
  if err != nil {
      return err
  }
  d, err := f.Stat()
  if err != nil {
      return err
  }
  codeUsing(f, d)

5.6.2. For

Go中的for循环与C相似,但是也有不同的地方。Go只有for和 while, 没有do-while语句。这里有三种方式,只有一种方式 使用了分号。

  // Like a C for
  for init; condition; post { }

  // Like a C while
  for condition { }

  // Like a C for(;;)
  for { }

短声明使得在循环里声明下标变量很容易。

  sum := 0
  for i := 0; i < 10; i++ {
      sum += i
  }

如果你要遍历一个array, slice, string or map or 从通道(channel)读数 range将是你最好的选择。

  var m map[string]int
  sum := 0
  for _, value := range m {  // key is unused
      sum += value
  }

对于字符串,range能为你更好的工作,比如解析UTF-8并单独输出Unicode字符.

  for pos, char := range "日本語" {
      fmt.Printf("character %c starts at byte position %d\n", char, pos)
  }

打印出:

  character 日 starts at byte position 0
  character 本 starts at byte position 3
  character 語 starts at byte position 6

最后,Go中没有逗号运算符(comma operator)和++与--运算, 如果你想执行多个变量在for语句中,你可以使用并行参数(parallel assignment).

  // Reverse a
  for i, j := 0, len(a)-1; i < j; i, j = i+1, j-1 {
      a[i], a[j] = a[j], a[i]
  }

5.6.3. Switch

Go中的switch要比C更全面,C的表达式仅仅只有常数或整数。 每个分支(cases)从上到下进行匹配取值, 如果switch没有表达式 那么switches是真。所有才有可能使用switch替换

  func unhex(c byte) byte {
      switch {
      case '0' <= c && c <= '9':
          return c - '0'
      case 'a' <= c && c <= 'f':
          return c - 'a' + 10
      case 'A' <= c && c <= 'F':
          return c - 'A' + 10
      }
      return 0
  }

没有自动掉到下一分支,但分支可以是逗号分隔的列表:

  func shouldEscape(c byte) bool {
      switch c {
      case ' ', '?', '&', '=', '#', '+', '%':
          return true
      }
      return false
  }

这个操作数组的程序和上面的相似是通过两个switch语句。

  // Compare returns an integer comparing the two byte arrays
  // lexicographically.
  // The result will be 0 if a == b, -1 if a < b, and +1 if a > b
  func Compare(a, b []byte) int {
      for i := 0; i < len(a) && i < len(b); i++ {
          switch {
          case a[i] > b[i]:
              return 1
          case a[i] < b[i]:
              return -1
          }
      }
      switch {
      case len(a) < len(b):
          return -1
      case len(a) > len(b):
          return 1
      }
      return 0
  }

switch可以动态的取得接口变量的数据类型,比如: type switch就是 用关键字type插入到接口类型后面的括号来判断类型, 如果switch 在表达式中声明了一个变量,在分支上就有相应的类型。

  switch t := interfaceValue.(type) {
  default:
      fmt.Printf("unexpected type %T", t)  // %T prints type
  case bool:
      fmt.Printf("boolean %t\n", t)
  case int:
      fmt.Printf("integer %d\n", t)
  case *bool:
      fmt.Printf("pointer to boolean %t\n", *t)
  case *int:
      fmt.Printf("pointer to integer %d\n", *t)
  }