接下来学习go语言的基本流程控制,包含if、switch、for、defer四个关键字。

if

go语言与其他语言的一个不同之处,就是控制语句不需要额外的括号包裹,if语句的使用如下

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
if x < 0 {
    return "hi,i'm work"
}

if x > 0 {
    return "a"
} else if x < 0 {
    return "b"
} else {
    return "c"
}

上面的用例介绍基础用法,go的独特之处还有一点,就是可以在if中插入简单的表达式

1
2
3
4
5
6
func pow(x, n, lim float64) float64 {
	if v := math.Pow(x, n); v < lim {
		return v
	}
	return lim
}

当然,上面的用法中v的作用于就仅限制于if语句内,想要在if语句外访问,则还是需要定义在if语句外部。同理,if语句内部的变量需要传递下去,则需要在if语句之前声明好变量。

switch

go语言中switch的语法逻辑是从上到下,每个分支判断,分支判断为true时则执行该分支,用法和其他语言差不多,不过go语言中,每种case情况不需要单独的写break,go默认只执行单个case下的逻辑,下面的举例可以查看基础用法,此外当多个分支存在同一结果时,可以将多种匹配数值逗号拼接,写在同一行进行简化处理:

 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
switch a {
	case "a":
		fmt.Println("a")
	case "b":
		fmt.Println("b")
    case "c","d","e":
        fmt.Println("c-e")
	default:
        fmt.Printf("%s.\n", os)
}

switch {
	case a == "a":
		fmt.Println("a")
	case a == "b":
		fmt.Println("b")
	default:
        fmt.Printf("%s.\n", os)
}


switch os := runtime.GOOS; os {
	case "darwin":
		fmt.Println("OS X.")
	case "linux":
		fmt.Println("Linux.")
	default:
        fmt.Printf("%s.\n", os)
}

从上面的例子可以看到和if类似,switch也允许使用短表达式。此外,go语言中switch结合类型断言以及channel还有其独特的用法,这里简单举例,具体情况之后再详细讨论。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
switch i:= x.(type) {
    case nil:
        fmt.Println("type is nil")
    case int:
        fmt.Println("type is int")
    case bool:
        fmt.Println("type is bool")
    case string:
        fmt.Println("type is string")
    case func(int) int:
        fmt.Println("type is func(int)int")
    default:
        fmt.Println("type others")
}

使用switch最后还有一些简单的控制流转需要说明,即break和fallthrough两个关键字,break可以在当个case下立刻终止执行,跳出switch逻辑,fallthrough的作用则是继续往下执行。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
switch {
case false:
    fmt.Println("1、case 条件语句为 false")
    fallthrough
case true:
    fmt.Println("2、case 条件语句为 true")
    fallthrough
case false:
    fmt.Println("3、case 条件语句为 false")
    fallthrough
case true:
    fmt.Println("4、case 条件语句为 true")
case false:
    fmt.Println("5、case 条件语句为 false")
    fallthrough
default:
    fmt.Println("6、默认 case")
}
// 最终输出
// case 条件语句为 true
// case 条件语句为 false
// case 条件语句为 true

for

for语句作为go语言的唯一循环关键字,用法和其他语言差不多,格式上可以划分三个部分,第一是初始化部分,第二是循环终止判断部分,第三是迭代推进部分。此外,for也可以只写终止条件或者不写任何条件,通过break跳出循环。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
for i := 0; i < 10; i++ {
    sum += i
}

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

for i<20 {
    i++
}

for {
    if i>20 {
        break
    }
}

for语句除了上面基础用法外,还有一种比较常用的方法,即配合range使用。

1
2
3
4
5
6
7
8
lis := []int{1,2,3}
for idx,val := range lis{
    fmt.Println(idx,val)
}
m := map[int]string{1:"a",2:"b"}
for key,val := range m {
    fmt.Println(key,val)
}

使用for range的操作时需要注意,go语言里面的值拷贝的原因,如果我们在遍历指针数据,并利用了迭代变量地址时,需要注意迭代变量最后指向数据最后一个值的情况。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
type A struct {
    name string
}
arr := []A{
    A{"a"},
    A{"b"},
}

var res []*A
for _, v := range arr {
    res = append(res, &v)
}

上面的代码可以看到,我们在res里面存储的是v的地址,而v在循环结束后指向最后一个值,因此res里面存储的都是A{"b"}数据。

defer

defer是go语言的特色关键字,其作用是在函数中定义额外的执行逻辑,即函数执行完后还需要执行defer定义的语句。当一个函数内部有多个defer语句是,则执行的顺序是按照defer语句从上到下,先进后出的栈式顺序执行。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
func main() {
	fmt.Println("counting")

	for i := 0; i < 3; i++ {
		defer fmt.Println(i)
	}

	fmt.Println("done")
}
/* 输出结果
counting
done
2
1
0
*/

参考