GO语言基础案例讲解

GO语言基础案例讲解

      • 一. 字符打印
      • 二. 类型转换
      • 三.`strconv`包类型转换
      • 四.`math和rand`包的应用
      • 五.`switch`的使用和牛顿法逼近平方根
      • 六. 指针的使用和自定义结构体
      • 七. 数组和切片
      • 八. 切片和切片数组的使用(make)
      • 九.`range`切片的遍历
      • 十. 自定义数值生成二维数组
      • 十一. 映射
      • 十二. 映射练习
      • 十三. 函数也可当作值传递
      • 十四. 函数闭包
      • 十五. 斐波那契数闭包练习
      • 十六. 方法
      • 十七. 接口
      • 十八. 接口练习
      • 十九. 接口返回值和类型
      • 二十. 断言
      • 二十一. stringer使用,自定义打印输出
      • 二十二. 自定义错误函数
      • 二十三. 自定义错误返回练习
      • 二十四. `io.Reader`读取内容
      • 二十五. rot13Reader加密算法
      • 二十六. Image接口
      • 二十七. GO信道
      • 二十八. 信道并发计算切片和
      • 二十九. 带缓冲的通道
      • 三十. 通道计算斐波那契数列
      • 三十一. `select`选择通道
      • 三十二. 定时器通道
      • 三十三. 等价二叉树比较
      • 三十四. 互斥锁
      • 三十五. 模拟爬虫

一. 字符打印

package main // 所属包

import "fmt" // 导入fmt包用于打印

func main() { // 主函数程序运行入口
    c1 := '0' // 定义字符
    fmt.Println(c1)
    fmt.Printf("%T\n", c1) // 默认使用int32

    var temp int64 = 9999999999
    fmt.Println(temp)

    c2 := 'z'
    fmt.Println(c2)
    fmt.Printf("%T", c2)
    fmt.Println("--------------------------------------")

    var c3 byte = 'F'
    fmt.Printf("类型:%T,ascll值:%d,字符值:%c\n", c3, c3, c3)

    var c4 int = 23450
    fmt.Printf("%c\n", c4) // 定 

}

二. 类型转换

package main

import (
	"fmt"
	"strconv"
)

func main() {
	var a int64 = 99
	var b float64
	var c bool 
	d := "hello 清清"

	fmt.Printf("type:%T,%v\n", a, a)
	fmt.Printf("%v\n", b)
	fmt.Printf("%v\n", c)
	fmt.Printf("%v\n", d)

	//字符转换 %d:十进制格式输出
	var str string
	str = fmt.Sprintf("%d", a)                   // %d:十进制格式输出
	fmt.Printf("str type:%T,val:%s\n", str, str) // str type:string,val:99
	fmt.Printf("str type:%T,val:%q\n", str, str) // str type:string,val:"99"

	str = fmt.Sprintf("%f", b)                   // %f:浮点格式输出
	fmt.Printf("str type:%T,val:%v\n", str, str) // str type:string,val:0

	str = fmt.Sprintf("%t", c)                   // %t:布尔格式输出
	fmt.Printf("str type:%T,val:%q\n", str, str) // str type:string,val:"false"

	// strconv 进制转换
	f := strconv.FormatInt(-87, 2) // 二进制
	fmt.Printf("a:%s\n", f)        // -10111

	o := strconv.FormatUint(87, 2) // 二进制
	fmt.Printf("87:%s\n", o)       // 10111

	g := strconv.FormatBool(c)
	fmt.Printf("c:%q\n", g) // false

	fmt.Println("------------------------------------------------")

	m := "99"
	fmt.Printf("%T,%v\n", m, m) // string,99

	var p int64 = int64(b) // 强制转换
	fmt.Printf("%T,%v\n", p, p) // int64,0

	atoi, err := strconv.Atoi(m) // 字符串转换为整型
	fmt.Println("atoi:", atoi, err) // atoi:99 

}

三.strconv包类型转换

package main

import (
	"fmt"
	"strconv"
	"unsafe"
)

func main() {
	var str = "true"
	var b bool
	b, _ = strconv.ParseBool(str)

	fmt.Printf("b type:%T, b=%v \n", b, b) // b type:bool, b=true
	fmt.Println("----------------------------")

	var str2 = "123453"
	var num int64
	num, _ = strconv.ParseInt(str2, 10, 64)      // 10进制, 64位
	fmt.Printf("num type:%T, b=%v \n", num, num) // num type:int64, b=123453
	var num2 int = int(num)
	fmt.Printf("num type:%T, b=%v \n", num2, num2) // num type:int, b=123453
	fmt.Printf("-------------------\n")

	var str3 string = "123.456"
	var f1 float64
	f1, _ = strconv.ParseFloat(str3, 64)      // 64位
	fmt.Printf("f1 type:%T, b=%v \n", f1, f1) // f1 type:float64, b=123.456

	fmt.Println("------------------------")

	var str4 = "hello"
	i, err := strconv.ParseInt(str4, 10, 64) // 10进制, 64位
	fmt.Printf("i:%T, i=%v\n", i, i)         // i:int64, i=0 
	fmt.Println("err:", err)                 //err: strconv.ParseInt: parsing "hello": invalid syntax

	name := "jojo"
	fmt.Println("my name is " + name) // my name is jojo

	// unsafe.Sizeof 函数会返回一个 int 类型的值,表示指定类型的字节大小
	fmt.Print(unsafe.Sizeof(name)) // 16
}

四.math和rand包的应用

package main

import (
	"fmt"
	"math"
	"math/rand"
	"strconv"
	"time"
)

func main() {
	// 获取当前时间的 Unix 纳秒时间戳 确保数列改变
	rand.Seed(time.Now().UnixNano()) // 设置随机数种子
	fmt.Println(rand.Intn(10))       // 0-9

	//%g是一个格式化字符,用于以科学计数法或十进制形式打印浮点数
	fmt.Printf("my number is %g\n", math.Sqrt(50)) //my number is 7.0710678118654755
	fmt.Println(math.Pi)                           //3.141592653589793

	fmt.Println(add(21, 22))

	a, b := swap("hello", "world")

	fmt.Println(a, b) //world hello

	fmt.Println(split(0)) //0 0

	var c, python, java bool
	var i int
	fmt.Println(c, python, java, i) //false false false 0
	test1()

	fmt.Println(sum100()) //5050

	fmt.Println(sqrt2(-9), sqrt2(81)) //3 9

	fmt.Println(pow(3, 2, 10)) //9
	fmt.Println(pow(3, 3, 10)) //27
}

// pow(x,n,lim) calculates x^n and returns lim if overflow occurs 计算 x^n 并在发生溢出时返回 lim
func pow(x, n, lim float64) float64 {
	if v := math.Pow(x, n); v < lim {
		return v
	} else {
		fmt.Printf("%g>=%g\n", v, lim)
	}
	return lim
}

// sqrt2(x) returns the square root of x 计算 x 的平方根
func sqrt2(x float64) string {
	if x < 0 {
		/*
		- math.Sqrt(-x):表示需要格式化的浮点数,这里的math.Sqrt(-x)是为了示例负数开根号的情况。 
		- 'f':表示使用固定点表示法进行格式化。 
		- -1:表示小数点后的位数,-1表示使用默认位数。  精度
		- 64:表示浮点数的位数,这里是64位的浮点数。
		*/
		return strconv.FormatFloat(math.Sqrt(-x), 'f', -1, 64) 
	}
	return fmt.Sprint(math.Sqrt(x))
}

// add(x,y) returns the sum of x and y 计算 x 和 y 的和
func add(x, y int) int {
	return x + y
}

// swap(x
func swap(x, y string) (string, string) {
	return y, x
}

// split(sum) returns two integers, x and y, that sum to sum 计算 sum 的两个整数 x 和 y
func split(sum int) (x, y int) {
	x = sum * 4 / 9
	y = sum - x
	return
}

func test1() {
	var x, y int = 3, 4
	f := math.Sqrt(float64(x*x + y*y))
	z := uint(f) //将f转换为uint类型
	fmt.Println(x, y, f, z) //3 4 5 5
}

// sum100() returns the sum of 1+2+3+...+100 1+2+3+...+100 的和
func sum100() int {
	sum := 0
	for i := 0; i <= 100; i++ {
		sum += i
	}
	return sum
}

// 自身累加 每次翻倍 2 4 8 16 32 64 128 256 512 1024
func forTest() int {
	sum := 1
	for sum < 1000 {
		sum += sum
	}
	return sum
}

func foeTest2() int {
	sum := 1
	for sum < 100 {
		sum += sum
	}
	return sum
}

五.switch的使用和牛顿法逼近平方根

package main

import (
	"fmt"
	"math"
	"runtime"
	"time"
)

// z -= (z*z - x) / (2*z) 牛顿法逼近平方根
func sqrt(x float64) float64 {
	// 给定初始值
	z := 1.0
	for i := 0; i < 8; i++ {
		z -= (z*z - x) / (2 * z)
		fmt.Println("迭代次数", i+1, "的值:", z)
	}
	return z
}

// 获取运行系统
func switchTest() {
	fmt.Print("GO runs on ")
	switch os := runtime.GOOS; os { //os := runtime.GOOS; 初始化语句分号隔开
	case "darwin":
		fmt.Println("OS X.")
	case "linux":
		fmt.Println("Linux.")
	default:
		// freebsd, openbsd,
		// plan9, windows...
		fmt.Printf("%s.\n", os) // 打印默认值
	}
}

// 时间
func when() {
	fmt.Println("when's saturday?")
	today := time.Now().Weekday() // 获取今天星期
	switch time.Saturday {        // 6
	case today + 0:
		fmt.Println("Today")
	case today + 1:
		fmt.Println("Tomorrow")
	case today + 2:
		fmt.Println("In two days")
	case today + 3:
		fmt.Println("In three days")
	case today + 4:
		fmt.Println("In four days")
	default:
		fmt.Println("Too far away.")

	}
}

// 时间
func sayHi() {
	t := time.Now().Hour()
	switch {
	case t < 12:
		fmt.Println("Good morning!", t)
	case t <= 17:
		fmt.Println("Good afternoon!", t)
	default:
		fmt.Println("Good evening!", t)
	}
}

func main() {
	fmt.Println("计算值:", sqrt(36))        //6
	fmt.Println("math库:", math.Sqrt(36)) //6

	switchTest()

	fmt.Println(runtime.GOOS) // windows
	defer when()              // 延迟执行

	sayHi()
	/*
		GO runs on windows.
		windows
		Good afternoon! 14
		when's saturday?
		Tomorrow
	*/

}

六. 指针的使用和自定义结构体

package main

import "fmt"

// 指针示例
func testP1() {
	var i, j float64 = 42, 2701 // 声明变量 i, j 并初始化
	p := &i                     // p 指向 i
	*p = 21                     // 相当于 i = 21
	fmt.Println(*p)             // 21

	p = &j         // p 指向 j
	*p = *p / 2    // 相当于 j = j / 2
	fmt.Println(j) // 1350.5
}

// Person 自定义结构体 类似于JAVA中的类
type Person struct {
	Name string // 姓名
	Age  int    // 年龄
}

// Vertex 自定义结构体 坐标
type Vertex struct {
	X float64
	Y float64
}

func main() {
	testP1()
	fmt.Println(Person{
		"张三",
		18,
	})

	person := Person{
		"张三",
		18,
	}

	fmt.Println(person.Name)// 张三

	v := Vertex{3, 5}
	fmt.Println(v.Y) // 5

	//初始化
	var (
		v1 = Vertex{1, 2}
		v2 = Vertex{X: 3}
		v3 = Vertex{Y: 6}
		//p  = &Vertex{6, 6}
		p = &v1
	)

	fmt.Println(v1, v2, v3, p.X) // {1 2} {3 0} {0 6} 1
}

七. 数组和切片

package main

import "fmt"

func testArr() {

	//var strs [2]string // 定义一个长度为2的字符串数组
	strs := [...]string{"hello", "world"} //[...]这个语法表示一个数组的长度可以使用 ... 让编译器计算数组的长度,基于提供的初始化值的数量
	strs[0] = "hello"
	strs[1] = "world"
	fmt.Println(strs[0], strs[1])
	fmt.Println(len(strs)) // 2

	params := [6]int{1, 2, 3, 4, 5}
	fmt.Println(params) // [1 2 3 4 5 0] 最后默认值为0
}

func main() {
	testArr()
	// 切片
	primes := [6]int{2, 3, 5, 7, 11, 13}
	fmt.Println(primes[0:6])// [2 3 5 7 11 13]
	fmt.Println("------------------------------------")
	names := [4]string{
		"John",
		"Paul",
		"George",
		"Ringo",
	}
	a := names[0:2] // [John Paul]
	b := names[1:3]// [Paul George]
	fmt.Println(a, b)// [John Paul] [Paul George]
	b[0] = "JOJO" // 切片的元素是可以修改的
	fmt.Println(a, b)// [JOJO Paul] [JOJO George]
	fmt.Println("------------------------------------")
	q := []int{1, 2, 3, 4, 5}
	fmt.Println(q, len(q))// [1 2 3 4 5] 5

	r := []bool{true, false, true, true, false, true}
	fmt.Println(r)// [true false true true false true]
	fmt.Println("------------------------------------")

	// 结构体切片
	s := []struct {
		i int
		b bool
	}{
		{1, true},
		{2, false},
		{3, true},
		{4, true},
		{5, false},
		{6, true},
	}

	fmt.Println(s)
}

八. 切片和切片数组的使用(make)

package main

import (
	"fmt"
	"strings"
)

func main() {
	s := []int{2, 3, 5, 7, 11, 13, 17, 19}
	printSlice(s) // [2 3 5 7 11 13 17 19]

	s = s[:0]     // 长度为零
	printSlice(s) // []

	// 扩展长度
	s = s[0:4]
	printSlice(s) // [2 3 5 7]

	// 舍弃前两个值
	s = s[2:]
	printSlice(s) // [5 7 11 13 17 19]

	fmt.Println("-------------------")
	arr := []int{2, 3, 5, 7, 11, 13, 17, 19}

	fmt.Println(len(arr), cap(arr)) // 8 8
	fmt.Println("-------------------------------------------------")

	var i []int
	fmt.Println(len(i), cap(i), i) // 0 0 []
	if i == nil {
		fmt.Println("i is nil")
	}
	fmt.Println("----------------------make---------------------------")

	// make函数返回一个指定长度的切片,并用零值填充。 make初始化切片
	a := make([]int, 5) // len(a) == 5, cap(a) == 5
	print("a:", a)      // a: [0 0 0 0 0]

	// 第二个参数指定切片的容量,第三个参数指定切片的长度
	b := make([]int, 0, 5) // len(b) == 0, cap(b) == 5
	print("b", b)          // b []

	c := b[:2]
	print("c", c) // c [0 0]

	// 新切片的容量会根据原始切片的容量和新切片的起始位置来计算。
	d := c[2:5]
	print("d", d)   // d: []
	fmt.Println(cap(d))// 3
	ticTacToeGame() 

	// 添加切片
	fmt.Println("----------------------append---------------------------")
	addSlice()
}

func addSlice() {
	var s []int
	printSlice(s)

	s = append(s, 1, 2, 3)
	printSlice(s)// [1 2 3]
	s = append(s, 4, 5, 6)
	printSlice(s)// [1 2 3 4 5]
	p := &s
	fmt.Println(p)// &[1 2 3 4 5 6]

}

// 井字游戏
func ticTacToeGame() {
	// 二维数组
	border := [][]string{
		{"_", "_", "_"},
		{"_", "_", "_"},
		{"_", "_", "_"},
	}
	border[0][0] = "X"
	border[0][2] = "O"
	border[2][2] = "X"
	border[1][1] = "O"
	fmt.Printf("%s\n", border[0])
	fmt.Printf("%s\n", border[1])
	fmt.Printf("%s\n", border[2])

	fmt.Println("-------------------------------------------")
	for i := 0; i < len(border); i++ {
		fmt.Printf("%s\n", strings.Join(border[i], " "))// X_O_X
	}
}

func printSlice(s []int) {
	fmt.Printf("s len=%d cap=%d %v\n", len(s), cap(s), s) // %v 打印数组 cap(s)表示容量
}

func print(s string, x []int) {
	fmt.Printf("%s len=%d cap=%d %v\n", s, len(x), cap(x), x)
}

九.range切片的遍历

package main

import "fmt"

func main() {
	range1()
	range2()
}

func range2() {
	pow := make([]int, 11)// 声明切片 pow 长度为11
	for i := range pow {// 遍历切片i:索引
		pow[i] = 1 << uint(i) //1* 2**i 左移位运算
	}
	fmt.Println(pow)

	for _, v := range pow { // 遍历切片v:值
		fmt.Print(v, " ") // 打印切片值 1 2 4 8 16 32 64 128...
	}
}

func range1() {
	pow := []int{1, 2, 4, 8, 16, 32, 64, 128}
	for i, v := range pow { // 遍历切片i:索引 v:值
		fmt.Printf("2**%d=%d\n", i, v) // 打印索引和值 2**0=1 2**1=2 2**2=4 2**3=8 2**4=16 2**5=32 2**6=64 2**7=128
	}
}

十. 自定义数值生成二维数组

package main

import (
	"fmt"
	"math"
)

// 切片 二维数组
func main() {
	fmt.Println(pic2(3, 4))
}

func pic2(dy, dx int) [][]uint8 {
	// 创建一个二维数组 容量为dy
	result := make([][]uint8, dy)
	// 遍历二维数组
	for x := 0; x < dy; x++ {
		result[x] = make([]uint8, dx)
		for y := 0; y < dx; y++ {
			result[x][y] = uint8(math.Pow(float64(x), float64(y)))
		}
	}
	return result
}

十一. 映射

package main

import (
	"fmt"
	"strings"
)

