1,if 语句

if boolean_expression {
    
}

if boolean_expression {
  
} else {
  
}

// 多分支
if boolean_expression1 {
 
} else if boolean_expression2 {
  
} else if boolean_expressionN {
  
} else {
  
}

Go 支持在 if 后的布尔表达式前,进行一些变量的声明,在 if 布尔表达式前声明的变量,叫它 if 语句的自用变量。顾名思义,这些变量只可以在 if 语句的代码块范围内使用,比如下面代码中的变量 a、b 和 c:

func main() {
    // 中间要用分号隔开
    if a, c := f(), h(); a > 0 {
        println(a)
    } else if b := f(); b > 0 {
        println(a, b)
    } else {
        println(a, b, c)
    }
}

2,for 循环

Go 中的 for 循环有多种写法:

var sum int
for i := 0; i < 10; i++ {
    sum += i
}
println(sum)


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


i := 0
for ; i < 10; {
    println(i)
    i++
}  


i := 0
for i < 10 {
    println(i)
    i++
}  


// 无限循环,死循环的三种种形式
for { 	// 最简洁,推荐使用
   // 循环体代码
}

for true {
   // 循环体代码
}

for ; ; {
   // 循环体代码
}

3,for-range 语句

// 对于一个切片,用 for 循环遍历
var sl = []int{1, 2, 3, 4, 5}
for i := 0; i < len(sl); i++ {
    fmt.Printf("sl[%d] = %d\n", i, sl[i])
}

// 等价的 for-range 遍历方法
for i, v := range sl {
    fmt.Printf("sl[%d] = %d\n", i, v)
}

// for-range 的几个变种
// 只遍历下标值
for i := range sl {	
  // ... 
}

// 只遍历值,忽略下标
for _, v := range sl {
  // ... 
}

// 既不关心下标,也不关心值
for range sl {
  // ... 
}

遍历 string 类型:

var s = "中国人"
for i, v := range s {
    fmt.Printf("%d %s 0x%x\n", i, string(v), v)
}

// 结果如下
0 中 0x4e2d
3 国 0x56fd
6 人 0x4eba

for-range 与 channel

当 channel 类型变量作为 for range 语句的迭代对象时,for range 会尝试从 channel 中读取数据:

var c = make(chan int)
for v := range c {
   // ... 
}
  • for range 每次从 channel 中读取一个元素后,会把它赋值给循环变量 v,并进入循环体
  • 当 channel 中没有数据可读的时候,for range 循环会阻塞在对 channel 的读操作上
  • 直到 channel 关闭时,for range 循环才会结束,这也是 for range 循环与 channel 配合时隐含的循环判断条件

for range 与 Goroutine 的坑

比如下面代码:

func main() {
    var m = []int{1, 2, 3, 4, 5}  
             
    for i, v := range m {
        go func() {
            time.Sleep(time.Second * 3)
            fmt.Println(i, v)
        }()
    }

    time.Sleep(time.Second * 10)
}

// 其实际输出是
4 5
4 5
4 5
4 5
4 5

//
func main() {
    var m = []int{1, 2, 3, 4, 5}

    for i, v := range m {
        go func(i, v int) {
            time.Sleep(time.Second * 3)
            fmt.Println(i, v)
        }(i, v)
    }

    time.Sleep(time.Second * 10)
}

// 输出的结果如下(具体的结果依赖于 CPU 调度)
0 1
1 2
2 3
3 4
4 5

4,continue 与 break 语句

Go 语言提供了 continue 语句和 break 语句。

Go 语言中的 continue 在 C 语言 continue 语义的基础上又增加了对 label 的支持。

带 label 的 continue 语句,通常出现于嵌套循环语句中,被用于跳转到外层循环并继续执行外层循环语句的下一个迭代,比如:

func main() {
    var sl = [][]int{
        {1, 34, 26, 35, 78},
        {3, 45, 13, 24, 99},
        {101, 13, 38, 7, 127},
        {54, 27, 40, 83, 81},
    }

outerloop:
    for i := 0; i < len(sl); i++ {
        for j := 0; j < len(sl[i]); j++ {
            if sl[i][j] == 13 {
                fmt.Printf("found 13 at [%d, %d]\n", i, j)
                continue outerloop
            }
        }
    }
}

5,switch-case 语句

func readByExtBySwitch(ext string) {
    switch ext {
    case "json":
        println("read json file")
    case "jpg", "jpeg", "png", "gif":
        println("read image file")
    case "txt", "md":
        println("read text file")
    case "yml", "yaml":
        println("read yaml file")
    case "ini":
        println("read ini file")
    default:
        println("unsupported file extension:", ext)
    }
}

如果 switch 表达式匹配到了某个 case 表达式,那么程序就会执行这个 case 对应的代码分支。这个分支后面的 case 表达式将不会再得到求值机会,即便后面的 case 表达式求值后也能与 switch 表达式匹配上,Go 也不会继续去对这些表达式进行求值了。

无论 default 分支出现在什么位置,它都只会在所有 case 都没有匹配上的情况下才会被执行的。

只要类型支持比较操作,都可以作为 switch 语句中的表达式类型。比如整型、布尔类型、字符串类型、复数类型、元素类型都是可比较类型的数组类型,甚至字段类型都是可比较类型的结构体类型,也可以。

6,type-switch 语句

Go 语言的 switch 语句还支持求值结果为类型信息的表达式,也就是 type switch 语句。

func main() {
    // x 必须是一个接口类型变量
    // 通过x.(type),可以获得变量 x 的动态类型信息
    var x interface{} = 13
    switch x.(type) {
    case nil:
        println("x is nil")
    case int:
        println("the type of x is int")
    case string:
        println("the type of x is string")
    case bool:
        println("the type of x is string")
    default:
        println("don't support the type")
    }
}

通过x.(type),我们除了可以获得变量 x 的动态类型信息之外,也能获得其动态类型对应的值信息:

func main() {
    var x interface{} = 13
     
    // v 存储的是变量 x 的动态类型对应的值信息
    switch v := x.(type) {
    case nil:
        println("v is nil")
    case int:
        println("the type of v is int, v =", v)
    case string:
        println("the type of v is string, v =", v)
    case bool:
        println("the type of v is bool, v =", v)
    default:
        println("don't support the type")
    }
}

// 输出结果如下
the type of v is int, v = 13

(完。)