返回首页 - Notes - 2017

Go 语言拾遗


语言基础

Go 的函数定义可以给返回值命名,这样 return 语句可以选择省略后续的参数

支持多重赋值:var a, b int = 1, 2

:= 可以用于简化变量赋值,但只能用在函数体里面,且不能用于常量,如:a, b := true, "Ruchee"

数据类型

数据类型的强制转换和 C语言 写法一致:int a = int(b)

Go 中,字符串不可变,如果需要修改字符串,需要写成下面这样:

// 方法一
s := "Ruchee"
c := []byte(s)   // 先将字符串转成 byte 型数组
c[0] = 'x'       // 然后修改 byte 型数组的元素
s2 := string(c)  // 最后再将 byte 型数组转回字符串

// 方法二
s := "Ruchee"
s = "x" + s[1:]  // 字符串虽然不能更改,但是可以切片

可以用 defer 声明在函数返回前执行的语句,如果有多个 defer 声明,则先声明的后执行


iota

关键字 iota 用于声明 enum 时使用,默认的开始值是 0,在 const 中每增加一行加 1,多个 iota 在同一行值不变

如果没有显式指定值,每个 const 分组的第一个常量默认设置为它的 0 值,后续的常量默认设置为它前面那个常量的值,如果前面那个常量的值是 iota,则它也会被设置为 iota

const (
  x = iota  // x == 0
  y = iota  // y == 1
  z = iota  // z == 2
  w         // 这里隐式的声明是 w = iota,因此 w == 3
)

const v = iota  // 每遇到一个 const 关键字,iota 就会重置,此时 v == 0

const (
  h, i, j = iota, iota, iota  // 因为 iota 在同一行值相同,所以 h == i == j == 0
)

const (
  a       = iota              // a == 0
  b       = "B"               // 每增加一行,iota 会自增 1
  c       = iota              // c == 2
  d, e, f = iota, iota, iota  // d == e == f == 3
  g       = iota              // g == 4
)

复合结构

Go 是有指针的,很不错的设定

结构体的定义:

type Vertex struct {
  X int
  Y int
}

访问结构体字段:

v := Vertex{2, 3}
v.X = 10

数组的大小不可变,声明格式为:var arr [10]int,由于长度也是数组类型的一部分,所以 [3]int[4]int 是不同的类型

数组之间的赋值是值的赋值,也就是说传递一个数组参数给某个函数,传的是值,而不是传的引用

slice 是动态数组,但并不是真正意义上的数组,而是一个引用类型,总是指向一个底层的 array

slice 支持切片操作,和 Python 类似,其声明格式为:var s = []int{1, 2, 3, 4, 5},可用 len(s) 获取元素的个数

可用 make 方法创建 slice,可用 append 方法向一个 slice 添加元素

map 的声明格式:var m map[string]int

函数也是值,且有 闭包 的概念,通过 args_name ...int 的形式支持可变参数

Go 虽然没有类的概念,但可以在结构体类型上定义方法:

package main

import (
  "fmt"
  "math"
)

type Vertex struct {
  X, Y float64
}

func (v *Vertex) Abs() float64 {
  return math.Sqrt(v.X*v.X + v.Y*v.Y)
}

func main() {
  v := &Vertex{3, 4}
  fmt.Println(v.Abs())
}

接口使用的是 隐式接口,可方便地用于实现鸭子类型

在某个数据类型上实现 string() 方法,即可在输出该数据类型的变量时自动调用该方法来呈现


数组和 slice 的区别


newmake 的区别

  1. new 用于各种类型的内存分配,make 用于内建类型(mapslicechannel)的内存分配
  2. new 返回指针,make 返回初始化后的(非零)值

import 的特殊用法

// 前面加个点号,调用该包的函数时可以省略包名,如:Println("Hello Go")
import(
  . "fmt"
)

// 前面加个别名,调用该包的函数时可以用这个自定义的别名来调用,如:f.Println("Hello Go")
import(
  f "fmt"
)

// 前面加个下划线,代表不直接使用该包的函数,而是只调用这个包里面的 init 函数
import (
  "database/sql"
  _ "github.com/ziutek/mymysql/godrv"
)

程序执行流程

  1. 先解析 package main 所在的单元,获取其导入的包
  2. 依次执行各个导入包的 包级常量变量 的初始化,然后调用包的 init 函数
  3. 如果导入的包又导入了其他包,那先依次处理完这些 导入的包导入的其他包,然后再返回处理 package main 导入的包
  4. 最后执行 package main 单元的常量、变量初始化,接着调用 package maininit 函数,然后再调用 package mainmain 函数

并发控制


辅助工具


date:2017-06-12、2017-06-13、2017-06-15、2017-06-21