Golang笔记
目录
Hello World!
package main
import "fmt"
func main() {
    fmt.Println("Hello World!")
}
变量定义
package main
import "fmt"
func varInit() {
    var a, b int = 3, 4
    var s = "Hello"
    c, d := true, 3.14
    var (
        e = "你"
        f = 6
    )
    fmt.Println(a, b, s, c, d, e, f)
}
func main() {
    varInit()
}
内建类型
- 
bool
 - 
string
 - 
有无符号的int, int8, int16, int32, int64
 - 
uintptr
 - 
byte = uint8
 - 
rune= uint32
 - 
float32, float64, complex64, complex128
 - 
强制类型转换
 
常量与枚举
package main
import "fmt"
func consts() {
    const filename = "a.txt"
    const (
        a = 1
        b = 2
    )
    fmt.Println(filename, a, b)
}
func enums() {
    const (
        windows = iota
        linux
        mac
    )
    const (
        b = 1 << (10 * iota)
        kb
        mb
        gb
        tb
        pb
    )
    fmt.Println(windows, linux, mac)
    fmt.Println(b, kb, mb, gb, tb, pb)
}
func main() {
    consts()
    enums()
}
条件语句
package main
import (
    "fmt"
    "io/ioutil"
)
func main() {
    const filename = "a.txt"
    if content, err := ioutil.ReadFile(filename); err != nil {
        fmt.Println(err)
    } else {
        fmt.Println(content)
    }
}
循环语句
package main
import (
    "fmt"
    "strconv"
)
func convertToBin(n int) string {
    result := ""
    for ; n > 0; n /= 2 {
        result = strconv.Itoa(n%2) + result
    }
    return result
}
func main() {
    fmt.Println(convertToBin(5))
}
函数
package main
import (
    "fmt"
)
func eval(a, b int, op string) (int, error) {
    switch op {
    case "+":
        return a + b, nil
    case "-":
        return a - b, nil
    case "*":
        return a * b, nil
    case "/":
        return a / b, nil
    default:
        return 0, fmt.Errorf("unsupported operation: %s", op)
    }
}
func main() {
    fmt.Println(eval(1, 2, "*"))
}
指针
package main
import (
    "fmt"
)
func swap(a, b *int) {
    *a, *b = *b, *a
}
func main() {
    a, b := 2, 3
    // go里的参数传递都是值传递,对于比较大的数据使用指针可以避免资源浪费
    swap(&a, &b)
    fmt.Println(a, b)
}
ntln(a, b)
}
数组
package main
import "fmt"
func main() {
    // 数组是值类型
    var (
        arr1 [6]int
        arr2 = [3]int{1, 2, 3}
        arr3 = [...]int{4, 5, 6}
    )
    fmt.Println(arr1, arr2, arr3)
    // range关键字
    for i := range arr2 {
        fmt.Println(i)
    }
    for i, v := range arr2 {
        fmt.Println(i, v)
    }
}
切片
package main
import "fmt"
func modifyArr(s []int) {
    s[0] = 99
}
func printSlice(s []int) {
    fmt.Printf("len=%d, cap=%d", len(s), cap(s))
    fmt.Println("")
}
func main() {
    var sli []int // nil
    printSlice(sli)
    sli = append(sli, 0)
    printSlice(sli)
    sli1 := make([]int, 8)
    fmt.Println(sli1)
    arr := [...]int{0, 1, 2, 3, 4}
    // s是对arr的一个view,使用切片可以不传指针而方便地修改数组
    s := arr[1:3]
    fmt.Println(s)
    modifyArr(s)
    fmt.Println(s)   // [99 2]
    fmt.Println(arr) // [0 99 2 3 4]
    s1 := s[1:3]
    fmt.Println(s1) // [2 3]
    // slice: ptr(指向切片起始位置), len,cap(从起始位置到数组末尾的长度)
    // s[i]不可超越len,s[:n]不可超越cap
    s2 := append(s1, 100)
    fmt.Println(s2)  // [2, 3, 100]
    fmt.Println(arr) // [0 99 2 3 100]
    originSSlice := []int{0, 1, 2, 3, 4}
    destSlice := make([]int, 16)
    copy(destSlice, originSSlice)
    fmt.Println(destSlice) // [0 1 2 3 4 0 0 0 0 0 0 0 0 0 0 0]
    deleteRes := append(originSSlice[:2], originSSlice[3:]...)
    fmt.Println(deleteRes)    // [0 1 3 4]
    fmt.Println(originSSlice) // [0 1 3 4 4]
}
Map
package main
import "fmt"
func main() {
    m := map[string]int{
        "Windows": 0,
        "Mac":     1,
        "Linux":   100,
    }
    m1 := make(map[string]string)
    var m2 map[string]int
    fmt.Println(m1, m2 == nil) // map[] true
    // python3.5之后dict是有序的,但map是无序的
    for k, v := range m {
        fmt.Println(k, v)
    }
    fmt.Println(m["android"]) // 会取到对应类型的零值
    val, ok := m["nothing"]
    fmt.Println(val, ok) // 0 false
    delete(m, "Windows")
    fmt.Println(m)
}
寻找最长不含重估字符的字串
package main
func solute(s string) {
    m := make(map[rune]int)
    start := 0
    maxLen := 0
    for i, v := range []rune(s) {
        if old, ok := m[v]; ok && old >= start {
            start = old + 1
        }
        if i-start+1 > maxLen {
            maxLen = i - start + 1
        }
        m[v] = i
    }
    print(maxLen)
}
func main() {
    solute("pwwkew")
}
结构体
package main
import "fmt"
type TreeNode struct {
    value       int
    left, right *TreeNode
}
func CreateNode(value int) *TreeNode {
    // 返回局部变量的地址并不会使外界调用时的代码出现问题
    return &TreeNode{value: value}
}
func (node TreeNode) PrintNode() {
    fmt.Println(node.value)
}
// 需要改变内容、结构过大时应使用指针接收者;保持接收者类型一致性
// 接受者类型并不会直接影响调用
func (node *TreeNode) SetNode(value int) {
    // 使用指针接收者以方便修改数据
    if node == nil {
        // nil指针也可以调用方法
        fmt.Println("Ignored")
        return
    }
    node.value = value
}
func (node *TreeNode) Traverse() {
    if node == nil {
        // 边界条件
        return
    }
    node.left.Traverse()
    node.PrintNode()
    node.right.Traverse()
}
func main() {
    root := TreeNode{value: 3}
    root.left = &TreeNode{4, nil, nil}
    root.right = &TreeNode{5, nil, nil}
    root.right.left = new(TreeNode)
    root.left.right = CreateNode(6)
    root.Traverse()
}
封装
包
- 
每个目录为一个包(不要求包名和目录名一致)
 - 
main包包含可执行入口
 - 
为结构定义的方法必须放在同一个包内(的相同或者不同文件)
 
针对包的权限:标识符首字母大写代表public、首字母小写代表private
扩充已有(系统或自定义)类型
- 使用组合
 
package trn
import "fmt"
type TreeNode struct {
    Value       int
    Left, Right *TreeNode
}
func CreateNode(value int) *TreeNode {
    return &TreeNode{Value: value}
}
func (node *TreeNode) PrintNode() {
    fmt.Println(node.Value)
}
func (node *TreeNode) SetNode(value int) {
    if node == nil {
        fmt.Println("Ignored")
        return
    }
    node.Value = value
}
func (node *TreeNode) Traverse() {
    if node == nil {
        return
    }
    node.Left.Traverse()
    node.PrintNode()
    node.Right.Traverse()
}
package main
import "go_code/trn"
type myTreeNode struct {
    node *trn.TreeNode
}
func (myNode *myTreeNode) TraverseL() {
    if myNode == nil || myNode.node == nil {
        return
    }
    (&myTreeNode{myNode.node.Left}).TraverseL()
    (&myTreeNode{myNode.node.Right}).TraverseL()
    myNode.node.PrintNode()
}
func main() {
    root := trn.TreeNode{Value: 3}
    root.Left = &trn.TreeNode{4, nil, nil}
    root.Right = &trn.TreeNode{5, nil, nil}
    root.Right.Left = new(trn.TreeNode)
    root.Left.Right = &trn.TreeNode{Value: 6}
    test := myTreeNode{&root}
    test.TraverseL()
}
- 定义别名
 
