Go 语言函数
函数是 Go 里面的基本代码块,Go 函数的功能非常强大,以至于被认为拥有函数式编程语言的多种特性。由于函数内容较多,这里将会分为多个章节去讲解:
函数简介
每一个Go 程序都包含很多的函数:函数是程序基本的代码块。
Go是编译型语言,所以函数编写的顺序是无关紧要的。不过,鉴于可读性的需求,最好把 main()
函数写在文件的前面,其他函数按照一定逻辑顺序进行编写(例如函数被调用的顺序)。
编写多个函数的主要目的是将一个需要很多行代码的复杂问题分解为一系列简单的任务来解决(与面向对象中的封装类似)。而且,同一个函数可以被调用多次,有助于代码重(chong)用。
当函数执行到代码块最后一行(}
之前)或者 return
语句的时候会退出,其中 return
语句可以带有零个或多个参数,这些参数将作为 返回值 供调用者使用。简单的 return
语句也可以用来结束 for
死循环,或者结束一个协程(goroutine
)。
Go 里面有三种类型的函数:
- 普通的带有名字的函数
- 匿名函数或者lambda函数
- 方法(Methods)
除了main()、init()
函数外,其它所有类型的函数都可以有参数与返回值。函数 参数、返回值 以及它们的类型被统称为函数签名。
Go 程序中,函数的左大括号({
)不允许单独占用一行。比如,这样是不正确的 Go 代码:
func g()
{
}
它必须是这样的:
func g() {
}
函数被调用的基本格式如下:
pack1.Function(arg1, arg2, …, argn)
Function
是 pack1
包里面的一个函数,括号里的是被调用函数的实参(argument
),这些值被传递给被调用函数的形参(parameter
)。函数被调用的时候,这些实参将被复制,然后传递给被调用函数。函数能多次调用其他函数,这些被调用函数按调用顺序执行,理论上,函数调用其他函数的次数是无穷的(直到函数调用栈被耗尽)。
一个简单的函数调用其他函数的例子:
package main
func main() {
println("In main before calling greeting")
greeting()
println("In main after calling greeting")
}
func greeting() {
println("In greeting: Hi!!!!!")
}
代码输出:
In main before calling greeting
In greeting: Hi!!!!!
In main after calling greeting
函数可以将其他函数调用作为它的参数,只要这个被调用函数的返回值个数、返回值类型和返回值的顺序
与调用函数所需求的实参是一致的,例如:
假设 f1 需要 3 个参数 f1(a, b, c int)
同时 f2 返回 3 个参数 f2(a, b int) (int, int, int)
就可以这样调用 f1
f1(f2(a, b))。
函数重载(function overloading
)指的是可以编写多个同名函数,只要它们拥有不同的形参与/或者不同的返回值
,在 Go 里面函数重载是不被允许的。这将导致一个编译错误:
funcName redeclared in this book, previous declaration at lineno
Go 语言不支持这项特性的主要原因是函数重载需要进行多余的类型匹配影响性能,没有重载意味着只是一个简单的函数调度。所以你需要给不同的函数使用不同的名字,我们通常会根据函数的特征对函数进行命名。
如果需要申明一个在外部定义的函数,你只需要给出函数名与函数签名,不需要给出函数体:
// implemented externally
func flushICache(begin, end uintptr)
函数也可以以申明的方式被使用,作为一个函数类型,就像:
type binOp func(int, int) int
在这里,不需要函数体 {}。
函数可以赋值给变量,就像 add := binOp
一样。
这个变量知道自己指向的函数的签名,所以给它赋一个具有不同签名的函数值会编译失败。
函数值(functions value
)之间可以相互比较,如果它们引用的是相同的函数或者都是 nil
的话,则认为它们是相同的函数。函数不能在其它函数里面声明(不能嵌套),不过我们可以通过使用 匿名函数 来破除这个限制。
目前 Go 没有泛型(generic
)的概念,也就是说它不支持多种类型的函数。