函数
Go语言函数式组织好的,可重复使用的,用于执行指定任务的代码块。Go语言中定义函数使用func关键字,具体格式如下:
func 函数名(参数)(返回值) { 函数体}
函数可以没有参数或接受多个参数
例如:
package basicimport "fmt"func Add(a, b int) int { var ret = a + b fmt.Printf("The sum of %d and %d is: %d\n", a, b, ret) return ret}func Callback(y int, f func(int, int) int) { f(y, 5) // this becomes Add(1, 5)}func DoCallback(y int) { Callback(y, Add) // this becomes Add(1, 5)}
在本例中,add接受两个int类型的参数
注意:go和java不同,函数的返回类型,在函数名之后
当连续两个或者多个函数的已命名形参类型相同时,除最后一个类型以外,其他都可以省略。
在本例中
func Add(a int, b int) int {....}
其中的两个参数
a int, b int
被缩写为
a, b int
函数的定义:
函数构成代码执行的逻辑结构
除了main函数,返回值以及它们的类型被统称为函数签名
func function_name( [parameter list] ) [return_types] { body(函数体)}
函数定义:
①、func:函数由func开始声明
②、function_name:函数名称,函数名和参数列表一起构成了函数签名
③、parameter list:参数列表,参数就像一个占位符,当函数被调用时,你可以将值传递给参数,这个值被称为实际参数,参数列表指定的是参数类型,顺序,及参数个数,参数是可选的,也就是说函数也可以不包含参数。
④、return_types:返回类型,函数返回列值,return_types是该列值的数据类型,有些功能不需要返回值,这种情况下return_types不是必须的。
⑤、body(函数体):函数定义的代码集合
形式参数列表描述了函数的参数名和参数类型
这些参数作为局部变量,其值由参数调用者提供,返回值列表描述了函数返回值的变量名以及类型
如果函数返回一个无名变量或者没有返回值,返回值列表的括号是可以省略的。
如果一个函数声明不包括返回值列表,那么函数体执行完毕,不会返回任何值。
// 这个函数计算两个int型输入数据的和,并返回int型的和func Add(a int, b int) int { // Go需要使用return语句显式地返回值 return a + b}fmt.Println(Add(3,4)) //函数的调用
在Add函数中a和b是形参名 3和4是调用时的传入参数,函数返回了一个int类型的值
返回值也可以像形式参数一样被命名。
在这种情况下,每个返回值被声明成一个局部变量,并根据该返回值的类型,将其初始化为0.
如果一个函数在声明时,包含返回值列表,该函数必须以return语句结尾,除非函数明显无法运行到结尾处,例如函数在结尾处调用了panic异常或者函数中存在无限循环。
函数如何调用:
那么函数在使用的时候如何被调用呢?
package.Function(arg1, arg2, …, argn)
1、Function是package包里面的一个函数,括号里的是被调用函数的实参:这些值被传递给被调用函数的形参。
2、函数被调用的时候,这些实参将被复制然后传递给被调用函数。
3、函数一般是在其他函数里面被调用的,这个其它函数被称为调用函数。
4、函数能多次调用其他函数,这些被调用函数按顺序执行,理论上,函数调用其他函数的次数是无序的直到函数调用栈被耗尽。
函数的变长参数
在函数中有的时候可能也会遇到你要传递的参数的类型是多个(传递变长参数)如这样的函数
func patent(a,b,c ...int)
参数是采用...type的形式传递,这样的函数被称为变参函数
只有声明没有body的函数
你可能遇到没有声明函数体的函数声明,这表示该函数不是以go实现的
这样的声明定义了函数标识符
package mathfunc Sin(x float64) float //implemented in assembly language
返回多值的函数
函数可以返回任意数量的返回值
swap函数返回了两个字符串
//函数定义func Swap(x, y string) (string, string) { return y, x}//函数调用func main() { basic.BasicTypeDemo() basic.BasicTypeDemo() basic.DoCallback(1) basic.Callback(1, basic.Add) a, b := basic.Swap("hello", "world") fmt.Println(a, b)}
命名返回值
Go的返回值可被命名,它们会被视作定义在函数顶部的变量。
返回值的名称应当具有一定的意义,它可以作为文档使用。
没有参数的return语句,返回已命名的返回值,也就是直接返回
func Split(sum int) (x, y int) { x = sum * 4 / 9 y = sum - x return}
函数的使用
x, y := basic.Split(40)fmt.Println(x, y)
直接返回语句应当仅用在上面这样的短函数中,在长的函数中他们会影响代码的可读性。
匿名返回值
函数签名中命名返回值变量,只指定返回值类型,由return指定返回值
// 匿名返回值func Split02(sum int) (int, int) { var x = sum * 4 / 9 var y = sum - x return x, y}
任何一个非命名返回值在return语句里面都要明确指出包含返回值的变量或是一个可计算的值。
建议:尽量使用命名返回值:会使代码更清晰,更简短,同时更加容易读懂。
将函数作为参数传递
在Go语言中,函数是一等公民,也就是说,函数可以像值一样传递和使用。因此,可以将函数作为参数传递给其他函数,实现更加灵活的编程方式:
//类型:func(int, int) intfunc Add(a, b int) int { var ret = a + b fmt.Printf("The sum of %d and %d is: %d\n", a, b, ret) return ret}func Callback(y int, f func(int, int) int) { f(y, 5) // this becomes Add(1, 5)} func DoCallback(y int) { Callback(y, Add) // this becomes Add(1, 5)}
调用方法:
basic.DoCallback(1)basic.Callback(1, basic.Add)
输出结果:
The sum of 1 and 5 is: 6The sum of 1 and 5 is: 6
内置函数:
Go语言拥有一些不需要进行导入操作就可以使用的内置函数
它们有时可以针对不同的类型进行操作:例如,len,cap和append,或必须用于系统级的操作,例如:panic,因而,它们需要直接获得编译器的支持
内置接口:
type error interface { //只要实现了Error()函数,返回值为String的都实现了err接口 Error() String}
递归函数:
当一个函数在其函数体内调用自身,则称之为递归
递归是一种强有力的技术特别是在处理数据结构的过程中。
// 递归函数定义func Processing(n int) (res int) { if n <= 1 { res = 1 } else { res = Processing(n-1) + Processing(n-2) } return} //调用 result := 0 for i := 0; i <= 10; i++ { result = basic.Processing(i) fmt.Printf("processing(%d) is: %d\n", i, result)}
输出结果:
processing(0) is: 1processing(1) is: 1processing(2) is: 2processing(3) is: 3processing(4) is: 5processing(5) is: 8processing(6) is: 13processing(7) is: 21processing(8) is: 34processing(9) is: 55processing(10) is: 89
在使用递归函数时经常会遇到一个重要的问题就是栈溢出:一般会出现在大量的递归调用导致的程序栈内存分配耗尽。
这个问题可以通过一个名为懒惰求值的技术解决,在go语言中,我们可以使用管道channel和goroutine也会通过这个方案来优化斐波那契数列的生成问题。
这样许多问题都可以使用优雅的递归来解决,比如说著名的快速排序算法