在上一篇博文中,我们介绍了Go里的运算符和流程控制的使用,接下来将记录一下Go里面函数的使用:
设计规则
- 大写字母开头的变量是可以导出的,也就是其他包可以读取的,是公有变量。小写字母开头的变量是不可导出的,是私有变量
- 大写字母开头的函数也是一样,相当于
class
中的带有public
关键字的公有函数;小写字母开头的就是有private
关键字的私有函数
函数
函数是Go语言中的核心设计,它通过关键字func
来进行声明,举个栗子:
package main
import "fmt"
func max(a int, b int) int{
if a >= b{
return a
}else{
return b
}
}
func main() {
fmt.Println(max(10, 5))
}
这里,我们传递两个参数,a、b都是int类型,并且返回一个int类型。接下来,我们定义一个无返回值的函数,如下:
package main
import "fmt"
func print_info(a int, b int){
fmt.Println(a, b)
}
func main(){
print_info(10, 5)
}
// 打印结果如下
10 5
在Go中,还支持函数返回多个值,如下:
package main
import "fmt"
func exchange(a int, b int) (int, int) {
return b, a
}
func main() {
a := 1
b := 2
fmt.Println(exchange(a, b))
}
在函数exchange
中,我们可以看到传递的两个参数都是int类型,我们可以将其简化,如下:
func exchange(a, b int) (int, int) {
return b, a
}
Go语言中,函数还支持变参,接受变参的函数有着不定数量的参数。下面,我们来定义一个接收变参的函数,如下:
package main
import "fmt"
func print_args(args ...int){
fmt.Printf("args type is %T, length = %d, args = %v", args, len(args), args)
}
func main(){
print_args(1, 2, 3)
}
// 打印结果如下
args type is []int, length = 3, args = [1, 2, 3]
args ...int
表示接收不定长参数,并且参数皆为int类型。从打印结果可以得知,args
是一个slice
,所以我们可以使用slice
的一些操作!不定长参数的位置只能放在最后面,否则将发生编译错误,下面这种定义方式是错误的:
func test(args ..int, a int) {
...
}
当有返回值情况,我们还可以定义变量来接收,那么这种情况之下,函数我们可以这样定义:
func test() (a int, b int){
a, b = 1, 2
return
}
观察这种写法,可以看出,我们定义了两个变量作为返回值,所以return的时候,无需再进行写出该返回什么,而且a,b已经定义好了,所以我们只需为其赋值即可,所以这里用的是=
而不是:=
在Go中,同样可以使用return
来终止函数,如下:
func test(){
for i:=0; i < 1000; i++ {
if i == 100{
return
}
fmt.Println(i)
}
fmt.Println("aaaaaaaaaa")
}
如此,当循环到100时会终止函数,因此aaaaaaaaaa的打印将永远不会执行!
传值与传指针
当我们传一个参数值到被调用函数里面时,实际上是传了这个值的一份Copy,当在被调用函数中修改参数值的时候,调用函数中相应实参不会发生任何变化,因为数值变化只作用在copy上,下面我们举个栗子验证一下:
package main
import "fmt"
func add(a int) int {
a++
return a
}
func main() {
a := 3
fmt.Println(add(a)) // 4
fmt.Println(a) // 3
}
如果,有这样的需求,我们在执行某个函数过后,改变其值,这时我们就需要传递一个指针过去。修改后的代码如下:
package main
import "fmt"
func test(x *int) int {
*x++
return *x
}
func main() {
x := 3
fmt.Println(test(&x))
fmt.Println(x)
}
执行结果如下:
4
4
defer
Go语言中有种不错的设计,即延迟语句。使用延迟语句,可以延迟一个函数的调用,使用方式如下:
package main
import "fmt"
func main() {
defer fmt.Println("aaaaaaaaaa")
fmt.Println("bbbbbbbbbb")
}
// 打印结果如下
bbbbbbbbbb
aaaaaaaaaa
值得注意的是,如果一个函数中有多个defer,它会遵循后进先出的原则依次执行,哪怕函数或者某个延迟调用发生错误,这些调用依旧会执行,如下:
package main
import "fmt"
func division(a, b int) (result int){
result = a / b
return
}
func main() {
defer fmt.Println("aaaaaaaaaa")
defer fmt.Println("bbbbbbbbbb")
defer division(10 ,0)
defer fmt.Println("cccccccccc")
}
上面代码将会依次打印“cccccccccc”、“bbbbbbbbbb”、“aaaaaaaaaa”,最后程序崩溃
函数别名
我们还可以像基本类型一样,给函数起一个别名,并进行初始化,如下:
package main
import "fmt"
type MyFunc func(int, int) int
func add(a, b int) int{
return a + b
}
func main(){
var f Myfunc
f = add
fmt.Println(f(1, 2)) // 3
}
下面我们来进行分析一下代码:首先定义了一个函数别名,该函数具有两个参数而且都为int类型,并具有一个返回值也是int类型!在main函数中,声明了一个变量f为我们定义的别名类型,然后将其赋值给另一个函数add,然后调用,本质上其实是调用了add函数!
另外,Go中我们还可以将这个定义别名为一个类型以参数进行传递,如下:
func call(a, b int, f MyFunc) (result int){
result = f(a, b)
return
}
匿名函数
package main
import "fmt"
func main(){
f := func(){
fmt.Println("这是一个匿名函数")
}
f()
}
// 打印结果如下
这是一个匿名函数
观察上面代码可以得知,这是使用:=
进行初始化赋值给一个变量,而后进行调用。接下来,我们进行定义一下有参数有返回值情况的匿名函数,如下:
package main
import "fmt"
func main(){
f := func(a, b int) int {
return a + b
}
fmt.Println(f(1, 1))
}
// 打印结果如下
2
我们还可以定义一个匿名函数的同时,让你实现自调用,如下:
package main
import "fmt"
func main(){
func() {
fmt.Println("这是一个自调用的匿名函数")
}()
}
// 打印结果如下
这是一个自调用的匿名函数
接下来,我们再来看一下有参数有返回值的情况,如下:
package main
import "fmt"
func main(){
result := func(a, b int) (result int){
result = a + b
return
}(20, 30)
fmt.Println(result)
}
// 打印结果如下
50
匿名函数还可以作为一个函数的返回值使用,如下:
package main
import "fmt"
func test() func(num int) int {
return func(num int) (result int) {
result = num * num
return
}
}
func main() {
f := test()
fmt.Println(f(2))
}
// 打印结果如下
4