学到什么
- 如何调用函数?
- 如何构造函数?
- 函数如何返回多个值?
- 如何构造匿名函数?
- 如何传递函数?
- 内置函数有哪些?
介绍
函数是基本的代码块,它负责将一个复杂问题分解为不同的函数提供调用与复用。
编写函数时,无需关注顺序,因为 Go 语言是编译型的。
在 Go 语言中有三种函数类型:
- 基本格式:有命名的函数,直接调用完事。
- 匿名函数:没有名字的函数。
- 结构体携带的函数:也可以称之为方法,后续结构体再展开讲解。
基本格式
func Fun1(arg1 T, arg2 T) T {
...
return r1
}
Fun1
为自定义的函数名称。arg1
和arg2
为自定义参数名称,声明了两个参数,可以再增加。T
代表 Go 语言中的任意类型,使用时替换成 int、string、slice 等等类型。- 小括号后紧跟函数返回值类型。
return
为函数返回的关键字,携带要返回的值,函数内之后的逻辑将不会执行。- 函数体的第一个花括号必须紧跟在函数后。
举例:
// 计算两个数之和并且返回
func AddNum(n1 int, n2 int) int {
return n1 + n2
}
函数也可以没有返回值,这个时候就无需 return
关键字,例如: main()
入口函数、 init()
初始化函数。
当函数体内出现了 panic
函数,用于抛出异常,这时如果定义了返回类型, return
关键字就可以选择省略。
返回多个值
Go 语言函数中有个特点,可以多个值返回。在声明返回值类型时,可以不指定名称,也可以指定名称,啥意思呢,往下看。
1. 无名称
func Fun1(arg1 T, arg2 T) (T, T) {
...
return r1, r2
}
和“基本格式”的不同点:
- 当需要返回至少两个值时,返回类型需要用小括号包裹,以逗号分隔。
- 使用
return
携带多个返回值。
2. 有名称
func Fun1(arg1 T, arg2 T) (n1 T, n2 T) {
...
return
}
- 返回值类型指定了名称后,在
return
返回时,可以不带值,当然也可以都带上。 - 当有了名称,即使是 1 个返回类型,也需要用小括号包裹。
为什么有了名称 return
就不用携带值呢?
因为相当于在返回时,初始化好了返回值,例如上面的格式中 n1
和 n2
就是初始化的两个变量,在函数运算中,只要将返回结果存入 n1
和 n2
中,不存就按照初始化返回,当然也可以 return
携带值。
函数调用
构造好一个函数后,如何调用,格式如下:
r1, r2 := Fun1(param1, param2)
调用时传递了两个参数,返回时接受两个返回值。
如果接受多个值时,某个值我不想使用时,是不能搁置在那的,不然编译器会报错,需要使用下划线 "_" 替代,表示我不用。
r1, _ := Fun1(param1, param2)
匿名函数
匿名函数就是在构造函数时,函数没有名称,想调用时,需要把匿名函数赋值给一个变量,或者在构造时直接调用。
1. 赋值给变量
fun1 := func (arg1 T, arg2 T) T {
...
return r1
}
赋值后, fun1
就是一个函数类型的变量, 调用格式:fun1(param1, param2)
。
2. 构造时调用
func (arg1 T, arg2 T) T {
...
return r1
}(param1, param2)
在构造函数时,花括号后紧跟参数传递 (param1, param2)
,不需要赋值给一个变量,直接构造后马上调用。
传递函数
在 Go 语言中,函数是“一等公民”,它和 int 、string 等等,都是一个级别,可以作为参数进行传递。
举例:
// function/deliver.go
package main
// callback 是一个函数类型参数
func Calc(callback func(n1 int, n2 int) int) int {
x, y := 3, 4
return callback(x, y)
}
// 计算两数之积
func Mul(n1 int, n2 int) int {
return n1 * n2
}
func main() {
// 第一个:传递一个匿名函数
Calc(func(n1 int, n2 int) int {
return n1 + n2
})
// 第二个:传递一个定义好的函数
Calc(Mul)
}
分别演示了两种函数的传递方式,第一个匿名函数计算两数之和,第二个用定义好的函数计算两数之积。当然传递函数不止是通过参数,也可以是函数返回值、切片元素保存、map 值保存等等。
声明函数类型
声明函数类型,意思就是可以自定义一个函数类型,给这个函数取一个别名,像例如 int
一样很方便的去声明变量或者参数类型。
type CallbackFunc func(n1 int, n2 int) int
现在自定义了一个名为 CallbackFunc
的函数类型,下来看如何使用:
func Calc(callback CallbackFunc) int {
...
}
Calc
函数有一个函数参数,这个参数的类型名称为 CallbackFunc
。
函数参数
1. 参数类型省略
在声明函数参数时,有时候会遇到连续声明多个相同类型,这个时候,就可以只保留一个类型名称。
// 没精简的
func Fun1(arg1 string, arg2 int, arg3 int)
// 精简后
func Fun1(arg1 string, arg2, arg3 int)
精简后, arg2
参数后省略了 int,这样就和它后面的参数类型一致。
2. 值传递与引用传递
我们先定下参数称呼,函数调用时传递的参数称为实参,构造函数时的参数称为形参。
在 Go 语言中,切片(slice)、map、接口(interface)、通道(channel)这样的引用类型都是默认使用引用传递,在函数内修改形参是会改变实参的值。
对于切片,有种情况会打破引用传递这个规律,具体可以看看 《内置集合 - 切片》 这篇文章。
对于其它剩下的类型,默认都是值传递,函数接收到的形参只是副本,函数内对形参的更改是不会影响到实参的。
如果希望更改实参的值,可以传递指针,在实参前增加“&”符号,表示取实参的地址,例如: Fun1(¶m)
。
3. 变长参数
当构造函数时,函数的最后一个参数是 ...T
形式时,称为变长参数,它可以接受至少 0 个数据。
// 一个固定参数,一个变长参数
// nums 实际是一个切片
func Func1(str string, nums ...int) {
...
}
调用例子如下:
// 没传递变长参数
Func1("miao")
// 给变长参数传递不同数量的值
Func1("miao", 1)
Func1("miao", 1, 2)
当把一个切片类型传递给可变参数时,在切片后跟着 ...
三个点,传递给变长参数,表示将切片元素展开。
nums := []int{1, 2, 3}
Func1("miao", nums...)
内置函数
在 Go 语言中,有一些函数无需导入任何包就可以使用,下来对这些函数简要说明一下。
总共 15 个内置函数,如下:
- make:为切片,map、通道类型分配内存并初始化对象。
- len:计算数组、切片、map、通道的长度。
- cap:计算数组、切片、通道的容量。
- delete:删除 map 中对应的键值对。
- append:将数据添加到切片的末尾。
- copy:将原切片的数据复制到新切片中。
- new:除切片、map、通道类型以外的类型分配内存并初始化对象,返回的类型为指针。
- complex:生成一个复数。
- real:获取复数的实部。
- imag:获取复数的虚部
- print:将信息打印到标准输出,没有换行。
- println:将信息打印到标准输出并换行。
- close:关闭通道。
- panic:触发程序异常。
- recover:捕捉 panic 的异常信息。
总结
本篇我对 Go 语言中的函数进行了系统的讲解,也列举了 15 个内置函数。对于内置函数的使用,有的在前面文章使用过,有的还没有,先做一个整体的了解,等到了用的时候再详查。