1.1 Go语言函数概况
每一个程序都包含很多的函数:函数是基本的代码块。
Go 里面有三种类型的函数:
- 普通的带有名字的函数
- 匿名函数或者lambda函数
- 方法
除了main()、init()函数外,其它所有类型的函数都可以有参数与返回值。函数参数、返回值以及它们的类型被统称为函数签名。
为了对Go语言中的函数有一个基本的认识,我们来看如下的例程:
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
通过观察以上代码及输出,我们不难对Go语言中的函数产生一个基本的认识。由于Go语言是编译型语言,因此代码中函数的位置不影响其编译时从main()函数开始编译,但为了简洁易懂,我们最好将main()函数写在第一行。
当函数执行到代码块最后一行(}之前)或者return语句的时候会退出,其中return语句可以带有零个或多个参数;这些参数将作为返回值供调用者使用。简单的return语句也可以用来结束 for 死循环,或者结束一个协程(goroutine)。
函数可以将其他函数调用作为它的参数,只要这个被调用函数的返回值个数、返回值类型和返回值的顺序与调用函数所需求的实参是一致的,例如:
- 假设 f1 需要 3 个参数f1(a, b, c int),同时 f2 返回 3 个参数f2(a, b int) (int, int,
int),就可以这样调用 f1:f1(f2(a, b))。
函数重载(function overloading)指的是可以编写多个同名函数,只要它们拥有不同的形参与/或者不同的返回值。在 Go 里面函数重载是不被允许的,Go 语言不支持这项特性的主要原因是函数重载需要进行多余的类型匹配影响性能;没有重载意味着只是一个简单的函数调度。所以你需要给不同的函数使用不同的名字,我们通常会根据函数的特征对函数进行命名。
Go 没有泛型(generic)的概念,也就是说它不支持那种支持多种类型的函数。不过在大部分情况下可以通过接口(interface),特别是空接口与类型选择与/或者通过使用反射来实现相似的功能。
函数是一等值(first-class value):它们可以赋值给变量,就像add := binOp一样。
1.2 Go语言函数的参数与返回值
相比与 C、C++、Java 和 C#,多值返回是 Go 的一大特性。函数能够接收参数供自己使用,也可以返回零个或多个值(我们通常把返回多个值称为返回一组值)。
我们通过观察下列例程来感受Go语言中的参数传递。
package main
import"fmt"
func main() {
fmt.Printf("Multiply 2 * 5 * 6 = %d\n", MultiPly3Nums(2, 5, 6)
}
funcMultiPly3Nums(a int, b int, c int)int {
return productreturn a * b * c
}
如果一个函数需要返回四到五个值,我们可以传递一个切片给函数(如果返回值具有相同类型)或者是传递一个结构体(如果返回值具有不同的类型)。因为传递一个指针允许直接修改变量的值,消耗也更少。
package main
import "fmt"
var num int = 10
var x1,x2 int
func main() {
x1,x2 = getX1AndX2(num)
printValues()
x1,x2 = getX1AndX2_2(num)
printValues()
}
func printValues() {
fmt.Printf("num = %d, x1 = %d, x2 = %d", num, x1,x2)
}
func getX1AndX2 (input int) (int,int) {
return 2 * input,3 * input
}
func getX1AndX2_2 (input int) (x1 int, x2 int) {
x1 = 2 * input
x2 = 3 * input
return
}
如上,multiple_return.go 里的函数带有一个int参数,返回两个int值;其中一个函数的返回值在函数调用时就已经被赋予了一个初始零值。getX2AndX3与getX2AndX3_2两个函数演示了如何使用非命名返回值与命名返回值的特性。
当需要返回多个非命名返回值时,需要使用()把它们括起来,比如(int, int)。命名返回值作为结果形参(result parameters)被初始化为相应类型的零值,当需要返回的时候,我们只需要一条简单的不带参数的return语句。
需要注意的是,即使只有一个命名返回值,也需要使用()括起来。
1.3 空白符
空白符用来匹配一些不需要的值,然后丢弃掉,下面的 blank_identifier.go 就是很好的例子。
package main
import"fmt"
func main() {
var i1 int
var f1 float32
i1, _, f1 = ThreeValues()
fmt.Printf("The int: %d, the float: %f \n", i1, f1)
}
func ThreeValues()(int, int, float32) {
return 5, 6, 7.5
}
如上,ThreeValues是拥有三个返回值的不需要任何参数的函数,在下面的例子中,我们将第一个与第三个返回值赋给了i1与f1。第二个返回值赋给了空白符_,然后自动丢弃掉。