作为一门现代语言,Go 的特色之一是函数为一级函数(First-class function),可以作为值来进行传递。
函数作为值
例如你定义一个取最大值的函数max
,你可以将此函数作为值传递给maximum
:
package main
import "fmt"
func max(a, b int) int {
if a > b {
return a
}
return b
}
func main() {
maximum := max
fmt.Println(max(10, 5)) // 10
fmt.Println(maximum(10, 5)) // 10
}
可以看到,被max
参考的函数,也被maximum
参考着,因而,现在透过max
或者maximum
,都可以调用函数。
因为 Go 类型推断能力的关系,上头的maximum
并不用定义类型,而可以直接参考max
函数的类型,那么,max
或者是maximum
的类型是什么呢?
package main
import "fmt"
import "reflect"
func max(a, b int) int {
if a > b {
return a
}
return b
}
func main() {
maximum := max
fmt.Println(reflect.TypeOf(max)) // func(int, int) int
fmt.Println(reflect.TypeOf(maximum)) // func(int, int) int
}
可以看到,函数的类型包括了func
、参数类型与返回值类型,但不用定义函数、参数与返回值的名称。
定义函数变量
你可以仅定义一个变量可用来参考特定类型的函数,例如:
package main
import "fmt"
func max(a, b int) int {
if a > b {
return a
}
return b
}
func main() {
var maximum func(int, int) int
fmt.Println(maximum) // nil
maximum = max
fmt.Println(maximum(10, 5)) // 10
}
若想先定义一个maximum
变量,可以在之后参考max
函数,可以使用类型func(int, int) int
来定义,通常,定义函数变量时,若想免于冗长的函数类型定义,可以使用type
来定义一个新的类型名称:
package main
import "fmt"
type BiFunc func(int, int) int // 定义了新类型
func max(a, b int) int {
if a > b {
return a
}
return b
}
func main() {
var maximum BiFunc
fmt.Println(maximum) // nil
maximum = max
fmt.Println(maximum(10, 5)) // 10
}
在上例中,BiFunc
是个新的定义类型(defined type),底层类型(underlying type)为func(int, int) int
,Go 会认定两者属于不同类型,因为新的类型会拥有新的名称,在 Go 1.9 前,这是避免冗长函数类型定义的唯一方式。
不过,就这边来说,实际上只是想要func(int, int) int
能有个简短一点的名称,从 Go 1.9 开始,可以为类型取别名,别名就只是同一类型的另一个名称,:
package main
import "fmt"
type BiFunc = func(int, int) int // 类类型名定义
func max(a, b int) int {
if a > b {
return a
}
return b
}
func main() {
var maximum BiFunc
fmt.Println(maximum) // nil
maximum = max
fmt.Println(maximum(10, 5)) // 10
}
在这边,BiFunc
只是func(int, int) int
的另一个名称,而不是新的类型。
函数变量既然是个变量,也就可以对它取指针,例如:
package main
import "fmt"
type BiFunc = func(int, int) int
func max(a, b int) int {
if a > b {
return a
}
return b
}
func main() {
var maximum BiFunc
fmt.Println(&maximum) // 0x1040a130
// fmt.Println(&max)
}
如上,你可以对maximum
取指针,得到变量地址,不过,你不能对定义的max
取指针,去除程序中最后一个注解的话,会发生 cannot take the address of max 的错误。
回调应用
因为函数可以当作值传递,因此,对于函数中流程几乎相同,只有少数操作不同的情况,就可以将操作不同的部份以回调(Callback)函数取代。例如,可以设计一个filter
函数,用来过滤出符合特定条件的值:
package main
import "fmt"
type Predicate = func(int) bool
func filter(origin []int, predicate Predicate) []int {
filtered := []int{}
for _, elem := range origin {
if predicate(elem) {
filtered = append(filtered, elem)
}
}
return filtered
}
func greaterThan7(n int) bool {
return n > 7
}
func lessThan5(n int) bool {
return n < 5
}
func main() {
data := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
fmt.Println(filter(data, greaterThan7))
fmt.Println(filter(data, lessThan5))
}
在这个例子中,filter
函数重用了for range
与if
等流程,只要传入过滤用的函数,就可以让filter
具有各种的过滤用途。
除了作为值传递之外,Go 的函数还可以是匿名函数,且具有闭包(Closure)的特性,这将在下一篇文件加以说明。