补充关于闭包,递归,匿名函数,回调函数,strings包、strconv包的使用。

闭包

go语言支持函数式编程:
支持将一个函数作为另一个函数的参数,也支持将一个函数作为另一个函数的返回值。

闭包(closure):
一个外层函数中,有内层函数,该内层函数中,会操作外层函数的局部变量(外层函数中的参数,或者外层函数中直接定义的变量),并且该外层函数的返回值就是这个内层函数。这个内层函数和外层函数的局部变量,统称为闭包结构。

局部变量的生命周期会发生改变,正常的局部变量随着函数调用而创建,随着函数的结束而销毁。
但是闭包结构中的外层函数的局部变量并不会随着外层函数的结束而销毁,因为内层函数还要继续使用。

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
package main

import "fmt"

func main() {

res1 := increment() //res1 = fun
fmt.Printf("%T\n", res1) //func() int
fmt.Println(res1)
v1 := res1()
fmt.Println(v1) //1
v2 := res1()
fmt.Println(v2) //2
fmt.Println(res1())
fmt.Println(res1())
fmt.Println(res1())
fmt.Println(res1())

res2 := increment()
fmt.Println(res2)
v3 := res2()
fmt.Println(v3) //1
fmt.Println(res2())

fmt.Println(res1())
}

func increment() func() int { //外层函数
//1.定义了一个局部变量
i := 0
//2.定义了一个匿名函数,给变量自增并返回
fun := func() int { //内层函数
i++
return i
}
//3.返回该匿名函数
return fun
}

输出结果:

1
2
3
4
5
6
7
8
9
10
11
12
func() int
0xdbfd60
1
2
3
4
5
6
0xdbfd40
1
2
7

递归

递归函数(recursion):一个函数自己调用自己,就叫做递归函数。
一个函数自己调用自己,就叫做递归调用,一个递归函数一定要有出口,否则会陷入死循环。

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package main

import "fmt"

func main() {

sum := getSum(5)
fmt.Println(sum)

res := getFibonacci(12)
fmt.Println(res)
}
func getFibonacci(n int) int {
if n == 1 || n == 2 {
return 1
}
return getFibonacci(n-1) + getFibonacci(n-2)
}

func getSum(n int) int {
fmt.Println("**********")
if n == 1 {
return 1
}
return getSum(n-1) + n
}

运行结果:

1
2
3
4
5
6
7
**********
**********
**********
**********
**********
15
144

匿名函数

匿名:没有名字
匿名函数:没有名字的函数。

定义一个匿名函数,直接进行调用。通常只能使用一次。也可以使用匿名函数赋值给某个函数变量,那么就可以调用多次了。

匿名函数:
Go语言是支持函数式编程:

将匿名函数作为另一个函数的参数,回调函数
将匿名函数作为另一个函数的返回值,可以形成闭包结构。

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
package main

import "fmt"

func main() {
fun1()
fun1()
fun2 := fun1
fun2()

//匿名函数
func() {
fmt.Println("我是一个匿名函数。。")
}()

fun3 := func() {
fmt.Println("我也是一个匿名函数。。")
}
fun3()
fun3()

//定义带参数的匿名函数
func(a, b int) {
fmt.Println(a, b)
}(1, 2)

//定义带返回值的匿名函数
res1 := func(a, b int) int {
return a + b
}(10, 20) //匿名函数调用了,将执行结果给res1
fmt.Println(res1)

res2 := func(a, b int) int {
return a + b
} //将匿名函数的值,赋值给res2
fmt.Println(res2)

fmt.Println(res2(100, 200))
}

func fun1() {
fmt.Println("我是fun1()函数。。")
}

输出结果:

1
2
3
4
5
6
7
8
9
10
我是fun1()函数。。
我是fun1()函数。。
我是fun1()函数。。
我是一个匿名函数。。
我也是一个匿名函数。。
我也是一个匿名函数。。
1 2
30
0x63e160
300

