Go中关于流程控制方面主要有if-elsefor循环,switch。另外,Go中还多了一种新的控制结构select,这种结构包含一个类型开关和一个多路通信复用器;ifswitch接受类似for的可选初始化语句;breakcontinuegoto语句采用可选标签来标识要中断或继续以及要跳转的内容;与C相比,Go的控制结构语法也有些区别:没有小括号,并且控制体必须包含在一对大括号中。

关于select的用法,将在后续关于并发的文章中讲。

一 if语句

if表达式外无需小括号 ( ) ,而大括号 { } 则是必须的。强制使用大括号是为了鼓励将简单的if语句拆分成多行。这是一个好习惯,特别是当控制体中包含returnbreak语句时。

在Go中,一个简单的if语句如下:

if x > 0 {
  return y;
}

示例:

package main

import (
    "fmt"
    "math"
)

func main() {
    fmt.Println(sqrt(16))
    fmt.Println(sqrt(-9))
}

func sqrt(x float64) string {
    if x < 0 {
        return sqrt(-x) + "i"
    }
    return fmt.Sprint(math.Sqrt(x))
}

二 简短语句

if 语句可以在条件表达式前执行一个简单的语句。该语句声明的变量作用域仅在 if 之内。

package main

import (
    "fmt"
    "math"
)

func main() {
    fmt.Println(
        pow(3, 2, 10),
        pow(3, 3, 20),
    )
}

func pow(x, n, lim float64) float64 {
    if v := math.Pow(x, n); v < lim {
        return v
    }

    // return v // 错误:undefined: v
    return lim
}

上述示例中的第16行中,语句v := math.Pow(x, n)会优先求值。然后再拿结果进行比较。

类比一下:Java中类似语法可以这样写:

int r;
if ((r = Math.round(x) > 0) {
   return r;
}

在 if 的简短语句中声明的变量同样可以在任何对应的 else 块中使用。
例如,将上述示例中的pow函数改为:

func pow(x, n, lim float64) float64 {
    if v := math.Pow(x, n); v < lim {
        return v
    } else {
        fmt.Printf("pow(%v, %v, %v)超出lim参数范围,结果将被截取为%v\n", x, n, lim, lim)
    }

    // return v // 错误:undefined: v
    return lim
}

在Go中,如果if语句之后没有进入下一个语句,则意味着该if控制体中最后一行是 breakcontinuegoto或者return(这里省略了非必须的else语句):

f, err := os.Open(name)
if err != nil {
    return err
}
codeUsing(f)
联想:在Java中,breakcontinue只能出现在循环体中,而goto是保留关键字。

下面是一种常见的情况,其中代码必须处理防止一系列错误。由于错误情况将以return语句结束,因此之后的代码不需要else语句。

f, err := os.Open(name)
if err != nil {
    return err
}
d, err := f.Stat()
if err != nil {
    f.Close()
    return err
}
codeUsing(f, d)
类比一下:这种做法在Java和其他语言中也很常见,避免过多的if-else嵌套。

三 重新声明和重新赋值

上一节中的最后一个示例演示了:=简短声明的工作原理。调用os.Open的声明:

f, err := os.Open(name)

该语句声明了两个变量ferr。几行之后,调用f.Stat:

d, err := f.Stat()

看起来好像是声明了derr。但请注意,err出现在两个语句中。这种重复是合法的:err是由第一个语句声明的,但在第二个语句中仅是重新赋值而已。这意味着对f.Stat的调用使用上面声明的现有err变量,并只是给它一个新值。

:=声明中,即使已经声明变量v,也可能再次出现变量v,前提是:

  • 此声明与v的现有声明在同一范围内(如果v已在外部作用域中声明,当前声明将创建一个新变量)
  • 初始化中的相应的值可分配给v,并且
  • 声明中至少有一个其他变量是新声明的。

这种特殊的特性纯粹是为了实用,能够简单的复用同一个变量,而且这种用法很常见。

值得注意的是,在Go中,函数参数和返回值的作用域范围与函数体相同,即使它们在大括号之外的词法上出现。

参考:
https://golang.org/doc/effective_go.html#if
https://golang.org/ref/spec#If_statements

文章目录