// 映射
func main() {
	// 创建和初始化映射
	//colors := map[string]string{}
	// 创建映射
	var colors map[string]string // key类型为string,value类型为string
	fmt.Println(colors == nil)   // true
	// 初始化映射后才可使用
	colors = make(map[string]string)
	fmt.Println(colors == nil) // false
	// 给映射赋值
	colors["red"] = "#ff0000"
	colors["green"] = "#00ff00"
	colors["blue"] = "#0000ff"
	fmt.Println(colors)
	for i, v := range colors {//遍历映射
		fmt.Println(i, v)//i为key,v为value
	}
	fmt.Println("--------------------first end----------------------------------")

	fruits := map[string]int{//创建映射 key为string,value为int类型
		"apple":  5,
		"pear":   3,
		"banana": 2,
	}
	fmt.Println(len(fruits)) // 3
	fmt.Println("the number of bananas are", fruits["banana"])// 2
	// 删除 key
	delete(fruits, "apple")
	fmt.Println(fruits)//{"pear":3,"banana":2}
	fmt.Println("--------------------second end----------------------------------")

	// 判断key是否存在
	m := make(map[string]int)//创建映射
	fmt.Println(m == nil) //false
	m["key1"] = 1
	m["key2"] = 2
	fmt.Println(m)//{"key1":1,"key2":2}

	v, ok := m["key1"]//获取key1的值
	fmt.Println(v, ok) // 1, true

	if ok {
		fmt.Println("key1 exists", v)
	} else {
		fmt.Println("key1 not exists")
	}

	v, ok = m["key3"]
	if ok {
		fmt.Println("key3 exists", v)
	} else {
		fmt.Println("key3 not exists")
	}
	fmt.Println("--------------------third end----------------------------------")

	// 结构体
	type Vertex struct {
		Lat, Long float64 // 经纬度 都为float64类型
	}
	// 创建映射
	//var s map[string]Vertex
	// 初始化映射
	s := make(map[string]Vertex)// key为string,value为Vertex类型
	fmt.Println(s == nil) // false
	
	var strs []string = strings.Fields("he sc sd")//按照空格进行字符串切割 
	fmt.Println(strs) //[he sc sd]
	
	s["Bell Labs"] = Vertex{40.68433, -74.39967}
	s["Google"] = Vertex{37.42202, -122.08408}
	fmt.Println(s["Bell Labs"])//{40.68433 -74.39967}
	fmt.Println(s["Google"])//{37.42202 -122.08408}
	fmt.Println(s) //map[Bell Labs:{40.68433 -74.39967} Google:{37.42202 -122.08408}]
}

十二. 映射练习

package main

import (
	"golang.org/x/tour/wc"
	"strings"
)

// WordCount 映射练习 计算字符串中每个单词出现的次数
func WordCount(s string) map[string]int {
	strs := strings.Fields(s)
	// var strs []string = strings.Fields(s)
	m := make(map[string]int) // 声明一个空的map
	for i := 0; i < len(strs); i++ {
		_, ok := m[strs[i]]
		if ok {
			m[strs[i]] += 1 // 键值对自增
		} else {
			m[strs[i]] = 1 // 键值对赋值
		}
	}
	return m
}

func main() {
	wc.Test(WordCount)
	// 测试结果如下:
    /*
	PASS
	 f("I am learning Go!") =
	  map[string]int{"Go!":1, "I":1, "am":1, "learning":1}
	PASS
	 f("The quick brown fox jumped over the lazy dog.") =
	  map[string]int{"The":1, "brown":1, "dog.":1, "fox":1, "jumped":1, "lazy":1, "over":1, "quick":1, "the":1}
	PASS
	 f("I ate a donut. Then I ate another donut.") =
	  map[string]int{"I":2, "Then":1, "a":1, "another":1, "ate":2, "donut.":2}
	PASS
	 f("A man a plan a canal panama.") =
	  map[string]int{"A":1, "a":2, "canal":1, "man":1, "panama.":1, "plan":1}
	*/
}

十三. 函数也可当作值传递

package main

import (
	"fmt"
	"math"
)

// 函数也可当作值传递 fn:参数 类型:func(float64, float64) float64 返回值:float64
func compute(fn func(float64, float64) float64) float64 {
	return fn(3, 4)
}

func main() {
	// 自定义函数计算平方根 原点到点(x,y)的距离
	hyp := func(x, y float64) float64 {
		return math.Sqrt(x*x + y*y)
	}
	// 调用函数 参数为hyp
	fmt.Println(compute(hyp)) // 5

	fmt.Println(hyp(6, 8)) // 10

	fmt.Println(compute(math.Pow)) // 81

}

十四. 函数闭包

package main

import "fmt"

// 函数闭包  也就是返回一个函数,内部函数控制外部函数的变量 使其可以在其他地方修改此函数的私有变量
func new() func(int) int {
	sum := 0                 // 定义一个变量
	return func(x int) int { // 定义一个返回函数
		sum += x
		return sum // 返回sum
	}
}
func main() {
	// 返回一个闭包函数
	newer := new() // 赋值给新变量
	for i := 0; i < 5; i++ {
		fmt.Print(newer(i), " ") // 调用返回的函数控制函数内部变量sum

	}
}

十五. 斐波那契数闭包练习

package main

import "fmt"

// 斐波那契数 实现闭包练习 0, 1, 1, 2, 3, 5, ...
func fibonacci() func() int {
	a, b := 0, 1 //初始化前两个值 后一个值是前两个值的和
	return func() int {
		result := a
		a, b = b, a+b
		return result
	}
}
func main() {
	f := fibonacci() // 返回函数
	for i := 0; i < 30; i++ {
		// 调用函数
		fmt.Print(f(), " ") //输出斐波那契数 0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181 6765 10946 17711 28657 46368 75025 121393 196418 317811 514229
	}
}

十六. 方法

方法的定义类似于函数,但在方法名之前有一个额外的参数(接收者),它定义了这个方法可以被哪个类型的实例调用。

package main

import (
	"fmt"
	"math"
)

// Abs 方法 方法的定义类似于函数,但在方法名之前有一个额外的参数(接收者),它定义了这个方法可以被哪个类型的实例调用。
// 记住:方法只是个带接收者参数的函数
// Abs 定义方法
func (v Vertex2) Abs() float64 {
	return math.Sqrt(v.X*v.X + v.Y*v.Y) // 调用内置函数 math.Sqrt
}

