Go基础学习笔记(一):基础语法、内建容器、面向对象、依赖管理、面向接口、函数式编程

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表达式,但是有匿名函数

对应课程

Google资深工程师深度讲解Go语言

猜你喜欢

转载自blog.csdn.net/qq_40378034/article/details/125365565