1、基础语法
1)、变量定义
1)使用var关键字
var a, b, c bool
var s1, s2 string = "hello", "world"
可放在函数内,或直接放在包内
使用var()集中定义
var (
aa = 3
ss = "kkk"
bb = true
)
让编辑器自动决定类型
var a, b, c, s = 3, 4, true, "def"
2)使用:=定义变量
a, b, c, s := 3, 4, true, "def"
只能在函数内使用
2)、内建变量类型
1)内建变量类型
- bool,string
- int(uint),int8(uint8),int16(uint16),int32(uint32),int64(uint64),uintptr:开头是u是无符号整数,不带u是有符号整数;固定长度的:int8、int16,int32,int64,int不规定长度,32位操作系统是32位,64位操作系统是64位
- byte,rune:rune是字符类型,相当于char类型,长度是32位
- float32,float64,complex64,complex128:complex是复数,complex64的实部和虚部都是32位
2)强制类型转换:类型转换是强制的,Go语言只有强制类型转换没有隐式类型转换
func triangle() {
var a, b int = 3, 4
var c int
// c = math.Sqrt(a*a + b*b) ×
c = int(math.Sqrt(float64(a*a + b*b)))
fmt.Println(c)
}
3)、常量与枚举
1)常量定义
const数值可作为各种类型使用
func consts() {
const a, b = 3, 4
c := math.Sqrt(a*a + b*b)
fmt.Println(c)
}
2)使用常量定义枚举类型
- 普通枚举类型
- 自增值枚举类型
func enums() {
const (
cpp = iota
java
python
golang
)
fmt.Println(cpp, java, python, golang)
const (
b = 1 << (10 * iota)
kb
gb
tb
pb
)
fmt.Println(b, kb, gb, tb, pb)
}
3)变量定义要点回顾
- 变量类型写在变量名之后
- 编译器可推测变量类型
- 没有char,只有rune
- 原生支持复数类型
4)、条件语句
1)if
if的条件里不需要括号
const filename = "abc.txt"
contents, err := ioutil.ReadFile(filename)
if err != nil {
fmt.Println(err)
} else {
fmt.Printf("%s\n", contents)
}
const filename = "abc.txt"
if contents, err := ioutil.ReadFile(filename); err != nil {
fmt.Println(err)
} else {
fmt.Printf("%s\n", contents)
}
- if的条件里可以赋值
- if的条件里赋值的变量作用域就在这个if语句里
2)switch
func grade(score int) string {
g := ""
switch {
case score < 0 || score > 100:
panic(fmt.Sprintf("Wrong score:%d", score))
case score < 60:
g = "D"
case score < 80:
g = "C"
case score < 90:
g = "B"
case score <= 100:
g = "A"
}
return g
}
- switch会自动break,除非使用fallthrough
- switch后可以没有表达式
5)、循环
1)for
func convertToBinary(n int) string {
result := ""
for ; n > 0; n /= 2 {
lsb := n % 2
result = strconv.Itoa(lsb) + result
}
return result
}
func printFile(filename string) {
file, err := os.Open(filename)
if err != nil {
panic(err)
}
scanner := bufio.NewScanner(file)
for scanner.Scan() {
fmt.Println(scanner.Text())
}
}
- for的条件里不需要括号
- for的条件里可以省略初始条件,结束条件,递增表达式
无限循环:
func forever() {
for {
fmt.Println("abc")
}
}
2)基本语法要点回顾
- for,if后面的条件没有括号
- if条件里也可定义变量
- 没有while
- switch不需要break,也可以直接switch多个条件
6)、函数
1)函数
func eval(a, b int, op string) int {
switch op {
case "+":
return a + b
case "-":
return a - b
case "*":
return a * b
case "/":
return a / b
default:
panic("unsupported operation:" + op)
}
}
函数可返回多个值
// 13/3=4...1
func div(a, b int) (int, int) {
return a / b, a % b
}
func div(a, b int) (q, r int) {
q = a / b
r = a % b
return
}
- 函数返回多个值时可以起名字
- 仅用于非常简单的函数
- 对于调用者而言没有区别
函数作为参数
func apply(op func(int, int) int, a, b int) int {
p := reflect.ValueOf(op).Pointer()
opName := runtime.FuncForPC(p).Name()
fmt.Printf("Calling function %s with args (%d,%d)\n", opName, a, b)
return op(a, b)
}
func main() {
fmt.Println(apply(func(a, b int) int {
return int(math.Pow(float64(a), float64(b)))
}, 3, 4)) // Calling function main.main.func1 with args (3,4)
}
可变参数列表
func sum(numbers ...int) int {
s := 0
for i := range numbers {
s += numbers[i]
}
return s
}
2)函数语法要点回顾
- 返回值类型写在最后面
- 可返回多个值
- 函数作为参数
- 没有默认参数,可选参数
7)、指针
1)指针
指针不能运算
2)参数传递
Go语言只有值传递一种方式
值传递:
指针传递:
obj:
cache中pData是指向data的指针,值传递时只是cache里的pData指针被拷贝了一份,但还是指向同一个data
func swap(a, b int) {
b, a = a, b
}
func main() {
a, b := 3, 4
swap(a, b)
fmt.Println(a, b) // 3 4
}
func swap(a, b *int) {
*b, *a = *a, *b
}
func main() {
a, b := 3, 4
swap(&a, &b)
fmt.Println(a, b) // 4 3
}
2、内建容器
1)、数组
1)数组的定义
var arr1 [5]int
arr2 := [3]int{
1, 3, 5}
arr3 := [...]int{
2, 4, 6, 8, 10}
fmt.Println(arr1, arr2, arr3)
var grid [4][5]int
fmt.Println(grid)
数量写在类型前面
2)数组的遍历
numbers := [6]int{
1, 2, 3, 4, 5, 6}
for i := 0; i < len(numbers); i++ {
fmt.Println(numbers[i])
}
numbers := [6]int{
1, 2, 3, 4, 5, 6}
for i, v := range numbers {
fmt.Println(i, v)
}
numbers := [6]int{
1, 2, 3, 4, 5, 6}
for _, v := range numbers {
fmt.Println(v)
}
- 可通过_省略变量
- 不仅range,任何地方都可通过_省略变量
- 如果只要i,可写成
for i := range numbers
3)数组是值类型
func printArray(arr [5]int) {
arr[0] = 100
for i, v := range arr {
fmt.Println(i, v)
}
}
func main() {
var arr [5]int
printArray(arr)
fmt.Println(arr) // [0 0 0 0 0]
}
- [10]int和[20]int是不同类型
- 调用
func f(arr [10]int)
会拷贝数组 - 在Go语言中一般不直接使用数组
func printArray(arr *[5]int) {
arr[0] = 100
for i, v := range arr {
fmt.Println(i, v)
}
}
func main() {
var arr [5]int
printArray(&arr)
fmt.Println(arr) // [100 0 0 0 0]
}
2)、切片的概念
1)Slice(切片)
func updateSlice(s []int) {
s[0] = 100
}
func main() {
arr := [...]int{
0, 1, 2, 3, 4, 5, 6, 7}
fmt.Println(arr[2:6]) // [2 3 4 5]
fmt.Println(arr[:6]) // [0 1 2 3 4 5]
fmt.Println(arr[2:]) // [2 3 4 5 6 7]
s := arr[:]
fmt.Println(s) // [0 1 2 3 4 5 6 7]
updateSlice(s)
fmt.Println(s) // [100 1 2 3 4 5 6 7]
}
slice本身没有数据,是对底层array的一个view
2)Slice的扩展
arr := [...]int{
0, 1, 2, 3, 4, 5, 6, 7}
s1 := arr[2:6]
s2 := s1[3:5]
fmt.Println(s1) // [2 3 4 5]
fmt.Println(s2) // [5 6]
Slice的实现:
- slice可以向后扩展,不可以向前扩展
- s[i]不可以超越len(s),向后扩展不可以超越底层数组cap(s)
arr := [...]int{
0, 1, 2, 3, 4, 5, 6, 7}
s1 := arr[2:6]
s2 := s1[3:5]
fmt.Printf("s1=%v, len(s1)=%d, cap(s1)=%d\n", s1, len(s1), cap(s1)) // s1=[2 3 4 5], len(s1)=4, cap(s1)=6
fmt.Printf("s2=%v, len(s2)=%d, cap(s2)=%d\n", s2, len(s2), cap(s2)) // s2=[5 6], len(s2)=2, cap(s2)=3
3)、切片的操作
1)向Slice添加元素
arr := [...]int{
0, 1, 2, 3, 4, 5, 6, 7}
s1 := arr[2:6]
s2 := s1[3:5]
fmt.Printf("s1=%v, len(s1)=%d, cap(s1)=%d\n", s1, len(s1), cap(s1)) // s1=[2 3 4 5], len(s1)=4, cap(s1)=6
fmt.Printf("s2=%v, len(s2)=%d, cap(s2)=%d\n", s2, len(s2), cap(s2)) // s2=[5 6], len(s2)=2, cap(s2)=3
s3 := append(s2, 10)
s4 := append(s3, 11)
s5 := append(s4, 12)
fmt.Println(s3) // [5 6 10]
fmt.Println(s4) // [5 6 10 11]
fmt.Println(s5) // [5 6 10 11 12] s4 and s5 no longer view arr
- 添加元素时如果超过cap,系统会重新分配更大的底层数组
- 由于值传递的关系,必须接收append的返回值
2)Slice其他操作
func printSlice(s []int) {
fmt.Printf("len=%d, cap=%d\n", len(s), cap(s))
}
func main() {
// creating slice
var s []int // zero value for slice is nil
for i := 0; i < 100; i++ {
printSlice(s)
s = append(s, 2*i+1)
}
fmt.Println(s)
s1 := []int{
2, 4, 6, 8}
printSlice(s1)
s2 := make([]int, 16)
printSlice(s2)
s3 := make([]int, 16, 32)
printSlice(s3) // len=16, cap=32
// copying slice
copy(s2, s1)
fmt.Println(s2) // [2 4 6 8 0 0 0 0 0 0 0 0 0 0 0 0]
// deleting elements from slice
s2 = append(s2[:3], s2[4:]...)
fmt.Println(s2) // [2 4 6 0 0 0 0 0 0 0 0 0 0 0 0]
// popping from front
front := s2[0]
s2 = s2[1:]
// popping from back
tail := s2[len(s2)-1]
s2 = s2[:len(s2)-1]
fmt.Println(front) // 2
fmt.Println(tail) // 0
fmt.Println(s2) // [4 6 0 0 0 0 0 0 0 0 0 0 0]
}
4)、 Map
m := map[string]string{
"one": "java",
"two": "go",
"three": "python",
}
m2 := make(map[string]int)
var m3 map[string]int
fmt.Println(m, m2, m3)
// traversing map
for k, v := range m {
fmt.Println(k, v)
}
// getting values
one := m["one"]
fmt.Println(one)
if one2, ok := m["one"]; ok {
fmt.Println(one2)
}
// deleting values
delete(m, "three")
fmt.Println(m)
1)Map的操作
- 创建:
make(map[string]int)
- 获取元素:
m[key]
- key不存在时,获得value类型的初始值
- 用
value, ok := m[key]
来判断是否存在key - 用delete删除一个key
2)Map的遍历
- 使用range遍历key,或者遍历key value对
- 不保证遍历顺序,如需顺序,需手动对key排序
- 使用len获得元素个数
3)map的key
- map使用哈希表,必须可以比较相等
- 除了slice、map、function的内建类型都可以作为key
- struct类型不包含上述字段,也可以作为key
5)、Map例题
对于每一个字母x
- lastOccurred[x]不存在,或者<start -> 无需操作
- lastOccurred[x]>=start -> 更新start
- 更新lastOccurred[x],更新maxLength
func lengthOfLongestSubstring(s string) int {
lastOccurred := make(map[byte]int)
start, maxLength := 0, 0
for i, ch := range []byte(s) {
lastI, ok := lastOccurred[ch]
if ok && lastI >= start {
start = lastOccurred[ch] + 1
}
if i-start+1 > maxLength {
maxLength = i - start + 1
}
lastOccurred[ch] = i
}
return maxLength
}
6)、字符和字符串处理
1)rune相当于go的char
s := "你好世界!"
fmt.Println(len(s)) // 15
for i, ch := range s {
// ch is a rune
fmt.Printf("(%d %X)", i, ch) // (0 4F60)(3 597D)(6 4E16)(9 754C)(12 FF01)
}
fmt.Println()
fmt.Println(utf8.RuneCountInString(s)) // 5
bytes := []byte(s)
for len(bytes) > 0 {
ch, size := utf8.DecodeRune(bytes)
bytes = bytes[size:]
fmt.Printf("%c", ch)
} // 你好世界!
fmt.Println()
for i, ch := range []rune(s) {
fmt.Printf("(%d %c)", i, ch)
} // (0 你)(1 好)(2 世)(3 界)(4 !)
fmt.Println()
- 使用range遍历pos rune对
- 使用
utf8.RuneCountInString
获得字符数量 - 使用len获得字节长度
- 使用[]byte获得字节
最长不含重复字符的子字符串支持中文
func lengthOfLongestSubstring(s string) int {
lastOccurred := make(map[rune]int)
start, maxLength := 0, 0
for i, ch := range []rune(s) {
lastI, ok := lastOccurred[ch]
if ok && lastI >= start {
start = lastOccurred[ch] + 1
}
if i-start+1 > maxLength {
maxLength = i - start + 1
}
lastOccurred[ch] = i
}
return maxLength
}
2)其他字符串操作
- Fields,Split,Join
- Contains,Index
- ToLower,ToUpper
- Trim,TrimRight,TrimLeft
3、面向对象
1)、结构体和方法
1)面向对象
- Go语言仅支持封装,不支持继承和多态
- Go语言没有class,只有struct
2)结构的创建
type treeNode struct {
value int
left, right *treeNode
}
func createNode(value int) *treeNode {
return &treeNode{
value: value}
}
func main() {
root := treeNode{
value: 3}
root.left = &treeNode{
}
root.right = &treeNode{
5, nil, nil}
root.right.left = new(treeNode)
root.left.right = createNode(2)
}
- 使用自定义工厂函数
- 注意返回了局部变量的地址
3)为结构定义方法
package main
import "fmt"
type treeNode struct {
value int
left, right *treeNode
}
func (node treeNode) print() {
fmt.Print(node.value, " ")
}
func (node treeNode) setValue(value int) {
node.value = value
}
func (node *treeNode) setValue2(value int) {
node.value = value
}
// 中序遍历
func (node *treeNode) traverse() {
if node == nil {
return
}
node.left.traverse()
node.print()
node.right.traverse()
}
func createNode(value int) *treeNode {
return &treeNode{
value: value}
}
func main() {
root := treeNode{
value: 3}
root.left = &treeNode{
}
root.right = &treeNode{
5, nil, nil}
root.right.left = new(treeNode)
root.left.right = createNode(2)
root.print() // 3
fmt.Println()
root.right.left.setValue(4)
root.right.left.print() // 0
root.right.left.setValue2(4)
root.right.left.print() // 4
fmt.Println()
root.traverse() // 0 2 3 4 5
}
- 显示定义和命名方法接收者
- 只有使用指针才可以改变结构内容
- nil指针也可以调用方法
4)值接收者 vs 指针接收者
- 要改变内容必须使用指针接收者
- 结构过大也考虑使用指针接收者
- 一致性:如有指针接收者,最好都是指针接收者
- 值接收者是Go语言特有
- 值/指针接收者均可接收值/指针
2)、包和封装
1)封装
- 名字一般使用CamelCase
- 首字母大写:public
- 首字母小写:private
2)包
- 每个目录一个包
- main包包含可执行入口
- 为结构定义的方法必须放在同一个包内,可以是不同文件
go mod init tree
package node
import "fmt"
type TreeNode struct {
Value int
Left, Right *TreeNode
}
func (node TreeNode) Print() {
fmt.Print(node.Value, " ")
}
func (node TreeNode) SetValue(value int) {
node.Value = value
}
func (node *TreeNode) SetValue2(value int) {
node.Value = value
}
func CreateNode(value int) *TreeNode {
return &TreeNode{
Value: value}
}
package node
// 中序遍历
func (node *TreeNode) Traverse() {
if node == nil {
return
}
node.Left.Traverse()
node.Print()
node.Right.Traverse()
}
package main
import (
"fmt"
"tree/node"
)
func main() {
root := node.TreeNode{
Value: 3}
root.Left = &node.TreeNode{
}
root.Right = &node.TreeNode{
5, nil, nil}
root.Right.Left = new(node.TreeNode)
root.Left.Right = node.CreateNode(2)
root.Print() // 3
fmt.Println()
root.Right.Left.SetValue(4)
root.Right.Left.Print() // 0
root.Right.Left.SetValue2(4)
root.Right.Left.Print() // 4
fmt.Println()
root.Traverse() // 0 2 3 4 5
}
3)、扩展已有类型
如何扩充系统类型或者别人的类型
- 定义别名
- 使用组合
1)使用组合
package main
import (
"fmt"
"tree/node"
)
type myTreeNode struct {
node *node.TreeNode
}
// 后序遍历
func (myNode *myTreeNode) postOrder() {
if myNode == nil || myNode.node == nil {
return
}
left := myTreeNode{
myNode.node.Left}
left.postOrder()
right := myTreeNode{
myNode.node.Right}
right.postOrder()
myNode.node.Print()
}
func main() {
root := node.TreeNode{
Value: 3}
root.Left = &node.TreeNode{
}
root.Right = &node.TreeNode{
5, nil, nil}
root.Right.Left = new(node.TreeNode)
root.Left.Right = node.CreateNode(2)
root.Print() // 3
fmt.Println()
root.Right.Left.SetValue(4)
root.Right.Left.Print() // 0
root.Right.Left.SetValue2(4)
root.Right.Left.Print() // 4
fmt.Println()
root.Traverse() // 0 2 3 4 5
fmt.Println()
myRoot := myTreeNode{
&root}
myRoot.postOrder() // 2 0 4 5 3
}
2)定义别名
go mod init queue
package queue
type Queue []int
func (q *Queue) Push(v int) {
*q = append(*q, v)
}
func (q *Queue) Pop() int {
head := (*q)[0]
*q = (*q)[1:]
return head
}
func (q *Queue) IsEmpty() bool {
return len(*q) == 0
}
package main
import (
"fmt"
"queue/queue"
)
func main() {
q := queue.Queue{
1}
q.Push(2)
q.Push(3)
fmt.Println(q.Pop()) // 1
fmt.Println(q.Pop()) // 2
fmt.Println(q.IsEmpty()) // false
fmt.Println(q.Pop()) // 3
fmt.Println(q.IsEmpty()) // true
}
4)、使用内嵌来扩展已有类型
package main
import (
"fmt"
"tree/node"
)
type myTreeNode struct {
*node.TreeNode // Embedding 内嵌
}
// 后序遍历
func (myNode *myTreeNode) postOrder() {
if myNode == nil || myNode.TreeNode == nil {
return
}
left := myTreeNode{
myNode.Left}
left.postOrder()
right := myTreeNode{
myNode.Right}
right.postOrder()
myNode.Print()
}
func (myNode *myTreeNode) Traverse() {
fmt.Println("This method is shadowed.")
}
func main() {
root := myTreeNode{
&node.TreeNode{
Value: 3}}
root.Left = &node.TreeNode{
}
root.Right = &node.TreeNode{
5, nil, nil}
root.Right.Left = new(node.TreeNode)
root.Left.Right = node.CreateNode(2)
root.Print() // 3
fmt.Println()
root.Right.Left.SetValue(4)
root.Right.Left.Print() // 0
root.Right.Left.SetValue2(4)
root.Right.Left.Print() // 4
fmt.Println()
root.TreeNode.Traverse() // 0 2 3 4 5
fmt.Println()
root.Traverse() // This method is shadowed.
root.postOrder() // 2 0 4 5 3
}
4、Go语言的依赖管理
1)、依赖管理
依赖管理的三个阶段:GOPATH,GOVENDOR,go mod
2)、go mod的使用
go mod init gomodtest
go get -u go.uber.org/zap
go.mod文件中指定了依赖库的版本,同时新增了一个go.sum的文件
package main
import "go.uber.org/zap"
func main() {
logger, _ := zap.NewProduction()
logger.Warn("warning test")
}
指定版本:
go get -u go.uber.org/[email protected]
go mod:
- 由go命令统一的管理,用户不必关心目录结构
- 初始化:go mod init
- 增加依赖:go get
- 更新依赖:go get [@v…],go mod tidy
5、面向接口
1)、接口的概念
package infra
import (
"io/ioutil"
"net/http"
)
type Retriever struct {
}
func (Retriever) Get(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)
}
package testing
type Retriever struct {
}
func (Retriever) Get(url string) string {
return "fake content"
}
package main
import (
"fmt"
"learngo/downloader/infra"
"learngo/downloader/testing"
)
func getTestingRetriever() retriever {
return testing.Retriever{
}
}
func getInfraRetriever() retriever {
return infra.Retriever{
}
}
type retriever interface {
Get(url string) string
}
func main() {
url := "https://blog.csdn.net"
fmt.Println(getInfraRetriever().Get(url))
fmt.Println(getTestingRetriever().Get(url))
}
2)、duck typing的概念
- 像鸭子走路,像鸭子叫(长得像鸭子),那么就是鸭子
- 描述事物的外部行为而非内部结构
- 严格说Go语言属于结构化类型系统,类似duck typing
3)、接口的定义和实现
1)接口的定义
- 接口由使用者定义
2)接口的实现
- 接口的实现是隐式的
- 只要实现接口里的方法
package mock
type Retriever struct {
Contents string
}
func (r Retriever) Get(url string) string {
return r.Contents
}
package real
import (
"net/http"
"net/http/httputil"
"time"
)
type Retriever struct {
UserAgent string
TimeOut time.Duration
}
func (r Retriever) Get(url string) string {
resp, err := http.Get(url)
if err != nil {
panic(err)
}
result, err := httputil.DumpResponse(resp, true)
defer resp.Body.Close()
if err != nil {
panic(err)
}
return string(result)
}
package main
import (
"fmt"
"learngo/retriever/mock"
"learngo/retriever/real"
)
type Retriever interface {
Get(url string) string
}
func download(r Retriever) string {
return r.Get("https://blog.csdn.net")
}
func main() {
var r Retriever
r = real.Retriever{
}
fmt.Println(download(r))
r = mock.Retriever{
"this is a fake blog.csdn.net"}
fmt.Println(download(r))
}
4)、接口的值类型
package mock
type Retriever struct {
Contents string
}
func (r Retriever) Get(url string) string {
return r.Contents
}
package real
import (
"net/http"
"net/http/httputil"
"time"
)
type Retriever struct {
UserAgent string
TimeOut time.Duration
}
func (r *Retriever) Get(url string) string {
resp, err := http.Get(url)
if err != nil {
panic(err)
}
result, err := httputil.DumpResponse(resp, true)
defer resp.Body.Close()
if err != nil {
panic(err)
}
return string(result)
}
package main
import (
"fmt"
"learngo/retriever/mock"
"learngo/retriever/real"
"time"
)
type Retriever interface {
Get(url string) string
}
func download(r Retriever) string {
return r.Get("https://blog.csdn.net")
}
func inspect(r Retriever) {
switch v := r.(type) {
case mock.Retriever:
fmt.Println("Contents:", v.Contents)
case *real.Retriever:
fmt.Println("UserAgent:", v.UserAgent)
}
}
func main() {
var r Retriever
r = mock.Retriever{
Contents: "this is a fake blog.csdn.net"}
fmt.Printf("%T %v\n", r, r) // mock.Retriever {this is a fake blog.csdn.net}
inspect(r) // Contents: this is a fake blog.csdn.net
r = &real.Retriever{
UserAgent: "Mozilla/5.0", TimeOut: time.Minute}
fmt.Printf("%T %v\n", r, r) // *real.Retriever &{Mozilla/5.0 1m0s}
inspect(r) // UserAgent: Mozilla/5.0
// Type assertion
realRetriever := r.(*real.Retriever)
fmt.Println(realRetriever.TimeOut)
if mockRetriever, ok := r.(mock.Retriever); ok {
fmt.Println(mockRetriever.Contents)
} else {
fmt.Println("not a mock retriever")
}
}
1)接口变量里面有什么
- 接口变量自带指针
- 接口变量同样采用值传递,几乎不需要使用接口的指针
- 指针接收者实现只能以指针方式使用;值接收者都可以
2)查看接口变量
- 表示任何类型:interface{}
- Type Assertion
- Type Switch
package queue
type Queue []interface{
}
func (q *Queue) Push(v interface{
}) {
*q = append(*q, v)
}
func (q *Queue) Pop() interface{
} {
head := (*q)[0]
*q = (*q)[1:]
return head
}
func (q *Queue) IsEmpty() bool {
return len(*q) == 0
}
package main
import (
"fmt"
"learngo/queue/queue"
)
func main() {
q := queue.Queue{
1}
q.Push(2)
q.Push(3)
fmt.Println(q.Pop()) // 1
fmt.Println(q.Pop()) // 2
fmt.Println(q.IsEmpty()) // false
fmt.Println(q.Pop()) // 3
fmt.Println(q.IsEmpty()) // true
q.Push("abc")
fmt.Println(q.Pop()) // abc
}
5)、接口的组合
package mock
type Retriever struct {
Contents string
}
func (r *Retriever) Get(url string) string {
return r.Contents
}
func (r *Retriever) Post(url string, form map[string]string) string {
r.Contents = form["contents"]
return "ok"
}
package main
import (
"fmt"
"learngo/retriever/mock"
"learngo/retriever/real"
"time"
)
const url = "https://blog.csdn.net"
type Retriever interface {
Get(url string) string
}
type Poster interface {
Post(url string, form map[string]string) string
}
func download(r Retriever) string {
return r.Get(url)
}
func post(poster Poster) {
poster.Post(url,
map[string]string{
"name": "xiaoming",
})
}
type RetrieverPoster interface {
Retriever
Poster
}
func session(s RetrieverPoster) string {
s.Post(url, map[string]string{
"contents": "another fake blog.csdn.net",
})
return s.Get(url)
}
func inspect(r Retriever) {
switch v := r.(type) {
case *mock.Retriever:
fmt.Println("Contents:", v.Contents)
case *real.Retriever:
fmt.Println("UserAgent:", v.UserAgent)
}
}
func main() {
var r Retriever
retriever := mock.Retriever{
Contents: "this is a fake blog.csdn.net"}
r = &retriever
fmt.Printf("%T %v\n", r, r) // *mock.Retriever &{this is a fake blog.csdn.net}
inspect(r) // Contents: this is a fake blog.csdn.net
r = &real.Retriever{
UserAgent: "Mozilla/5.0", TimeOut: time.Minute}
fmt.Printf("%T %v\n", r, r) // *real.Retriever &{Mozilla/5.0 1m0s}
inspect(r) // UserAgent: Mozilla/5.0
// Type assertion
realRetriever := r.(*real.Retriever)
fmt.Println(realRetriever.TimeOut)
if mockRetriever, ok := r.(*mock.Retriever); ok {
fmt.Println(mockRetriever.Contents)
} else {
fmt.Println("not a mock retriever")
}
fmt.Println(session(&retriever)) // another fake blog.csdn.net
}
6)、常用系统接口
- Stringer
- Reader/Writer
6、函数式编程
1)、函数式编程
1)函数式编程 vs 函数指针
- 函数是一等公民:参数、变量、返回值都可以是函数
- 高阶函数
- 函数 -> 闭包
2)正统函数式编程
- 不可变性:不能有状态,只有常量和函数
- 函数只能有一个参数
3)闭包
func adder() func(int) int {
sum := 0
return func(v int) int {
sum += v
return sum
}
}
func main() {
a := adder()
for i := 0; i < 10; i++ {
fmt.Printf("0 + 1 + ... + %d = %d\n", i, a(i))
}
}
2)、函数式编程示例
1)斐波那契数列
func fibonacci() func() int {
a, b := 0, 1
return func() int {
a, b = b, a+b
return a
}
}
func main() {
f := fibonacci()
for i := 0; i < 10; i++ {
fmt.Println(f())
}
}
2)为函数实现接口
func fibonacci() 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 > 1000 {
return 0, io.EOF
}
s := fmt.Sprintf("%d\n", next)
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 := fibonacci()
printFileContents(f)
}
3)使用函数遍历二叉树
package node
import "fmt"
type TreeNode struct {
Value int
Left, Right *TreeNode
}
func (node TreeNode) Print() {
fmt.Print(node.Value, " ")
}
func CreateNode(value int) *TreeNode {
return &TreeNode{
Value: value}
}
package node
import "fmt"
func (node *TreeNode) Traverse() {
node.TraverseFunc(func(node *TreeNode) {
node.Print()
})
fmt.Println()
}
// 中序遍历
func (node *TreeNode) TraverseFunc(f func(*TreeNode)) {
if node == nil {
return
}
node.Left.TraverseFunc(f)
f(node)
node.Right.TraverseFunc(f)
}
package main
import (
"fmt"
"learngo/tree/node"
)
func main() {
root := node.CreateNode(3)
root.Left = node.CreateNode(0)
root.Right = node.CreateNode(5)
root.Right.Left = node.CreateNode(4)
root.Left.Right = node.CreateNode(2)
root.Traverse() // 0 2 3 4 5
nodeCount := 0
root.TraverseFunc(func(node *node.TreeNode) {
nodeCount++
})
fmt.Println("node count:", nodeCount) // node count: 5
}
4)Go语言闭包的应用
- 更为自然,不需要修饰如何访问自由变量
- 没有Lambda表达式,但是有匿名函数
对应课程: