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號