一、函数的定义
Go 语言中 函数
也是一种数据类型,有内存地址,且未被初始化的函数的值为 nil
。
函数由函数名、参数和返回值组成,使用 func
关键字定义,{}
中定义函数体。
func funcName(arg1 argType, arg2 argType) (returnVar1 varType, returnVar2 varType) { //funcBody } 复制代码
Go 中函数的参数可以是不定长的,并且可以有多个返回值。
返回值的定义
函数的返回值第一种定义方式
Go 函数只有一个返回值是可以将括号省略,有多个返回值时需要使用括号报告,并且括号中的数据类型与 函数体中 return
关键字返回的变量一一对应。参数类型如果相同,可以只在最后一个参数后面跟上参数类型,其他可以省略。
func main(){ sum := zuluAdd(1,4) fmt.Println(sum) sum1, isTrue := zuluAddwithTwoReturn(2, 6) fmt.Println(sum1, isTrue) } func zuluAdd(x, y int) int { return x + y } func zuluAddwithTwoReturn(x, y int) (int, bool) { return x + y, true } 复制代码
执行上述代码,输出结果如下:
5 8 true 复制代码
函数的返回值第二种定义方式
返回值第二种定义方式,将返回值在函数定义的时候初始化,而不是在函数体内初始化(函数体中不要再使用 :=
或者 var 定义返回变量),并且可以省略 return
关键字后的返回值变量名。
func main(){ sum := yankeeAdd(1,4) fmt.Println(sum) sum1, isTrue := yankeeAddwithTwoReturn(2, 6) fmt.Println(sum1, isTrue) } func yankeeAdd(x, y int) (sum int) { sum = x + y return } func yankeeAddwithTwoReturn(x, y int) (sum int, isTrue bool) { sum = x + y isTrue = true return } 复制代码
执行上述代码,输出结果如下:
5 8 true 复制代码
第二种返回值的定义方式是最常用的
通过省略号设置不定长参数
Go 中的函数可以设置不定长参数,不定长参数有两种表示方式,第一种时使用 ...
表示
func main(){ argsSum1, argsLen1 := xrayAddwithArgsByDots(2, 6) fmt.Println(argsSum1, argsLen1) argsSum2, argsLen2 := xrayAddwithArgsByDots(2, 6, 7, 9) fmt.Println(argsSum2, argsLen2) } func xrayAddwithArgsByDots(args ...int) (argsSum, argsLen int) { for _, val := range args { argsSum += val } argsLen = len(args) fmt.Printf("%T\n", args) return } 复制代码
执行上述代码,输出结果如下:
[]int 8 2 []int 24 4 复制代码
根据输出结果可以确定,不定长参数的类型是切片类型。
所以第二种表示不定长参数的表示方式就是可以直接放一个切片作为参数,因为切片时可以自动扩容的。
func main(){ argsSum1, argsLen1 := xrayAddwithArgsBySlice([]int{2, 6}) fmt.Println(argsSum1, argsLen1) argsSum2, argsLen2 := xrayAddwithArgsBySlice([]int{2, 6, 7, 9}) fmt.Println(argsSum2, argsLen2) } func xrayAddwithArgsBySlice(args []int) (argsSum, argsLen int) { for _, val := range args { argsSum += val } argsLen = len(args) fmt.Printf("%T\n", args) return } 复制代码
这两种不定长参数的定义有什么区别?
首先可以确定的是切片是引用传递,那么 ...
传递是值传递还是引用传递?可以通过代码来进行验证。
func main(){ args := []int{1, 3, 5} xrayAddwithArgsByDots(args...) fmt.Println(args) fmt.Printf("%v\n", &args[0]) fmt.Println("传递单独赋值的变量") x := args[0] y := args[1] xrayAddwithArgsByDots(x, y) fmt.Println(args) fmt.Printf("%v\n", &args[0]) } func xrayAddwithArgsByDots(args ...int) { fmt.Printf("%v\n", &args[0]) fmt.Println(args) args[0] = 100 fmt.Println(args) } 复制代码
执行上述代码,输出结果如下:
0xc00001a078 [1 3 5] [100 3 5] [100 3 5] 0xc00001a078 传递单独赋值的变量 0xc00001c0e0 [100 3] [100 3] [100 3 5] 0xc00001a078 复制代码
...
定义不定长参数传入一个切片进行拆解时,是引用传递,但是如果将切片拆解并单独赋值给变量,再讲赋值的变量传入不定长参数的函数,这种是值传递(本质上传递的还是基本数据类型)。
...
在 append 添加一个slice 的时候也会用到。
匿名函数
当函数只在某一处会使用,不会在其他地方使用时,为了避免函数名泛滥,可以定义匿名函数。匿名函数调用时参数在 {}
后面传递,使用 ()
包裹。
func main(){ res := func(x, y int) int { return x + y }(1, 2) fmt.Println(res) // 匿名函数的调用 fmt.Println(func(x, y int) int { return x * y }(2, 3)) } 复制代码
执行上述函数,输出代码如下:
3 6 复制代码
二、函数的 ”一等公民(FirstClass)“ 特性
Go 中函数的 “一等公民” 特性是指函数可以存储在变量中,可以作为参数传递给函数,可以在函数中创建并作为返回值从函数返回。
函数存储在变量中
将函数赋值给一个变量,并查看变量的类型。
func main(){ // 第一种定义方式,函数类型为 func(int, int) int, 类似其他如 float63, int64等 var whiskeyFunc1 func(int, int) int = whiskeyAdd fmt.Printf("%T\n", whiskeyFunc1) // 直接使用 := 简写方式 whiskeyFunc := whiskeyAdd fmt.Printf("%T\n", whiskeyFunc) res := whiskeyFunc(1, 2) fmt.Println(res) } func whiskeyAdd(x, y int) (sum int) { sum = x + y return } 复制代码
执行上述代码,输出结果如下:
func(int, int) int func(int, int) int 3 复制代码
要注意区分函数名和保存函数的变量名。
当然匿名函数也可以赋值给一个变量。
func main(){ funcVal := func(x, y int) int { return x + y } fmt.Println(funcVal(1, 2)) } 复制代码
函数作为参数传递
使用 type
关键字自定义函数类型
首先自己定义出来一个类型,这个类型是函数类型,就跟 int 类型 map 类型是一样的
func main(){ // 定义一个函数类型(自定义的函数类型)的变量,将victoryImpl函数赋值给函数变量 var myVictor victor = victorImpl res := myVictor(7, 2) fmt.Println(res) // 也可以直接将匿名函数赋值给自定义的函数类型的变量 var myVictor2 victor = func(x, y int) int { return x - y } fmt.Println(myVictor2(3, 2)) } // 定义一个函数类型 type victor func (x, y int) int // 定义一个函数实现 func victorImpl(x, y int) int { return x % y } 复制代码
执行上述代码,输出结果如下:
1 1 复制代码
将函数作为另一个函数的参数
以一个过滤列表中的元素为例
func main(){ balances := []int{10, 15, 13, 20, 22} fmt.Println(filter(balances)) } func filter(balances []int) (greaterThanTen []int){ greaterThanTen = make([]int, 0) for _, balance := range balances { if balance >= 10 { greaterThanTen = append(greaterThanTen, balance) } } return } 复制代码
执行上述代码,输出结果如下:
[10 15 13 20 22] 复制代码
如果筛选条件变化了,那么可以将筛选条件定义为一个函数 greater,该函数返回布尔值,在 filter 函数中传入 greater 函数作为参数
func main(){ balances := []int{10, 15, 13, 30, 22} // greater 函数赋值给一个变量 greateFunuc := greater fmt.Println(filter(balances, greateFunuc)) } func filter(balances []int, funcParam func(int, int) bool) (greaterThanTen []int){ greaterThanTen = make([]int, 0) for _, balance := range balances { if greater(balance, 20) { greaterThanTen = append(greaterThanTen, balance) } } return } func greater(item, val int) (isGreater bool) { if item > val { return true } return false } 复制代码
执行上述代码,输出结果如下:
[30 22] 复制代码
函数作为参数在链式处理类函数中应用也很广泛。