命令
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
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的条件里不需要括号,可以省略初始条件,结束条件,递增表达式
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语言只有值传递一种方式
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
实现
扩展
添加元素
代码
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
代码
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包包含可执行入口,为结构定义的方法必须放在同一个包内,可以是不同文件
结构体
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
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
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
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
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"
}
接口
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)
}
函数与闭包
函数是一等公民:参数,变量,返回值都可以是函数
函数-》闭包
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))
}
}
正统写法
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)
}
}
闭包应用,斐波那契数列
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
为函数实现接口
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)
}
使用函数遍历二叉树
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语句为先进后出
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
统一错误处理
错误封装
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
}
调用
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
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
结尾
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
}
性能测试
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
检测数据访问冲突
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)
}
交出控制权
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
不要通过共享内存来通信,通过通信来共享内存
基本语法
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结束
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进行调度,配合定时器
//创建通道
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
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是墙
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
算法
// 读取地图
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
}