百味皆苦 java后端开发攻城狮

golang基础

2022-08-23
百味皆苦

命令

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

origin_img_v2_3fc829a4-01bf-4f26-9d91-6e0de060035g

实现

origin_img_v2_b1c7fafa-48d9-4018-9916-819956f720bg

扩展

origin_img_v2_ef54f709-a1e4-463a-a9df-cf0dc716f12g

添加元素

origin_img_v2_1044dc48-bb9b-4b70-a30c-35a0ee0ca5bg

代码

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))
	}
}

正统写法

img_v2_0b2679c0-647f-4f35-9486-04cdbfe1de5g

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
}

Similar Posts

Comments