type Vertex2 struct {
	X, Y float64
}

func point() {
	// 练习指针
	a := 10
	b := &a
	*b = 10 * 10   // 100
	fmt.Println(a) // 100
}

type Vertex3 struct {
	X, Y float64
}

func (v Vertex3) comp() {
	v.X = v.X + 1
	v.Y = v.Y + 1
}

func (v *Vertex3) comp2() {
	v.X = v.X + 1
	v.Y = v.Y + 1
}

// MyFloat 自定义类型
type MyFloat float64

// 定义方法 MyFloat.fbs() float64 返回一个 float64 类型的值
func (f MyFloat) fbs() float64 {
	if f < 0 {
		return float64(-f)
	} else {
		return float64(f)
	}
}

func main() {
	//point()
	v := Vertex2{6, 8}
	fmt.Println(v.Abs()) // 10 调用方法

	// 值接收者 有副本 原始 Vertex 实例不会被修改
	s := Vertex3{9, 10}
	s.comp() 
	fmt.Println(s) // {9 10} 不变

	// 指针接收者
	m := &Vertex3{1, 2}
	m.comp2() 
	fmt.Println(m) // 2,3 值发生改变

	fmt.Println("--------------next----------------------")
	f := MyFloat(-math.Sqrt2) // 根号2
	fmt.Println("f:", f) // -1.4142135623730951
	fmt.Println(f.fbs()) // 绝对值 1.4142135623730951
}

十七. 接口

package main

import "fmt"

// 接口
func main() {
	// 调用接口方法
	s := Square{sideLength: 10}
	fmt.Println(s.Area(), s.Perimeter()) // 输出 100 40
}

// Shape (形状) 定义通用接口 interface:接口
type Shape interface {
	Area() float64      // 面积
	Perimeter() float64 // 周长
}

// Square 定义结构体类型
type Square struct { // 正方形
	sideLength float64 // 边长
}

// Area 实现接口中的方法Area float64
func (s Square) Area() float64 {
	return s.sideLength * s.sideLength
}

// Perimeter 实现接口中的方法Perimeter float64
func (s Square) Perimeter() float64 {// 周边长
	return 4 * s.sideLength
}

十八. 接口练习

package main

import "fmt"

// go-19.go接口练习

func main() {
	// 初始化结构体
	d := Dog{"莱福", 2}
	// 调用结构体的方法
	d.eat("大肉骨头")
	d.run("100km/h")
	fmt.Println("------------------------------------------")
	
	// 接口赋值
	var a animal
	// 赋值结构体
	a = Dog{"小花", 1}
	a.eat("骨头")
	a.run("10km/h")
	
	fmt.Println("---------------end---------------------------")
	
	var i I = T{"hello world"}
	i.M() // 调用接口方法

}

// animal接口
type animal interface {
	// 定义接口方法
	eat(s string)
	run(s string)
}

// Dog Dog结构体
type Dog struct {
	name string
	age  int
}
// Cat Cat结构体
type Cat struct {
	name string
	age  int
}

// Dog结构体的eat方法 (d Dog)为参与者 实现接口中的eat(s string)方法
func (d Dog) eat(s string) {
	fmt.Printf("my dog'name is %v, 年龄:%v,它爱吃%v\n", d.name, d.age, s)
}

// Dog结构体的run方法 (d Dog)为参与者 实现接口中的run(s string)方法
func (d Dog) run(s string) {
	fmt.Println(d.name, "跑得很快!!!时速:", s)
}

// I I接口
type I interface {
	M()
}

// T T结构体
type T struct {
	S string
}

// M T结构体的M方法 (t T)为参与者 实现接口I中的M()方法
func (t T) M() {
	fmt.Println(t.S)
}

十九. 接口返回值和类型

接口也是有返回值和类型的

package main

import "fmt"

// 接口也是有返回值和类型的

func main() {
	var m M

	describe(m) // type:    value:

	m = MyInt(10)
	describe(m) // type:main.MyInt    value:10

	fmt.Println("-------------------------------------------------")

	var i interface{} // 空接口 可以存储任意类型的值
	testBlank(i)      // 空接口: 类型: 值:

	i = 66
	testBlank(i) // 空接口: 类型:int 值:66

	i = "你好~"
	testBlank(i) // 空接口: 类型:string 值:你好~

	i = 420              // int
	v, ok := i.(float64) // 断言false 判断变量i的类型是否为float64 失败则为0
	fmt.Println(v, ok)   // 0 false
}

// M 接口
type M interface {
	M() // 接口方法 无返回值
}

// S 结构体
type S struct {
	s string
}

// M 接口方法
func (s S) M() {
	fmt.Printf(s.s)
}

// MyInt 自定义类型
type MyInt int

// M 接口方法
func (i MyInt) M() {
	fmt.Println(i)
}

// describe 接口信息
func describe(m M) {
	fmt.Printf("type:%T    value:%v\n", m, m)
}

// 空接口可以存储任意类型的值
func testBlank(i interface{}) {
	fmt.Printf("空接口: 类型:%T 值:%v\n", i, i)
}

二十. 断言

断言通常用于判断一个接口类型的值是否为特定的类型

package main

import (
	"fmt"
	"unsafe"
)

// 类型选择
func main() {
	// 类型选择 interface{}
	var i interface{}

	i = 55
	do(i)

	i = "hello"
	do(i)

	i = true
	do(i)
}
func do(i interface{}) {
	switch v := i.(type) { // 断言
	case int:
		fmt.Println("my value is", v)
	case string:
		fmt.Println("my value is", v)
	default:
		n, err := fmt.Printf("i dont know %T~~~\n", v)
		fmt.Println(n, err)
		s := "Hello, 世界" // 一个中文占三个字节
		r := "i dont know bool~~~\n"
		fmt.Println(len(s), unsafe.Sizeof(s), len(r)) // 这将输出字符串s的字节长度和字符串r的字节长度 13 16 20 	 
	}
}

二十一. stringer使用,自定义打印输出

内置接口,自定义类型实现此方法就可自定义打印模板

type Stringer interface {
    String() string
}
package main

import "fmt"

