Go的函数
Go的函数
函数定义
Go语言中支持函数、匿名函数和闭包
func 函数名(参数)(返回值){
函数体
}
func Sum(x int, y int) int {
return x + y
}
函数可变参数
可变参数是指函数的参数数量不固定。Go语言中的可变参数通过在参数名后加...
来标识
func Sum2(x ...int) int {
// x是一个切片
fmt.Println(x)
sum := 0
for _, v := range x {
sum = sum + v
}
return sum
}
func main() {
ret1 := Sum2()
ret2 := Sum2(10)
ret3 := Sum2(10, 20)
ret4 := Sum2(20, 30)
fmt.Println(ret1, ret2, ret3, ret4)
}
// []
// [10]
// [10 20]
// [20 30]
// 0 10 30 50
可变参数要放在固定参数的后面
func Sum3(x int, y ...int) int {
sum := x
for _, v := range y {
sum += v
}
return sum
}
func main() {
ret1 := Sum3(10)
ret2 := Sum3(10, 20)
ret3 := Sum3(10, 20, 30)
ret4 := Sum3(10, 20, 30, 40)
fmt.Println(ret1, ret2, ret3, ret4)
}
// 10 30 60 100
返回值命名
func calc(x, y int) (sum, sub int) {
sum = x + y
sub = x - y
}
变量作用域
全局变量
全局变量定义在函数外,在程序运行周期内都有效
var num int64 = 10
func main() {
fmt.Println(num)
}
// 10
局部变量
函数内定义的变量无法在该函数外使用
情况一:
func Sum() {
var num int64 = 10
}
func main() {
fmt.Println(num) //会报错
}
情况二
func main() {
var num int64 = 10
if num > 5 {
i := 2
}
fmt.Println(i) // 会报错
}
定义函数类型
type myCalculation func(int, int) int
myCalculation是一种函数类型,这种函数接收两个int类型的参数,并返回int类型的返回值
func add(x, y int) int {
return x + y
}
func sub(x, y int) int {
return x - y
}
add 、sub都是myCalculation类型的函数
var c myCalculation
c = add
add 、sub都能赋值给myCalculation类型的的变量,上面的c变量就是例子
函数类型的变量
声明函数类型的变量并且为该变量赋值
type myCalculation func(int, int) int
func add(x, y int) int {
return x + y
}
func sub(x, y int) int {
return x - y
}
func main() {
var c myCalculation
c = add
fmt.printf("type of c: %T\n", c) // type of c: main.myCalculation
fmt.Println(c(1, 2)) // 像调用add一样调用c
f := add // 将函数add赋值给变量f
fmt.Printf("type of f: %T\n", f) // type of f: func(int, int) int
fmt.Println(f(10, 20)) // 30
}
高阶函数
高阶函数分为函数作为参数和函数作为返回值两部分
函数作为参数
func add(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
函数作为返回值
func add(x, y int) int {
return x + y
}
func sub(x, y int) int {
return x - y
}
func do(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() {
ret, msg := do("+")
if ret != nil {
fmt.Println(ret(10, 5))
}
fmt.Println(msg)
}
匿名函数
Go语言中函数内部不能再像之前那样定义函数了,只能定义匿名函数
func(参数)(返回值){
函数体
}
匿名函数因为没有函数名,所以没办法像普通函数那样调用,所以匿名函数需要保存到某个变量或者作为立即执行函数
func main() {
add := func(x, y int) {
fmt.Println(x + y)
}
add(1, 2) // 通过变量调用匿名函数
// 自执行函数 匿名函数定义完加()直接执行
func(x, y int) {
fmt.Println(x + y)
}(3, 4)
}
匿名函数多用于实现回调函数和闭包
闭包
闭包指的是一个函数和与其相关的引用环境组合而成的实体
闭包=函数+引用环境
例子一
// adder运行后返回一个函数,这个函数传入一个int类型的参数后,会自加上一次运行返回的结果
func adder() func(int) int {
var x int
return func(y int) int {
x += y
return x
}
}
func main() {
f := adder()
fmt.Println(f(6)) // x = 0 + 6
fmt.Println(f(8)) // x = 6 + 8
fmt.Println(f(10)) // x = 14 +8
f2 := adder()
fmt.Println(f2(12)) // x = 0 + 12
fmt.Println(f2(14)) // x = 12 + 14
}
变量f
是一个函数并且它引用了其外部作用域中的x
变量,此时f
就是一个闭包
在f
的生命周期内,变量x
也一直有效
例子二
func adder2(x int) func(int) int {
return func(y int) int {
x += y
return x
}
}
func main() {
f := adder2(1)
fmt.Println(f(2)) // x = 1 + 2
fmt.Println(f(4)) // x = 3 + 4
fmt.Println(f(6)) // x = 7 + 6
f2 := adder2(5)
fmt.Println(f2(8)) // x = 5 + 8
fmt.Println(f2(10)) // x = 13 + 10
}
例子三
func makeSuffixFunc(suffix string) func(string) string {
return func(name string) string {
// 检测字符串name是否为suffix结尾的,不是的话就加上
if !strings.HasSuffix(name, suffix) {
return name + suffix
}
return name
}
}
func main() {
jpgFunc := makeSuffixFunc(".jpg")
txtFunc := makeSuffixFunc(".txt")
fmt.Println(jpgFunc("test")) // test.jpg
fmt.Println(jpgFunc("test2.jpg")) // test2.jpg
fmt.Println(txtFunc("test3.jpg")) // test.jpg.txt
fmt.Println(txtFunc("test4")) // test4.txt
}
例子四
// calc函数传入一个int参数,返回2个函数
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 main() {
f1, f2 := calc(20)
fmt.Println(f1(3), f2(5)) // base = 20 + 3, base = 23 - 5
fmt.Println(f1(4), f2(6)) // base = 18 + 4, base = 22 - 6
fmt.Println(f1(2), f2(1)) // base = 16 + 2, base = 18 -1
}
defer语句
Go语言中的defer
语句会将其后面跟随的语句进行延迟处理
在defer
归属的函数即将返回时,将延迟处理的语句按defer
定义的逆序进行执行
先被defer
的语句最后被执行,最后被defer
的语句,最先被执行
例子一
func main() {
fmt.Println("start")
defer fmt.Println("hello")
defer fmt.Println("world")
defer fmt.Println("666")
fmt.Println("end")
}
// start
// end
// 666
// world
// hello
defer
主要用于资源清理、文件关闭、解锁及记录时间等
defer执行时机
Go语言的函数中return
语句在底层并不是原子操作,它分为给返回值赋值和RET指令两步
return x分为:返回值=x ---> RET指令
defer执行时机
返回值=x ---> 运行defer ---> RET指令
defer经典案例
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
}
func main() {
fmp.Println(f1()) // 5
fmp.Println(f2()) // 6
fmp.Println(f3()) // 5
fmp.Println(f4()) // 5
}
defer面试题
func calc(index string, a, b int) int {
ret := a + b
fmt.Println(index, a, b, ret)
return ret
}
func main() {
x := 1
y := 2
// 函数会先确定参数 calc("AA", 1, calc("A", 1, 2))
defer calc("AA", x, calc("A", x, y))
x = 10
// 函数会先确定参数 calc("BB", 10, calc("B", 10, 2))
defer calc("BB", x, calc("B", x, y))
y = 20
}
// A 1 2 3
// B 10 2 12
// BB 10 12 22
// AA 1 3 4
defer注册要延迟执行的函数时该函数所有的参数都需要确定其值
go的内置函数
内置函数 | 介绍 |
---|---|
close | 主要用来关闭channel |
len | 用来求长度,比如string、array、slice、map、channel |
new | 用来分配内存,主要用来分配值类型,比如int、struct。返回的是指针 |
make | 用来分配内存,主要用来分配引用类型,比如chan、map、slice |
append | 用来追加元素到数组、slice中 |
panic和recover | 用来做错误处理 |
panic/recover
Go1.12
没有异常机制, 使用panic/recover
模式来处理错误
panic
可以在任何地方引发,但recover
只有在defer
调用的函数中有效
func funcA() {
fmt.Println("func A")
}
func funcB() {
panic("panic in B")
}
func funcC() {
fmt.Println("func C")
}
func main() {
funcA()
funcB()
funcC()
}
/*
func A
panic: panic in B
goroutine 1 [running]:
main.funcB(...)
/Users/tianbao/go/src/go_test/main.go:11
main.main()
/Users/tianbao/go/src/go_test/main.go:18 +0x3e
exit status 2
*/
程序运行期间funcB
中引发了panic
导致程序崩溃,异常退出了
这个时候我们就可以通过recover
将程序恢复回来,继续往后执行
func funcA() {
fmt.Println("func A")
}
func funcB() {
defer func() {
err := recover()
// 程序出现panic错误, 可以通过recover恢复过来
if err != nil {
fmt.Println("recover in funcB")
}
}()
panic("panic in B")
}
func funcC() {
fmt.Println("func C")
}
func main() {
funcA()
funcB()
funcC()
}
// func A
// recover in funcB
// func C
recover()
必须搭配defer
使用defer
一定要在可能引发panic
的语句之前定义