回调函数

高阶函数:
根据go语言的数据类型的特点,可以将一个函数作为另一个函数的参数。

fun1(),fun2()
将fun1函数作为了fun2这个函数的参数。

fun2函数:就叫高阶函数
接收了一个函数作为参数的函数,高阶函数

fun1函数:回调函数
作为另一个函数的参数的函数,叫做回调函数。

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
package main

import "fmt"

func main() {
//设计一个函数,用于求两个整数的加减乘除运算
fmt.Printf("%T\n", add) //func(int, int) int
fmt.Printf("%T\n", oper) //func(int, int, func(int, int) int) int

res1 := add(1, 2)
fmt.Println(res1)

res2 := oper(10, 20, add)
fmt.Println(res2)

res3 := oper(5, 2, sub)
fmt.Println(res3)

fun1 := func(a, b int) int {
return a * b
}

res4 := oper(10, 4, fun1)
fmt.Println(res4)

res5 := oper(100, 8, func(a, b int) int {
if b == 0 {
fmt.Println("除数不能为零")
return 0
}
return a / b
})
fmt.Println(res5)

}
func oper(a, b int, fun func(int, int) int) int {
fmt.Println(a, b, fun) //打印3个参数
res := fun(a, b)
return res
}

// 加法运算
func add(a, b int) int {
return a + b
}

// 减法
func sub(a, b int) int {
return a - b
}

输出结果:

1
2
3
4
5
6
7
8
9
10
11
func(int, int) int
func(int, int, func(int, int) int) int
3
10 20 0xb8fc20
30
5 2 0xb8fc40
3
10 4 0xb8fc60
40
100 8 0xb8fc80
12

字符串

Go中的字符串是一个字节的切片。
可以通过将其内容封装在“”中来创建字符串。Go中的字符串是Unicode兼容的,并且是UTF-8编码的。

字符串是一些字节的集合。理解为一个字符的序列。
每个字符都有固定的位置(索引,下标,index:从0开始,到长度减1)
示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
package main

import "fmt"

func main() {
//1.定义字符串
s1 := "hello中国"
s2 := `hello world`
fmt.Println(s1)
fmt.Println(s2)

//2.字符串的长度:返回的是字节的个数
fmt.Println(len(s1))
fmt.Println(len(s2))

//3.获取某个字节
fmt.Println(s2[0]) //获取字符串中的第一个字节
a := 'h'
b := 104
fmt.Printf("%c,%c,%c\n", s2[0], a, b)

//4.字符串的遍历
for i := 0; i < len(s2); i++ {
//fmt.Println(s2[i])
fmt.Printf("%c\t", s2[i])
}
fmt.Println()

//for range
for _, v := range s2 {
//fmt.Println(i,v)
fmt.Printf("%c", v)
}
fmt.Println()

//5.字符串是字节的集合
slice1 := []byte{65, 66, 67, 68, 69}
s3 := string(slice1) //根据一个字节切片,构建字符串
fmt.Println(s3)

s4 := "abcdef"
slice2 := []byte(s4) //根据字符串,获取对应的字节切片
fmt.Println(slice2)

//6.字符串不能修改
fmt.Println(s4)
//s4[2] = 'B'
}

输出结果:

1
2
3
4
5
6
7
8
9
10
11
hello中国
hello world
11
11
104
h,h,h
h e l l o w o r l d
hello world
ABCDE
[97 98 99 100 101 102]
abcdef

strings包

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
package main

import (
"fmt"
"strings"
)