package queue
type Q []int
func (q *Q) Push(value int) {
    *q = append(*q, value)
}
func (q *Q) Pop() int {
    tail := (*q)[len(*q)-1]
    *q = (*q)[:len(*q)-1]
    return tail
}
func (q *Q) IsEmpty() bool {
    return len(*q) == 0
}
package main
import (
    "fmt"
    "go_code/queue"
)
func main() {
    q := queue.Q{1}
    q.Push(2)
    q.Push(3)
    fmt.Println(q)
    fmt.Println(q.IsEmpty())
    fmt.Println(q.Pop())
    fmt.Println(q)
    fmt.Println(q.IsEmpty())
    fmt.Println(q.Pop())
    fmt.Println(q)
    fmt.Println(q.IsEmpty())
}
- 内嵌
 
package main
import "go_code/trn"
type myTreeNode struct {
    *trn.TreeNode // 内嵌
}
func (myNode *myTreeNode) Traverse() {
    if myNode == nil || myNode.TreeNode == nil {
        return
    }
    // 减少代码量myNode.TreeNode.Left -> myNode.Left
    (&myTreeNode{myNode.Left}).Traverse()
    (&myTreeNode{myNode.Right}).Traverse()
    myNode.PrintNode()
}
func main() {
    // myTreeNode自动获取内嵌结构体的属性和方法
    root := myTreeNode{&trn.TreeNode{Value: 3}}
    root.Left = &trn.TreeNode{4, nil, nil}
    root.Right = &trn.TreeNode{5, nil, nil}
    root.Right.Left = new(trn.TreeNode)
    root.Left.Right = &trn.TreeNode{Value: 6}
    // 相较于传统面向对象的重载,内嵌仍可调用被shadowed的方法,
    // 但不是通过子类对象赋值基类
    root.Traverse()
    root.TreeNode.Traverse()
}
依赖管理
- 
GOPATH:摆烂管理
 - 
GO VENDER:打补丁
 - 
GO MOD:直接用
 
GOPATH
go env -w GOPATH=
go env -w GO111MODULE=off
# 临时
export GOPATH=
export GO111MODULE=off
mkdir src
go get -u xxx.xxx.org/xxx
GO VENDER
每个项目下自建vender目录管理自己的库
GO MOD
import 路径 = go mod 下的module name + 包相对于go mod的相对目录
go env -w GO111MODULE=on
# 切换本地代理
go env -w GOPROXY=https://goproxy.cn,direct
go get -u xxx.xxx.org/xxx@vn.nn
# 清理无用版本
go mod tidy
旧项目迁移GO MOD
go env -w GO111MODULE=on
# 切换本地代理
go env -w GOPROXY=https://goproxy.cn,direct
go mod init modname
# 检查编译是否可以通过,编译全部子目录文件可以考虑使用go install ./...
go build ./...
go中没有类似python中的__name__ == '__main__'的用法,所以如果要写不同的main入口只能将其放入不同目录
接口
- Go中接口的实现是隐式的
 
