函数 Go语言中支持函数,匿名函数,闭包。
函数的定义 使用func
关键字进行函数定义
函数名:由字母、数字、下划线组成。但函数名的第一个字母不能是数字。在同一个包内,函数名也称不能重名(包的概念详见后文)。
参数:参数由参数变量和参数变量的类型组成,多个参数之间使用,
分隔。
返回值:返回值由返回值变量和其变量类型组成,也可以只写返回值的类型,多个返回值必须用()
包裹,并用,
分隔。
函数体:实现指定功能的代码块。
Example:
1 2 3 4 func sum (x int , y int ) (s int ) { return x + y }
1 2 3 4 func f1 () { fmt.Println("一个既不需要参数也没有返回值的函数" ) }
函数的调用 通过函数名()
的方式调用
1 2 s := sum(1 , 2 ) fmt.Println(s)
调用有返回值的函数时,可以不接收其返回值
参数 类型简写 函数的参数中如果相邻变量的类型相同时,则可以省略类型。
1 2 3 4 5 func f2 (a, b int , c, d bool ) { fmt.Println(a+b) fmt.Println(c&&d) }
可变参数 可变参数是指函数的参数数量不固定。Go语言中的可变参数通过在参数名后加...
来标识。
注意:可变参数通常作为函数的最后一个参数。
1 2 3 4 5 6 7 8 9 10 func f3 (x string , b ...int ) int { fmt.Println("一个可变参数的函数" ) fmt.Println(x) var sum int for a := range b { sum += a } return sum }
返回值 Go语言通过return
关键字向外输出返回值。
多返回值 Go语言中支持多返回值,函数如果有多个返回值时必须用()
将所有返回值包裹起来。
1 2 3 4 5 6 func f4 (x , y int ) (sum , mit int ) { mit = x - y sum = x + y return sum, mit }
返回值命名 函数定义时可以给返回值命名,并在函数体中直接使用这些变量,最后使用return
关键字即可返回,与函数体中的得到结果的顺序无关,它会自动对应返回值变量名。
1 2 3 4 5 6 func f4 (x , y int ) (sum , mit int ) { mit = x - y sum = x + y return }
变量作用域
defer Go语言中的defer
语句会将其后面跟随的语句进行延迟处理。在defer
归属的函数即将返回时,将延迟处理的语句按defer
定义的逆序进行执行,也就是说,先被defer
的语句最后执行,最后被defer
的语句,最先被执行。
1 2 3 4 5 6 7 func main () { fmt.Println("Start" ) defer fmt.Println(1 ) defer fmt.Println(2 ) defer fmt.Println(3 ) fmt.Println("End" ) }
输出:
由于defer
语句的延迟特性,所以defer
语句能非常方便的处理资源释放的问题。
比如:资源清理、文件关闭、解锁及记录时间等。
defer执行的时机 在Go语言的函数中return
语句在底层并不是原子操作,他分为给返回值赋值何和RET指令两步。而defer
语句执行的时机就在返回赋值操作后,RET指令执行前。
经典案例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 func f1 () int { x := 5 defer func () { x++ }() return x } func f2 () (x int ) { defer func () { x++ }() return 5 } func f3 () (y int ) { x :=5 defer func () { x++ }() return x } func f4 () (x int ) { defer func (x int ) { x++ }(x) return 5 }
面试题:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 package mainimport "fmt" func main () { x := 1 y := 2 defer calc("AA" , x, calc("A" , x, y)) x = 10 defer calc("BB" , x, calc("B" , x, y)) y = 20 } func calc (index string , a int , b int ) int { ret := a + b fmt.Println(index, a, b, ret) return ret }
函数类型与变量 定义函数类型 Go语言中使用type
关键字来定义函数类型
1 type calculation func (int , int ) int //定义一个入参为int , int ,出参为int ,类型的变量
定义之后就可以像内置类型一样使用string
,bool
,int
等类型一样去声明变量
函数类型变量 使用自定义函数类型去声明变量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 type calculation func (int , int ) int var a calculationa = add i := a(1 , 3 ) fmt.Println(i) a = sub j := a(8 , 3 ) fmt.Println(j) func add (x, y int ) int { return x + y } func sub (x, y int ) int { return x - y }
高阶函数 函数作为参数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 package mainimport "fmt" func main () { sum := calc(3 , 5 , add) fmt.Println(sum) c := calc(9 , 3 , sub) fmt.Println(c) } 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) }
函数作为返回值 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 func main () { type op func (int ,int ) int //定义函数类型变量 var d op d = do("+" ) s1 := d(2 , 4 ) fmt.Println(s1) d = do("-" ) s2 := d(24 , 7 ) fmt.Println(s2) } func do (s string ) func (int , int ) int { switch s { case "+" : return add case "-" : return sub default : return nil } } func add (x, y int ) int { return x + y } func sub (x, y int ) int { return x - y }
匿名函数 函数还可以作为返回值,但只能定义匿名函数,即没有函数名的函数。
匿名函数没有函数名,所以不能像普通函数那样调用,所以匿名函数需要保存到某个变量或者作为立即执行函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 package mainimport "fmt" var f1 = func (x, y int ) (sum int ) { sum = x + y fmt.Println("全局匿名函数" ) return } func main () { sum := f1(2 , 5 ) fmt.Println(sum) f2 := func (x ...int ) { var sum int for _, v := range x { sum += v } fmt.Println("局部匿名函数" ) fmt.Println(sum) } f2(1 , 2 , 3 , 4 , 5 ) func (x, y int ) { fmt.Println(x + y) fmt.Println("立即执行函数" ) }(5 ,6 ) }
匿名函数多用于实现回调函数和闭包。
闭包 闭包指的是一个函数和其他相关的引用环境组合而成的实体。
闭包=函数+引用环境
基础:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 func adder() func(int) int { var x int return func(y int) int { x += y return x } } func main() { f := adder() fmt.Println(f(10)) //10 fmt.Println(f(20)) //30 fmt.Println(f(30)) //60 }
进阶1:
1 2 3 4 5 6 7 8 9 10 11 12 13 func adder (x int ) func (int ) int { return func (y int ) int { x += y return x } } func main () { f := adder(10 ) fmt.Println(f(10 )) fmt.Println(f(20 )) fmt.Println(f(30 )) }
进阶2:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 func makeSuffixFunc (suffix string ) func (string ) (string ) { return func (name string ) string { if !strings.Contains(name, suffix) { return name + suffix } return name } } func main () { jpgFunc := makeSuffixFunc(".jpg" ) txtFunc := makeSuffixFunc(".txt" ) fmt.Println(jpgFunc("女神" )) fmt.Println(txtFunc("武陵外传" )) }
进阶3:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 func cale (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 main () { f1, f2 := cale(10 ) fmt.Println(f1(1 ), f2(2 )) fmt.Println(f1(3 ), f2(4 )) fmt.Println(f1(5 ), f2(6 )) }
进阶4:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 func f1 (f func () ) { fmt.Println("this is f1" ) f() } func f2 (x, y int ) { fmt.Println("this is f2" ) fmt.Println(x + y) } func f3 (f func (x, y int ) , x , y int ) func () { return func () { f(x, y) } } func main () { f1(f3(f2, 100 , 200 )) }
panic()/recover() Go语言目前是没有异常机制的,但是使用panic/recover
模式来处理错误。panic()可以在任何地方引发,但是recover
只有在defer
调用的函数中有效。
程序报错:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 func main () { funcA() funcB() funcC() } func funcA () { fmt.Println("A" ) } func funcB () { panic ("B执行出错了" ) fmt.Println("B" ) } func funcC () { fmt.Println("C" ) }
输出:
1 2 3 4 5 6 7 8 9 10 A panic : B执行出错了goroutine 1 [running]: main.funcB(...) D:/Develop/Sources/GoWorkSpace/src/github.com/DurianLollipop/StudyGo/day03/painc_recover/main.go :16 main.main() D:/Develop/Sources/GoWorkSpace/src/github.com/DurianLollipop/StudyGo/day03/painc_recover/main.go :7 +0x9d Process finished with exit code 2
程序运行期间引发panic导致程序崩溃,异常退出了。这个时候我们就可以通过recover将程序恢复回来继续往后执行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 func main () { funcA() funcB() funcC() } func funcA () { fmt.Println("A" ) } func funcB () { defer func () { err := recover () if err != nil { fmt.Println("recover in B" ) } }() panic ("B执行出错了" ) fmt.Println("B" ) } func funcC () { fmt.Println("C" ) }
分金币 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 package mainimport "fmt" var ( coins = 50 users = []string { "Matthew" , "Sarah" , "Augustus" , "Heidi" , "Emilie" , "Peter" , "Giana" , "Adriano" , "Aaron" , "Elizabeth" , } distribution = make (map [string ]int , len (users)) ) func main () { left := dispatchCoin() fmt.Printf("金币分配情况:%v, 剩下:%d" , distribution, left) } func dispatchCoin () int { for _, name := range users { var count int for _, c := range name { switch c { case 'e' , 'E' : count++ break case 'i' , 'I' : count += 2 break case 'o' , 'O' : count += 3 break case 'u' , 'U' : count += 4 break } } distribution[name] = count coins -= count } return coins }