Go基础3
Posted 骑着哈哥去旅行
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Go基础3相关的知识,希望对你有一定的参考价值。
目录
一.map
1.1 声明map
map是一种无序的基于key-value
的数据结构,Go语言中的map是引用类型,必须初始化才能使用。
Go语言中 map
的定义语法如下:
map[KeyType]ValueType
-
KeyType:表示键的类型。
-
ValueType:表示键对应的值的类型。
map类型的变量默认初始值为nil,需要使用make()函数来分配内存。
make(map[KeyType]ValueType, cap)
cap表示map的容量,该参数虽然不是必须的,但是我们应该在初始化map的时候就为其指定一个合适的容量。
package main
import "fmt"
func main()
m1 := make(map[string]int, 10)
m1["hpl"], m1["laowang"] = 18, 35
fmt.Println(m1)
fmt.Println(m1["hpl"]) // 取出键对应的值
// 如果键不存在,将返回0
// fmt.Println(m1["xxx"]) // 0(不推荐这种写法)
value, ok := m1["xxx"] // 如果xxx键存在,value就是键对应的值,ok就睡返回true;如果xxx键不存在,value就返回0,ok就会返回false
fmt.Println(value, ok)
if !ok
fmt.Println("查无此key")
else
fmt.Println(value)
map[hpl:18 laowang:35]
18
0 false
查无此key
map也支持在声明的时候填充元素
package main
import "fmt"
func main()
// map也支持声明的时候填充元素
user := map[string]string
"name1": "hpl",
"name2": "laowang",
"name3": "laoxu",
"name4": "pangzi",
fmt.Println(user)
map[name1:hpl name2:laowang name3:laoxu name4:pangzi]
1.2 map的遍历
Go语言中使用for range
遍历map比较方便。
package main
import "fmt"
func main()
// map也支持声明的时候填充元素
user := map[string]string
"name1": "hpl",
"name2": "laowang",
"name3": "laoxu",
"name4": "pangzi",
fmt.Println(user)
for key, value := range user
fmt.Println(key, value)
map[name1:hpl name2:laowang name3:laoxu name4:pangzi]
name1 hpl
name2 laowang
name3 laoxu
name4 pangzi
1.2.1 只遍历key
package main
import "fmt"
func main()
// map也支持声明的时候填充元素
user := map[string]string
"name1": "hpl",
"name2": "laowang",
"name3": "laoxu",
"name4": "pangzi",
fmt.Println(user)
// 如果只想遍历key
for key := range user
fmt.Println(key)
map[name1:hpl name2:laowang name3:laoxu name4:pangzi]
name1
name2
name3
name4
1.2.2 只遍历value
package main
import "fmt"
func main()
// map也支持声明的时候填充元素
user := map[string]string
"name1": "hpl",
"name2": "laowang",
"name3": "laoxu",
"name4": "pangzi",
fmt.Println(user)
// 只想遍历value
for _, value := range user
fmt.Println(value)
map[name1:hpl name2:laowang name3:laoxu name4:pangzi]
hpl
laowang
laoxu
pangzi
1.2.3 按照指定的顺序遍历map
package main
import (
"fmt"
"sort"
)
func main()
map1 := map[int]string
2505180213: "小狗13",
2505180215: "小狗15",
2505180210: "小狗10",
2505180214: "小狗14",
2505180212: "小狗12",
2505180211: "小狗11",
// 取出map1中所有的key
s1 := make([]int, 0, 100)
for key := range map1
s1 = append(s1, key)
fmt.Println(s1)
// 对s1进行排序
sort.Ints(s1)
fmt.Println(s1)
// 按照排序后的key遍历map1
for _, value := range s1
fmt.Println(value, map1[value])
[2505180214 2505180212 2505180211 2505180213 2505180215 2505180210]
[2505180210 2505180211 2505180212 2505180213 2505180214 2505180215]
2505180210 小狗10
2505180211 小狗11
2505180212 小狗12
2505180213 小狗13
2505180214 小狗14
2505180215 小狗15
注意: 遍历map时的元素顺序与添加键值对的顺序无关。
1.3 delete()删除键值对
使用delete()
内建函数从map中删除一组键值对
delete(map, key)
-
map:表示要删除键值对的map
-
key:表示要删除的键值对的键
package main
import "fmt"
func main()
// map也支持声明的时候填充元素
user := map[string]string
"name1": "hpl",
"name2": "laowang",
"name3": "laoxu",
"name4": "pangzi",
fmt.Println(user)
// 删除
delete(user, "name4") // 删除指定的键值对
fmt.Println(user)
delete(user, "xxx") // 删除不存在的键值对,什么都不做,也不会报错
fmt.Println(user)
map[name1:hpl name2:laowang name3:laoxu name4:pangzi]
map[name1:hpl name2:laowang name3:laoxu]
map[name1:hpl name2:laowang name3:laoxu]
1.4 元素为map类型的切片
package main
import "fmt"
func main()
mapslice := make([]map[string]string, 10)
fmt.Println(mapslice) // [map[] map[] map[] map[] map[] map[] map[] map[] map[] map[]]
mapslice[0] = make(map[string]string, 10)
mapslice[0]["hpl"] = "靓仔"
mapslice[0]["laowang"] = "haha"
mapslice[0]["pangzi"] = "xixi"
fmt.Println(mapslice)
[map[] map[] map[] map[] map[] map[] map[] map[] map[] map[]]
[map[hpl:靓仔 laowang:haha pangzi:xixi] map[] map[] map[] map[] map[] map[] map[] map[] map[]]
1.5 值为切片类型的map
package main
import (
"fmt"
)
func main()
sliceMap := make(map[string][]string, 10)
fmt.Println(sliceMap) // map[]
key := "hpl"
value, ok := sliceMap[key]
if !ok
value = make([]string, 0, 2)
value = append(value, "开心", "难过")
sliceMap[key] = value
fmt.Println(sliceMap)
map[]
map[hpl:[开心 难过]]
1.6 练习
1.6.1 写一个程序,统计字符串中每个单词出现的次数。比如:”how do you do”中how:1 do:2 you:1。
package main
import (
"fmt"
"strings"
)
func main()
str1 := "how do you do"
str2 := strings.Split(str1, " ")
m1 := make(map[string]int, 10)
for _, value := range str2
if _, ok := m1[value]; !ok
m1[value] = 1
else
m1[value]++
fmt.Println(m1) // map[do:2 how:1 you:1]
map[do:2 how:1 you:1]
1.6.2 观察下面代码,写出最终的打印结果。
func main()
type Map map[string][]int
m := make(Map)
s := []int1, 2
s = append(s, 3)
fmt.Printf("%+v\\n", s)
m["xxx"] = s
s = append(s[:1], s[2:]...)
fmt.Printf("%+v\\n", s)
fmt.Printf("%+v\\n", m["xxx"])
[1 2 3]
[1 3]
[1 3 3]
1.6.3 回文判断
就是从左往右读和从右往左读都是一样的就是回文,例:上海自来水来自海上。
package main
import "fmt"
func main()
// 回文判断(就是从左往右读和从右往左读都是一样的就是回文)
// 例:上海自来水来自海上
s1 := "上海自来水来自海上"
runeType := []rune(s1)
count := len(runeType)
for i := 0; i < count/2; i++
if runeType[i] != runeType[count-1-i]
fmt.Println("s1不是回文字符串")
return
fmt.Println("s1是回文字符串")
s1是回文字符串
二.函数
函数是组织好的、可重复使用的、用于执行指定任务的代码块。Go语言中支持函数、匿名函数和闭包。
2.1 函数的定义
Go语言中定义函数使用func
关键字,具体格式如下:
func 函数名(参数)(返回值)
函数体
函数名:由字母、数字、下划线组成。但函数名的第一个字母不能是数字。在同一个包内,函数名也称不能重名。
参数:参数由参数变量和参数变量的类型组成,多个参数之间使用
,
分隔。返回值:返回值由返回值变量和其变量类型组成,也可以只写返回值的类型,多个返回值必须用
()
包裹,并用,
分隔。函数体:实现指定功能的代码块。
注意:函数的参数和返回值都是可选的。
2.2 函数的基本使用
package main
import "fmt"
// 函数的基本使用
func f1()
fmt.Println("这是函数f1")
func f2() int
res := 6 + 7
return res
// 多个返回值
func f3() (int, int)
return 2, 3
// 函数的定义
func sum1(x int, y int) (ret int)
ret = x + y
return
// 函数的定义变种
func sum2(x int, y int) int
ret := x + y
return ret
// 变种
func sum3(x, y int) (ret int)
ret = x + y
return
// 变种
func f4(x, y, z int, e, r, t string) int
res := x - y
return res
// 可变长参数
// 可变长的参数必须写在参数的最后
func f5(x string, y ...int) // y就是可以传递你多个值,但是值都是int类型
fmt.Println(x)
// y是一个切片,...后面是什么类型的,就是什么类型的切片
fmt.Println(y)
func main()
fmt.Println("这是函数f1")
fmt.Println(f2())
_, res3 := f3()
fmt.Println(res3)
res1 := sum1(3, 4)
fmt.Println(res1)
res2 := sum2(5, 6)
fmt.Println(res2)
res4 := sum3(34, 56)
fmt.Println(res4)
f5("我喜欢你")
f5("我喜欢你", 1, 2, 3, 4)
这是函数f1
13
3
7
11
90
我喜欢你
[]
我喜欢你
[1 2 3 4]
注意:在一个命名函数中不能再声明命名函数。
2.3 defer语句
Go语言中的defer
语句会将其后面跟随的语句进行延迟处理。在defer
归属的函数即将返回时,将延迟处理的语句按defer
定义的逆序进行执行,也就是说,先被defer
的语句最后被执行,最后被defer
的语句,最先被执行。
package main
import "fmt"
func f1()
fmt.Println("111111111")
defer fmt.Println("222222222") // defer会被延时执行,延时到函数即将返回时再被执行
defer fmt.Println("333333333")
fmt.Println("444444444")
func main()
f1()
111111111
444444444
333333333
222222222
由于defer
语句延迟调用的特性,所以defer
语句能非常方便的处理资源释放问题。比如:资源清理、文件关闭、解锁及记录时间等。
2.3.1 defer的执行时机
在Go语言的函数中return
语句在底层并不是原子操作,它分为给返回值赋值和return指令两步。而defer
语句执行的时机就在返回值赋值操作后,return指令执行前。
package main
import "fmt"
func f1() int
x := 5
defer func()
x++
()
return x
func f2() (x int)
defer func()
x++
()
return 5
func f3() (y int)
x := 5
defer func()
x++
()
return x
func f4() (x int)
defer func(x int)
x++
(x)
return 5
func main()
fmt.Println(f1())
fmt.Println(f2())
fmt.Println(f3())
fmt.Println(f4())
5
6
5
5
2.3.2 defer面试题
func calc(index string, a, b int) int
ret := a + b
fmt.Println(index, a, b, ret)
return ret
func main()
x := 1
y := 2
defer calc("AA", x, calc("A", x, y))
x = 10
defer calc("BB", x, calc("B", x, y))
y = 20
// 求运行结果?
A 1 2 3
B 10 2 12
BB 10 12 22
AA 1 3 4
2.4 函数中变量的作用域
2.4.1 全局变量
全局变量是定义在函数外部的变量,它在程序整个运行周期内都有效。 在函数中可以访问到全局变量。
package main
import "fmt"
//定义全局变量num
var num int64 = 10
func testGlobalVar()
fmt.Printf("num=%d\\n", num) //函数中可以访问全局变量num
func main()
testGlobalVar() //num=10
2.4.2 局部变量
局部变量又分为两种: 函数内定义的变量无法在该函数外使用,例如下面的示例代码main函数中无法使用test函数中定义的变量x:
func test()
//定义一个函数局部变量x,仅在该函数内生效
var x int64 = 100
fmt.Printf("x=%d\\n", x)
func main()
test()
fmt.Println(x) // 此时无法使用变量x
如果局部变量和全局变量重名,优先访问局部变量。
package main
import "fmt"
//定义全局变量num
var num int64 = 10
func testNum()
num := 100
fmt.Printf("num=%d\\n", num) // 函数中优先使用局部变量
func main()
testNum() // num=100
通常我们会在if条件判断、for循环、switch语句上使用这种定义变量的方式。
func testLocalVar2(x, y int)
fmt.Println(x, y) //函数的参数也是只在本函数中生效
if x > 0
z := 100 //变量z只在if语句块生效
fmt.Println(z)
//fmt.Println(z)//此处无法使用变量z
还有之前学过的for循环语句中定义的变量,也是只在for语句块中生效:
func test()
for i := 0; i < 10; i++
fmt.Println(i) //变量i只在当前for语句块中生效
//fmt.Println(i) //此处无法使用变量i
2.5 函数作为参数和返回值
函数中函数可以作为参数,也可以作为返回值。
package main
import (
"fmt"
"reflect"
)
// 函数类型
func f1()
fmt.Println("陕西省汉中市")
func f2() int
return 23
func f3(x func() int) int // 函数作为参数
return 66
func f4(x func() int) func() int // 函数作为参数和返回值
return f2
func ff(x, y int) int
return 44
func f5(x func(int) int) func(int, int) int // 函数作为参数和返回值
return ff
func main()
a := f1
fmt.Println(reflect.TypeOf(a)) // func()
b := f2
fmt.Println(reflect.TypeOf(b)) // func() int
c := f3
fmt.Println(reflect.TypeOf(c)) // func(func() int) int
d := f4
fmt.Println(reflect.TypeOf(d)) // func(func() int) func() int
e := f5
fmt.Println(reflect.TypeOf(e)) // func(func(int) int) func(int, int) int
2.6 匿名函数和闭包
2.6.1 匿名函数
func(参数)(返回值)
函数体
匿名函数因为没有函数名,所以没办法像普通函数那样调用,所以匿名函数需要保存到某个变量或者作为立即执行函数:
func main()
// 将匿名函数保存到变量
add := func(x, y int)
fmt.Println(x + y)
add(10, 20) // 通过变量调用匿名函数
//自执行函数:匿名函数定义完加()直接执行
func(x, y int)
fmt.Println(x + y)
(10, 20)
匿名函数多用于实现回调函数和闭包。
2.6.2 闭包
闭包指的是一个函数和与其相关的引用环境组合而成的实体。简单来说,闭包=函数+引用环境
。
func adder() func(int) int
var x int
return func(y int) int
x += y
return x
func main()
var f = adder()
fmt.Println(f(10)) //10
fmt.Println(f(20)) //30
fmt.Println(f(30)) //60
f1 := adder()
fmt.Println(f1(40)) //40
fmt.Println(f1(50)) //90
变量f
是一个函数并且它引用了其外部作用域中的x
变量,此时f
就是一个闭包。 在f
的生命周期内,变量x
也一直有效。 闭包进阶示例1:
func adder2(x int) func(int) int
return func(y int) int
x += y
return x
func main()
var f = adder2(10)
fmt.Println(f(10)) //20
fmt.Println(f(20)) //40
fmt.Println(f(30)) //70
f1 := adder2(20)
fmt.Println(f1(40)) //60
fmt.Println(f1(50)) //110
闭包进阶示例2:
func makeSuffixFunc(suffix string) func(string) string
return func(name string) string
if !strings.HasSuffix(name, suffix)
return name + suffix
return name
func main()
jpgFunc := makeSuffixFunc(".jpg")
txtFunc := makeSuffixFunc(".txt")
fmt.Println(jpgFunc("test")) //test.jpg
fmt.Println(txtFunc("test")) //test.txt
闭包进阶示例3:
func calc(base int) (func(int) int, func(int) int)
add := func(i int) int
base += i
return base
sub := func(i int) int
base -= i
return base
return add, sub
func main()
f1, f2 := calc(10)
fmt.Println(f1(1), f2(2)) //11 9
fmt.Println(f1(3), f2(4)) //12 8
fmt.Println(f1(5), f2(6)) //13 7
2.7 内置函数
内置函数 | 介绍 |
---|---|
close | 主要用来关闭channel |
len | 用来求长度,比如string、array、slice、map、channel |
new | 用来分配内存,主要用来分配值类型,比如int、struct、string。返回的是指针 |
make | 用来分配内存,主要用来分配引用类型,比如channel、map、slice |
append | 用来追加元素到数组、slice中 |
panic和recover | 用来做错误处理 |
2.7.1 panic和recover
Go语言中目前(Go1.18.2)是没有异常机制,但是使用panic/recover
模式来处理错误。 panic
可以在任何地方引发,但recover
只有在defer
调用的函数中有效。
package main
import "fmt"
func f1()
fmt.Println("这是f1函数")
func f2()
panic("f2函数中出现了一个严重的错误")
fmt.Println("这是f2函数")
func f3()
fmt.Println("这是f3函数")
func main()
f1()
f2()
f3()
这是f1函数
panic: f2函数中出现了一个严重的错误
goroutine 1 [running]:
main.f2(...)
D:/projects/GoProjects/day5/错误处理.go:10
main.main()
D:/projects/GoProjects/day5/错误处理.go:20 +0x66
exit status 2
程序运行期间f2
中引发了panic
导致程序崩溃,异常退出了。这个时候我们就可以通过recover
将程序恢复回来,继续往后执行。
package main
import "fmt"
func f1()
fmt.Println("这是f1函数")
func f2()
defer func()
err := recover()
// 如果程序出现了panic错误,可以通过recover恢复过来
if err != nil
fmt.Println("recover in f2")
()
panic("f2函数中出现了一个严重的错误")
fmt.Println("这是f2函数")
func f3()
fmt.Println("这是f3函数")
func main()
f1()
f2()
f3()
这是f1函数
recover in f2
这是f3函数
注意:
recover()
必须搭配defer
使用。
defer
一定要在可能引发panic
的语句之前定义。
2.8 练习
1. 分金币
/*
你有50枚金币,需要分配给以下几个人:Matthew,Sarah,Augustus,Heidi,Emilie,Peter,Giana,Adriano,Aaron,Elizabeth。
分配规则如下:
a. 名字中每包含1个'e'或'E'分1枚金币
b. 名字中每包含1个'i'或'I'分2枚金币
c. 名字中每包含1个'o'或'O'分3枚金币
d: 名字中每包含1个'u'或'U'分4枚金币
写一个程序,计算每个用户分到多少金币,以及最后剩余多少金币?
程序结构如下,请实现 ‘dispatchCoin’ 函数
*/
var (
coins = 50
users = []string
"Matthew", "Sarah", "Augustus", "Heidi", "Emilie", "Peter", "Giana", "Adriano", "Aaron", "Elizabeth",
distribution = make(map[string]int, len(users))
)
func main()
left := dispatchCoin()
fmt.Println("剩下:", left)
package main
import (
"fmt"
"strings"
)
var (
coins = 50
users = []string
"Matthew", "Sarah", "Augustus", "Heidi", "Emilie", "Peter", "Giana", "Adriano", "Aaron", "Elizabeth",
distribution = make(map[string]int, len(users))
)
// 计算给定字符串中某个字符出现的个数,返回应得的金币数量
func byteNum(str1 string) (num int)
str1 = strings.ToLower(str1) // 将字符串中所有的字符小写
for _, v := range str1
switch v
case 'e':
num++
case 'i':
num += 2
case 'o':
num += 3
case 'u':
num += 4
return
// 计算每个人应分得数量以及总的花费数量
func dispatchCoin() (map[string]int, int)
var count int
for _, value := range users
num := byteNum(value)
count += num
distribution[value] = num
return distribution, count
func main()
left, count := dispatchCoin()
fmt.Println("每个人应分得:", left)
fmt.Println("剩下:", coins-count)
每个人应分得: map[Aaron:3 Adriano:5 Augustus:12 Elizabeth:4 Emilie:6 Giana:2 Heidi:5 Matthew:1 Peter:2 Sarah:0]
剩下: 10
2.9 递归
递归就是自己调用自己
1.计算阶乘
package main
import "fmt"
// 计算阶乘(方法1)
func factorial(num int) (ret int)
ret = 1
for i := 1; i <= num; i++
ret *= i
return
// 计算阶乘(方法2)
func factorial2(num int) int
if num < 1
return 1
return num * factorial2(num-1)
func main()
// 计算N的阶乘
fmt.Println(factorial(4))
fmt.Println(factorial2(5))
2.有N个台阶,一次可以走一步,也可以走两步,一共有多少种走法?
package main
import "fmt"
func fMethods(num int) int
if num < 1
return 0
else if num == 1 // 如果只有一个台阶就只有一种走法
return 1
else if num == 2 // 如果只有两个台阶就只有两种走法
return 2
return fMethods(num-1) + fMethods(num-2)
func main()
// 上台阶问题
// 有N个台阶,一次可以走一步,也可以走两步,一共有多少种走法?
fmt.Println(fMethods(3))
三. 自定义类型和类型别名
3.1 自定义类型
package main
import (
"fmt"
"reflect"
)
// type后面跟的是类型
type myInt int
func main()
var num myInt
num = 50
fmt.Println(num, reflect.TypeOf(num))
50 main.myInt
注:type后面跟的是类型。
3.2 类型别名
package main
import (
"fmt"
"reflect"
)
// type后面跟的是类型
type myInt int // 自定义类型
type yourInt = int // 类型别名
func main()
var num myInt
num = 50
fmt.Println(num, reflect.TypeOf(num))
var num2 yourInt
num2 = 34
fmt.Println(num2, reflect.TypeOf(num2))
50 main.myInt
34 int
以上是关于Go基础3的主要内容,如果未能解决你的问题,请参考以下文章