好好学习,天天向上

Go, go|小白的go小抄:控制结构和函数

言小白年初许下了关于golang的愿望,这是来践约的。

另外,本小抄无扫盲功效,如需从零开始,参见A Tour of Go

控制结构

重新声明和重新赋值

1
2
3
4
f, err := os.Open(name)
// 声明两个变量:f和err
d, err := f.Stat()
// 此处,err已经在上面语句中声明了,因此这里仅仅是重新赋值。这意味着,对f.Stat的调用使用上面已经声明的变量err,并仅仅给它赋予一个新的值

:=声明中,变量v即使已经声明过,也可以出现在:=的左端,只要它满足以下: * 声明和变量v的现有声明位于同一个作用域(如果v在外部作用域中已经被声明了,那么,该声明将会创建一个新的变量。这里需要注意的是,在Go中,函数参数和函数返回值的作用域和函数体相同,即使在语义上,它们出现在函数花括号外部) * 初始化中相应的值是可以被赋予v的(注意:重新声明并非引进一个新变量,而是可以看成重新赋值),并且 * 在此声明中,至少存在一个新声明的变量

For

三种用法:

1
2
3
4
5
6
7
8
// 就像C的for
for init; condition; post {}

// 就像C的while
for condition {}

// 就像C的f(;;)
for {}
第一种用法可以用来很容易的在循环中声明索引变量:
1
2
3
4
sum := 0
for i := 0; i < 10; i++ {
sum += i
}
当遍历数组/切片/字符串/map,或者从channel读取值的时候,可以使用range:
1
2
3
4
5
6
7
8
9
10
11
12
for key, value := range oldMap {
newMap[key] = value
}
// 当仅需要第一个项的时候,也可以把第二个省略掉
for key := range m {
delete(m, key)
}
// 当只需要第二项的时候,使用_
sum := 0
for _, value := rang array {
sum += value
}
> 注意:对于string,range的每个元素都是一个rune

Switch

1
2
3
4
5
6
7
8
9
10
11
12
func unhex(c byte) byte {
switch {
case '0' <= c && c <= '9':
return c - '0'
caes 'a' <= c && c <= 'f':
return c - 'a' + 10
case 'A' <= c && c <= 'F':
return c - 'A' + 10
}
return 0
}

Go的switch语句比C跟广泛一些。 * 表达式无需是常量或者整型。如果switch没有表达式,那么默认为true。 * 对于case的评估是自上而下直至找到一个匹配的 * 可以使用breakbreak默认情况下退出当前的switch。如果switch语句外层嵌套了for循环,而要退出外层的for循环的话,那么可以使用标签,即在循环上面放一个标签(例如:Loop),然后直接break此标签(例如:break Loop)。当然,continue也接受一个可选的标签作为参数,但是它仅对循环有效

type-switch

用来发现一个接口变量的动态类型。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var a interface{}
a = 3
// 下面定义了一个变量v,这个变量在各个case语句中都有对应的类型。
// 在这种情况下,建议重用变量名,即switch v := v.(type)。这样实际上,在每个case中声明了一个具有相同名称,但是不同类型的新变量。
switch v := a.(type) {
case bool:
// v类型为bool
fmt.Printf("%T %t\n", v, v)
case int:
// v类型为int
fmt.Printf("%T %d\n", v, v)
default:
fmt.Printf("unexpected type %T\n", v)
}

函数

多值返回

和Python不一样,对于go的多值返回,在指定输入参数后,需要指定要返回的值列表。例如:

1
2
3
func test(a int, b string) (string, error){
//...
}
否则会报错:too many arguments to return

多值返回的好处在于: * 带错误返回 * 避免的需要传递一个指针来返回一个值

命名返回参数

在Go中,可以命名一个函数的返回参数,并将其作为常规变量使用(就如函数接收参数一样)。此时,返回参数会被初始化为其类型的零值。如果函数直接return(不带任何参数),那么返回参数的当前值就会被当成返回值返回。

不强制命名返回参数,但是这能让代码更短更清晰。

missing return at end of function

在Go 1.1,无需总是在函数末端添加return语句,但是需要有个结束声明。

推荐阅读: - return - Terminating statement

Defer

Go会在执行defer语句的函数返回之前立即执行defer语句后面的函数。常见的使用场景为解锁一个mux,或者关闭文件。

1
2
3
4
5
6
7
8
func Contents(filename string)(string error) {
f, err := os.Open(filename)
if err != nil {
return "", err
}
defer f.Close()
....
}
对诸如Close()这样的函数使用defer有两个好处: * 保证你永远不会忘记做清理操作 * 将其放在诸如Open()这样的函数后面,使得代码更清晰

注意:在defer func()中,函数func的参数(如果函数是个方法,那么也包含接收者)会在defer执行的时候计算,而不是在函数func执行的时候计算。这样避免了在函数执行的时候变量值的改变,也意味着单个defer调用可以defer多个函数执行。例如:

1
2
3
4
for i := 0; i < 5; i++ {
defer fmt.Printf("%d ", i)
}
// 输出:4 3 2 1 0
另外,可以看出,被defer的函数执行顺序遵循LIFO

参考

请言小午吃个甜筒~~