函数作为参数,函数是第一类对象,可作为参数传递,只要被调用函数的返回值个数,返回值类型和返回值的顺序与调用函数所需要的实参是一致的,就可以吧这个被调用 的函数当做其他函数的参数。
func f1(a,b,c int)
func f2(a,b int)(int,int,int)
func f1(f2(a,b))函数f1需要三个参数,同时f2返回三个参数
package main
import "fmt"
func pipe(ff func() int) int {
return ff()
}
type FormatFunc func(s string, x, y int) string
func format(ff FormatFunc, s string, x, y int) string {
return ff(s, x, y)
}
func main() {
s1 := pipe(func() int { return 100 })
s2 := format(func(s string, x, y int) string {
return fmt.Sprintf(s, x, y)
}, "%d,%d", 10, 20)
fmt.Println(s1, s2)
}
有返回值的函数,必须有明确的终止语句。
函数作为类型
函数go语言中也是一种变量,前面多次通过type来定义它,它的类型就是所有拥有相同参数与相同返回值的一种函数类型
type typeName func(input1 inputType1,input2 inputType2[,...])(result resultType1[,...])
函数作为类型的最大好处在于可以吧这个类型的函数当做值来传递
package main
import (
"fmt"
)
func isOdd(v int) bool { //取奇数
if v%2 == 0 {
return false
}
return true
}
func isEven(v int) bool { //取偶数
if v%2 == 0 {
return true
}
return false
}
type boolFunc func(int) bool
func filter(slice []int, f boolFunc) []int {
var result []int
for _, value := range slice {
if f(value) {
result = append(result, value)
}
}
return result
}
func main() {
slice := []int{3, 1, 4, 5, 9, 2}
fmt.Println("slice=", slice)
odd := filter(slice, isOdd)
fmt.Println("odd:", odd)
even := filter(slice, isEven)
fmt.Println("even:", even)
}
在写一些通用的接口的时候,把函数当做值和类型会非常有用,上面的例子中的boolFunc就是一个函数类型,isOdd参数和返回值与boolFunc类型一样,通过这种模式可以实现各种各样的逻辑,使得程序变得非常灵活。
可变参数,函数有着不定数量的参数,在很多时候都会遇到,go语言函数支持可变参数,为了做到这一点,首先需要定义函数使其接收变参
func myfunc(arg ...int) { }
go语言中,函数的最后一个参数古国是type的形式,那这个函数就是一个变参函数,它可以处理边长的参数,而这个长度可以是0,注意的是在变参函数中,无论变参有多少个,他们的类型全部都一样,以上面的函数为例,函数体中变量arg是一个int类型的切片,所以这函数的参数是一个类似int类型的切片,改参数可以通过前面提到的for循环结构来迭代。
package main
import "fmt"
func main() {
age := ageMinOrMax("min", 1, 3, 2, 0)
fmt.Printf("年龄最小的是%d岁\n", age)
agrArr := []int{7, 9, 3, 5, 1}
age = ageMinOrMax("max", agrArr...)
fmt.Printf("年龄最大的是%d岁\n", age)
}
func ageMinOrMax(m string, a ...int) int {
if len(a) == 0 {
return 0
}
if m == "max" {
max := a[0]
for _, v := range a {
if v > max {
max = v
}
}
return max
} else if m == "min" {
min := a[0]
for _, v := range a {
if v < min {
min = v
}
}
return min
} else {
e := -1
return e
}
}
package main
import "fmt"
func main() {
ageArr := []int{7, 9, 3, 5, 1}
f1(ageArr...)
}
func f1(arr ...int) {
f2(arr...)
fmt.Println("")
f3(arr)
}
func f2(arr ...int) {
for _, char := range arr {
fmt.Printf("%d", char)
}
}
func f3(arr []int) {
for _, char := range arr {
fmt.Printf("%d", char)
}
}
package main
import "fmt"
func main() {
ageArr := []int{7, 9, 3, 5, 1}
f1(ageArr...)
}
func f1(arr ...int) {
f2(arr...)
fmt.Println("")
f3(arr...)
}
func f2(arr ...int) {
for _, char := range arr {
fmt.Printf("%d", char)
}
}
func f3(arr ...int) {
for _, char := range arr {
fmt.Printf("%d", char)
}
}
// func f3(arr []int) {
// for _, char := range arr {
// fmt.Printf("%d", char)
// }
// }
变长参数可以作为对应来兴的slice进行二次传递,从内部实现机制上来说,类型...int本质上是一个数组切片,[]int,也就是arr可以用for循环来获取每个元素的值的原因。
前后写需要保持一直,f3(arr) 对应的func f3(arr []int)后者 f3(arr ...)
函数f2和f3的最大区别在于调用方式
f2(1,3,7,13) 可变参数,随便写
f3([]int{1,3,7,13})需要加上[]int{}来构造一个数组切片示例
最后关于可变参数为多种类型的情况,之前的例子都是吧变参类型约束为一种类型,但如果希望传递任意类型,可以指定类型为interface{},也就是接口。
func Printf(format string,args...interface{}) {
}
常用的printf函数就是一个变参函数,而且不限制参数类型,用interface{}传递任意类型数据是go语言的习惯用法,interface{}是类型安全的,除了使用空接口解决这个问题,还可以使用结构体解决。