// stringer使用
func main() {
    // 自定义类型打印输出模板
    a := People{"张三", 18}
    fmt.Println(a) // (Name:张三,Age:18)

    fmt.Println("----------------Sprintf用法---------------------")
    // 转换字符串
    b := 66
    str := fmt.Sprintf("number is %d", b)
    fmt.Println(str) // number is 66

    ip := IPAddr{1, 2, 3, 4}
    fmt.Println(ip) // 1.2.3.4
}

type People struct {
    Name string
    Age  int
}

// 实现string接口方法String()
func (p People) String() string {
    return fmt.Sprintf("(Name:%s,Age:%d)", p.Name, p.Age)
}

// IPAddr 自定义类型字节数组
type IPAddr [4]byte

// 实现string接口方法String()
func (ip IPAddr) String() string {
    // 字节数组转换成字符串
    return fmt.Sprintf("%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3])
}

二十二. 自定义错误函数

实现error接口中的Error()方法

type error interface {
	Error() string
}
package main

import (
    "fmt"
    "strconv"
)

func main() {
    // 尝试将字符串转换为整数
    i, err := strconv.Atoi("42")

    // 检查是否有错误发生
    if err != nil {
       // 如果有错误,打印错误并返回
       fmt.Printf("couldn't convert number: %v\n", err)
       return
    }

    // 如果没有错误,打印转换后的整数
    fmt.Println("Converted integer:", i)

    fmt.Println("-------------------------------------------------")

    err2 := doSomething()
    if err2 != nil {
       fmt.Println(err2)
    }
}

// MyError 自定义返回错误类型
type MyError struct {
    msg  string
    code int
}

// 结构体实现error接口中的Error方法
func (m *MyError) Error() string {
    return fmt.Sprintf("msg:%s\ncode:%d", m.msg, m.code)
}

// 一个返回自定义错误的函数
func doSomething() error {
    return &MyError{"something wrong", 100}
}

二十三. 自定义错误返回练习

package main

import (
	"fmt"
	"math"
)

// Sqrt 自定义返回sqrt错误
func Sqrt(x float64) (float64, error) {
	if x < 0 {
		return 0, ErrNegativeSqrt(x)
	}
	return math.Sqrt(x), nil
}

func main() {
	fmt.Println(Sqrt(2))
	fmt.Println(Sqrt(-2))
	fmt.Println(Sqrt(-24))
}

// ErrNegativeSqrt 自定义错误类型
type ErrNegativeSqrt float64

// Error 实现error接口中的Error方法
func (e ErrNegativeSqrt) Error() string {
	return fmt.Sprintf("cannot Sqrt negative number: %v", float64(e))
}

二十四. io.Reader读取内容

package main

import (
    "fmt"
    "golang.org/x/tour/reader"
    "io"
    "strings"
)

// io reader
func main() {
    r := strings.NewReader("你好!!!") // 字符串读取器 返回一个 io.Reader
    b := make([]byte, 6)            // 字节数组 一次读取 6 个字节
    for {                           // 循环读取
       n, err := r.Read(b)             // 读取到字节数组 b 中
       fmt.Println(string(b[:n]), err) // 打印读取到的字节数组 - string() 是一个内置函数,它将给定的字节切片转换为对应的字符串。
       if err == io.EOF {              // 读取到 EOF表示读取结束
          break
       }
    }
    fmt.Println("--------------------------------------------------------------")

    reader.Validate(MyReader{}) // 验证 MyReader 是否实现了 io.Reader OK
    fmt.Println("--------------------------------------------------------------")

    my := MyReader{}
    s := make([]byte, 10)
    read, err := my.Read(s)
    if err != nil {
       return
    }
    fmt.Println(read, err, string(s)) // 1  AAAAAAAAAAAA
}

type MyReader struct{}

// 给 MyReader 添加一个 Read([]byte) (int, error) 方法
func (r MyReader) Read(b []byte) (int, error) {
    for i := range b {
       b[i] = 'A'
    }
    return len(b), nil
}

二十五. rot13Reader加密算法

package main

import (
    "io"
    "os"
    "strings"
)

// rot13Reader

// rot13Reader 结构体 定义了一个返回一个 io.Reader类型的参数
type rot13Reader struct {
    r io.Reader
}

// Read 实现了 io.Reader 接口
func (r rot13Reader) Read(b []byte) (int, error) {
    // 调用 r.r.Read(b) 读取数据
    n, err := r.r.Read(b)
    if err != nil { // 读取出错
       return n, err // 返回错误信息
    }
    for i := 0; i < n; i++ { //n=读取的字节数
       b[i] = rot13(b[i]) // 调用 rot13 函数 加密 变为后面第13个字母
    }
    return n, nil

}

func main() {
    s := strings.NewReader("Lbh penpxrq gur pbqr!")
    r := rot13Reader{s}
    io.Copy(os.Stdout, &r) // You cracked the code!
}

// rot13加密算法 函数 返回字母 b 经过 13 位旋转后的值
func rot13(b byte) byte {
    switch {
    case b >= 'A' && b <= 'Z':
       return 'A' + (b-'A'+13)%26
    case b >= 'a' && b <= 'z':
       return 'a' + (b-'a'+13)%26
    default:
       return b
    }
}

二十六. Image接口

package main

import (
    "fmt"
    "golang.org/x/tour/pic"
    "image"
    "image/color"
)

// Image接口

func main() {
    rgba := image.NewRGBA(image.Rect(0, 0, 100, 100)) // 100x100的RGBA图像
    fmt.Println(rgba.Bounds()) // {0 0 100 100}
    fmt.Println(rgba.At(0, 0))// {0 0 0 0}
    fmt.Println("--------------------------------------------")

    image := Image{100, 100} // 100x100的图像
    pic.ShowImage(image)// 显示图像
}

type Image struct {
    width, height int
}

// Bounds 方法返回图像的边界 
func (img Image) Bounds() image.Rectangle {
    return image.Rect(0, 0, img.width, img.height)
}

// ColorModel 方法返回图像的颜色模型
func (img Image) ColorModel() color.Model {
    return color.RGBAModel
}

// At 方法返回图像中指定点的颜色
func (img Image) At(x, y int) color.Color {
    v := uint8(x + y)
    return color.RGBA{R: v, G: v, B: 255, A: 255} // 返回一个RGBA颜色 
}

二十七. GO信道

package main

import (
    "fmt"
    "time"
)

func main() {
    // 开启一个协程,并将参数传递给协程
    go say("hello")
    say("world") // 主协程执行完毕后,会立即执行,不会等待协程执行完毕

    c := make(chan int)               // 声明一个通道 chan int 通道中存放的是int类型的数据
    go printNumbers(c)                // 开启一个协程,并将c作为参数传递给协程
    fmt.Printf("type:%T, c:%v", c, c) //chan int 0xc00005e120
    v := <-c                          // 接收数据 从通道中读取数据,并赋值给v

    for num := range c { // 遍历通道中的数据
       fmt.Println("num:", num)
       fmt.Println("v:", v)
    }

}

func say(s string) {
    for i := 0; i < 5; i++ {
       time.Sleep(500 * time.Millisecond) // 500ms 等待执行完毕
       fmt.Println("say:", s)
    }
}

// 通道通信
func printNumbers(c chan int) {
    for i := 0; i < 5; i++ {
       c <- i // 发送数据到通道
    }
    close(c) // 关闭通道
}

二十八. 信道并发计算切片和

package main

// 使用信道计算和
func main() {
    c := make(chan int)                      // 创建一个信道 c
    numbers := []int{1, 3, 5, -9, -8, -9, 8} // 定义一个 int 类型的切片
    go sum(numbers[:len(numbers)/2], c)      // 启动一个协程,计算切片的前一半的和
    go sum(numbers[len(numbers)/2:], c)      // 启动一个协程,计算切片的后一半的和
    x, y := <-c, <-c                         // 从 c 中取出两个值 x, y
    println(x, y, x+y)                       // 打印 x, y, x+y

}
// 计算切片的和
func sum(s []int, c chan int) {
    sum := 0 // 定义一个变量 sum
    for _, v := range s {// 遍历切片 s
       sum += v
    }
    c <- sum // 将和送入c 信道
}

二十九. 带缓冲的通道

package main

import (
    "fmt"
    "time"
)

// 带缓冲的通道
func main() {
    c := make(chan int, 5) // 缓冲区大小为5
    go func() {            // 开启一个协程
       for i := 0; i < 10; i++ {
          c <- i
       }
    }() //()表示立即调用匿名函数

    time.Sleep(2 * time.Second) // 等待2秒
    fmt.Println("--")
    for i := 0; i < 10; i++ {
       v := <-c // 接收数据
       println("Received:", v)
    }
}

三十. 通道计算斐波那契数列

package main

import (
    "fmt"
    "time"
)

func main() {
    c := make(chan int, 10) // 缓冲区为10
    // 关闭通道后,再往通道中写入数据,会导致panic异常
    go fibonacci(cap(c), c) // 开启一个协程,计算斐波那契数列

    fmt.Println("-----------------")
    for i := range c { // 遍历通道
       fmt.Println(i, " ")
    }

}

// 计算斐波那契数列 n为通道的容量
func fibonacci(n int, c chan int) {
    time.Sleep(5 * time.Second)
    fmt.Println("5s stop")
    x, y := 0, 1             // 初始化斐波那契数列的初始值
    for i := 0; i < n; i++ { // 循环n次,每次从通道中读取一个数
       c <- x        // 将计算结果写入通道
       x, y = y, x+y // 计算下一个斐波那契数列的值
    }
    close(c) // 关闭通道
}

三十一. select选择通道

package main

import (
    "fmt"
)

// select 等待多个通道的操作

func main() {
    channel1 := make(chan int, 10) // 缓冲区为10
    channel2 := make(chan int, 10)
    channel3 := make(chan int, 10)
    channel1 <- 111 // 向缓冲区中写入数据
    channel2 <- 222 // 向缓冲区中写入数据
    //close(channel1) // 关闭通道
    //close(channel2) // 关闭通道
    msg3 := 999
    /* go func() {
       for i := 0; i < 10; i++ {
          channel1 <- i
          channel2 <- -i
       }
       close(channel1)
       close(channel2)
    }()*/
    //for c1 := range channel1 {
    // fmt.Println(c1, "  ")
    //}
    //for c2 := range channel2 {
    // fmt.Print(c2, " ")
    //}

    fmt.Println("--------select---------")

    //time.Sleep(2 * time.Second)
    for {
       select { // 选择一个可用的通道进行接收操作
       // 布尔值 ok 为 true 表示成功从通道接收到数据,而 false 则表示通道已被关闭且没有更多数据可接收
       case msg1, ok := <-channel1:
          fmt.Println("111", msg1, ok) // 没有数据可读取就会阻塞不会执行
       case msg2, ok := <-channel2:
          fmt.Println("222", msg2, ok)
       case channel3 <- msg3:
          fmt.Println("send:", msg3)
       default:
          fmt.Println("No channel is ready")
          return
       }
    }

}

斐波那契数列

package main

import "fmt"

func main() {
    c := make(chan int)    // 只能存储一个数据 int 没有缓冲
    quit := make(chan int) // 只能存储一个数据
    go func() {
       for i := 0; i < 10; i++ {
          c <- i // 向 c 写入数据
       }
       close(c)
    }() // 启动一个协程,从 c 读取数据 匿名函数 立即启动

    go func() {
       for i := 0; i < 10; i++ {
          fmt.Print(<-c, " ") // 从 c 读取数据
       }
       quit <- 0 // 向 quit 写入数据
    }()

    fibonacci2(c, quit)
}

// 斐波那契数列
func fibonacci2(c, quit chan int) { // 接收两个通道参数
    x, y := 0, 1 // 初始化 x, y
    for {
       select { // 选择一个可用的通道进行接收操作
       case c <- x: // 向 c 写入数据
          x, y = y, x+y // 计算下一个斐波那契数列的值
       case <-quit:
          fmt.Println("quit 程序结束")
          return
       }
    }
}

三十二. 定时器通道

package main

import (
    "fmt"
    "time"
)

func main() {
    // 创建一个定时器,每隔100毫秒触发一次回调函数 每隔 100 毫秒发送当前时间的值到 tick 通道。
    tick := time.Tick(100 * time.Millisecond)
    // 创建一个定时器,在 500 毫秒后触发一次回调函数,在 500 毫秒后发送当前时间的值到 boom 通道。
    boom := time.After(500 * time.Millisecond)
    for { // for { ... }: 这是一个无限循环,用于不断检查 tick 和 boom 通道。
       select { // 选择一个通道,当该通道有值时,执行对应的代码。
       case <-tick:
          fmt.Println("tick:", <-tick)
       case <-boom:
          fmt.Println("boom")
          return
       default:
          fmt.Println("    .")
          time.Sleep(50 * time.Millisecond)
       }
    }
    //for {
    // fmt.Println(<-tick)
    //}
}

三十三. 等价二叉树比较

package main

import (
    "fmt"
    "golang.org/x/tour/tree"
)

// 二叉树查找比较 等价二叉查找树
func main() {
    t1 := tree.New(1)         // 返回树结构
    t2 := tree.New(1)         // 返回树结构
    b := compare(t1, t2)      // 二叉树比较 等价二叉查找树
    fmt.Println("t1==t2:", b) // t1==t2: true
}

// Walk 二叉树遍历
func Walk(t *tree.Tree, ch chan int) {
    if t == nil { // 空树 没有叶子节点
       return // 递归函数结束条件
    }
    Walk(t.Left, ch)  // 左子树先序遍历
    ch <- t.Value     // 根节点值传递 放入 ch 管道
    Walk(t.Right, ch) // 右子树后序遍历
}

// compare 二叉树比较 等价二叉查找树
func compare(t1, t2 *tree.Tree) bool {
    c1 := make(chan int) // 管道 传递数据 无缓冲默认1,按顺序传递数据
    c2 := make(chan int)
    go Walk(t1, c1)           // 协程 协同执行 并行执行
    go Walk(t2, c2)           // 协程 协同执行 并行执行
    for i := 0; i < 10; i++ { // 循环 10 次 10 条数据
       x, y := <-c1, <-c2
       fmt.Println(x, y) // 打印 10 条数据
       if x != y {       // 循环结束条件
          return false // 二叉树不等价 等价二叉查找树
       }
    }
    return true // 二叉树等价 等价二叉查找树
}

三十四. 互斥锁

package main

import (
    "fmt"
    "sync"
    "time"
)

// 互斥锁的计数器
func main() {
    // 并发安全的计数器
    counter := SafeCounter{v: make(map[string]int)}

    for i := 1; i <= 1000; i++ {
       go counter.Increment("somekey") // 并发安全的计数器 Increment方法 调用1000次
    }
    time.Sleep(time.Second) // 等待计数器完成 1s
    fmt.Println(counter.Value("somekey"))
}

// SafeCounter 互斥锁的计数器
type SafeCounter struct {
    v   map[string]int // 共享变量
    mux sync.Mutex     // 互斥锁
}

// Increment 增加计数器的值 方法
func (c *SafeCounter) Increment(key string) {
    c.mux.Lock()   // 加锁
    c.v[key]++     // 增加计数器的值
    c.mux.Unlock() // 解锁
}

// Value 返回计数器的值 方法
func (c *SafeCounter) Value(key string) int {
    c.mux.Lock() // 加锁
    // lock后同一时刻只有一个goroutine访问c.v[key]
    defer c.mux.Unlock() // 解锁 defer语句在函数执行完毕后执行
    return c.v[key]      // 返回计数器的值
}

三十五. 模拟爬虫

// 模拟并行抓取
package main

import (
    "fmt"
    "sync"
)

// Fetcher 是一个接口,用于从一个 URL 下载内容。
type Fetcher interface {
    // Fetch 返回 URL 的 body 内容,并且将在这个页面上找到的 URL 放到一个 slice 中。
    Fetch(url string) (body string, urls []string, err error)
}

// Crawl 使用 fetcher 从某个 URL 开始递归的爬取页面,直到达到最大深度。
func Crawl(url string, depth int, fetcher Fetcher) {
    if depth <= 0 {
       return
    }
    body, urls, err := fetcher.Fetch(url)
    if err != nil {
       fmt.Println(err)
       return
    }
    fmt.Printf("found: %s %q\n", url, body)
    for _, u := range urls {
       Crawl(u, depth-1, fetcher)
    }
}

// fakeFetcher 是返回若干结果的 Fetcher。
type fakeFetcher map[string]*fakeResult

type fakeResult struct {
    body string
    urls []string
}

func (f fakeFetcher) Fetch(url string) (string, []string, error) {
    if res, ok := f[url]; ok {
       return res.body, res.urls, nil
    }
    return "", nil, fmt.Errorf("not found: %s", url)
}

// fetcher 是填充后的 fakeFetcher。
var fetcher = fakeFetcher{
    "https://golang.org/": &fakeResult{
       "The Go Programming Language",
       []string{
          "https://golang.org/pkg/",
          "https://golang.org/cmd/",
       },
    },
    "https://golang.org/pkg/": &fakeResult{
       "Packages",
       []string{
          "https://golang.org/",
          "https://golang.org/cmd/",
          "https://golang.org/pkg/fmt/",
          "https://golang.org/pkg/os/",
       },
    },
    "https://golang.org/pkg/fmt/": &fakeResult{
       "Package fmt",
       []string{
          "https://golang.org/",
          "https://golang.org/pkg/",
       },
    },
    "https://golang.org/pkg/os/": &fakeResult{
       "Package os",
       []string{
          "https://golang.org/",
          "https://golang.org/pkg/",
       },
    },
}

// 并行抓取
var wg sync.WaitGroup

func crawlURLs(urls []string) {
    defer wg.Done()
    for _, url := range urls {
       Crawl(url, 3, fetcher)
    }
}

func main() {
    urls := make([]string, 0)
    for url := range fetcher {
       urls = append(urls, url)
    }
    wg.Add(1)
    go crawlURLs(urls)

    Crawl("https://golang.org/", 4, fetcher)

    wg.Wait()
}

你可能感兴趣的:(GO,golang,开发语言,后端)