// retriever/main.go
package main
import (
	"fmt"
	"learn1/retriever/test"
	"learn1/retriever/web"
	"time"
)
type Retriever interface {
	Get(url string) string
}
func download(r Retriever) string {
	return r.Get("http://www.baidu.com")
}
func main() {
	var r Retriever
	r = web.Retriever{TimeOut: time.Minute}
	fmt.Println(download(r))
	r = test.Retriever{Contents: "测试"}
	fmt.Println(download(r))
}
// retriever/test/retriever.go
package test
type Retriever struct {
	Contents string
}
func (r Retriever) Get(url string) string {
	return r.Contents
}
// retriever/web/retriever.go
package web
import (
	"net/http"
	"net/http/httputil"
	"time"
)
type Retriever struct {
	TimeOut time.Duration
}
func (r Retriever) Get(url string) string {
	response, err := http.Get(url)
	if err != nil {
		panic(err)
	}
	bytes, err := httputil.DumpResponse(response, true)
	response.Body.Close()
	if err != nil {
		panic(err)
	}
	return string(bytes)
}
- 接口变量包含了实现者类型信息和实现者值信息
 
// retriever/main.go
package main
import (
	"fmt"
	"learn1/retriever/test"
	"learn1/retriever/web"
	"time"
)
type Retriever interface {
	Get(url string) string
}
func download(r Retriever) string {
	return r.Get("http://www.baidu.com")
}
func main() {
	var r Retriever
	r = web.Retriever{TimeOut: time.Minute}
	//fmt.Println(download(r))
	r = test.Retriever{Contents: "测试"}
	//fmt.Println(download(r))
	// type assertion
	if testRetriever, ok := r.(test.Retriever); ok {
		fmt.Println(testRetriever.Contents)
	} else {
		fmt.Println("web.Retriever")
	}
}
- 接口变量自带指针
 - 接口变量同样采用值传递,几乎不需要使用接口的指针
 - 指针接收者实现只能以指针方式使用,值接收者则无此限制
 - 任何类型:interface{}
 - 接口的组合
 
type Retriever interface {
	Get(url string) string
}
type Poster interface {
	Post(url string) string
}
type WebTool interface {
	Retriever
	Poster
	Delete(target map[string]string) bool
}
- 常用系统接口(很像Python中的某些魔法方法)
- Stringer
 - Reader
 - Writer
 
 
函数式编程
闭包
错误处理与资源管理
defer
- 类似栈,在函数返回前执行,多个defer语句先定义的后执行
 - defer语句中的变量定义时确定值
 
panic
- 停止当前函数执行
 - 一直向上范围,执行每一层的defer
 - 如果没有遇见recover,程序退出
 
recover
- 仅在defer调用中使用
 - 获取panic的值
 - 可重新panic
 
测试
表格驱动测试
// learn_t/triangle.go
package main
import "math"
func triangle(a, b int) int {
	return int(math.Sqrt(float64(a*a + b*b)))
}
//learn_t/triangle_test.go
package main
import "testing"
func TestTriangle(t *testing.T) {
	tests := []struct{ a, b, c int }{
		{3, 4, 5},
		{5, 12, 13},
		{6, 8, 10},
		{30000, 40000, 50001},
	}
	for _, te := range tests {
		if actual := triangle(te.a, te.b); actual != te.c {
			t.Errorf("triangle(%d, %d) got %d expected %d", te.a, te.b, actual, te.c)
		}
	}
}
// terminal
// go test .
代码覆盖率
#代码覆盖率
go test -coverprofile=c.out
#输出html
go tool cover -html=c.out
// 性能测试
// learn_t/triangle_test.go
package learn1
import "testing"
func BenchmarkTriangle(b *testing.B) {
	x, y, z := 3, 4, 5
	for i := 0; i < b.N; i++ {
		if actual := triangle(x, y); actual != z {
			b.Errorf("triangle(%d, %d) got %d expected %d", x, y, actual, z)
		}
	}
}
// terminal
// go test -bench .
                    
                
                
            
        
浙公网安备 33010602011771号