func main() {
s1 := "helloworld"
//1.是否包含指定的内容-->bool
fmt.Println(strings.Contains(s1, "abc"))
//2.是否包含chars中任意的一个字符即可
fmt.Println(strings.ContainsAny(s1, "abcd"))
//3.统计substr在s中出现的次数
fmt.Println(strings.Count(s1, "lloo"))

//4.以xxx前缀开头,以xxx后缀结尾
s2 := "20230525课堂笔记.txt"
if strings.HasPrefix(s2, "202305") {
fmt.Println("23年5月的文件。。")
}
if strings.HasSuffix(s2, ".txt") {
fmt.Println("文本文档。。")
}

//索引
//helloworld
fmt.Println(strings.Index(s1, "lloo")) //查找substr在s中的位置,如果不存在就返回-1
fmt.Println(strings.IndexAny(s1, "abcdh")) //查找chars中任意的一个字符,出现在s中的位置
fmt.Println(strings.LastIndex(s1, "l")) //查找substr在s中最后一次出现的位置

//字符串的拼接
ss1 := []string{"abc", "world", "hello", "ruby"}
s3 := strings.Join(ss1, "-")
fmt.Println(s3)

//切割
s4 := "123,4563,aaa,49595,45"
ss2 := strings.Split(s4, ",") //数组
//fmt.Println(ss2)
for i := 0; i < len(ss2); i++ {
fmt.Println(ss2[i])
}

//重复,自己拼接自己count次
s5 := strings.Repeat("hello", 5)
fmt.Println(s5)

//替换
//helloworld
s6 := strings.Replace(s1, "l", "*", -1)
fmt.Println(s6)
//fmt.Println(strings.Repeat("hello",5))

s7 := "heLLo WOrlD**123.."
fmt.Println(strings.ToLower(s7))
fmt.Println(strings.ToUpper(s7))

/*
截取子串:
substring(start,end)-->substr
str[start:end]-->substr
包含start,不包含end下标
*/
fmt.Println(s1)
s8 := s1[:5]
fmt.Println(s8)
fmt.Println(s1[5:])
}

输出结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
false
true
0
23年5月的文件。。
文本文档。。
-1
0
8
abc-world-hello-ruby
123
4563
aaa
49595
45
hellohellohellohellohello
he**owor*d
hello world**123..
HELLO WORLD**123..
helloworld
hello
world

strconv包

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
package main

import (
"fmt"
"strconv"
)

func main() {
//1.bool类型
s1 := "true"
b1, err := strconv.ParseBool(s1)
if err != nil {
fmt.Println(err)
return
}
fmt.Printf("%T,%t\n", b1, b1) //%t是bool类型

ss1 := strconv.FormatBool(b1)
fmt.Printf("%T,%s\n", ss1, ss1)

//2.整数
s2 := "100"
i2, err := strconv.ParseInt(s2, 2, 64)
if err != nil {
fmt.Println(err)
return
}
fmt.Printf("%T,%d\n", i2, i2)

ss2 := strconv.FormatInt(i2, 10)
fmt.Printf("%T,%s\n", ss2, ss2)

//itoa(),atoi()
i3, err := strconv.Atoi("-42") //转为int类型
fmt.Printf("%T,%d\n", i3, i3)
ss3 := strconv.Itoa(-42)
fmt.Printf("%T,%s\n", ss3, ss3)
}

输出结果:

1
2
3
4
5
6
bool,true
string,true
int64,4
string,4
int,-42
string,-42

“strconv”用于进行字符串和基本类型之间的转换。在main函数中,首先演示了布尔类型的转换。使用strconv.ParseBool函数将s1转换为bool类型的变量b1。如果转换过程中出现错误,则会打印错误信息并返回。否则,使用fmt.Printf函数输出b1的类型和值。接下来,使用strconv.FormatBool函数将b1转换为字符串类型的变量ss1,演示了整数类型的转换,使用strconv.ParseInt函数将s2按照二进制解析为int64类型的变量i2。使用strconv.FormatInt函数将i2按照十进制转换为字符串类型的变量ss2,使用strconv.Atoi函数将字符串”-42”转换为int类型的变量i3,使用strconv.Itoa函数将整数-42转换为字符串类型的变量ss3。