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. 类型及转换

  1. 基本类型
    • int, int32, int64, int8, float32, float64
    • uint, uint32, uint64
    • bool
    • rune (即 int32), byte (即 int8)
  2. 复杂类型
    • string
    • array
    • slice
    • map
    • 不支持 tuple
    • interface{}

其中 slice 与 map 是仅有的两种指针类型:

  1. 可以与 nil 比较
  2. 做为参数或返回值时自动使用的传引用的语义
  3. 作为 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. 分配与释放

  1. golang 并不存在`返回局部变量的引用`这种问题, 编译器会根据逃逸分析的结果确定是否需要分配在堆上
  2. 可以显式的通过 new 要求分配在堆上
  3. 对于指针类型: slice 和 map, 可以通过 make 以`空值`初始化
  4. chan 只能通过 make 初始化
  5. 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

  1. 与 map 一样, slice 是一个指针, 如果是结构体成员, 需要显式的给它分配空间, 但 array 不需要
  2. [3]int 与 [2]int 并不是相同的 array 类型
  3. array 可以直接比较大小, 例如 [2]int{1,2} == [2]int{1,2} 为 true, 所以 array 可以做为 map 的 key, 但 slice 并不可以
  4. 大部分情况下 slice 用的更频繁
  5. 需要使用 make 来生成特定大小的 slice
  6. 多维的 slice, x:=make([][]int,m); for i:=0;i<m;i++ {x[i]=make([]int,n)}
  7. 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 不支持泛型, 但有一些折衷的方法:

  1. slice, map 本身算泛型容器
  2. 通过空的 interface{}

    func foo(v interface{}) () {
        switch v.(type) {
        case int:
            fmt.Println(v.(int))
        default:
            fmt.Println("unknow type")
        }
    }
    
  3. 通过实现 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

Author: [email protected]
Date: 2018-01-02 Tue 00:00
Last updated: 2019-12-17 Tue 10:36

知识共享许可协议