golang基础
命令
go build:编译go文件;跨平台编译命令:env GOOS=linux GOARCH=amd64 go build
go install:编译并将文件打包成库放在pkg下
go get:用于获取go的第三方包,通常会默认从Git repo上pull最新版本
go fmt:统一代码风格和排版
go test:运行当前包目录下的tests,go的test文件一般以XXX_test.go命名
变量
内建变量类型:
bool,string
(u)int,(u)int8,(u)int16,(u)int32,(u)int64,uintptr
byte,rune
float32,float64,complex64,complex128
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
package main
import (
"fmt"
"math"
)
//定义包内变量,必须加var,或者放在var括号内,且不能用:=
var (
globalA = 11
globalB = "global"
globalC = true
)
//变量默认值
func variableDefaultValue() {
var a int
var b string
fmt.Printf("int default value:%d \nstring default value:%q \n", a, b)
}
//变量初始化
func variableInitValue() {
var a, b int = 3, 4
var s string = "hello world"
println(a, b, s)
}
//类型推断
func variableTypeDeduction() {
var a, b, c, s = 3, 4, true, "deduction"
println(a, b, c, s)
}
//简便赋初始值
func variableInitShoter() {
//只有第一次赋值时才可以用:=
a, b, c, s := 3, 4, true, "deduction"
a = 5
println(a, b, c, s)
}
/*
计算三角函数
只能进行强制类型转换,不能隐式转换
*/
func countTriangle() {
var a, b int = 3, 4
var c int
c = int(math.Sqrt(float64(a*a + b*b)))
println("a²+b²={}²", c)
}
const fileName = "常量"
//定义常量
func delConst() {
const (
d, e = 6, 7
)
const a, b = 3, 4
var c int
c = int(math.Sqrt(a*a + b*b))
println(fileName, c)
}
//定义枚举类型
func enums() {
const (
//iota可以用来做自增
b = 1 << (10 * iota)
kb
mb
gb
tb
pb
)
println(b, kb, mb, gb, tb, pb)
}
func main() {
println("变量默认值")
variableDefaultValue()
println("变量初始化")
variableInitValue()
println("变量类型推断")
variableTypeDeduction()
println("简便赋初始值")
variableInitShoter()
println("包内变量")
println(globalA, globalB, globalC)
println("计算三角函数")
countTriangle()
println("定义常量")
delConst()
println("枚举")
enums()
}
/*
变量默认值
int default value:0
string default value:""
变量初始化
3 4 hello world
变量类型推断
3 4 true deduction
简便赋初始值
5 4 true deduction
包内变量
11 global true
计算三角函数
a²+b²={}² 5
定义常量
常量 5
枚举
1 1024 1048576 1073741824 1099511627776 1125899906842624
*/
判断与循环
if的条件里可以赋值,赋值的变量作用域就在这个if语句里
Switch会自动break,除非使用fallthrough
for的条件里不需要括号,可以省略初始条件,结束条件,递增表达式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
package main
import (
"bufio"
"fmt"
"io/ioutil"
"os"
"strconv"
)
//if
func testIf() {
const fileName = "abc.txt"
contents, err := ioutil.ReadFile(fileName)
if err != nil {
println("读取文件出错了")
fmt.Println(err)
} else {
println("%s\n", contents)
}
}
//switch
func testSwitch(score int) string {
grade := ""
switch {
case score < 60:
grade = "D"
case score < 70:
grade = "C"
case score < 80:
grade = "B"
case score <= 100:
grade = "A"
default:
panic(fmt.Sprintf("wrong score %d \n", score))
}
return grade
}
//for,把一个整数转换为二进制
func testFor(n int) string {
result := ""
for ; n > 0; n /= 2 {
lsb := n % 2
result = strconv.Itoa(lsb) + result
}
return result
}
//读取文件
func printFile(fileName string) {
//if file, err := os.Open(fileName);err == nil {
// fmt.Println(file)
//}else {
// fmt.Println("error:",err)
// panic(err)
//}
file, err := os.Open(fileName)
if err != nil {
panic(err)
}
scanner := bufio.NewScanner(file)
for scanner.Scan() {
fmt.Println(scanner.Text())
}
}
func main() {
println("====if====")
testIf()
println("====switch====")
println(testSwitch(78))
println("====for====")
println(testFor(13))
}
/*
====if====
读取文件出错了
open abc.txt: The system cannot find the file specified.
====switch====
B
====for====
1101
*/
函数与指针
指针不能运算
go语言只有值传递一种方式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
package main
import (
"fmt"
"math"
"reflect"
"runtime"
)
//返回多个参数,返回商和余数
func div(a, b int) (int, int) {
return a / b, a % b
}
func div2(a, b int) (q, r int) {
q = a / b
r = a % b
return a, b
}
//函数作为参数
func apply(op func(int, int) int, a, b int) int {
pointer := reflect.ValueOf(op).Pointer()
pName := runtime.FuncForPC(pointer).Name()
fmt.Printf("calling function %s with arges (%d,%d) \n", pName, a, b)
return op(a, b)
}
//可变参数
func sum(numbers ...int) int {
s := 0
for i := range numbers {
s += numbers[i]
}
return s
}
//利用指针实现数据交换
func swap(a, b *int) {
*a, *b = *b, *a
}
func main() {
println("====多返回值函数====")
println(div(13, 2))
println("====函数作为参数====")
fmt.Println(apply(func(a int, b int) int {
return int(math.Pow(float64(a), float64(b)))
}, 3, 4))
println("====可变参数====")
println(sum(1, 2, 3, 4, 5))
println("====利用指针实现数据交换====")
a, b := 3, 4
swap(&a, &b)
println(a, b)
}
/*
====多返回值函数====
6 1
====函数作为参数====
calling function main.main.func1 with arges (3,4)
81
====可变参数====
15
====利用指针实现数据交换====
4 3
*/
数组与切片
数量写在类型前
数组是值类型
切片slice本身没有数据,是对底层数组的一个view
实现
扩展
添加元素
代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
package main
import "fmt"
func initArrays() {
//定义数组
var arr1 [3]int
arr2 := [3]int{1, 2, 3}
arr3 := [...]int{2, 4, 6, 8}
//遍历数组
for i := 0; i < len(arr3); i++ {
fmt.Println(arr3[i])
}
for i := range arr3 {
fmt.Println(arr3[i])
}
for i, v := range arr3 {
//打印下标和值
fmt.Println(i, v)
}
for _, v := range arr3 {
//只打印值,下标返回参数用占位符接
fmt.Println(v)
}
//二维数组
var arr4 [2][3]int
fmt.Println(arr1, arr2, arr3)
fmt.Println(arr4)
}
//值传递,参数也可以是指针类型
func arrayArges(arr [3]int) {
for i, v := range arr {
//打印下标和值
fmt.Println(i, v)
}
//go是值传递,在方法外打印不会被替换
arr[0] = 100
}
//切片
func updateArrays(arr []int) {
arr[0] = 100
}
func slice() {
arr := [...]int{0, 1, 2, 3, 4, 5, 6, 7}
//包前不包后
fmt.Println("arr[2:6] = ", arr[2:6])
fmt.Println("arr[2:] = ", arr[2:])
fmt.Println("arr[:6] = ", arr[:6])
fmt.Println("arr[:] = ", arr[:])
//传递的是一个视图view,所以数据会发生改变
updateArrays(arr[:])
fmt.Println("arr update after = ", arr)
}
//扩展切片
func extendSlilce() {
arr := [...]int{0, 1, 2, 3, 4, 5, 6, 7}
fmt.Println("arr is = ", arr)
s1 := arr[2:6]
s2 := s1[3:5]
fmt.Printf("s1 = %v, len(s1) = %d, cap(s1) = %d\n", s1, len(s1), cap(s1))
fmt.Printf("s2 = %v, len(s2) = %d, cap(s2) = %d\n", s2, len(s2), cap(s2))
s3 := append(s2, 10)
s4 := append(s3, 11)
s5 := append(s4, 12)
fmt.Printf("s2 = %v, s3 = %v, s4 = %v \n", s3, s4, s5)
fmt.Println("append after arr is = ", arr)
}
func printSlice(arr []int) {
fmt.Printf("%v,len = %d, cap = %d \n", arr, len(arr), cap(arr))
}
//make方法
func makeSlice() {
s1 := []int{2, 4, 6, 8}
printSlice(s1)
s2 := make([]int, 16)
printSlice(s2)
s3 := make([]int, 10, 32)
printSlice(s3)
}
//copy
func copySlice() {
s1 := []int{2, 4, 6, 8}
printSlice(s1)
s2 := make([]int, 16)
printSlice(s2)
copy(s2, s1)
printSlice(s2)
}
//delElementSlice
func delElementSlice() {
s1 := []int{2, 4, 6, 8}
printSlice(s1)
s2 := append(s1[:2], s1[3:]...)
printSlice(s2)
}
func main() {
println("====定义数组====")
initArrays()
println("====切片====")
slice()
println("====扩展切片====")
extendSlilce()
println("====makeSlice====")
makeSlice()
println("====copySlice====")
copySlice()
println("====delElementSlice====")
delElementSlice()
}
/*
====定义数组====
2
4
6
8
2
4
6
8
0 2
1 4
2 6
3 8
2
4
6
8
[0 0 0] [1 2 3] [2 4 6 8]
[[0 0 0] [0 0 0]]
====切片====
arr[2:6] = [2 3 4 5]
arr[2:] = [2 3 4 5 6 7]
arr[:6] = [0 1 2 3 4 5]
arr[:] = [0 1 2 3 4 5 6 7]
arr update after = [100 1 2 3 4 5 6 7]
====扩展切片====
arr is = [0 1 2 3 4 5 6 7]
s1 = [2 3 4 5], len(s1) = 4, cap(s1) = 6
s2 = [5 6], len(s2) = 2, cap(s2) = 3
s2 = [5 6 10], s3 = [5 6 10 11], s4 = [5 6 10 11 12]
append after arr is = [0 1 2 3 4 5 6 10]
====makeSlice====
[2 4 6 8],len = 4, cap = 4
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0],len = 16, cap = 16
[0 0 0 0 0 0 0 0 0 0],len = 10, cap = 32
====copySlice====
[2 4 6 8],len = 4, cap = 4
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0],len = 16, cap = 16
[2 4 6 8 0 0 0 0 0 0 0 0 0 0 0 0],len = 16, cap = 16
====delElementSlice====
[2 4 6 8],len = 4, cap = 4
[2 4 8],len = 3, cap = 4
*/
map
map的操作
map的遍历
map的key
代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
package main
import "fmt"
//定义map
func initMap() {
//初始化定义
m := map[string]string{
"name": "xixixi",
"course": "golang",
"site": "imooc",
"quality": "notbad",
}
fmt.Println(m)
//定义空map
m2 := make(map[string]string) //m2 == empty map
fmt.Println(m2)
var m3 map[string]string //m3 == nil
fmt.Println(m3)
//获取值
courseName, ok := m["course"]
fmt.Println("courseName is exist:", ok, ", courseName: ", courseName)
if couseName, ok := m["couse"]; ok {
fmt.Println(couseName)
} else {
fmt.Println("key is not exits")
}
}
//遍历map
func iteratorMap() {
m := map[string]string{
"name": "xixixi",
"course": "golang",
"site": "imooc",
"quality": "notbad",
}
//遍历map
for k, v := range m {
fmt.Println(k, v)
}
}
//删除map元素
func delMapEle() {
m := map[string]string{
"name": "xixixi",
"course": "golang",
"site": "imooc",
"quality": "notbad",
}
name, ok := m["name"]
fmt.Println(name, ok)
delete(m, "name")
name, ok = m["name"]
fmt.Println(name, ok)
}
//寻找最长不含有重复字符的子串
func letcode(s string) int {
lastOccurred := make(map[rune]int)
start := 0
maxLength := 0
for i, ch := range []rune(s) {
if lastOccurred[ch] >= start {
start = lastOccurred[ch] + 1
}
if i-start+1 > maxLength {
maxLength = i - start + 1
}
lastOccurred[ch] = i
}
return maxLength
}
func main() {
println("====定义map====")
initMap()
println("====遍历map====")
iteratorMap()
println("====删除map元素====")
delMapEle()
}
/*
====定义map====
map[course:golang name:xixixi quality:notbad site:imooc]
map[]
map[]
courseName is exist: true , courseName: golang
key is not exits
====遍历map====
name xixixi
course golang
site imooc
quality notbad
====删除map元素====
xixixi true
false
*/
字符与字符串
字符
结构体
不论是地址还是结构本身,一律使用.访问成员
只有使用指针才能改变结构内容
nil指针也可以调用方法
封装:名字一般使用CamelCase,首字母大写代表public,首字母小写代表private
包:每个目录一个包,main包包含可执行入口,为结构定义的方法必须放在同一个包内,可以是不同文件
结构体
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
package tree
import "fmt"
//结构体
type TreeNode struct {
Value int
Left, Right *TreeNode
}
//结构体方法
func (node TreeNode) PrintValue() {
fmt.Println(node.Value)
}
//类似于setter方法
func (node *TreeNode) SetValue(valueparam int) {
node.Value = valueparam
}
//类似于构造方法
func CreateNode(valueparam int) *TreeNode {
return &TreeNode{Value: valueparam}
}
//中序遍历
func (node *TreeNode) Traverse() {
if node == nil {
return
}
node.Left.Traverse()
node.PrintValue()
node.Right.Traverse()
}
func main() {
var root TreeNode
root = TreeNode{Value: 3}
root.Left = &TreeNode{}
root.Right = &TreeNode{5, nil, nil}
root.Right.Left = new(TreeNode)
root.Left.Right = CreateNode(2)
root.PrintValue()
nodes := []TreeNode{
{Value: 3},
{},
{6, nil, &root},
}
fmt.Println(nodes)
}
结构体2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package embed
import (
"fmt"
"go-demo/oop/tree"
)
type myTreeNode struct {
*tree.TreeNode //embedding内嵌
}
//后续遍历
func (myNode *myTreeNode) postOrder() {
if myNode == nil || myNode.TreeNode == nil {
return
}
left := myTreeNode{myNode.Left}
right := myTreeNode{myNode.Right}
left.postOrder()
right.postOrder()
myNode.postOrder()
}
//重写
func (myNode *myTreeNode) Traverse() {
fmt.Println("this is a myTreeNode traverse method")
}
main
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package main
import (
"fmt"
"go-demo/oop/tree"
)
func main() {
//包名.结构体
var root tree.TreeNode
root = tree.TreeNode{Value: 3}
root.Left = &tree.TreeNode{}
root.Right = &tree.TreeNode{5, nil, nil}
root.Right.Left = new(tree.TreeNode)
root.Left.Right = tree.CreateNode(2)
root.PrintValue()
nodes := []tree.TreeNode{
{Value: 3},
{},
{6, nil, &root},
}
fmt.Println(nodes)
root.Traverse()
}
/*
3
[{3 <nil> <nil>} {0 <nil> <nil>} {6 <nil> 0xc000004078}]
0
2
3
0
5
*/
接口
接口的实现是隐式的,只要实现接口里的方法
接口变量自带指针
接口变量同样采用值传递,几乎不需要使用接口的指针
指针接收者实现只能以指针形式使用,值接收者都可
结构体1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
package normalInterface
import (
"fmt"
"io/ioutil"
"net/http"
)
//结构体
type NormalGetUrl struct {
Contents string
Url string
Form map[string]string
}
//实现系统接口,类似于重写tostring方法
func (normal *NormalGetUrl) String() string {
return fmt.Sprintf("NormalGetUrl toString contents:%s", normal.Contents)
}
func (normal *NormalGetUrl) Set(url string, from map[string]string) string {
normal.Url = url
normal.Form = from
normal.Contents = from["contents"]
return "ok"
}
func (normal *NormalGetUrl) GetUrl() string {
return normal.Url
}
// Print 发送http请求获取页面数据
func (NormalGetUrl) Print(url string) string {
resp, err := http.Get(url)
if err != nil {
panic(err)
}
defer resp.Body.Close()
bytes, _ := ioutil.ReadAll(resp.Body)
return string(bytes)
}
结构体2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package unNormalInterface
type UnNormalGetUrl struct {
Contents string
Url string
Form map[string]string
}
func (u *UnNormalGetUrl) Set(url string, from map[string]string) string {
u.Url = url
u.Form = from
u.Contents = from["contents"]
return "ok"
}
func (u *UnNormalGetUrl) GetUrl() string {
return u.Url
}
// Print 发送http请求获取页面数据
func (UnNormalGetUrl) Print(url string) string {
return "Print is unnormal"
}
接口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
package main
import (
"fmt"
"go-demo/interface/unNormalInterface"
)
type GetPoUrl interface {
GetUrl() string
}
// PrintUrl 打印接口
type PrintUrl interface {
Print(string) string
}
// Seter 赋值接口
type Seter interface {
Set(url string, from map[string]string) string
}
// PrintAndSeter 组合接口
type PrintAndSeter interface {
PrintUrl
Seter
GetPoUrl
}
func CombainInterface(ps PrintAndSeter) {
ps.Set("http://www.google.com", map[string]string{
"contents": "google content",
"name": "google",
})
url := ps.GetUrl()
fmt.Printf("combainInterface--->url:%s\n", url)
}
func getInterface() PrintAndSeter {
//return &normalInterface.NormalGetUrl{}
return &unNormalInterface.UnNormalGetUrl{}
}
func main() {
var p PrintAndSeter = getInterface()
CombainInterface(p)
}
函数与闭包
函数是一等公民:参数,变量,返回值都可以是函数
函数-》闭包
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package main
import "fmt"
func addr() func(i int) int {
sum := 0
return func(v int) int {
sum += v
return sum
}
}
func main() {
a := addr()
for i := 0; i < 10; i++ {
fmt.Printf("0+1+....+%d = %d \n", i, a(i))
}
}
正统写法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package main
import "fmt"
type iAdder func(int) (int, iAdder)
func addr2(base int) iAdder {
return func(i int) (int, iAdder) {
return base + i, addr2(base + i)
}
}
func main() {
a := addr2(0)
for i := 0; i < 10; i++ {
var s int
s, a = a(i)
fmt.Printf("0+1+....+%d = %d \n", i, s)
}
}
闭包应用,斐波那契数列
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package main
import "fmt"
func fbnq() func() int {
a, b := 0, 1
return func() int {
a, b = b, a+b
return a
}
}
func main() {
f := fbnq()
fmt.Println(f())
fmt.Println(f())
fmt.Println(f())
fmt.Println(f())
fmt.Println(f())
fmt.Println(f())
fmt.Println(f())
}
//1 1 2 3 5 8 13
为函数实现接口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
package main
import (
"bufio"
"fmt"
"io"
"strings"
)
// 函数实现intGen接口
func fbnq() intGen {
a, b := 0, 1
return func() int {
a, b = b, a+b
return a
}
}
//定义接口
type intGen func() int
func (g intGen) Read(p []byte) (n int, err error) {
next := g()
//设置最大值上限
if next > 10000 {
return 0, io.EOF
}
s := fmt.Sprintf("%d\n", next)
// TODO: incorrect if p is too small!
return strings.NewReader(s).Read(p)
}
func printFileContents(reader io.Reader) {
scanner := bufio.NewScanner(reader)
for scanner.Scan() {
fmt.Println(scanner.Text())
}
}
func main() {
f := fbnq()
printFileContents(f)
}
使用函数遍历二叉树
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package tree
import "fmt"
func (node *Node) Traverse() {
node.TraverseFunc(func(n *Node) {
n.Print()
})
fmt.Println()
}
func (node *Node) TraverseFunc(f func(*Node)) {
if node == nil {
return
}
node.Left.TraverseFunc(f)
f(node)
node.Right.TraverseFunc(f)
}
go语言闭包更加自然,不需要修饰如何访问自由变量
没有lambda表达式,但是有匿名函数
错误处理和资源管理
defer调用
确保调用在函数结束时发生
参数在defer语句时计算
defer语句为先进后出
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
package main
import (
"fmt"
"os"
"bufio"
)
func tryDefer() {
for i := 0; i < 100; i++ {
defer fmt.Println(i)
if i == 30 {
// Uncomment panic to see
// how it works with defer
panic("printed too many")
}
}
}
//把斐波那契数写入到文件中
func writeFile(filename string) {
file, err := os.OpenFile(filename,
os.O_EXCL|os.O_CREATE|os.O_WRONLY, 0666)
//错误处理
if err != nil {
if pathError, ok := err.(*os.PathError); !ok {
panic(err)
} else {
fmt.Printf("%s, %s, %s\n",
pathError.Op,
pathError.Path,
pathError.Err)
}
return
}
//关闭资源
defer file.Close()
writer := bufio.NewWriter(file)
defer writer.Flush()
//调用以前写的斐波那契数列
f := fib.Fibonacci()
for i := 0; i < 20; i++ {
fmt.Fprintln(writer, f())
}
}
func main() {
tryDefer()
writeFile("fib.txt")
}
defer使用
open/close
lock/unlock
printHeader/printFooter
统一错误处理
错误封装
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
package filelisting
import (
"fmt"
"io/ioutil"
"net/http"
"os"
"strings"
)
//路径
const prefix = "/list/"
type userError string
func (e userError) Error() string {
return e.Message()
}
func (e userError) Message() string {
return string(e)
}
//读取文件
func HandleFileList(writer http.ResponseWriter,
request *http.Request) error {
fmt.Println()
if strings.Index(
request.URL.Path, prefix) != 0 {
return userError(
fmt.Sprintf("path %s must start "+
"with %s",
request.URL.Path, prefix))
}
path := request.URL.Path[len(prefix):]
file, err := os.Open(path)
if err != nil {
return err
}
defer file.Close()
all, err := ioutil.ReadAll(file)
if err != nil {
return err
}
writer.Write(all)
return nil
}
调用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
package main
import (
"log"
"net/http"
_ "net/http/pprof"
"os"
"imooc.com/ccmouse/learngo/errhandling/filelistingserver/filelisting"
)
type appHandler func(writer http.ResponseWriter,
request *http.Request) error
func errWrapper(
handler appHandler) func(
http.ResponseWriter, *http.Request) {
return func(writer http.ResponseWriter,
request *http.Request) {
// panic
defer func() {
if r := recover(); r != nil {
log.Printf("Panic: %v", r)
http.Error(writer,
http.StatusText(http.StatusInternalServerError),
http.StatusInternalServerError)
}
}()
err := handler(writer, request)
if err != nil {
log.Printf("Error occurred "+
"handling request: %s",
err.Error())
// user error
if userErr, ok := err.(userError); ok {
http.Error(writer,
userErr.Message(),
http.StatusBadRequest)
return
}
// system error
code := http.StatusOK
switch {
case os.IsNotExist(err):
code = http.StatusNotFound
case os.IsPermission(err):
code = http.StatusForbidden
default:
code = http.StatusInternalServerError
}
http.Error(writer,
http.StatusText(code), code)
}
}
}
type userError interface {
error
Message() string
}
func main() {
http.HandleFunc("/",
errWrapper(filelisting.HandleFileList))
err := http.ListenAndServe(":8888", nil)
if err != nil {
panic(err)
}
}
发起一个http请求:localhost:8888/list/abc.txt
panic和recover
Panic停止当前函数执行
Panic一直向上返回,并执行每一层的defer语句
panic如果没有碰到recover,程序就退出
recover仅在defer调用中使用
recover获取panic的值
recover如果无法处理,可以重新panic
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package main
import (
"fmt"
)
func tryRecover() {
defer func() {
r := recover()
if r == nil {
fmt.Println("Nothing to recover. " +
"Please try uncomment errors " +
"below.")
return
}
if err, ok := r.(error); ok {
fmt.Println("Error occurred:", err)
} else {
panic(fmt.Sprintf(
"I don't know what to do: %v", r))
}
}()
// Uncomment each block to see different panic
// scenarios.
// Normal error
//panic(errors.New("this is an error"))
// Division by zero
//b := 0
//a := 5 / b
//fmt.Println(a)
// Causes re-panic
//panic(123)
}
func main() {
tryRecover()
}
测试
表格驱动测试
文件名必须以_test.go结尾
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package test
import (
"math"
"testing"
)
//测试方法,测试勾股定理
func TestTriangle(t *testing.T) {
tests := []struct{ a, b, c int }{
{3, 4, 5},
{5, 12, 13},
{8, 15, 17},
{12, 35, 37},
{30000, 40000, 50000},
}
for _, tt := range tests {
if actual := calcTriangle(tt.a, tt.b); actual != tt.c {
t.Errorf("calcTriangle(%d, %d); "+
"got %d; expected %d",
tt.a, tt.b, actual, tt.c)
}
}
}
//勾股定理算法
func calcTriangle(a, b int) int {
var c int
c = int(math.Sqrt(float64(a*a + b*b)))
return c
}
性能测试
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
package test
import "testing"
//正常逻辑测试
func TestSubstr(t *testing.T) {
tests := []struct {
s string
ans int
}{
// Normal cases
{"abcabcbb", 3},
{"pwwkew", 3},
// Edge cases
{"", 0},
{"b", 1},
{"bbbbbbbbb", 1},
{"abcabcabcd", 4},
// Chinese support
{"这里是慕课网", 6},
{"一二三二一", 3},
{"黑化肥挥发发灰会花飞灰化肥挥发发黑会飞花", 8},
}
for _, tt := range tests {
actual := lengthOfNonRepeatingSubStr(tt.s)
if actual != tt.ans {
t.Errorf("got %d for input %s; "+
"expected %d",
actual, tt.s, tt.ans)
}
}
}
// 性能测试
func BenchmarkSubstr(b *testing.B) {
s := "黑化肥挥发发灰会花飞灰化肥挥发发黑会飞花"
for i := 0; i < 13; i++ {
s = s + s
}
b.Logf("len(s) = %d", len(s))
ans := 8
//重置时间,只计算下面代码运行的时间
b.ResetTimer()
for i := 0; i < b.N; i++ {
actual := lengthOfNonRepeatingSubStr(s)
if actual != ans {
b.Errorf("got %d for input %s; "+
"expected %d",
actual, s, ans)
}
}
}
// 最大不重复子串算法
func lengthOfNonRepeatingSubStr(s string) int {
lastOccurred := make(map[rune]int)
start := 0
maxLength := 0
for i, ch := range []rune(s) {
if lastI, ok := lastOccurred[ch]; ok && lastI >= start {
start = lastI + 1
}
if i-start+1 > maxLength {
maxLength = i - start + 1
}
lastOccurred[ch] = i
}
return maxLength
}
并发编程
协程Coroutine
轻量级“线程”
非抢占式多任务处理,由协程主动交出控制权
编译器、解析器、虚拟机层面的多任务
多个协程可能在一个或者多个线程上运行
goRoutine的定义
任何函数只要前面加上go就能交给调度器运行
不需要在定义时区分是否是异步函数
调度器在合适的点进行切换
IO/select
channel
等待锁
有时函数调用
runtime.Gosched()
只是参考,不能保证切换,不能保证在其他地方不切换
使用-race检测数据访问冲突
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package main
import (
"fmt"
"time"
)
func main() {
for i := 0; i < 1000; i++ {
//并发执行
go func(i int) {
for {
fmt.Printf("Hello from "+
"goroutine %d\n", i)
}
}(i)
}
//让主函数暂停一毫秒
time.Sleep(time.Millisecond)
}
交出控制权
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
func main() {
var a [10]int
for i := 0; i < len(a); i++ {
//并发执行
go func(ii int) {
for {
a[ii]++
//交出控制权
runtime.Gosched()
}
}(i)
}
//让主函数暂停一毫秒
time.Sleep(time.Millisecond)
fmt.Println(a)
}
// [588 494 461 510 380 314 355 380 371 371]
通道channel
不要通过共享内存来通信,通过通信来共享内存
基本语法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
func work(id int, c chan int) {
for n := range c {
fmt.Printf("channel id is %d received %c \n", id, n)
}
}
// 收数据的通道
func createWork(id int) chan<- int {
c := make(chan int)
//匿名函数
//创建一个协程往通道里写入数据
go work(id, c)
return c
}
func chanDemo() {
var channels [10]chan<- int
for i := 0; i < 10; i++ {
//创建一个通道
channels[i] = createWork(i)
}
for i := 0; i < 10; i++ {
//写入
channels[i] <- 'a' + i
}
time.Sleep(time.Millisecond)
}
// 缓冲区通道
func bufferedChannel() {
//建立一个缓冲区是3的通道
c := make(chan int, 3)
go work(0, c)
c <- 'a'
c <- 'b'
c <- 'c'
c <- 'd'
time.Sleep(time.Millisecond)
}
func channelClose() {
//建立一个缓冲区是3的通道
c := make(chan int, 3)
go work(0, c)
c <- 'a'
c <- 'b'
c <- 'c'
c <- 'd'
//关闭通道
close(c)
time.Sleep(time.Millisecond)
}
func main() {
//通道作为一等公民
chanDemo()
//缓冲区
bufferedChannel()
//发送方关闭,接收方range读取
channelClose()
}
共享内存
使用channel来等待goroutine结束
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
func doWork(id int, w worker) {
//循环读取通道
for n := range w.in {
fmt.Printf("Worker %d received %c\n",
id, n)
w.done()
}
}
// 定义一个结构体
type worker struct {
in chan int
done func()
}
// 创建结构体
func createWorker(id int, wg *sync.WaitGroup) worker {
w := worker{
in: make(chan int),
done: func() {
wg.Done()
},
}
//执行任务
go doWork(id, w)
return w
}
func chanDemo() {
//利用体统提供的等待方法
var wg sync.WaitGroup
var workers [10]worker
for i := 0; i < 10; i++ {
workers[i] = createWorker(i, &wg)
}
//一共有20个任务
wg.Add(20)
for i, worker := range workers {
worker.in <- 'a' + i
}
for i, worker := range workers {
worker.in <- 'A' + i
}
//等待任务完成
wg.Wait()
}
func main() {
chanDemo()
}
使用select进行调度,配合定时器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
//创建通道
func generator() chan int {
out := make(chan int)
go func() {
i := 0
for {
time.Sleep(
time.Duration(rand.Intn(1500)) *
time.Millisecond)
out <- i
i++
}
}()
return out
}
//打印通道里的内容
func worker(id int, c chan int) {
for n := range c {
time.Sleep(time.Second)
fmt.Printf("Worker %d received %d\n",
id, n)
}
}
func createWorker(id int) chan<- int {
c := make(chan int)
go worker(id, c)
return c
}
func main() {
var c1, c2 = generator(), generator()
var worker = createWorker(0)
var values []int
tm := time.After(10 * time.Second)
tick := time.Tick(time.Second)
for {
var activeWorker chan<- int
var activeValue int
if len(values) > 0 {
activeWorker = worker
activeValue = values[0]
}
//用select进行监听,哪个通道里有内容就先打印哪个通道
select {
case n := <-c1:
values = append(values, n)
case n := <-c2:
values = append(values, n)
case activeWorker <- activeValue:
values = values[1:]
case <-time.After(800 * time.Millisecond):
fmt.Println("timeout")
case <-tick:
fmt.Println(
"queue len =", len(values))
case <-tm:
fmt.Println("bye")
return
}
}
}
传统同步机制atomic
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
type atomicInt struct {
value int
lock sync.Mutex
}
func (a *atomicInt) increment() {
fmt.Println("safe increment")
//对一段代码进行加锁
func() {
a.lock.Lock()
defer a.lock.Unlock()
a.value++
}()
}
func (a *atomicInt) get() int {
a.lock.Lock()
defer a.lock.Unlock()
return a.value
}
func main() {
var a atomicInt
a.increment()
go func() {
a.increment()
}()
time.Sleep(time.Millisecond)
fmt.Println(a.get())
}
广度优先算法实现迷宫
地图,0是路,1是墙
1
2
3
4
5
6
7
6 5
0 1 0 0 0
0 0 0 1 0
0 1 0 1 0
1 1 1 0 0
0 1 0 0 1
0 1 0 0 0
算法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
// 读取地图
func readMaze(filename string) [][]int {
file, err := os.Open(filename)
if err != nil {
panic(err)
}
var row, col int
fmt.Fscanf(file, "%d %d", &row, &col)
maze := make([][]int, row)
for i := range maze {
maze[i] = make([]int, col)
for j := range maze[i] {
fmt.Fscanf(file, "%d", &maze[i][j])
}
}
return maze
}
// 坐标结构体
type point struct {
i, j int
}
// 四个边界点
var dirs = [4]point{
{-1, 0}, {0, -1}, {1, 0}, {0, 1}}
func (p point) add(r point) point {
return point{p.i + r.i, p.j + r.j}
}
func (p point) at(grid [][]int) (int, bool) {
if p.i < 0 || p.i >= len(grid) {
return 0, false
}
if p.j < 0 || p.j >= len(grid[p.i]) {
return 0, false
}
return grid[p.i][p.j], true
}
func walk(maze [][]int,
start, end point) [][]int {
steps := make([][]int, len(maze))
for i := range steps {
steps[i] = make([]int, len(maze[i]))
}
Q := []point{start}
for len(Q) > 0 {
cur := Q[0]
Q = Q[1:]
if cur == end {
break
}
for _, dir := range dirs {
next := cur.add(dir)
val, ok := next.at(maze)
if !ok || val == 1 {
continue
}
val, ok = next.at(steps)
if !ok || val != 0 {
continue
}
if next == start {
continue
}
curSteps, _ := cur.at(steps)
steps[next.i][next.j] =
curSteps + 1
Q = append(Q, next)
}
}
return steps
}
func main() {
maze := readMaze("maze/maze.in")
steps := walk(maze, point{0, 0},
point{len(maze) - 1, len(maze[0]) - 1})
for _, row := range steps {
for _, val := range row {
fmt.Printf("%3d", val)
}
fmt.Println()
}
// TODO: construct path from steps
}













