函数function
聽聽聽聽- Go函数不支持 嵌套、重载和默认参数
聽聽聽聽- 但支持以下特性:
聽聽聽聽 聽 聽无需声明原形、不定长变参、多返回值、命令返回值参数、匿名函数、闭包
聽聽聽聽- 定义函数使用关键字func,且大括号不能另起一行(所有有大括号的均遵循此原则)
聽聽聽聽- 函数也可以作为一种类型的使用,直接赋值给变量(匿名函数)
定义一个函数
聽聽聽聽格式:func name( 传入的变量1 类型,变量2 类型 ) [ 返回变量 类型,变量 类型 ]{ }
聽聽聽聽- 传入的变量可以没有,也可以使多个
聽聽聽聽- 当传入的变量类型相同时,可以全部省略只留最后一个
聽聽聽聽聽聽聽聽func a(a,b,c int) {}
聽聽聽聽- 返回值可以有多个,返回值类型相同,也可以只留最后一个,其中返回变量名称可以省略,省略的话,就需要每返回一个写一个变量的类型了,如果指定了返回某个局部变量,那么这个变量就已经被定义,那么在函数体内即可直接使用。
聽聽聽聽- 不指定返回变量名称,那么需要在函数尾部写入 return 变量1,变量2, 如果指定了返回的变量名,那么只需要写上return即可。
聽聽聽聽- 传入的参数个数,也可以不定(不定长变参),使用...来表示,在函数体内存储这些数据的类型为slice
聽聽聽聽func A(a ...int) 聽-->...int必须放在最后
聽聽聽聽- 如果传入的值有1个string,有n个int,那么只能 fun A(b string, a ...int)这种形式接受
聽聽聽聽- 如果传入的参数是一个常规的int、string等类型的话,属于值传递(默认),即只是值得拷贝,而如果传递sllice,则是引用传递(其实slice也属于值拷贝,只不过,slice拷贝的是内存地址。而直接修改内存地址会影响源数据)
聽聽聽聽- 如果需要把int、string类型的值传入并修改,那么就需要把这些类型的变量的内存地址传入
package聽main import聽"fmt" func聽main()聽{ 聽聽聽聽a聽:=聽2 聽聽聽聽A(a) 聽聽聽聽fmt.Println(a) } func聽A(a聽int)聽{ 聽聽聽聽i聽:=聽3 聽聽聽聽fmt.Println(i) } 结果: 3 2
把变量a的地址传入到函数中
package聽main import聽"fmt" func聽main()聽{ 聽聽聽聽a聽:=聽2 聽聽聽聽A(&a)聽聽聽聽//&a表示取a的内存地址 聽聽聽聽fmt.Println(a) } func聽A(a聽*int)聽{聽聽聽聽//定义指针类型,指向a的内存地址 聽聽聽聽*a聽=聽3聽聽聽聽//直接对内存地址进行赋值 聽聽聽聽fmt.Println(*a) } 结果: 3 3
参数传递(传值与传指针)
聽聽聽聽函数的参数传递分为两种,值传递,和引用传递,值传递指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数。默认情况下,GO是值传递,在调用过程中不会影响到实际参数。
聽聽聽聽变量在内存中是存放于一定的地址上的,修改变量实际是修改变量地址处的内存。只有让局部函数知道参数的内存地址,才能修改变量的值。所以引用传递的时候需要把变量的内存地址传入到局部函数内(&a,&表示传递变量的内存地址),并将函数的参数类型调整为*int,即改为指针类型,才能在函数中修改变量的值,此时参数仍然是copy传递的,只不过copy的是一个指针。
函数作为其他变量的值
聽聽聽聽在Go语言中,一切皆类型,函数也可以被命名为变量,然后对变量进行函数的调用
package聽main import聽"fmt" func聽main()聽{ 聽聽聽聽a聽:=聽A 聽聽聽聽a() } func聽A()聽{ 聽聽聽聽fmt.Println("Func聽A") } 结果: Func聽A
匿名函数
聽聽聽聽在定义函数的时候不指定函数的名称,而是把函数直接赋值给某个变量的函数叫做匿名函数,调用这个函数的时候,直接使用变量的名称即可。(因为golang中的func不支持函数嵌套,使用匿名函数可以达到嵌套的效果) 聽 匿名函数不能作为顶级函数(最外层)
package聽main import聽"fmt" func聽main()聽{ 聽聽聽聽a聽:=聽func()聽{ 聽聽聽聽fmt.Println("Func") } 聽聽聽聽a() }
闭包函数
聽聽聽聽所谓闭包函数就是将整个函数的定义一气呵成写好并赋值给一个变量。然后用这个变量名作为函数名去调用函数体。闭包函数对它外层的函数中的变量具有访问和修改的权限
package聽main import聽"fmt" func聽main()聽{ 聽聽聽聽f聽:=聽closure(10)聽聽聽聽//调用闭包函数并传递10 聽聽聽聽fmt.Println(f(1))聽聽聽聽//传递1给返回的函数,10+1=11 聽聽聽聽fmt.Println(f(2))聽聽聽聽//传递2给返回的函数,10+2=12 } func聽closure(x聽int)聽func(int)聽int聽{聽聽聽//定义一个函数接收一个参数x,返回值也是一个函数接收一个变量y 聽聽聽聽return聽func(y聽int)聽int聽{聽聽聽聽//返回一个int,函数接收一个参数,返回x+y的值 聽聽聽聽聽聽聽聽return聽x聽+聽y 聽聽聽聽} } 结果: 11 12
defer
聽聽聽聽- 执行方式类似其他语言中的析构函数,在函数体执行结束后按照调用顺序的相反顺序逐个执行 (类似于栈的方式,先进后出,后进先出)
聽聽聽聽- 即使函数发生了严重错误也会执行
聽聽聽聽- 支持匿名函数的调用
聽聽聽聽- 常用语资源清理、文件关闭、解锁以及记录时间等操作
聽聽聽聽- 通过与匿名函数配合可在return之后修改函数计算结果
聽聽聽聽- 如果函数体内某个变量作为defer时匿名函数的参数,则在定义defer时已经获得了拷贝,否则则是引用某个变量的地址
聽聽聽聽- Go没有异常机制,但有panic/recover模式来处理错误
聽聽聽聽- Panic可以再任何地方引发,但recover只有在defer调用的函数中有效
例子
package聽main import聽"fmt" func聽main()聽{ 聽聽聽聽fmt.Println("a") 聽聽聽聽defer聽fmt.Println("1") 聽聽聽聽defer聽fmt.Println("2") 聽聽聽聽defer聽fmt.Println("3") } 结果: a 3 2 1 可以看到,在程序执行完毕后,defer是从最后一条语句开始执行的,证明了defer类似栈的运行方式
defer搭配循环的结果
package聽main import聽"fmt" func聽main()聽{ 聽聽聽聽for聽i聽:=聽0;聽i聽<聽3;聽i++聽{ 聽聽聽聽defer聽fmt.Println(i) 聽聽聽聽} } 结果: 2 1 0
panic/recover实例
主要用来对程序的控制,并且仅针对函数级别的错误进行收集与回调。使程序能继续运行下去
package聽main import聽"fmt" func聽main()聽{ 聽聽聽聽A() 聽聽聽聽B() 聽聽聽聽C() } func聽A()聽{ 聽聽聽聽fmt.Println("A") } func聽B()聽{ 聽聽聽聽defer聽func()聽{聽聽聽聽//这里定义defer执行一个匿名函数,用于捕捉panic,这里如果把defer放在panic之后那么程序执行到panic后就会崩溃,那么defer就不会生效 聽聽聽聽聽聽聽聽if聽err聽:=聽recover();聽err聽!=聽nil聽{聽聽聽聽//对引发的panic进行判断,由于手动触发了panic并发送了信息,那么用recover接收的异常返回值就要不为空,如果为nil表示没有异常,不为nil就表示异常了,这里对recover的返回值进行判断 聽聽聽聽} }() 聽聽聽聽panic("this聽is聽painc")//发送异常,异常信息为”this聽is聽panic“ } func聽C()聽{ 聽聽聽聽fmt.Println("C") } 结果: A C 由于在函数B中定义了异常的recover机制,所以不会迫使程序退出,会继续执行
panic/recover 实例2
package聽main import聽"fmt" func聽main()聽{ 聽聽聽聽fmt.Println("1") 聽聽聽聽fmt.Println("2") 聽聽聽聽f聽:=聽func()聽{ 聽聽聽聽聽聽聽聽defer聽func()聽{ 聽聽聽聽聽聽聽聽聽聽聽聽if聽err聽:=聽recover();聽err聽!=聽nil聽{ 聽聽聽聽聽聽聽聽聽聽聽聽fmt.Println("panic") 聽聽聽聽聽聽聽聽聽聽聽聽} 聽聽聽聽聽聽聽聽}() 聽聽聽聽panic("hello聽world") 聽聽聽聽fmt.Println("7") 聽聽聽聽} 聽聽聽聽f() 聽聽聽聽fmt.Println("8") 聽聽聽聽} 结果: 1 2 panic聽聽聽聽聽聽//打印panic说明程序已经成功的捕捉到了异常 8
聽聽聽聽定义了匿名函数,并赋值给了变量f,匿名函数中的"7"不会打印,因为执行到panic已经崩溃了,而我们在匿名函数内定义了recover捕捉,所以匿名函数会被退出,然后继续执行其他程序
扩展:
聽聽聽聽在go语言中是没有异常捕获机制的,通过panic/recover来实现错误的捕获以及处理,利用go函数多返回值的概念,来进行check,如果err等于nil表示没有发生错误,当程序发生比较严重的错误,严重到无法弥补,比如索引越界,由于我们不能准确的判断元素的个数,所以recover也没有意义,所以说这个时候就是一个panic。如果知道可能会索引越界,并且希望程序能从错误中回复回来,那么这时候就需要用到recover,一旦调用recover,系统就会认为你需要从panic状态恢复过来,当程序进入panic状态,那么正常的程序将不会被执行,那么需要定义defer来执行recover(),defer不管在任何状态下,都会执行,只要把recover放在defer中,那么不管程序发生了怎样的错误,程序都会回复过来,需要注意的是defer类似栈的模式,后进先出。在可能发生panic的程序之前,预先定义defer,否则程序运行到painc后直接崩溃了,这个时候他只会去检查预先定义好的defer,而你放在panic之后,将会失效
例子1:
判断奇偶数
package聽main import聽"fmt" func聽main()聽{ a聽:=聽[]int{1,聽2,聽3,聽4,聽5,聽6,聽7,聽8,聽9,聽10,聽11,聽12,聽13,聽14} 聽聽聽聽fmt.Println("the聽slice聽is聽",聽a) 聽聽聽聽fmt.Println("the聽odd聽is聽",聽odd(a)) 聽聽聽聽fmt.Println("the聽even聽is聽",聽even(a)) } func聽odd(num聽[]int)聽[]int聽{ 聽聽聽聽var聽result聽[]int 聽聽聽聽聽聽聽聽for聽_,聽value聽:=聽range聽num聽{ 聽聽聽聽聽聽聽聽聽聽聽聽if聽value%2聽==聽0聽{ 聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽result聽=聽append(result,聽value) 聽聽聽聽聽聽聽聽聽聽聽聽} 聽聽聽聽聽聽聽聽} 聽聽聽聽return聽result } func聽even(num聽[]int)聽[]int聽{ 聽聽聽聽var聽result聽[]int 聽聽聽聽聽聽聽聽for聽_,聽value聽:=聽range聽num聽{ 聽聽聽聽聽聽聽聽聽聽聽聽if聽value%2聽==聽0聽{ 聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽continue 聽聽聽聽聽聽聽聽} 聽聽聽聽result聽=聽append(result,聽value) 聽聽聽聽} return聽result }
思路:分别对切片进行过滤,偶数功能模块过滤一遍,挑出偶数,奇数功能模块过滤一遍,挑出奇数。缺点,模块复用 性差。
判断奇偶数:
package聽main import聽( 聽聽聽聽"fmt" ) type聽funcation聽func(int)聽bool func聽odd(num聽int)聽bool聽{ 聽聽聽聽if聽num%2聽==聽0聽{ 聽聽聽聽聽聽聽聽return聽false 聽聽聽聽} 聽聽聽聽return聽true } func聽even(num聽int)聽bool聽{ 聽聽聽聽if聽num%2聽==聽0聽{ 聽聽聽聽聽聽聽聽return聽true 聽聽聽聽聽聽聽聽} 聽聽聽聽return聽false } func聽filter(slice聽[]int,聽f聽funcation)聽[]int聽{ 聽聽聽聽var聽result聽[]int 聽聽聽聽聽聽聽聽for聽_,聽value聽:=聽range聽slice聽{ 聽聽聽聽聽聽聽聽聽聽聽聽if聽f(value)聽{ 聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽result聽=聽append(result,聽value) 聽聽聽聽聽聽聽聽聽聽聽聽} 聽聽聽聽聽聽聽聽} 聽聽聽聽return聽result } func聽main()聽{ 聽聽聽聽a聽:=聽[]int{1,聽2,聽3,聽4,聽5,聽6,聽7,聽8,聽9} 聽聽聽聽fmt.Println("the聽slice聽is聽",聽a) 聽聽聽聽fmt.Println("the聽odd聽is聽",聽filter(a,聽odd)) 聽聽聽聽fmt.Println("the聽even聽is聽",聽filter(a,聽even)) }
思路:把判断奇偶的功能模块化,然后再通过一个模块调奇偶判断模块,然后再用main函数组织,(使用func类型,进行功能模块的传递),有点,结构性强,逻辑强。
本文出自 “你的黑夜” 博客,请务必保留此出处http://lixin15.blog.51cto.com/3845983/1851888
有疑问加站长微信联系(非本文作者)