Go func

函数

Go 函数不支持嵌套,重载和默认参数

但支持以下特性:

  • 无需声明原型,不定长度变参,多返回值,命名返回值参数,匿名函数,闭包

定义函数使用关键字func,且左大括号不能另起一行

函数也可以作为一种类型使用

无需声明原型

1
2
3
4
5
6
7
8
func funcTest1(a int, b string) (int, string, int) {
fmt.Println(a, b)
return 1, "123", 123
}

func main() {
fmt.Println(funcTest1(1, "234"))
}

多参数同类型

参数

1
2
3
func funcTest2(a, b, c int) {
fmt.Println(a, b, c)
}

返回值

1
2
3
4
5
func funcTest2(a, b, c int) (a1, b1, c1 int) {
fmt.Println(a, b, c)
a1, b1, c1 = a+1, b+1, c+1
return
}

return与返回声明中的参数名一样就可以省略不用写,不过一般情况下,按照编码要求还是需要写得

1
2
3
4
5
func funcTest2(a, b, c int) (a1, b1, c1 int) {
fmt.Println(a, b, c)
a1, b1, c1 = a+1, b+1, c+1
return a1, b1, c1
}

不定长度变参

java 中参数可变的例子

1
2
private static int sumUp(int... values) {    
}

在go中也可以

1
2
3
4
5
6
7
8
9
10
11
func funcTest3(a ...int) int {
max := math.MinInt64
for i, _ := range a {
fmt.Println(a[i])
if a[i] > max {
max = a[i]
}

}
return max
}

...[type]必须是最后一个参数,是一个slice

以下是错误的

1
func funcTest(a ...int, b string)

正确

1
func funTest(b string, a ...int)

...[type]可变参数可以看作是一个slice,但是与slice不同的是,不是引用类型,实际上得到是一个值拷贝。

如果参数是slice,是拷贝slice的内存地址,而不是值。

1
2
3
4
5
6
7
8
9
10
11
12
13
func funcTest5(s []int) {
for i,v := range s {
s[i] = v + 1
}
fmt.Println("func:", s)
}

func main() {
s := []int{1, 2, 3}
fmt.Println("before:", s)
funcTest5(s)
fmt.Println("after", s)
}

输出

1
2
3
before: [1 2 3]
func: [2 3 4]
after [2 3 4]

一般类型都是值拷贝,如果需要影响到传入的值,需要传入值的地址。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
- func funcTest5(s []int) {
+ func funcTest5(s []int, a *int) {
for i, v := range s {
s[i] = v + 1
}
+ *a = 2
- fmt.Println("func:", s)
+ fmt.Println("func:", s, a)
}

func main() {
s := []int{1, 2, 3}
- fmt.Println("before:", s)
- funcTest5(s)
- fmt.Println("after", s)
+ a := 1
+ fmt.Println("before:", s, a)
+ funcTest5(s, &a)
+ fmt.Println("after", s, a)
}

输出

1
2
3
before: [1 2 3] 1
func: [2 3 4] 0xc00001a098
after [2 3 4] 2

函数也可以作为一种类型使用

这个特性与JavaScript中差不多,定义一个变量等于函数,使用这个变量相当于使用这个函数。在Go中一切皆类型

1
2
3
4
5
6
7
8
func funcTest6() {
fmt.Println("hello everyone")
}

func main() {
a := funcTest6
a()
}

输出

1
hello everyone

匿名函数

1
2
3
4
5
6
func main() {
a := func() {
fmt.Println("这是一个匿名函数")
}
a()
}

没有命令函数名称。是一个代码块。

闭包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
func funcTest7(x int) func(int) int {
fmt.Println("func test 7", x, &x)
return func(y int) int {
x++
fmt.Println("func test 7 ", x, &x, y)
return x + y
}
}

func main() {
f := funcTest7(10)
fmt.Println(f(1))
fmt.Println(f(2))
}

输出

1
2
3
4
5
func test 7 10 0xc00001a098
func test 7 11 0xc00001a098 1
12
func test 7 12 0xc00001a098 2
14

3次输出x的地址是一样的。

defer

执行方式类似其他语言中的析构函数,在函数体执行结束后按照调用顺序的相反顺序逐个执行(先进后出,栈堆)。即使函数发生严重错误也会执行,支持匿名函数的调用。通常用于资源清理,文件关闭,解锁以及记录时间等操作。通过与匿名函数配合可在return之后修改函数计算结果。如果函数体内某个变量作为defer时匿名函数的参数,则在定义defer时即已经获得了拷贝,否则引用某个变量的地址。。

Go 中没有异常机制,但是有 panic/recover模式来处理错误

panic可以在任何地方引发,但是recover只有在defer调用的函数中有效

相反顺序

1
2
3
4
5
func main() {
fmt.Println("a")
defer fmt.Println("b")
defer fmt.Println("c")
}

输出

1
2
3
a
c
b

按照调用顺序的相反顺序逐个执行

1
2
3
4
5
func main() {
for i := 0; i < 3; i++ {
defer fmt.Println(i)
}
}

输出

1
2
3
2
1
0

闭包

在这个匿名函数中,i被当作一个地址的引用,引用这个局部变量,当循环结束时,i = 3 。在main函数结束后,开始执行defer中的代码。

1
2
3
4
5
for i := 0; i < 3; i++ {
defer func() {
fmt.Println(i)
}()
}

输出

1
2
3
3
3
3

可以改成这样:

1
2
3
4
5
for i := 0; i < 3; i++ {
defer func(a int) {
fmt.Println(a)
}(i)
}

输出

1
2
3
2
1
0

panic

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
func funcTest8(){
fmt.Println("func test 8")
}

func funcTest9(){
panic("func test 9")
}

func funcTest10(){
fmt.Println("func test 10")
}

func main() {
funcTest8()
funcTest9()
funcTest10()
}

输出

1
2
3
4
5
6
func test 8
panic: func test 9

goroutine 1 [running]:
main.funcTest9(...)
... 省略

并没有继续往下执行到test10

recover()

1
2
3
4
5
6
7
8
func funcTest9() {
+ defer func() {
+ if err := recover(); err != nil {
+ fmt.Println("Recover in test 9")
+ }
+ }()
panic("func test 9")
}

需要在panic前面加入以上代码,输出

1
2
3
func test 8
Recover in test 9
func test 10

练习

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
func main() {
var fs = [4]func(){}

for i := 0; i < 4; i++ {
defer fmt.Println("defer i = ", i)
defer func() {
fmt.Println("defer_closure i = ", i)
}()
fs[i] = func() {
fmt.Println("closure i = ", i)
}
}

for _, f := range fs {
f()
}
}

输出

1
2
3
4
5
6
7
8
9
10
11
12
closure i =  4
closure i = 4
closure i = 4
closure i = 4
defer_closure i = 4
defer i = 3
defer_closure i = 4
defer i = 2
defer_closure i = 4
defer i = 1
defer_closure i = 4
defer i = 0

fs[i]() 中的i,因为使用的是外部的i,实际上调用外部 i的地址,当循环体结束时,i = 4 ,所以输出closure i = 4

defer 使用的匿名函数与上面一样,是闭包的思想。

- the End -
0%