if语句

  1. if语句后紧跟一个或多个语句组成,注意布尔表达式不能用01
  2. 如果表达式求值为true,则执行"if"分支,否则执行"else"分支。
  3. GoLang不支持三目运算 a > b ? a : b,官方的解释是三目运算会导致复杂的表达式
1
2
3
4
5
6
7
8
// 1) if expr { }
//	OR 
// 2) if Init; expr { }
if 布尔表达式 {		
    // 布尔表达式为true时执行
} else {
    // 布尔表达式为false时执行
}
  1. 由于ifswitch都接受初始化语句,因此通常会看到用于设置局部变量的语句。
    • 该语句在计算表达式之前执行。
    • ifelse if后都可以跟【Init; expr】语句。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package main

import (
    "fmt"
)

func main()  {
    // 这里注意,这里使用的是 ; 不是 ,
    //  1. ;分号:用于分隔语句
    //  2. ,逗号:常用语分隔变量多返回赋值形式
    if x := 1; x < 10 {
        fmt.Println("12345")
    } else if x > 0 { // else if x = 2; x > 0 {}
        fmt.Println("6789")
    } else {
        fmt.Println("147258369")
    }
    
    // be equivalent to
    // 注意:xx 的作用域,当前分支以后都适用
    
    {
        xx := 1
        if xx < 10 {
            fmt.Println("12345")
        } else {
            if xx > 0 {
                fmt.Println("6789")
            } else {
                fmt.Println("147258369")
            }
        }
    }
    
    // else if 全部都可以转换成if else形式,这里是为了更好的理解初始化语句所在块位置
    
    // Output:
    // 12345
    // 12345
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
func Ts() {
    rand.Seed(time.Now().UnixNano())
    n := rand.Intn(100)
    
    // 1. 验证 if Init; expr {} 语句
    // 2. 验证 else if Init; expr {} 语句
    if x := 2; x+n > 5 {
        print(x) // 2
    } else if x = 1; x-n > 2 {
        print(x) // 1
    } else if n == 2 {
        print(x) // 1
    } else {
        print(x) // 1
    }
}
  1. if语句没有进入下一个语句,即正文以breakcontinuegotoreturn结尾时,省略不必要的else
1
2
3
4
f, err := os.Open(name)
if err != nil {
    return err
}

break语句

  1. 一个break的作用范围为该语句出现的最内部的结构,它可以用于任何形式的for循环。
  2. switchselect语句中,break语句的作用是跳过整个代码块,继续执行switchselect外后续的代码。
  3. 语句中如果有标签,则必须是包含forswitchselect语句的标签。并且该标签是可以执行终止的。
  4. break两个作用:
    • 针对for关键字结束循环。
    • 针对switchselect关键字跳出整个代码块。但switchselectcase后是默认自带break,如果显示写上也只是跳出switchselect块。
  5. breakswitch中可以作为if分支结束条件。
1
2
3
4
5
6
7
switch {
    case true:
    if true {
        break	// 比如这里,退出case分支
    }
    // 其他代码 ...
}
  1. 因此在switchselect块中使用break关键字只能跳出【当前】switchselect块。如果想跳出外层for循环则需要break Label加上标签名称。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package main

import (
    "fmt"
)

func main()  {
    fmt.Println("start")
OuterLoop:	// 定义标签
    for i := 0; i < 3; i++ {
        fmt.Println("i 循环:", i)
        for j := 0; j < 3; j++ {
            fmt.Println("j 循环:", j)
            // switch和select默认是自带break的
            switch j {
            case 0:
                fmt.Println("break")
                break   // break 只是跳出switch语句块,不会跳出到for块
            case 2:
                fmt.Println("2 OuterLoop")
                // switch默认带有break语句,这里指明break语句要跳出的标签位置
                break OuterLoop // 直接跳出整个循环
            }
            fmt.Println("switch:", j)
        }
    }

    fmt.Println("end")
    
    // Output:
    // start
    // i 循环: 0
    // j 循环: 0
    // break
    // switch: 0
    // j 循环: 1
    // switch: 1
    // j 循环: 2
    // 2 OuterLoop
    // end
}
  1. 注意break label; label(标签)只能是之前出现的,continue关键字也是。但是goto关键字却可以跳转到后面的标签处。

continue语句

  1. 关键字continue用在关键字for(结束本次循环,继续下次循环),但不是无条件执行下一次循环,执行之前依旧需要满足循环的判断条件。
  2. 如果有一个标签,那么它必须是一个封闭的for语句,并且是当前执行进程的标签。
1
2
3
4
5
6
7
8
9
RowLoop:
for y, row := range rows {
    for x, data := range row {
        if data == endOfRow {
            continue RowLoop
        }
        row[x] = data + bias(x, y)
    }
}
  1. 使用示例:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
func main() {
    rows := []string{"hello", "world"}
rowLoop:
    for _, row := range rows {
        for _, data := range row {
            if data == 'l' {
                // continue 用于结束本次循环,进行下一次循环
                continue rowLoop
            }
            fmt.Printf("%c\n", data)
        }
    }
    
    // Output:
    // h
    // e
    // w
    // o
    // r
}

标签

  1. forswitchselect语句都可以配合标签(label)形式的标识符使用。标签可以在代码的任何地方(函数体内)。
    • 即某一行第一个可以冒号(:)结尾的单词(Gofmt 会将后续代码自动移至下一行)
    • 标签的名称是大小写敏感的,为了提升可读性(可以首字母大小,可读性比较高就行)。
1
2
ERROR: // Error or err
log.Panic("error")	// 标签后的代码
  1. 标签用于breakcontinuegoto语句,定义从未使用的标签是非法的,不能编译成功。(定义了标签一定要使用)

goto语句

  1. goto语句是跳转到具体有相同函数内相应标签的语句。(结合标签使用,通常用在一些公共代码部分或循环逻辑处)
1
goto ERROR
  1. Go语言不鼓励多层嵌套使用标签和goto语句,因为它们会导致非常糟糕的程序设计,而且总有更加可读的替代方案来实现相同得需求。
  2. 块外的goto语句不能跳转到该块内的标签。(只能平级跳或跳出到外层块)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
// 以下代码不能通过编译
package main

import "fmt"

var x int = 10

func main() {
    if x % 2 == 1 {
        goto L1 // L1标签在块内
    }
    
    for x < 10 {
        x--
        fmt.Println(x)
    L1: // 在for内部
        x--
        fmt.Println(x)
    }
}
  1. goto语句是可以跳转到后面出现的标签的,前提是满足块外的goto语句不能跳转到块内的标签。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
package main

import (
    "fmt"
)

var x = 10

func main()  {
    goto TL
    // 向后跳转到TL处,这种比较少用,因为接下面的代码永久不会执行
    fmt.Println(x)  // 这行代码不会执行

TL:
    fmt.Println("TL")
    
    // Output:
    // TL
}
  1. goto多用于跳转到前面代码的标签处,这样就形成了循环。

总结

  1. gotobreakcontinue:三个语句都可以配合标签(label)使用。
  2. 标签名区分大小写,定义后若不使用会造成编译错误。
  3. continuebreak配合标签(label)可用于多层循环跳出。
  4. goto是调整执行位置,与continuebreak配合标签(label)的结果并不相同。