Golang
Table of Contents
1. Golang
1.1. 风格
package helloworld type HelloWorld struct { privMem int PubMember int } func (this HelloWorld) PubMethod() {} func (this HelloWorld) privMethod() {} func main() { helloWorld := HelloWorld{1, 2} }
1.2. 变量
- var x int
- var x int = 10
- x := 10
- 可以通过 var x int = 10 定义全局变量
- const x = 10
1.3. 表达式
- bitwise
- and &
- or |
- not ^
- xor ^
- shift <<, >>, 其中 >> 和 c 一样区分算法与逻辑右移
- 支持 +=, &=, >>=,…
- 支持 ++, –, 是 stmt 而不是 expression
- !expression
- 支持 a,b = b,a
- if 与 switch 支持 initialization stmt: if x:=1+2; x>3 {}
- 条件判断必须值为 bool 的表达式, if x:=1; x {} 不合法
1.4. 函数
- 不支持 overloading
- 不支持默认参数
- 支持多个函数值
- 支持变长参数, 实际就是一个 slice
package main import "fmt" func echo (i1, i2 int, i3 int) (int, int, int) { return i1, i2, i3 } // 变长参数 func echo2(x ...int) ([]int) { return x } func main () { fmt.Println(echo(1, 2, 3)) fmt.Println(echo2(1, 2, 3)) }
1 2 3 [1 2 3]
1.5. 输入输出
- fmt.Println
- fmt.Printf
- fmt.Scanln
- fmt.Scanf
package main func main() { fmt.Printf("%d,%s", 1, "abc") fmt.Println(1, 2, 3, "ab") i, j := 0, 0 // stdin 为 1 2 fmt.Scanln("%d %d", &i, &j) fmt.Println(i, j) // 1 2 // stdin 为 1 2\n3 4 x, y := 0, 0 fmt.Scanf("%d %d\n%d %d", &i, &j, &x, &y) fmt.Println(i, j, x, y) // 1 2 3 4 }
1.6. 类型及转换
- 基本类型
- int, int32, int64, int8, float32, float64
- uint, uint32, uint64
- bool
- rune (即 int32), byte (即 int8)
- 复杂类型
- string
- array
- slice
- map
- 不支持 tuple
- interface{}
其中 slice 与 map 是仅有的两种指针类型:
- 可以与 nil 比较
- 做为参数或返回值时自动使用的传引用的语义
- 作为 struct 成员时需要显示的分配, 否则成员为 nil
package main func main() { var x int = 1 var y int32 = 1 var z int64 = 1 var a byte = 'a' var s string = "abc" var z rune = 'a' // rune 即 int32, byte 即 int8, 关于 byte, rune, string, 参考 golang::string var b bool = true var f float32 = 1.0 var d float64 = 1.0 x = int(d) d = float64(x) }
1.7. 分配与释放
- golang 并不存在`返回局部变量的引用`这种问题, 编译器会根据逃逸分析的结果确定是否需要分配在堆上
- 可以显式的通过 new 要求分配在堆上
- 对于指针类型: slice 和 map, 可以通过 make 以`空值`初始化
- chan 只能通过 make 初始化
- go 有 gc, 不需要手动释放
package main import "fmt" type Test struct { a int b int } func getTest() *Test { return &Test{1, 2} } func getTest2() *Test { // 不支持 new (Test{1,2}) 这种写法 t := new(Test) t.a = 1 t.b = 2 return t } func main() { t := getTest() fmt.Println(*t) t = getTest2() fmt.Println(*t) // map init with make m := make(map[int]int) fmt.Println(m == nil) var m2 map[int]int fmt.Println(m2 == nil) m3 := map[int]int{} fmt.Println(m3 == nil) var x int x2 := 0 fmt.Println(x == x2) // slice init with make // make slice 是可以指定一个长度 s := make([]int, 5) s2 := []int{0, 0, 0, 0, 0} fmt.Println(s, s2) }
1.8. string
- string 不可变
- 可以通过 []rune(s) 把 s 变为 rune slice, 以及通过 string([]rune{}) 转换回 string
- for k,v :=range (str) 返回的 v 类型是 rune, 即 utf16 编码
- str[i] 返回的数据为 byte
- len 返回长度
- 支持 "a"+"b"
- 支持像 slice 的 s[i:j] 操作
- string 不是指针类型, 无法与 nil 比较
- strings 库相关的函数
- HasPrefix, HasSurfix
- Split, Join
- Replace
- Repeat
- Contains
- Index, LastIndex
- strconv 库的 Atoi, Itoa
package main import "fmt" func main() { s := "abc" s += "def" x := len(s) fmt.Println(x) s += "大家好" for _, v := range s { fmt.Print(v," ") } fmt.Println() for i := 0; i < len(s); i++ { fmt.Print(s[i]," ") } fmt.Println() v := []rune(s) fmt.Println(v) fmt.Println(string(v)) }
6 97 98 99 100 101 102 22823 23478 22909 97 98 99 100 101 102 229 164 167 229 174 182 229 165 189 [97 98 99 100 101 102 22823 23478 22909] abcdef大家好
1.9. array 与 slice
- 与 map 一样, slice 是一个指针, 如果是结构体成员, 需要显式的给它分配空间, 但 array 不需要
- [3]int 与 [2]int 并不是相同的 array 类型
- array 可以直接比较大小, 例如 [2]int{1,2} == [2]int{1,2} 为 true, 所以 array 可以做为 map 的 key, 但 slice 并不可以
- 大部分情况下 slice 用的更频繁
- 需要使用 make 来生成特定大小的 slice
- 多维的 slice, x:=make([][]int,m); for i:=0;i<m;i++ {x[i]=make([]int,n)}
- copy 可以用来复制 slice
package main func main() { x := [2]int{1, 2} // x 是一个 array y := []int{1, 2} // y 是一个 slice z := x[:] // array 转换为 slice k := [2]int{} copy(k, z) // slice 转换为 array // 使用 copy 来复制 slice f := []int{1, 2, 3} t := make([]int, 2) // t 变为 []int{1,2}, f 的前两个元素被复制 copy(t, f) // 使用 append 来复制 slice t2 := append([]int{}, f...) }
slice 最常用的操作:
package main func main() { // append x := []int{1, 2, 3} x = append(x, 4, 5) x = append(x, []int{6, 7, 8}...) // slice x = x[1:2] // remove x = x[:len(x) - 1] // 模拟 stack 的 pop 操作 // len l := len(x) // range for _, v := range x { fmt.Println(v) } // make y := make([]int, 10) // 10x5 的二维 slice v := make([][]int, 10) for i := 0; i < 10; i++ { v[i] = make([]int, 5) } }
1.10. map
- map 与 slice 一样是指针类型
- 对 map 中不存在的项取值时会返回默认值 (0, "", nil 等), 如果要确定 entry 是否存在, 需要使用 x, exist:=m[key] 后判断 exist 是否为 true
- 有一个 delete 关键字,只用来删除 map key
- map 不支持 keys(), values(), items() 等方法
- slice, map 无法做为 key, 其它基本类型例如 bool, int, [2]int, string, pointer, channel 都可作为 key, 而且只包含这些类型的结构也可以作为 key
- struct 无法通过定义类似 `eq` 来做为 key
package main import "fmt" func main() { m := map[string]int{ "abc": 1, "bcd": 2, } fmt.Println(m["abc"]) if t, ok := m["xxx"]; ok { fmt.Println(t) } m["abc"] = 3 for k, v := range m { fmt.Println(k, v) } delete(m, "abc") fmt.Println(m) }
1 abc 3 bcd 2 map[bcd:2]
1.11. 抽象数据类型
- type xxx struct {}
- struct 中的指针类型需要初始化
- 没有构造函数, 可以自己提供一个 NewXXX 函数
- struct 中只有首字母大写的成员可以被其它 package 访问
- struct 不支持继承, 没有成员函数, 相关的功能通过给 func 添加 receiver 完成
- 若函数需要修改 receiver, 需要提供指针类型作为 receiver
package main import "fmt" type Pair struct { x int y int } func (p Pair) Dump() { fmt.Println(p.x, p.y) } func (this *Pair) Swap() { this.x, this.y = this.y, this.x } func NewPair(x, y int) Pair { return Pair{x, y} } func main() { p := NewPair(1, 2) p.Dump() p.Swap() p.Dump() }
1 2 2 1
golang 通过 duck typing 的方式支持 interface
package main import "fmt" type Animal interface { speak() } type Dog struct { } func (dog Dog) speak() { fmt.Println("bark") } func speak(x Animal) { x.speak() } func main() { speak(Dog{}) }
bark
1.12. 泛型
go 不支持泛型, 但有一些折衷的方法:
- slice, map 本身算泛型容器
通过空的 interface{}
func foo(v interface{}) () { switch v.(type) { case int: fmt.Println(v.(int)) default: fmt.Println("unknow type") } }
- 通过实现 interface, 例如 sort.Sort() 的实现方法
1.13. 控制结构
- for for 是唯一的循环结构, 没有 while, do
- if if 与 switch 支持 initialization stmt
- switch 支持 switch {} 与 switch x {} 两种形式, 不支持 case fall throught, 但支持同一个 case 多个条件
package main import ( "fmt" ) func main() { // for 1 for i := 0; i < 3; i++ { fmt.Println(i) } // for range for i, v := range []int{1, 2, 3} { fmt.Println(i, v) } // for range in map for k, v := range map[int]int{1: 1, 2: 2} { fmt.Println(k, v) } // while true for { } // while () for i < 1 { } // if i := 0 if i == 0 { } // if with initialization stmt m := map[int]int{1: 2} if t, ok := m[1]; ok { fmt.Println(t) } // switch x x := 1 switch x { case 1: fmt.Println(1) case 2, 3, 4: fmt.Println(2, 3, 4) default: fmt.Println("default") } // switch switch { case x == 1: fmt.Println(1) case x == 2 || x == 3 || x == 4: fmt.Println(2, 3, 4) } // swith with initialization stmt switch t, _ := m[1]; t { case 1: fmt.Println(1) } }
1.14. 排序
- sort.Ints
- sort.Strings
- sort.Slice
- sort.Sort
package main import ( "fmt" "sort" ) type Pair struct { x, y int } func main() { x := []int{4, 1, 2, 3} sort.Ints(x) fmt.Println(x) s := []string{"c", "b", "a"} sort.Strings(s) fmt.Println(s) // sort.Slice 适用于对所有 slice 排序 pairs := []Pair{{3, 2}, {2, 4}} sort.Slice(pairs, func(i, j int)bool { return pairs[i].x < pairs[j].x }) fmt.Println(pairs) // 更通用的 sort.Sort, 其中 sort.Reverse, sort.IntSlice 返回的 // 都是实际了 sort.Interface 的接口, 例如 IntSlice 实际为: // type IntSlice []int // func (p IntSlice) Len() int { return len(p) } // func (p IntSlice) Less(i, j int) bool { return p[i] < p[j] } // func (p IntSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] } // 一般情况下不需要使用 sort.Sort 接口 sort.Sort(sort.Reverse(sort.IntSlice(x))) fmt.Println(x) }
[1 2 3 4] [a b c] [{2 4} {3 2}] [4 3 2 1]
1.15. 最大堆
- container/heap 包实现了堆
- struct 需要自己实现 Len,Less, Swap, Pop, Push 方法
- 由于 golang 不支持泛型, 需要注意 Push, Pop 方法使用了 interface{} 做为参数和返回值, 导致 Pop 时需要手动将 interface{} 转换为需要的类型
package main import ( "container/heap" "fmt" ) type MinHeap []int func (h MinHeap) Len() int { return len(h) } func (h MinHeap) Less(i, j int) bool { return h[i] < h[j] } func (h MinHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] } func (ph *MinHeap) Pop() interface{} { t := (*ph)[len(*ph)-1] *ph = (*ph)[:len(*ph)-1] return t } func (ph *MinHeap) Push(x interface{}) { *ph = append(*ph, x.(int)) } func main() { h := MinHeap{p5, 4, 3, 2, 1} heap.Init(&h) heap.Push(&h, 10) fmt.Println(heap.Pop(&h).(int)) }
1
1.16. math
由于 golang 不支持泛型, math 库中许多函数无法像其它语言一样方便的使用, 因为 math 库大部分函数要求 float64 类型
- Abs, Floor, Ceil
- MinInt32, MaxInt32
- Pow
1.17. random
- rand.Intn(n)
- rand.Seed
package main import ( "fmt" "math/rand" "time" ) func main() { rand.Seed(time.Now().Unix()) fmt.Println(rand.Intn(10)) }
1.18. package
- import "fmt"
- import "container/heap"
- import ()
- import "../util/tree"
- import . "util/tree"
- import mytree "util/tree"
- package 中首字母大写的符号才可以被其它 package 使用
- package 是位于 $GOPATH 里指定的某个 {path} 的 src 目录下的目录名
1.19. 坑
- int 类似在 64bit 系统上为 int64, 而 c 一般是 int32
- struct 中的指针类型: map, slice 需要单独初始化
- slice 有时需要 copy, 防止别处与它共用
- x.(Pair).a = 1 失败, 无法对 x.(Pair) 赋值, 需要使用 z:=x.(Pair); z.a=1
lambda 函数递归定义时有问题
// 无法编译通过: package main func test() { foo := func() { foo() } } // 需要修改为: func test() { var foo func() foo = func() { foo() } }
map 无法赋值
package main type Pair struct { a int b int } func main() { m := map[int]Pair{} m[0] = Pair{1, 1} // 编译错误: cannot assign to struct field m[0].a in map m[0].a = 10 // 修改方法 1 tmp := m[0] tmp.a = 10 m[0] = tmp // 方法 2 m2 := map[int]*Pair{} m2[0] = &Pair{1, 1} m2[0].a = 10 }
无法修改的原因据说是 m[0] 对应的 Pair 的地址并不是固定的, map 会根据需要重新安排它的位置
1.20. channel
- c:=make(chan int, 10)
- 往 chan 写: c <- 1
- 从 chan 读: v := <-c
select
select { case msg := <- c1: fmt.Println("from c1") case msg := <- c2: fmt.Println("from c2") }
1.21. misc
const, iota
package main import "fmt" const ( FIRST = iota + 1 SECOND THIRD ) func main() { fmt.Println(FIRST, SECOND, THIRD) }
1 2 3
- go build -gcflags -S xxx.go
- go build -gcflags -m xxx.go