EffectiveGo-2-控制结构、函数

Parts of Effective Go

控制结构

if

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// simple if
if x > 0 {
return y
}

// 带初始化参数的 if
if err := file:Chmod(0661); err := nil {
log.Print(err)
return err
}

// 1.当 if 语句不会执行下一条语句
// 2.当 if 的执行体以 break OR continue OR goto OR return 结束 (例如:防御式编程,错误时 return)
// 不必要的 else 会被省略
f, err := os.Open(name)
if err != nil {
return err
}
doSomething(f)

重新声明与再次赋值

1
2
3
4
5
6
7
// err 被声明
f, err := os:Open(name)
if err != nil {
return err
}
// err 被重新声明并再次赋值
d, err := f.Stat()

上述处理方法是合法的
尽管在两次初始化声明中都存在了err变量
满足以下三个条件,已被声明的变量x可以再次出现在:=声明中

  • 本次声明与已声明的x处于同一作用域(若x已在外层作用域中声明过,则本次声明会创建一个新的变量)
  • 在初始化时,与其类型相同的值才能赋予x
  • 在本次声明中至少另有一个变量是新声明的

函数的形参和返回值在词法上处于大括号以外,但它们的作用域与该函数体仍然相同。

for

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// Go的for循环有三种形式
// 只有一种需要分号

// 1.Like a C for
for init; condition; post {
do()
}

// 2.Like a C while
for condition {
do()
}

// 3.Like a C for(;;)
for {
do()
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 若想遍历
// 数组/切片/字符串/映射/从信道中读取
// range
for key, value := range old {}
for key := range old {}
for _, value := range old {} // 空白标识符

// 对于字符串
// range能解析UTF-8
// 分离每个独立的Unicode码点
// 错误的编码将占用一个字节
// 并以 U+FFFD 代替
for pos, char := range "日本\x80語" { // \x80是非法的UTF-8编码
fmt.Printf("character %#U starts at byte position %d\n", char, pos)
}
// character U+65E5 '日' starts at byte position 0
// character U+672C '本' starts at byte position 3
// character U+FFFD '�' starts at byte position 6
// character U+8A9E '語' starts at byte position 7

Go has no comma operator and ++ and -- are statements not expressions
Go中没有逗号操作符,而++--是语句而非表达式

1
2
3
4
5
6
7
// 若想在for中使用多个变量
// 应采用平行赋值(parallel assignment)的方式
// 因为它会拒绝 ++ 和 --
// Reverse a
for i, j := 0, len(a)-1; i < j; i, j = i+1, j+1 {
a[i], a[j] = a[j], a[i]
}

switch

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
42
43
44
45
46
47
48
// case语句会自上而下逐一求值直到匹配为止
// 若swich后没有表达式,它将匹配true
func unhex(c byte) byte {
switch {
case '0' <= c && c <= '9':
return c - '0'
case 'a' <= c && c <= 'f':
return c - 'a' + 10
case 'A' <= c && c <= 'F':
return c - 'A' + 10
}
return 0
}

// switch并不会自动下溯
// 但case可通过逗号分隔来列举相同的处理条件
func shouldEscape(c byte) bool {
switch c {
case ' ', '?', '&', '=', '+', '%':
return true
}
return false
}

// break终止switch
Loop:
for n := 0; n < len(src); n += size {
switch {
case src[n] < sizeOne:
if validateOnly {
break // 跳出switch
}
size = 1
update(src[n])
case src[n] < sizeTwo:
if n+1 >= len(src) {
err = errShortInput
break Loop // 跳到标签Loop处 breaking label
}
if validateOnly {
break // 跳出switch
}
size = 2
update(scr[n] + src[n+1]<<shift)
}
}
// continue语句也可以使用一个可选的标签
// 不过continue只能在循环中使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// Compare 按字典顺序比较两个字节切片并返回一个整数。
// 若 a == b,则结果为零;若 a < b;则结果为 -1;若 a > b,则结果为 +1。
func Compare(a, b []byte) int {
for i := 0; i < len(a) && i < len(b); i++ {
switch {
case a[i] > b[i]:
return 1
case a[i] < b[i]:
return -1
}
}
switch {
case len(a) > len(b):
return 1
case len(a) < len(b):
return -1
}
return 0
}

类型选择

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
package main

import "fmt"

func main() {
var t interface{}
t = functionOfSomeType()
// Output: integer 1
switch t := t.(type) {
default:
fmt.Printf("unexpected type %T", t) // %T 输出 t 是什么类型
case bool:
fmt.Printf("boolean %t\n", t) // t 是 bool 类型
case int:
fmt.Printf("integer %d\n", t) // t 是 int 类型
case *bool:
fmt.Printf("pointer to boolean %t\n", *t) // t 是 *bool 类型
case *int:
fmt.Printf("pointer to integer %d\n", *t) // t 是 *int 类型
}
}

func functionOfSomeType() int {
return 1
}

函数

多值返回

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 从字节数组中的特定位置获取其值,并返回该数值和下一个位置
func nextInt(b []byte, i int) (int, int) {
for ; i < len(b) && !isDigit(b[i]); i++ {
}
x := 0
for ; i < len(b) && !isDigit(b[i]); i++ {
x = x*10 + int(b[i]) - '0'
}
return x, i
}

// 通过扫描输入的切片b来获取数字
func main() {
for i := 0; i < len(b); {
x, i := nextInt(b, i)
fmt.Println(x)
}
}

可命名结果形参

1
2
3
4
5
6
func nextInt(b []byte, pos int) (value, nextPos int) { return }
// 返回值被命名后,一旦该函数开始执行
// value nextPos会被初始化为与其类型相对应的零值
// 若该函数执行了一条不带实参的return
// 则结果形参的当前值将被返回
// 命名结果形参能使代码更加简短清晰

defer

defer语句用于预设一个函数调用,该函数会在执行defer的函数返回之前立即执行。
被推迟函数的实参在推迟执行时就会求值(defer时),而不是在调用执行时才求值。
被推迟的函数按照后进先出(LIFO)的顺序执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// Contents 将文件的内容作为字符串返回。
func Contents(filename string) (string, error) {
f, err := os.Open(filename)
if err != nil {
return "", err
}
defer f.Close() // f.Close 会在我们结束后运行。
var result []byte
buf := make([]byte, 100)
for {
n, err := f.Read(buf[0:])
result = append(result, buf[0:n]...) // append 将在后面讨论。
if err != nil {
if err == io.EOF {
break
}
return "", err // 我们在这里返回后,f 就会被关闭。
}
}
return string(result), nil // 我们在这里返回后,f 就会被关闭。
}
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
package main

import "fmt"

func main() {
// 1
fmt.Println(deferWithDefaultResult())
// 0
fmt.Println(deferWithNormalResult())
}

func deferWithDefaultResult() (result int) {
defer func() {
result++
}()
return
}

func deferWithNormalResult() int {
result := 0
defer func() {
result++
}()
return result
}