10. 函数
10. 函数

10. 函数

学到什么

  1. 如何调用函数?
  1. 如何构造函数?
  1. 函数如何返回多个值?
  1. 如何构造匿名函数?
  1. 如何传递函数?
  1. 内置函数有哪些?

介绍

函数是基本的代码块,它负责将一个复杂问题分解为不同的函数提供调用与复用。
编写函数时,无需关注顺序,因为它 Go 语言是编译型的。
在 Go 语言中总共分 3 中函数格式:
  • 基本格式:有命名的函数,直接调用完事。
  • 匿名函数:没有名字的函数。
  • 结构体携带的函数:也可以称之为方法,后续结构体再展开讲解。
 

基本格式

func Fun1(arg1 T, arg2 T) T { ... return r1 }
  • Fun1 为自定义的函数名称。
  • arg1arg2 为自定义参数名称,声明了两个参数,可以再增加。
  • 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 不用携带值呢?
因为相当于在返回时,初始化好了返回值,例如上面的格式中 n1n2 就是初始化的两个变量,在函数运算中,只要将返回结果存入 n1n2 中,不存就按照初始化返回,当然也可以 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(&param)

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 个内置函数,如下:
  1. make:为切片,map、通道类型分配内存并初始化对象。
  1. len:计算数组、切片、map、通道的长度。
  1. cap:计算数组、切片、通道的容量。
  1. delete:删除map中对应的键值对。
  1. append:将数据添加到切片的末尾。
  1. copy:将原切片的数据复制到新切片中。
  1. new:为切片、map、通道类型以外的类型分配内存并初始化对象,返回的类型为指针。
  1. complex:生成一个复数。
  1. real:获取复数的实部。
  1. imag:获取复数的虚部
  1. print:将信息打印到标准输出,没有换行。
  1. println:将信息打印到标准输出并换行。
  1. close:关闭通道。
  1. panic:触发程序异常。
  1. recover:捕捉 panic 的异常信息。
 

总结

本篇我对 Go 语言中的函数进行了系统的讲解,也列举了 15 个内置函数。对于内置函数的使用,有的在前面文章使用过,有的还没有,先做一个整体的了解,等到了用的时候再详查。