【函数定义】
func function_name([parameter1 type,parameter2 type])[return_value1 return_type1,return_value2 return_type2,...]{
//TODO
}
func : 函数有func开始声明
function_name : 函数名称,函数名和参数列表一起构成函数签名
parameter_list : 参数列表,参数就像是一个占位符,当函数被调用时,你可以将值船体给参数,这个值被称为实际参数。
参数列表指定的是参数类型、顺序、及参数个数。参数是可选的,也就是说函数也可以不包含参数。
return_types:返回类型,函数返回一列值。return_types 是该列值的数据类型。有些功能不需要返回值,这种情况下 return_types 不是必须的。
函数体:函数定义的代码集合
备注:除了main() init()函数外,其他的函数尊循以上格式
【函数类型】
Go中有三种类型:
- 普通的带有名字的函数
- 匿名函数或者lambda函数
- 方法(Method)(与结构体有关)
- 特殊的有main(),init()函数
【函数的书写格式及调用说明】
简单举例,如果没有参数、参数类型、返回值,返回类型等,可以省略
func functionName(){ // 大括号必须和函数声明同行
// TODO
}
使用方式:调用
package.FunctionName(arg1,arg2...) // 包中的函数若想被外部调用,函数必须首字母大写
使用方式:声明
type binOp func(int,int) int
调用说明:
- 包中的函数若想被外部调用,函数必须首字母大写
- Go中函数重载是不被允许的,会导致一个编译错误
- 函数也可以作为一个函数类型来声明
- 函数可以赋值给变量
- 一个变量不能被赋值不同的函数,就像一个变量不能被声明多次一样的道理
- Go没有泛型的概念
- 函数不能在其他函数里嵌套声明,也就是说不能再函数体内声明(创造)函数,一般在函数体内使用匿名函数来完成这个
- 函数值之间可以互相比较,如果它们引用的是相同的函数或者都是nil的话,则认为它们是相同的
【函数的参数】
按值传递:就是形参
引用传递:按变量的内存地址传递,即取变量的内存地址,可以说就是指针,&变量,例如&a
在函数调用中:切片(slice)字典(map)接口(interface)通道(channel)这样的引用类型都是默认使用引用传递(没有显式的指出指针)
备注:在函数返回值多的时候,可以使用切片(返回值具有相同类型)或者结构体(返回值具有不同类型)
【函数的返回值】
命名返回值:作为结果形参(result parameters)被初始化为响应类型的零值,当需要返回的时候,我们只需要一条简单的return。命名返回值 需要用()括起来,即使只有一个 func funcName()(ret int){}
非命名返回值:作为结果形参,需要返回的时候,return 后面需要跟返回的内容,多个非命名返回值 需要用括号()括起来,一个的时候不需要括起来,func funcName()(int,int)
1 // 举例 2 package main 3 4 import "fmt" 5 6 var num int = 10 7 var numx2, numx3 int 8 9 func main(){ 10 numx2, numx3 = getX2AndX3(num) 11 PrintValues() 12 numx2, numx3 = getX2AndX3_2(num) 13 PrintValues() 14 } 15 16 func PrintValues(){ 17 fmt.Printf("num = %d, 2x num = %d, 3x num = %d ", num, numx2, numx3) 18 } 19 20 func getX2AndX3(input int)(int, int){ // 定义了函数返回的参数个数及参数类型 21 return 2 * intput, 3 * input 22 } 23 // 使用了命名返回值 24 func getX2AndX3_2(input int)(x2 int, x3 int){ 25 x2 = 2 * input 26 x3 = 3 * input 27 // return x2, x3 28 return // 因为使用了命名返回值,所以return的时候,就按照命名返回值的变量返回。 29 }
备注:尽量使用命名返回值,使代码更加清晰,提高可读性
【传递变长参数】
如果函数的最后一个参数是采用...type(type表示数据类型)的形式,那么这个函数就可以处理一个变长的参数,这个长度可以为0,这样的函数叫变长函数
// 结构
func myFunc(a,b,arg ...int){}
// 事例及调用
func Greeting(prefix string, who ...string)
Greeting("Hello:","Joe","Anna","Robot") // 变量who的值为[]string{"Joe","Anna","Robot"}
在Greeting函数中,变量who的值为[]string{"Joe","Anna","Robot"}
如果参数被存储在一个数组中arr,则可以使用arr...的形式传递参数调用 函数
1 package main 2 3 import "fmt" 4 5 func main(){ 6 x := min(1,3,2,0) 7 fmt.Printf("The minimum is: %d ", x) 8 arr := []int{7,9,3,5,1} 9 x = min(arr...) 10 fmt.Printf("The minimum in array arr is:%d",x) 11 } 12 13 func min(a ...int)int{ 14 if len(a) == 0 { 15 return 0 16 } 17 min := a[0] 18 for _, v := range a{ 19 if v < min { 20 min = v 21 } 22 } 23 return min 24 }
如果变长的参数类型不相同怎么办?
使用结构体
在这个结构体中存储任意类型的参数
type Options struct{
par1 type1,
par2 type2,
par3 type3,
}
// 使用
FuncName(a,b,Option{par1:val1,par2:val2})
使用空接口:该方案不仅可以用于长度未知的参数,可以用于不确定类型的参数,一般使用for-range循环,switch结构对每个参数的类型进行判断
func typecheck(...,...,values ...interface{}){
for _, value := range values{
switch v := value.(type){
case int:...
case float:...
case string:...
case bool:...
default:...
}
}
}
【defer和追踪】
关键字defer允许我们推迟到函数返回之前一刻,才执行被defer修饰的某个语句或者函数。类似其他语言中的finally
1 // 示例 2 package main 3 4 import "fmt" 5 6 func main(){ 7 funciton1() 8 } 9 10 func function1(){ 11 fmt.Printf("In function1 at the top ") 12 defer function2() 13 fmt.Printf("In function1 at the bottom ") 14 } 15 16 func function2(){ 17 fmt.Printf("function2:Defered until the end of the calling function!") 18 } 19 20 // 输出 21 In function1 at the top 22 In function1 at the bottom 23 function2:Defered until the end of the calling function
使用场景:
关闭文件流,defer file.Close()
解锁一个加锁的资源 defer mu.Unclock()
打印最终报告 defer printFooter()
关闭数据库链接 defer disconnectFromDB()
【递归函数】
演示斐波那契和阶乘
1 package main 2 3 import "fmt" 4 5 func main(){ 6 result := 0 7 for i := 0; i<=10;i++{ 8 result = fibonacci(i) 9 fmt.Printf(result) 10 } 11 } 12 13 func fibonacci(n int)(ret int){ 14 if n < 2{ 15 ret = 1 16 }else{ 17 ret = fibonacci(n-1)+fibonacci(n-2) 18 } 19 return 20 }
1 package main 2 3 import "fmt" 4 5 func main(){ 6 var n int = 30 7 result := factorial(n) 8 fmt.Printf("n的阶乘是:%d",result) 9 10 } 11 12 func factorial(n int)(res int){ 13 if n > 0{ 14 res = n * factorial(n-1) 15 }else{ 16 res = 1 17 } 18 return 19 }
【闭包的应用:将函数作为返回值】
代码来展示Go中如何将函数作为返回值
1 package main 2 3 import "fmt" 4 5 func main(){ 6 func_param1 := add1(2) 7 fmt.Println(func_param1(3)) 8 func_param2 := add2() 9 fmt.Println(func_param2(3)) 10 } 11 12 func add1(n int) func(a int) int{ 13 return func(a int) int{ 14 return a + n 15 } 16 } 17 18 func add2() func(a int) int{ 19 return func(a int) int{ 20 return a + 2 21 } 22 } 23 24 // 输出结果 25 5 26 5
作为闭包的应用
1 package main 2 3 import "fmt" 4 5 func main(){ 6 var f = add() 7 fmt.Println(f(10)) 8 fmt.Println(f(20)) 9 fmt.Println(f(30)) 10 } 11 12 func add() func(a int) int{ 13 var x int 14 return func(a int) int{ 15 x += a 16 return x 17 } 18 } 19 // 输出结果 20 10 21 30 22 60
这个程序说明变量x的值在三次调用函数类型变量f()时,都被操作了累积了
下面使用闭包来实现斐波那契数列的程序,放弃递归的方法
1 package main 2 3 import "fmt" 4 5 func main(){ 6 7 var i int 8 f := fibonacci() 9 for i = 0; i < 10; i++{ 10 fmt.Println(f(i)) 11 } 12 } 13 14 func fibonacci() func(a int) int{ 15 var res int 16 var last int 17 var last_last int 18 return func(a int) int{ 19 if a < 2{ 20 res = a 21 }else{ 22 res = last_last + last 23 } 24 last_last = last 25 last = res 26 return res 27 } 28 }
【学习参考处:https://github.com/Unknwon/the-way-to-go_ZH_CN/blob/master/eBook/directory.md】