Go中的func

401 阅读4分钟

这是我参与8月更文挑战的第12天,活动详情查看:8月更文挑战

函数

一般指具有特定功能的代码块

格式

函数由关键字func来进行声明, 函数名, 参数列表, 返回值列表, 函数体, 返回语句来组成

 func 函数名(参数1, 参数2, ...) (返回值1, 返回值2, ...) {
   // 函数体
   return // 返回语句
 }

函数类型

如果函数的参数列表及类型, 返回值的列表及类型一样, 那么它们的类型为同一种函数类型

 func add(a, b int) int { 
   return a + b 
 }
 func sub(a, b int) int { 
   return a - b 
 }

add函数和sub函数就是同一种类型的函数

函数的定义

 // 无参无返回值的函数
 // 定义一个没有参数的函数
 func sayHi() {
    fmt.Println("Hi, go")
 }
 ​
 // 方式1: 我们可以将函数名直接赋值给一个变量, 变量类型由编译器自行推断
 f := sayHi
 fmt.Printf("%#v \n", f)
 ​
 // 方式2: 定义一个函数类型的变量, 将符合这个函数类型的函数赋值给这个变量
 // 定义一个函数类型
 type sayHelloType func(string)
 ​
 // 通过函数类型声明一个变量
 var f2 sayHelloType
 ​
 // 为函数变量赋值
 f2 = sayHello
 fmt.Printf("%#v \n", f2)
 ​

函数的调用

 // 方式1: 我们可以直接使用函数名来调用
 sayHi()
 sayHello("shaosiming")
 ​
 // 方式2: 也可以使用指向函数的变量名来调用
 f()
 f2("gopher")
 f3()

参数的传递

Go中的函数参数是值传递, 也就是传递的是实参的拷贝

对于非引用类型: int float64, struct

在函数中更改非引用类型参数的值, 实参不会改变 想要在函数中更改实参的值, 我们可以传实参的地址

值类型

 func changeValue(a int)  {
   a *= 10
 }
 ​
 func changeValue2(a *int)  {
   *a *= 10
 }
 ​
 // 非引用类型: 传值
 value := 10
 fmt.Println("changeValue调用前value的值: ", value)
 changeValue(value)
 fmt.Println("changeValue调用后value的值: ", value)
 ​
 // 非引用类型: 传地址
 value2 := 8
 fmt.Println("changeValue调用前value2的值: ", value2)
 changeValue2(&value2)
 fmt.Println("changeValue调用后value2的值: ", value2)

引用类型

 func changeValue3(m map[string]int)  {
   m["shaosiming"] = 15
 }
 ​
 // 引用类型
 m := map[string]int {"shaosiming":18, "dasiming":20}
 fmt.Println("changeValue3调用前m的值: ", m)
 changeValue3(m)
 fmt.Println("changeValue3调用前m的值: ", m)

变参函数

 func printMultiParam(a ...int)  {
   fmt.Println(a)
 }
 ​
 // 有时我们不能确定函数要接收的同类型的参数个数, 可以使用变参函数
 // 可以会多个参数
 printMultiParam(1, 3, 5, 7)
 ​
 // 也可以会一个切片, 获取这个切片中的多个值
 s := []int {2, 4, 6, 8}
 printMultiParam(s...)

函数的递归

 // 使用递归求阶乘
 func factorial(a int) int {
   if a == 1 {
     return  1
   }
   return a * factorial(a - 1)
 }
 ​
 // 使用循环求阶乘
 func factorial2(a int) int {
   res := 1
   for i := 1; i <= a; i++ {
     res *= i
   }
   return res
 }
 ​
 // 求阶乘
 fmt.Println(factorial(5))
 fmt.Println(factorial2(5))

匿名函数

 // 匿名函数不能独立存在
 // 可以直接在匿名函数后面加上()来进行调用
 func(str string) {
    fmt.Println("hi, ", str)
 }("shaosiming")
 ​
 // 也可以将匿名函数赋值给一个变量, 在合适的时候进行调用
 ff := func() {
    fmt.Println("这是一个匿名函数")
 }
 ​
 // 使用函数变量, 如果有参数的话,传入参数, 进行调用
 ff()

回调函数

 // 回调函数
 // 声明一种函数类型
 type CaluType func(int, int) int
 ​
 // 定义一个CaluType类型的函数
 func add(a, b int) int {
   return a + b
 }
 ​
 // 定义另一个CaluType类型的函数
 func sub(a, b int) int {
   return a - b
 }
 ​
 // 接收两个整型类型的数据和一个CaluType类型的函数
 func testFunc(a, b int, calu CaluType)  {
   res := calu(a, b)
   fmt.Println("结果为: ", res)
 }
 ​
 // 回调函数
 testFunc(3, 5, add)
 testFunc(3, 5, sub)

闭包函数

 // 闭包函数
 func testClosure() func(int)int {
   i := 2
   // 在函数内部捕获函数外部变量
   return func(value int) int {
     i += value
     return i
   }
 }
 ​
 // 每次调用testClosure函数都会返回一个新的函数实例
 // 因此返回的这个函数在调用的时候, i都会初始化为2, 所以调用这个函数输出的值是一样的
 fmt.Println(testClosure()(3))
 fmt.Println(testClosure()(3))
 fmt.Println(testClosure()(3))
 ​
 // 这里我们只调用了一次testClosure函数
 // 因此当我们调用closureFunc时, 对函数内部i的访问都是同一块内存
 closureFunc := testClosure()
 fmt.Println(closureFunc(3))
 fmt.Println(closureFunc(3))
 fmt.Println(closureFunc(3))

总结

Go中的函数几乎涵盖了所有语言的函数操作上的特性, 比如: 多参函数, 多返回值, 回调函数, 匿名函数和闭包函数. 如果接触过C语言, 你几乎可以瞬间学会使用Go中的函数, 也更加强大和灵活, 带给我们更加舒适的编程体验!!!