GO语言基础案例讲解
2023-12-13 04:20:25
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 <nil>
}
三.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:<nil> value:<nil>
m = MyInt(10)
describe(m) // type:main.MyInt value:10
fmt.Println("-------------------------------------------------")
var i interface{} // 空接口 可以存储任意类型的值
testBlank(i) // 空接口: 类型:<nil> 值:<nil>
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 <nil> 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()
}
文章来源:https://blog.csdn.net/qq_49195366/article/details/134921392
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!