土拨鼠-->函数
函数
在go语言中函数是重中之重,函数是程序实现具体功能的基本代码块,本章节主要介绍函数定义、函数进阶、函数高阶、匿名函数、闭包、defer、内置函数等。
函数定义
格式:
(1)函数名:由字母、数字、下划线组成,但函数名的第一个字母不能是数字,在同一个包中函数名不能重名;
(2)参数:由参数变量和参数类型组成,多个参数间使用英文逗号隔开;
(3)返回值:由返回值变量和其变量类型组成,也可只写返回值类型,多个返回值必须用()包裹,返回值间并用英文逗号隔开
(4)函数体:实现指定功能的代码块
函数的参数:
(1)参数类型简写
func sum(x, y int) int { return x + y }
(2)可变参数
func sum(x int, y ...int) int { fmt.Println(y) //参数y是切片 sum := x for _, v := range y { sum += v } return sum }
注意:可变参数必须作为函数最后的一个参数,可变参数是通过切片来实现的
函数返回值
(1)通过return关键字向外输出返回值
(2)多返回值
func calc(x, y int) (int, int) { sum := x + y sub := x - y return sum, sub }
(3)给返回值命名,可在函数中直接使用这些返回值变量,最后通过return返回
func calc(x, y int) (sum, sub int) { sum = x + y sub = x - y return }
函数进阶
(1)全局变量和局部变量
//全局变量定义在函数外部的变量 var num int64 = 10 func globalVar() { fmt.Printf("num=%d", num) }
//局部变量 //1.函数内定义的变量无法在该函数外使用 //2.如果局部变量和全局变量重名,优先使用局部变量 //3.if条件判断、for循环、switch上使用定义变量的方式 func testLocalVar(x, y int) { fmt.Println(x, y) if x > 0 { z := 1000 fmt.Println(z) } //fmt.Println(z) for i := 0; i < 10; i++ { fmt.Println(i) } //fmt.Println(i) }
(2)函数类型和变量
//函数类型 //定义一个函数类型 type calculation func(int, int) int func add(x, y int) int { return x + y } func sub(x, y int) int { return x - y } //凡是满足包括参数类型、参数个数,返回值类型、个数都一致,那么就可以说这个函数是这 //种函数的类型,就可以把该函数赋值给这种函数类型定义的变量 var c calculation //函数类型变量 c = add //函数赋值 c = sub c(2,3) //函数调用
高阶函数
(1)函数作为参数
func add(x, y int) int { return x + y } func sub(x, y int) int { return x - y } func calc(x, y int, op func(int, int) int) int { return op(x, y) } func main() { ret := calc(10, 20, add) fmt.Println(ret) //30 }
(2)函数作为返回值
func add(x, y int) int { return x + y } func sub(x, y int) int { return x - y } func doSomething(s string) (func(int, int) int, error) { switch s { case "+": return add, nil case "-": return sub, nil default: err := errors.New("无法识别的操作符") return nil, err } } func main() { f,_:=doSomething("+") res:=f(1,2) fmt.Println(ret) //3 }
匿名函数
匿名函数就是没有名称的函数,其无法像普通函数一样调用,需要保存到某个变量或者立即执行函数,这种函数多用于回调函数和闭包。
func main() { //将匿名函数保存到变量 s := func(x, y int) { fmt.Println(x + y) } s(1, 2) //通过变量调用匿名函数 //自执行函数,匿名函数定义完加()直接执行 func(x, y int) { fmt.Println(x - y) }(10, 2) }
闭包
闭包指的是函数和与其相关的引用环境组合而成的整体(实体);闭包本质是返回一个函数(匿名函数),但是返回函数会用到该函数外部的一些变量,它们共同组成一个整体,所以要分析清楚返回的匿名函数使用到了那些外部变量;总体说:闭包=函数+引用环境。
func adder1() func(int) int { var x int return func(y int) int { x += y return x } //闭包包含了 外部变量x和func这个函数 } func main() {变量 //闭包 var f = adder1() //函数f引用了外部作用域的x,此时f就是一个闭包,在f的生命周期内变量x也一直有效 fmt.Println(f(10)) //10 fmt.Println(f(20))//30 fmt.Println(f(30))//60 f1 := adder2(20) //20 fmt.Println(f1(40)) //60 fmt.Println(f1(50)) //110 } func adder2(x int) func(int) int { return func(y int) int { x += y return x } } func calc(base int) (func(int) int, func(int) int) { add := func(i int) int { base += i return base } sub := func(i int) int { base -= i return base } return add, sub }
闭包简单实现斐波那契数列:
func fibonacci() func() int { a, b := 0, 1 return func() int { a, b = b, a+b return a } } func main() { f := fibonacci() for i := 0; i < 20; i++ { //计算前20位的斐波那契额数列 fmt.Printf("%d ", f()) } }
defer语句
defer语义是延迟的意思,在go语言中使用defer,会将后面跟随的语句进行延迟处理,它的执行时机是defer语句归属的函数即将返回时,执行defer跟随的语句,如果一个函数中使用了多个defer,那么先被defer的语句后执行,最后defer的语句先执行。
func testDefer() { fmt.Println("start") defer fmt.Println(1) defer fmt.Println(2) defer fmt.Println(3) fmt.Println("end") } //输出 /* start end 3 2 1 */
defer底层机制是当执行到defer时,暂不执行,只是将defer后面的语句压入到独立的栈中,当函数执行完毕后,再从该独立栈按中照先入后出的方式出栈,并执行对应的语句。
内置函数
内置函数 | 介绍 |
close | 主要用来关闭channel |
len | 用来求长度,比如string、array、slice、map、channel |
new | 用来分配内存,主要用来分配值类型,比如int、struct,返回的是指针 |
make | 用来分配内存,主要用来分配引用类型,比如chan、map、slice |
append | 用来追加元素到数组、slice中 |
panic | 用来做错误处理,可以在任何地方引发/使用 |
recover | 用来做错误处理,recover只有在defer调用的函数中有效 |
func funcA() { fmt.Println("func A") } func funcB() { defer func() { err := recover() //如果程序出出现了panic错误,可以通过recover恢复过来 if err != nil { fmt.Println("recover in B") } }() panic("panic in B") } func funcC() { fmt.Println("func C") } func main() { //程序运行期间funcB中引发了panic导致程序崩溃,异常退出了。这个时候我们就可以通过recover将程序恢复回来,继续往后执行 funcA() funcB() funcC() }
注意:
(1)recover必须在defer中使用;
(2)defer一定要在可能引发panic的语句之前定义;
(3)发生panic时,一直向上返回,并执行每一层的defer。