1.函数1.1函数定义

Go语言中定义函数使用fun关键字,具体格式为func 函数名(参数)(返回值){ 函数体}其中:1.函数名:由字母,数字,下划线组成。但函数名的第一个字母不能是数字。在同一个包内,函数名不能重名。2.参数:参数由变量名和变量类型组成。3.返回值:可以只写返回值类型,多个返回值必须用()包裹,并用,分隔。4.函数体:实现指定功能的代码块。定义一个求两个数和的函数:func intSum(x int, y int) int { return x + y}函数的参数和返回值是可选的,我们也可以不传参也没有返回值。func sayHello() { fmt.Println("Hello vita")}1.2函数的调用

定义了函数后,可以通过 函数名()的方式调用函数。调用有返回值的函数时,可以不接收返回值。package mainimport "fmt"func sayHello(){ fmt.Println("hello vita")}func main() { sayHello()}结果:hello vita1.3参数1.3.1类型简写

函数的参数中如果相邻变量的类型相同,则可以省略类型。package mainfunc intSum(x,y int)int{ return x+y}func main() { intSum(1,2)}intSum函数有两个参数,x,y的类型都是int,因此可以省略x的类型,因为y后面有类型说明。1.3.2可变参数

可变参数即参数数量不固定。Go语言中的可变参数通过在参数后面加...来标识。注意:可变参数通常要作为函数的最后一个参数。package mainimport "fmt"func intSum(x ...int)int{ fmt.Println(x)//x是一个切片 sum:=0 for _,value :=range x{ sum+=value } return sum}func main() { sum1 := intSum(1,2,3,4,5) sum2 := intSum(1,2,3,4,5,6,7) sum3 := intSum(1,2,3,4,5,6,7,8,9) fmt.Println(sum1) fmt.Println(sum2) fmt.Println(sum3)}结果:[1 2 3 4 5][1 2 3 4 5 6 7][1 2 3 4 5 6 7 8 9]1528451.3.3固定参数搭配可变参数

可变参数放在固定参数的后面package mainimport "fmt"func intSum(x int,y ...int)int{ sum:=x for _,value :=range y{ sum+=value } return sum}func main() { sum1 := intSum(1,2,3,4,5) sum2 := intSum(1,2,3,4,5,6,7) sum3 := intSum(1,2,3,4,5,6,7,8,9) fmt.Println(sum1) fmt.Println(sum2) fmt.Println(sum3)}结果:1528451.4返回值1.4.1多返回值

Go语言中支持多返回值,函数如果有多个返回值,必须使用()把所有返回值包裹起来。package mainfunc calc(x,y int)(int,int){ sum :=x+y sub:=x-y return sum,sub}func main() { calc(3,2)}1.4.2返回值命名

函数定义时,可以给返回值命名,并在函数体中直接使用这些变量,最后通过return关键字返回。package mainfunc calc(x,y int)(sum int,sub int){ sum =x+y sub=x-y return }func main() { calc(3,2)}2.函数进阶2.1全局变量

全局变量是定义在函数外部的变量,他在程序整个运行周期内都有效。在函数中可以访问到全局变量。package mainimport "fmt"var num int64=100func testGlobalVar(){ fmt.Printf("num=%d\n",num)}func main() { testGlobalVar() //num=100}2.2局部变量

函数内定义的变量,无法在函数外使用。func testLocalVar() { //定义一个函数局部变量x,仅在该函数内生效 var x int64 = 100 fmt.Printf("x=%d\n", x)}func main() { testLocalVar() fmt.Println(x) // 此时无法使用变量x}

如果局部变量和全局变量重名,优先访问局部变量。package mainimport "fmt"//定义全局变量numvar num int64 = 10func testNum() { num := 100 fmt.Printf("num=%d\n", num) // 函数中优先使用局部变量}func main() { testNum() // num=100}

语句块中定义的变量,通常我们会在if条件判断,for循环,switch语句上使用这种定义变量的方式。func testLocalVar2(x, y int) { fmt.Println(x, y) //函数的参数也是只在本函数中生效 if x > 0 { z := 100 //变量z只在if语句块生效 fmt.Println(z) } //fmt.Println(z)//此处无法使用变量z}

for循环语句中定义的变量,也只在for语句块中生效func testLocalVar3() { for i := 0; i < 10; i++ { fmt.Println(i) //变量i只在当前for语句块中生效 } //fmt.Println(i) //此处无法使用变量i}2.3定义函数类型

我们可以使用type关键字定义一个函数类型,具体格式如下:type calculation func(int, int) int上面语句定义了一个calculation类型,它是一种函数类型,接收两个int参数,返回值类型为int。简单的说,凡是满足这个条件的函数都是calculation类型的函数,例如下面的add和sub是calculation类型。func add(x, y int) int { return x + y}func sub(x, y int) int { return x - y}add和sub都能赋值给calculation类型的变量。var c calculationc = add2.4函数类型变量

我们可以声明函数类型的变量并且为该变量赋值:func main() { var c calculation // 声明一个calculation类型的变量c c = add // 把add赋值给c fmt.Printf("type of c:%T\n", c) // type of c:main.calculation fmt.Println(c(1, 2)) // 像调用add一样调用c f := add // 将函数add赋值给变量f1 fmt.Printf("type of f:%T\n", f) // type of f:func(int, int) int fmt.Println(f(10, 20)) // 像调用add一样调用f}3.高阶函数

高阶函数分为函数作为参数和函数作为返回值。3.1函数作为参数

package mainimport "fmt"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() { ret2 := calc(10,20,add) fmt.Println(ret2) //30}3.2函数作为返回值

package mainimport ( "errors" "fmt")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() { addResult,err := do("+") fmt.Println(addResult(1,2),err) subResult,err := do("-") fmt.Println(subResult(2,1),err) informalResult,err := do(".") fmt.Println(informalResult,err)}结果:3 <nil>1 <nil><nil> 无法识别的操作符Process finished with exit code 03.3匿名函数

函数可以作为返回值,但是在GO语言中,只能定义匿名函数。匿名函数就是没有函数名的函数,匿名函数的定义格式如下:func(参数)(返回值){ 函数体}匿名函数因为没有函数名,所以不能像普通函数那样调用,所以匿名函数需要保存到某个变量或者作为立即执行函数。匿名函数多用于实现回调函数和闭包。

package mainimport "fmt"func main() { //将匿名函数保存到变量 add := func(x,y int) { fmt.Println(x+y) } add(10,20) //通过变量调用匿名函数 //自执行函数:匿名函数定义完加()直接执行 func(x,y int){ fmt.Println(x+y) }(10,20)}结果:3030Process finished with exit code 03.4闭包

闭包,在Python中,是外部函数的返回值是内部函数的应用,内部函数使用了外部函数的变量,这就构成了闭包。在Go语言中,也是同样的。

adder函数的返回值是一个内部的匿名函数,该匿名函数引用了外部的变量x,并且x变量在f的生命周期内一直有效。package mainimport "fmt"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 f1 := adder() fmt.Println(f1(40))//40 fmt.Println(f1(50))//90}结果:1030604090Process finished with exit code 0

闭包进阶示例1package mainimport "fmt"func adder2(x int) func(int) int { return func(y int) int { x += y return x }}func main() { f := adder2(10) fmt.Println(f(10))//20 fmt.Println(f(20))//40 fmt.Println(f(30))//70 f1 := adder2(20) fmt.Println(f1(40))//60 fmt.Println(f1(50))//110}结果:20407060110Process finished with exit code 0

闭包进阶示例2:package mainimport ( "fmt" "strings")func makeSuffixFunc(suffix string) func(string) string { return func(name string) string { if !strings.HasSuffix(name,suffix){ return name + suffix } return name }}func main() { jpgFunc := makeSuffixFunc(".jpg") textFunc := makeSuffixFunc(".txt") fmt.Println(jpgFunc("test")) fmt.Println(textFunc("test"))}结果:test.jpgtest.txtProcess finished with exit code 0

闭包进阶示例3:package mainimport "fmt"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(10) fmt.Println(f1(1),f2(2)) fmt.Println(f1(3),f2(4)) fmt.Println(f1(5),f2(6))}结果:11 912 813 7Process finished with exit code 04.defer语句4.1延迟调用特性

Go语言中的defer语句会将其后面跟随的语句进行延迟处理。在defer归属的函数即将返回时,将延迟处理的语句按defer定义的逆序进行执行。即先被defer的语句最后被执行,最后被defer的语句,最先执行。

package mainimport "fmt"func main() { fmt.Println("start") defer fmt.Println(1) defer fmt.Println(2) defer fmt.Println(3) fmt.Println("end")}结果:startend321Process finished with exit code 0由于defer语句延迟调用的特性,所以defer语句能非常方便的处理资源释放问题。比如:资源清理、文件关闭、解锁及记录时间等。4.2defer执行时机

在Go语言的函数中,return语句在底层并不是原子操作,它分为给返回值赋值和RET指令两步。defer语句执行时机就在赋值操作后,RET指令执行前。具体如图:

4.3defer经典案例

package mainimport "fmt"// 关于defer的面试题func f1() int { x := 5 defer func() { x++ }() return x // 1. 返回值=5 2. x++ 3. RET指令 ==> 5}func f2() (x int) { defer func() { x++ }() return 5 // 1. (汇编)返回值=x(5) 2. x++ 3.(汇编)RET ==> 6}func f3() (y int) { x := 5 defer func() { x++ }() return x // 1. (汇编)返回值=y(5) 2. x++ 3.(汇编)RET ==> 5}func f4() (x int) { defer func(x int) { x++ }(x) return 5 //1. (汇编)返回值=x(5) 2. x++(函数内部的x) 3.(汇编)RET ==> 5}func main() { fmt.Println(f1()) fmt.Println(f2()) fmt.Println(f3()) fmt.Println(f4())}4.4defer面试题

package mainimport "fmt"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 defer calc("AA", x, calc("A", x, y)) x = 10 defer calc("BB", x, calc("B", x, y)) y = 20}提示:defer注册要延迟执行的函数时该函数所有的参数都需要确定其值结果:A 1 2 3B 10 2 12BB 10 12 22AA 1 3 4Process finished with exit code 05.内置函数

5.1panic/recover

Go语言中,目前(Go1.12)是没有异常机制的,但是使用panic/recover模式可以处理错误。panic可以在任何地方引发,但是recover只有在defer调用的函数中有效。

package mainimport "fmt"func funcA() { fmt.Println("func A")}func funcB() { panic("func B")}func funcC() { fmt.Println("func C")}func main() { funcA() funcB() funcC()}

在程序运行期间,funcB中引发了panic,导致程序崩溃,异常退出了。这个时候,我们可以通过recover将程序恢复回来,继续往后执行。

package mainimport "fmt"func funcA() { fmt.Println("func A")}func funcB() { defer func() { err := recover() if err != nil{ fmt.Println("recover in B") } }() panic("func B")}func funcC() { fmt.Println("func C")}func main() { funcA() funcB() funcC()}结果:func Arecover in Bfunc CProcess finished with exit code 0

注意:1.recover()必须搭配defer使用。2.2.defer一定要在可能引发panic的语句之前定义。6.练习题

/*你有50枚金币,需要分配给以下几个人:Matthew,Sarah,Augustus,Heidi,Emilie,Peter,Giana,Adriano,Aaron,Elizabeth。分配规则如下:a. 名字中每包含1个'e'或'E'分1枚金币b. 名字中每包含1个'i'或'I'分2枚金币c. 名字中每包含1个'o'或'O'分3枚金币d: 名字中每包含1个'u'或'U'分4枚金币写一个程序,计算每个用户分到多少金币,以及最后剩余多少金币?程序结构如下,请实现 ‘dispatchCoin’ 函数*/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.Println("剩下:", left)}

package mainimport ( "fmt" "strings")var ( coins = 50 users = []string{ "Matthew", "Sarah", "Augustus", "Heidi", "Emilie", "Peter", "Giana", "Adriano", "Aaron", "Elizabeth", } distribution = make(map[string]int, len(users)))func dispatchCoin() int{ count := 0 for _,value := range users{ eCount := strings.Count(strings.ToLower(value),"e") iCount := strings.Count(strings.ToLower(value),"i") oCount := strings.Count(strings.ToLower(value),"o") uCount := strings.Count(strings.ToLower(value),"u") personCount := eCount*1+iCount*2+oCount*3+uCount*4 fmt.Printf("Pserson:%s,coins:%d\n",value,personCount) count += personCount } return coins-count}func main() { left := dispatchCoin() fmt.Println("剩下:", left)}结果:Pserson:Matthew,coins:1Pserson:Sarah,coins:0Pserson:Augustus,coins:12Pserson:Heidi,coins:5Pserson:Emilie,coins:6Pserson:Peter,coins:2Pserson:Giana,coins:2Pserson:Adriano,coins:5Pserson:Aaron,coins:3Pserson:Elizabeth,coins:4剩下: 10Process finished with exit code 0