为了加快阅读gopl(The Go Programming Language)书效率,决定输出笔记+leet实战以督促自己,加快速度、提升效果
1 程序结构
一个可执行程序如下,入口是main包下的main.go 文件中的main函数,包之间有依赖关系,编译器解析依赖时会递归地加载其依赖地包,所以在main函数之前,加载main包,递归加载main包所依赖的包如fmt,fmt又依赖别的C,会递归地加载C,是个DFS的过程,这样就加载了所有需要的包。
每个go文件包含以下几个元素
结构组成 | 语义 | 包含组件 | 样例 |
---|---|---|---|
package | 声明所在package |
- |
package main |
import | 引入依赖的包 | - |
import fmt import ( "fmt" "math" "os" ) |
var | 声明包级变量 | - |
var count int var ( width, height = 600, 320 cells = 100 ) |
const | 声明包级常量 | - |
|
type |
类型声明 类型重命名 | - |
type name int64 |
声明struct等自定义类型 |
type Circle struct { Point Radius int } | ||
func |
声明包级函数或方法
|
函数签名
|
方法
|
函数体
|
var name type cwd,err := os.Getwd() //短格式变量声明 | ||
const name type | |||
for 无小括号 for i := 1; i < len(os.Args); i++ { | |||
if无小括号 if err != nil { if _, err := io.Copy(dst, src); err != nil {// 带简易赋值语句 | |||
switch unit { case "C", "°C": f.Celsius = Celsius(value) return nil case "F", "°F": f.Celsius = FToC(Fahrenheit(value)) return nil } |
2 数据类型
与其它语言类似,分为基础类型和组合符合,不同的是Go不能隐式类型转换,如JAVA中会在数值操作时,自动将类型提升,如long类型和int类型是可以相加的,在计算之前会先将int类型提升到long类型再计算。而Go需要显式强制转换才可以进行算术操作或者比较等,否则编译报错。
2.1 基础类型
以上基础数据类型中最常用是字符串和整型,
2.1.1 整型
分为有符号和无符号两种,有符号和无符号又分为通用类型和细化到多少字节的类型,如int8、uint16等等。
需要注意的是,int8和int16两种类型的变量之间是不能做算术操作的,此时需要做强制转换,一般转化为通用类型。
Java语言中的byte类型此处可以对标unit8
2.1.2 字符串
与java不同的是,go的string的零值是空串"",而不是nil
数据
底层表示是个utf-8编码的字节数组,如下表和图。
fmt.Println(reflect.TypeOf('[')) // int32
fmt.Println(reflect.TypeOf('王'))// int32, Unicode编码?
操作
操作 | 含义 |
---|---|
len(s) | len函数获取底层字节数组uint8[]长度 |
RuneCountInString(s) |
按utf8解码后的字符长度 unicode/utf8包 |
s[i] | 取字节,返回 uint8 |
s[0:5] | 切片 |
转换 |
y := fmt.Sprintf("%d", x) strconv.Itoa(x) 整形转化字符串,类似Java的String.valueOf(int) strconv.FormatInt(int64(x), 2)) //转二进制字符串 strconv.Atoi("123") 字符串转整型 string(65) utf8码值转string s:=string(b) 字节数组->字符串 |
迭代 |
类似于遍历一个数组,遍历的是字符,不是字节哦 for i := range astr { for i := 0; i < len(s); i++ { if HasPrefix(s[i:], substr) { return true } } |
常用类库
package | 功能 |
---|---|
strings | search搜索、replace替换、compare比较、trim、split切分、join连接 |
bytes |
bytes.Buffer 类似Java的StringBuffer用于多次连接,防止不可变string拼接效率地下 |
strconv | 转化bool、int、float等 |
unicode |
IsDigit, IsLetter, IsUpper,ToUpper and ToLower |
2.2 复合类型
复合类型 | 声明定义&初始化 | 获取 | 写入 | 遍历 | 比较 |
---|---|---|---|---|---|
数组 |
|
| a[0] = 1 |
for i, v := range a {...} for i := range a {...} for _, v := range a { ... } |
== 逐一比较元素 |
Slice |
|
同上 可以超出len, 但不能超出cap |
同上 append | 同上 |
summer == nil 其余不能== len(s) == 0叛空 bytes.Equal([]byte,[]byte) |
Map |
var ages map[string]int // nil map[string]int{} //empty map ages := make(map[string]int) ages := map[string]int{ "alice": 31, "charlie": 34, } |
ages["charlie"] 不含key,返回零值 age, ok := ages["bob"] // 判断是否成功 不能获取元素地址 |
ages["charlie"] = 34 |
形式同上,key类型不同
|
同slice |
Struct |
type Point struct{ X, Y int } p := Point{1, 2} anim := gif.GIF{LoopCount: nframes} |
p.X x := &p.X |
p.X = 5000 |
== or !=. | |
Json |
data, err := json.Marshal(movies) 将结构体转化给字节数组 序列化MarshalIndent Unmarshal 反序列化 |
2.2.1 数组
定长同构序列。
定义和使用大多类似其它语言,与JAVA不同的是调用函数传参数组时是传递的copy,所以在函数内修改数组内容对于调用者并不可见,为了提升效率或者修改可见,需要使用显式指针传递,所以我们通常不用数组做为函数的参数,通常传入切片,切片允许函数内修改。
func zero(ptr [3]int) {
for i := range ptr {
ptr[i] = 0
}
}
func zero2(ptr *[3]int) {
for i := range ptr {
ptr[i] = 0
}
}
func main() {
d := [3]int{1, 2}
fmt.Println(d) // [1 2 0]
zero(d) // 并不能修改原数组 传递copy
fmt.Println(d) // [1 2 0]
zero2(&d)
fmt.Println(d) // [0 0 0]
}
2.2.2 Slice
变长同构序列,底层是数组,是数组的一个切片,语法与字符串的substring语法类似,arr[i:j]
以下面为例,months是一个定长数组[13]string,months[i:j]是一个切片[]string,切片包含起始指针、长度、容量;
func main() {
months := [...]string{1: "Jan", 2:"Feb", 3:"mar", 4:"apr", 5:"may", 6:"june", 7:"july", 8:"august", 9:"sep", 10:"oct", 11:"nov", 12: "December"}
Q2 := months[4:7]
summer := months[6:9]
fmt.Printf("%T\n", Q2)
fmt.Println(Q2) // ["April" "May" "June"]
fmt.Printf("%T\n", summer)
fmt.Println(summer) // ["June" "July" "August"]
}
常用lib操作
rotate, reverse,
可用做stack栈数据结构,也可用作队列
stack = append(stack, v) // push v
top := stack[len(stack)-1] // top of stack
stack = stack[:len(stack)-1] // pop
2.2.3 Map
k-v 无序集合,k的类型限制是必须能使用== 判断key是否相等,如果想用不支持==的类型做key,如slice,可以将其转换成string再做key。Go没有提供set类型,由map提供类似功能(map[string]bool)
2.2.4 Struct
异构聚合类型,包含一系列不同类型的字段,类似C的结构体,与之不同的是,go结构体的指针p引用字段不一样,C是*p.field或者p->field,而go的直接是p.field,与(*p).field等同。
同样是值传递,如需在函数体内修改,需要传递指针。
嵌套组合
还有个重要的特性,嵌套机制,类似于Java的组合,一个结构体可以包含另一个结构体类型的字段,此外还有匿名机制(语法糖),可以缩短引用链的长度,如下例的 一个轮子包含一个圈,一个圈包含一个圆心(Point),当引用圆心的x坐标时,链w.Circle.Point.X特别长,当使用匿名字段(默认是类型名),就可以用w.X引用了。但是字面量{}初始化时不能这样使用。
type Point struct {
X int
Y int
}
type Circle struct {
Point
Radius int
}
type Wheel struct {
Circle
Spokes int
}
var w Wheel
w.X = 8 // w.Circle.Point.X = 8
w.Y = 8 // w.Circle.Point.Y = 8
w.Radius = 5 // w.Circle.Radius = 5
w.Spokes = 20
fmt.Println(w)
new 和 make 均是用于分配内存:new 用于值类型和用户定义的类型,如自定义结构,make 用于内置引用类型(切片、map 和管道)。