Go笔记(十):函数
1、函数
函数一般用于执行某一特定的任务,go函数有三个要素:函数名称、返回类型、参数。
2、函数特性
1、go中不允许函数重载(overload),不允许函数同名;
2、go中函数不能嵌套函数,但可以嵌套匿名函数;
3、函数可作为参数传递给另一个函数;
4、函数的返回值可以是一个函数;
5、函数调用的时候,如果有参数传递给函数,则先拷贝参数的副本,再将副本传递给函数。
3、函数的声明
func function_name( [parameter list] ) [return_types] { // 函数体 }
function_name:函数名称,必选
[parameter list]:参数,可选
[return_types]:返回类型,可选
func:关键字,用来声明函数,必选
1、无参、无返回类型的函数
main函数也是一个无参、无返回类型的函数
1 package main 2 3 import "fmt" 4 5 // 无参 无返回值的函数 6 func demo() { 7 fmt.Printf("demo() 无参数无返回值") 8 } 9 10 // 函数 11 func main() { 12 demo() 13 }
执行结果如下:
2、有参、有返回类型的函数
在go函数中,可以不设置返回变量名。
1 package main 2 3 import "fmt" 4 5 // 获取两数的最大值 6 func getMaxValue(a int, b int) (max int) { 7 if a >= b { 8 max = a 9 } else { 10 max = b 11 } 12 return max 13 } 14 15 // go函数可不设置返回变量 16 func getMinValue(a int, b int) int { 17 if a <= b { 18 return a 19 } else { 20 return b 21 } 22 } 23 24 // 函数 25 func main() { 26 27 maxValue := getMaxValue(4, 6) 28 fmt.Printf("getMaxValue(4, 6) --> 最大值:%v\n", maxValue) 29 30 minValue := getMinValue(4, 6) 31 fmt.Printf("getMinValue(4, 6) --> 最小值:%v\n", minValue) 32 33 }
执行结果如下:
3、go函数返回值可以有多个
1 package main 2 3 import "fmt" 4 5 // go函数可以有多个返回值 6 func getInfo() (string, string) { 7 return "zs", "China" 8 } 9 10 // 返回值 11 func main() { 12 13 name, addr := getInfo() 14 fmt.Printf("getInfo() --> name = %v, addr = %v\n", name, addr) 15 16 }
执行结果如下:
4、函数的参数
1、参数必须指定数据类型
1 package main 2 3 import "fmt" 4 5 // 形参列表 6 func sub(a int, b int) int { 7 return a - b 8 } 9 10 // 参数 11 func main() { 12 // 实参列表 13 subValue := sub(4, 6) 14 fmt.Printf("sub(4, 6) --> subValue = %v\n", subValue) 15 }
执行结果如下:
2、参数传递
2.1、值传递
值传递,传递给函数的是该值拷贝后的副本,函数内部访问、修改的也是这个副本,不影响调用函数外部的值。
1 package main 2 3 import ( 4 "fmt" 5 ) 6 7 // 值传递, 函数内修改的是副本内容 8 func transferValue(num int) { 9 num = 4 10 } 11 12 // 值传递 13 func main() { 14 // 值传递 15 var num = 6 16 transferValue(num) 17 fmt.Printf("transferValue(num) --> num = %v\n", num) 18 }
执行结果如下:
2.2、引用传递
引用传递,map、slice、interface、channel等数据类型是指针类型,此类型的值传递拷贝的是指针,拷贝后的参数仍然指向底层数据结构,修改后会影响外部数据结构的值。
1 package main 2 3 import ( 4 "fmt" 5 ) 6 7 // 引用传递 8 func transferArr(slice01 []int) { 9 slice01[0] = 100 10 } 11 12 func transferMap(map01 map[string]string) { 13 map01["name"] = "jack" 14 } 15 16 // 引用传递 17 func main() { 18 19 // 切片传递 20 slice := []int{1, 2, 3} 21 transferArr(slice) 22 fmt.Printf("slice: %v\n", slice) 23 // map传递 24 map01 := map[string]string{ 25 "name": "timi", 26 "age": "18", 27 } 28 transferMap(map01) 29 fmt.Printf("map01: %v\n", map01) 30 }
执行结果如下:
3、变长参数
go中变长参数的声明需要在参数类型前加上 ... , 例如 (params ...int),示例如下:
1 package main 2 3 import ( 4 "fmt" 5 ) 6 7 // 变长参数 8 func variableArgs(params ...int) int { 9 sum := 0 10 for _, v := range params { 11 sum += v 12 } 13 return sum 14 } 15 16 // 函数 17 func main() { 18 19 // 可变参数1,2,3 20 sum01 := variableArgs(1, 2, 3) 21 fmt.Printf("sum01: %v\n", sum01) 22 // 可变参数1,4,5,6 23 sum02 := variableArgs(1, 4, 5, 6) 24 fmt.Printf("sum02: %v\n", sum02) 25 }
执行结果如下:
5、函数类型与函数变量
go中,可以使用type关键字定义函数类型变量,声明的变量可以被赋值函数,语法格式如下:
type fun func(int, int) int
示例代码如下:
1 package main 2 3 import ( 4 "fmt" 5 ) 6 7 // 求和函数 8 func add(a int, b int) int { 9 return a + b 10 } 11 12 // 函数 13 func main() { 14 15 // 声明函数类型 16 type varfunc func(int, int) int 17 // 声明函数变量 18 var vf varfunc 19 // 函数变量赋值,赋予的函数必须与函数变量的类型有相同的参数类型与返回值 20 vf = add 21 // 函数变量使用 22 sumValue := vf(1, 2) 23 fmt.Printf("sumValue: %v\n", sumValue) 24 }
执行结果如下:
6、函数参数与函数返回值
go中的函数,可作为函数的参数,传递给另外一个函数,也可以作为另外一个函数的返回值返回
6.1、函数作为参数
1 package main 2 3 import ( 4 "fmt" 5 ) 6 7 // 函数作为参数 8 func sayHello(name string) { 9 fmt.Printf("Hello, %s\n", name) 10 } 11 func paramByFunc(name string, f func(string)) { 12 f(name) 13 } 14 15 // 函数 16 func main() { 17 18 // 函数作为参数 19 paramByFunc("timi", sayHello) 20 }
执行结果如下:
6.2、函数作为返回值
1 package main 2 3 import ( 4 "fmt" 5 ) 6 7 // 求和函数 8 func add(a int, b int) int { 9 return a + b 10 } 11 12 // 相减函数 13 func sub(a int, b int) int { 14 return a - b 15 } 16 17 // 函数作为返回值 18 func returnValueByFunc(methodName string) func(int, int) int { 19 switch methodName { 20 case "add": 21 return add 22 case "sub": 23 return sub 24 default: 25 return nil 26 } 27 } 28 29 // 函数 30 func main() { 31 32 // 函数作为返回值 33 sumFunc := returnValueByFunc("add") 34 sumResult := sumFunc(3, 4) 35 fmt.Printf("sumResult: %v\n", sumResult) 36 }
执行结果如下:
7、匿名函数
go语言函数不能嵌套,但可在函数内部定义匿名函数。匿名函数是指 没有名称的函数,语法如下:
func ( [parameter list] ) ([return_types])
[parameter list]:参数列表,可以有多个,也可以没有;
示例代码如下:
1 package main 2 3 import ( 4 "fmt" 5 ) 6 7 // 匿名函数 8 func anonymousFunc() { 9 // 声明函数类型 10 type concatFunc func(string, string) string 11 // 声明函数变量 12 var cf concatFunc 13 // 声明匿名函数,并赋值给函数变量 14 cf = func(param1 string, param2 string) string { 15 return param1 + " " + param2 16 } 17 res := cf("hello", "timi") 18 fmt.Printf("anonymousFunc -> res: %v\n", res) 19 20 // 自身调用 匿名函数 21 sumResult := func(a int, b int) int { 22 return a + b 23 }(4, 5) 24 fmt.Printf("anonymousFunc -> sumResult: %v\n", sumResult) 25 } 26 27 // 函数 28 func main() { 29 // 匿名函数 30 anonymousFunc() 31 }
执行结果如下:
8、闭包
闭包是可以包含自由变量的代码块。闭包 = 函数 + 引用外部作用域变量
示例代码如下:
1 package main 2 3 import ( 4 "fmt" 5 "strings" 6 ) 7 8 // 闭包 9 // 获取序列号 10 func getSequence() func() int { 11 i := 0 12 return func() int { 13 i += 1 14 return i 15 } 16 } 17 // 变量nextNumber是一个函数,并且引用了外部作用域中的i变量,此时nextNumber就是一个闭包。 18 // 在nextNumber的生命周期内,变量i一致有效 19 func closePackDemo() { 20 /* nextNumber 为一个函数,函数 i 为 0 */ 21 nextNumber := getSequence() 22 23 /* 调用 nextNumber 函数,i 变量自增 1 并返回 */ 24 fmt.Println(nextNumber()) 25 fmt.Println(nextNumber()) 26 fmt.Println(nextNumber()) 27 28 /* 创建新的函数 nextNumber1,并查看结果 */ 29 nextNumberNew := getSequence() 30 fmt.Println(nextNumberNew()) 31 fmt.Println(nextNumberNew()) 32 } 33 // 添加后缀 34 func appendSuffix(suffix string) func(string) string { 35 return func(name string) string { 36 if !strings.HasSuffix(name, suffix) { 37 return name + suffix 38 } 39 return name 40 } 41 } 42 func closePackDemo2() { 43 // 初始化外部作用域变量 suffix 44 jpgFunc := appendSuffix(".jpg") 45 txtFunc := appendSuffix(".txt") 46 // 引用外部作用域的suffix,jpgFunc、txtFunc是一个闭包 47 fmt.Printf("jpgFunc(\"test\"): %v\n", jpgFunc("img")) 48 fmt.Printf("txtFunc(\"新建文件夹\"): %v\n", txtFunc("新建文件夹")) 49 } 50 // 获取计算参数结果 51 func cal(base int) (func(int) int, func(int) int) { 52 add := func(i int) int { 53 base += i 54 return base 55 } 56 sub := func(i int) int { 57 base -= i 58 return base 59 } 60 return add, sub 61 } 62 func closePackDemo3() { 63 fun01, fun02 := cal(5) 64 // 5 + 1 、 5 + 1 - 2 65 fmt.Println(fun01(1), fun02(2)) 66 // 5 + 1 - 2 + 10、 5 + 1 - 2 + 10 - 6 67 fmt.Println(fun01(10), fun02(6)) 68 // 重置生命周期 69 fun03, fun04 := cal(10) 70 fmt.Println(fun03(1), fun04(2)) 71 } 72 73 // 函数 74 func main() { 75 76 // 闭包 77 closePackDemo() 78 fmt.Println("======================") 79 closePackDemo2() 80 fmt.Println("======================") 81 closePackDemo3() 82 }
执行结果如下:
9、递归
递归:函数内部调用函数自身
·递归是函数调用自身;
·必须先定义函数退出条件,若无退出条件,递归将成为死循环;
·go语言递归函数很可能会产生一大堆goroutine,也可能会出现栈空间内存溢出问题。
1 package main 2 3 import ( 4 "fmt" 5 "strings" 6 ) 7 8 // 递归 9 // 斐波那契数列 F(0)=0,F(1)=1, F(n)=F(n - 1)+F(n - 2)(n ≥ 2 10 func fibonacci(n int) int { 11 if n == 0 { 12 return 0 13 } 14 if n == 1 || n == 2 { 15 return 1 16 } 17 return fibonacci(n-1) + fibonacci(n-2) 18 } 19 // 阶乘 n * (n-1) *...* 1 20 func factorial(n int) int { 21 if n == 1 { 22 return 1 23 } 24 return n * factorial(n-1) 25 } 26 27 // 函数 28 func main() { 29 30 // 递归应用 31 fibonacci := fibonacci(5) 32 factorial := factorial(5) 33 fmt.Printf("fibonacci: %v, factorial: %v\n", fibonacci, factorial) 34 }
执行结果如下:
10、defer语句
defer语句会将其后跟随的语句进行延迟处理。延迟处理的语句按defer定义的逆序进行执行。先被defer的语句后执行,后被defer的语句先执行。有点像栈的先进后出,defer最先定义的语句最后执行。
-> defer特性
1、关键字 defer 用于注册延迟调用
2、调用直到 return 前才被执行,可以用来做资源清理
3、多个defer语句,按先进后出的方式执行
4、defer语句中的变量,在defer声明时就已决定。
1 package main 2 3 import ( 4 "fmt" 5 "strings" 6 ) 7 8 // defer延迟处理,常用于 9 // 1、关闭文件句柄 10 // 2、释放锁资源 11 // 3.数据库连接释放 12 func deferFunc() { 13 fmt.Println("start...") 14 // 关键字 defer 用于注册延迟调用 15 // 多个defer语句,按先进后出的方式执行 16 defer fmt.Println("run01...") 17 defer fmt.Println("run02...") 18 defer fmt.Println("run03...") 19 fmt.Println("end...") 20 } 21 22 // 函数 23 func main() { 24 25 // defer延迟执行 26 deferFunc() 27 }
执行结果如下:
11、init函数
init初始化函数,先于main函数执行。
1、init函数主要特点
1、init函数先于main函数自动执行,不能被其他函数调用;
2、init函数没有输入参数、返回值;
3、每个包可以有多个init函数;包的每个源文件也可以有多个init函数;
4、不同包的init函数按照包导入的依赖关系决定执行顺序
2、初始化顺序
变量初始化 -> init() -> main
1 package main 2 3 import "fmt" 4 5 // 初始化顺序: 变量初始化 -> init() -> main() 6 // init函数 7 func init() { 8 fmt.Println("init()...") 9 } 10 11 // 变量初始化 12 var num int = getNum() 13 func getNum() int { 14 fmt.Println("var...") 15 return 100 16 } 17 18 // main函数 19 func main() { 20 fmt.Println("main...") 21 }
执行结果如下: