Go的第七课——函数

函数

函数简介

函数是 golang 中的一级公民,我们把所有的功能单元都定义在函数中,可以重复使用。函数包含函数的名称、参数列表和返回值类型,这些构成了函数的签名(signature),有点像 C++ 的名称粉碎机制。

函数的特性

  1. go语言中有3种函数:普通函数、匿名函数(没有名称的函数)、方法(定义在struct上的函数)。receiver
  2. go语言中不允许函数重载(overload),也就是说不允许函数同名。
  3. go语言中的函数不能嵌套函数,但可以嵌套匿名函数。
  4. 函数是一个值,可以将函数赋值给变量,使得这个变量也成为函数。
  5. 函数可以作为参数传递给另一个函数。I函数的返回值可以是一个函数。
  6. 函数调用的时候,如果有参数传递给函数,则先拷贝参数的副本,再将副本传递给函数。
  7. 函数参数可以没有名称。

函数定义

根据如下格式定义

1
2
3
func function_name([parameter_list]) [return_list]{
//main logic
}

最简单的一个加法函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package main

import "fmt"

func sum(a int, b int) int {
return a + b
}

func main() {
r := sum(1, 2)
fmt.Println(r)
}
/*
3
*/

返回值

一个函数可以有多个返回值,在返回的时候我们把返回的值用逗号隔开,接收的时候变量也使用逗号隔开。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package main

import "fmt"

func sum(a int, b int) (int, int) {
return a + b, a - b
}

func main() {
l, r := sum(2, 1)
fmt.Println(l, r)
}
/*
3 1
*/

参数

参数这里有点小意思,它可以支持可变长度的参数,如果我们有需要可以用这种写法:

1
func function_name(arg1 type1,arg2 type2,ARGS...type)(return_list)

这个意思就是至少要传两个参数,其余参数会进入 ARGS 切片中,我们在里面要使用需要对 ARGS 进行操作,并且这个可变参数的写法必须要放在最后一个参数。

前面还说过,在调用的时候我们也可以使用 ... 这个意思就是把一个切片的值分别当作函数的参数传递进去。

比如切片 a={1,2,3,4,5} 那么我调用 add(a...) 意思就是调用 add(1,2,3,4,5),这个写法还是挺方便的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package main

import "fmt"

func sum(ARGS ...int) int {
val := 0
for _, v := range ARGS {
val += v
}
return val
}

func main() {
var a = []int{1, 2, 3, 4, 5, 6, 7, 8}
val := sum(a...)
fmt.Println(val)
}
/*
36
*/

参数一般是传形参,在调用的时候会拷贝一份数据进去,但是如果传的是指针,那么修改指针所指向的值有可能导致原值发生变化。需要注意的是,数组,切片,channelmap 这些数据类型传递的时候本来就是以指针的形式传递,修改它们内部的值有可能导致原值发生变化。

不过看 B 站上有一条弹幕说是

go 的数组不是指针类型,修改它不会导致原值发生变化。

为此我专门测试了以下,结果果真如此。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package main

import "fmt"

func sum(a [5]int) {
a[0] = 1000
}

func main() {
var a [5]int
sum(a)
fmt.Println(a[0])
}
/*
0
*/

而如果是切片的话就会改变原值,不过我想数组传参需要指定长度的参数也太苛刻了,一般都是用切片吧,所以叫这个真也没必要。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package main

import "fmt"

func sum(a []int) {
a[0] = 1000
}

func main() {
var a = []int{0, 0}
sum(a)
fmt.Println(a[0])
}
/*
1000
*/

函数类型与函数变量

可以使用 type 关键字来定义一个函数类型,语法格式如下:

1
type identifier func(arg_list)(return_list)

用 C++ 的话说是定义了一个 identifier 类型,是一个函数指针类型。

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
package main

import "fmt"

type f func(int) int

func fun(a int) int {
return a
}

func fun1(b int) int {
return b + 1
}

func main() {
var ptr f
ptr = fun
s := ptr(1)
fmt.Println(s)
ptr = fun1
s = ptr(1)
fmt.Println(s)
}
/*
1
2
*/

在这里我们声明的 ptr 变量就属于 f 类的函数变量。

高阶函数

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
package main

import "fmt"

type pfunc func(string)

func say_hello(name string) {
fmt.Printf("Hello,%s\n", name)
}
func say_goodby(name string) {
fmt.Printf("Good bye,%s\n", name)
}
func call_function(f func(string), arg string) {
f(arg)

}
func main() {
var ptr pfunc
ptr = say_hello
call_function(ptr, "xia0ji233")
ptr = say_goodby
call_function(ptr, "xia0ji233")
}
/*
Hello,xia0ji233
Good bye,xia0ji233
*/

函数作为返回值

就是返回一个函数指针罢了。

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
package main

import "fmt"

type pfunc func(int, int) int

func add(a int, b int) int {
return a + b
}

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

func mul(a int, b int) int {
return a * b
}

func div(a int, b int) int {
return a / b
}

func cal(op string) pfunc {
switch op {
case "+":
return add
case "-":
return sub
case "*":
return mul
case "/":
return div
default:
return nil
}
}

func main() {
var v int
v = cal("+")(8, 2)
fmt.Println(v)
v = cal("-")(8, 2)
fmt.Println(v)
v = cal("*")(8, 2)
fmt.Println(v)
v = cal("/")(8, 2)
fmt.Println(v)
}
/*
10
6
16
4
*/

匿名函数

go语言函数不能嵌套,但是在函数内部可以定义匿名函数,实现一下简单功能调用。

所谓匿名函数就是,没有名称的函数。

语法格式如下:

1
2
3
func (arglist)(return_list){
//main_logic
}

匿名函数的特点就是没有函数名,不能通过函数名调用,只依赖于创建时候返回的一个函数指针,也就是赋值给函数变量的值,一但值丢失那么就无法通过任何形式去调用了,作用域不限,只要指针给出去了可以在任何地方调用。

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
package main

import "fmt"

type pfunc func(int, int) int

func call_function(f func(int, int) int, a int, b int) int {
return f(a, b)
}

func main() {
var v int
max := func(a int, b int) int {//匿名函数
if a > b {
return a
}
return b
}
v = max(1, 2)
fmt.Println(v)
v = call_function(max, 8, 9)
fmt.Println(v)
}
/*
2
9
*/