Go笔记(九):函数

1、函数

  函数一般用于执行某一特定的任务,go函数有三个要素:函数名称、返回类型、参数。

2、函数特性

  1、go中不允许函数重载(overload),不允许函数同名;

  2、go中函数不能嵌套函数,但可以嵌套匿名函数;

  3、函数可作为参数传递给另一个函数;

  4、函数的返回值可以是一个函数;

  5、函数调用的时候,如果有参数传递给函数,则先拷贝参数的副本,再将副本传递给函数。

3、函数的声明

func function_name( [parameter list] ) [return_types] {
   // 函数体
}

  function_name:函数名称,必选

  [parameter list]:参数,可选

  [return_types]:返回类型,可选

  func:关键字,用来声明函数,必选

1、无参、无返回类型的函数

  main函数也是一个无参、无返回类型的函数。

 1 package main
 2 
 3 import "fmt"
 4 
 5 // 无参 无返回值的函数
 6 func demo() {
 7    fmt.Printf("demo() 无参数无返回值")
 8 }
 9 
10 // 函数
11 func main() {
12    demo()
13 }

  执行结果如下: 

  

2、有参、有返回类型的函数

  在go函数中,可以不设置返回变量名。

 1 package main
 2 
 3 import "fmt"
 4 
 5 // 获取两数的最大值
 6 func getMaxValue(a int, b int) (max int) {
 7    if a >= b {
 8       max = a
 9    } else {
10       max = b
11    }
12    return max
13 }
14 
15 // go函数可不设置返回变量
16 func getMinValue(a int, b int) int {
17    if a <= b {
18       return a
19    } else {
20       return b
21    }
22 }
23 
24 // 函数
25 func main() {
26 
27    maxValue := getMaxValue(4, 6)
28    fmt.Printf("getMaxValue(4, 6)  --> 最大值:%v\n", maxValue)
29 
30    minValue := getMinValue(4, 6)
31    fmt.Printf("getMinValue(4, 6)  --> 最小值:%v\n", minValue)
32 
33 }

  执行结果如下:

  

3、go函数返回值可以有多个

 1 package main
 2 
 3 import "fmt"
 4 
 5 // go函数可以有多个返回值
 6 func getInfo() (string, string) {
 7    return "zs", "China"
 8 }
 9 
10 // 返回值
11 func main() {
12 
13    name, addr := getInfo()
14    fmt.Printf("getInfo() --> name = %v, addr = %v\n", name, addr)
15 
16 }

  执行结果如下:

4、函数的参数

1、参数必须指定数据类型

 1 package main
 2 
 3 import "fmt"
 4 
 5 // 形参列表
 6 func sub(a int, b int) int {
 7    return a - b
 8 }
 9 
10 // 参数
11 func main() {
12    // 实参列表
13    subValue := sub(4, 6)
14    fmt.Printf("sub(4, 6) --> subValue = %v\n", subValue)
15 }

  执行结果如下:

2、参数传递

2.1、值传递

  值传递,传递给函数的是该值拷贝后的副本,函数内部访问、修改的也是这个副本,不影响调用函数外部的值。

 1 package main
 2 
 3 import (
 4    "fmt"
 5 )
 6 
 7 // 值传递, 函数内修改的是副本内容
 8 func transferValue(num int) {
 9    num = 4
10 }
11 
12 // 值传递
13 func main() {
14    // 值传递
15    var num = 6
16    transferValue(num)
17    fmt.Printf("transferValue(num) --> num = %v\n", num)
18 }

  执行结果如下:

2.2、引用传递

  引用传递,map、slice、interface、channel等数据类型是指针类型,此类型的值传递拷贝的是指针,拷贝后的参数仍然指向底层数据结构,修改后会影响外部数据结构的值。

 1 package main
 2 
 3 import (
 4    "fmt"
 5 )
 6 
 7 // 引用传递
 8 func transferArr(slice01 []int) {
 9    slice01[0] = 100
10 }
11 
12 func transferMap(map01 map[string]string) {
13    map01["name"] = "jack"
14 }
15 
16 // 引用传递
17 func main() {
18 
19    // 切片传递
20    slice := []int{1, 2, 3}
21    transferArr(slice)
22    fmt.Printf("slice: %v\n", slice)
23    // map传递
24    map01 := map[string]string{
25       "name": "timi",
26       "age": "18",
27    }
28    transferMap(map01)
29    fmt.Printf("map01: %v\n", map01)
30 }

  执行结果如下:

3、变长参数

  go中变长参数的声明需要在参数类型前加上 ... , 例如 (params ...int),示例如下:

 1 package main
 2 
 3 import (
 4    "fmt"
 5 )
 6 
 7 // 变长参数
 8 func variableArgs(params ...int) int {
 9    sum := 0
10    for _, v := range params {
11       sum += v
12    }
13    return sum
14 }
15 
16 // 函数
17 func main() {
18 
19    // 可变参数1,2,3
20    sum01 := variableArgs(1, 2, 3)
21    fmt.Printf("sum01: %v\n", sum01)
22    // 可变参数1,4,5,6
23    sum02 := variableArgs(1, 4, 5, 6)
24    fmt.Printf("sum02: %v\n", sum02)
25 }

  执行结果如下:

5、函数类型与函数变量

  go中,可以使用type关键字定义函数类型变量,声明的变量可以被赋值函数,语法格式如下:

type fun func(int, int) int

  示例代码如下:

 1 package main
 2 
 3 import (
 4    "fmt"
 5 )
 6 
 7 // 求和函数
 8 func add(a int, b int) int {
 9    return a + b
10 }
11 
12 // 函数
13 func main() {
14 
15    // 声明函数类型
16    type varfunc func(int, int) int
17    // 声明函数变量
18    var vf varfunc
19    // 函数变量赋值,赋予的函数必须与函数变量的类型有相同的参数类型与返回值
20    vf = add
21    // 函数变量使用
22    sumValue := vf(1, 2)
23    fmt.Printf("sumValue: %v\n", sumValue)
24 }

  执行结果如下:

6、函数参数与函数返回值

  go中的函数,可作为函数的参数,传递给另外一个函数,也可以作为另外一个函数的返回值返回。

6.1、函数作为参数

 1 package main
 2 
 3 import (
 4    "fmt"
 5 )
 6 
 7 // 函数作为参数
 8 func sayHello(name string) {
 9    fmt.Printf("Hello, %s\n", name)
10 }
11 func paramByFunc(name string, f func(string)) {
12    f(name)
13 }
14 
15 // 函数
16 func main() {
17    
18    // 函数作为参数
19    paramByFunc("timi", sayHello)
20 }

  执行结果如下:

6.2、函数作为返回值

 1 package main
 2 
 3 import (
 4    "fmt"
 5 )
 6 
 7 // 求和函数
 8 func add(a int, b int) int {
 9    return a + b
10 }
11 
12 // 相减函数
13 func sub(a int, b int) int {
14    return a - b
15 }
16 
17 // 函数作为返回值
18 func returnValueByFunc(methodName string) func(int, int) int {
19    switch methodName {
20       case "add":
21          return add
22       case "sub":
23          return sub
24       default:
25          return nil
26    }
27 }
28 
29 // 函数
30 func main() {
31 
32    // 函数作为返回值
33    sumFunc := returnValueByFunc("add")
34    sumResult := sumFunc(3, 4)
35    fmt.Printf("sumResult: %v\n", sumResult)
36 }

  执行结果如下:

7、匿名函数

  go语言函数不能嵌套,但可在函数内部定义匿名函数。匿名函数是指 没有名称的函数,语法如下

func ( [parameter list] ) ([return_types])

  [parameter list]:参数列表,可以有多个,也可以没有;

  [return_types]:函数返回列表,可由多个,也可以没有。

示例代码如下:
 1 package main
 2 
 3 import (
 4    "fmt"
 5 )
 6 
 7 // 匿名函数
 8 func anonymousFunc() {
 9    // 声明函数类型
10    type concatFunc func(string, string) string
11    // 声明函数变量
12    var cf concatFunc
13    // 声明匿名函数,并赋值给函数变量
14    cf = func(param1 string, param2 string) string {
15       return param1 + " " + param2
16    }
17    res := cf("hello", "timi")
18    fmt.Printf("anonymousFunc -> res: %v\n", res)
19 
20     // 自身调用 匿名函数
21     sumResult := func(a int, b int) int {
22         return a + b
23     }(4, 5)
24     fmt.Printf("anonymousFunc -> sumResult: %v\n", sumResult)
25 }
26 
27 // 函数
28 func main() {
29    // 匿名函数
30    anonymousFunc()
31 }

  执行结果如下:

8、闭包

  闭包是可以包含自由变量的代码块。闭包 = 函数 + 引用外部作用域变量

  示例代码如下:

 1 package main
 2 
 3 import (
 4    "fmt"
 5    "strings"
 6 )
 7 
 8 // 闭包
 9 // 获取序列号
10 func getSequence() func() int {
11    i := 0
12    return func() int {
13       i += 1
14       return i
15    }
16 }
17 // 变量nextNumber是一个函数,并且引用了外部作用域中的i变量,此时nextNumber就是一个闭包。
18 // 在nextNumber的生命周期内,变量i一致有效
19 func closePackDemo() {
20    /* nextNumber 为一个函数,函数 i 为 0 */
21    nextNumber := getSequence()
22 
23    /* 调用 nextNumber 函数,i 变量自增 1 并返回 */
24    fmt.Println(nextNumber())
25    fmt.Println(nextNumber())
26    fmt.Println(nextNumber())
27 
28    /* 创建新的函数 nextNumber1,并查看结果 */
29    nextNumberNew := getSequence()
30    fmt.Println(nextNumberNew())
31    fmt.Println(nextNumberNew())
32 }
33 // 添加后缀
34 func appendSuffix(suffix string) func(string) string {
35    return func(name string) string {
36       if !strings.HasSuffix(name, suffix) {
37          return name + suffix
38       }
39       return name
40    }
41 }
42 func closePackDemo2() {
43    // 初始化外部作用域变量 suffix
44    jpgFunc := appendSuffix(".jpg")
45    txtFunc := appendSuffix(".txt")
46    // 引用外部作用域的suffix,jpgFunc、txtFunc是一个闭包
47    fmt.Printf("jpgFunc(\"test\"): %v\n", jpgFunc("img"))
48    fmt.Printf("txtFunc(\"新建文件夹\"): %v\n", txtFunc("新建文件夹"))
49 }
50 // 获取计算参数结果
51 func cal(base int) (func(int) int, func(int) int) {
52    add := func(i int) int {
53       base += i
54       return base
55    }
56    sub := func(i int) int {
57       base -= i
58       return base
59    }
60    return add, sub
61 }
62 func closePackDemo3() {
63    fun01, fun02 := cal(5)
64    // 5 + 1 、 5 + 1 - 2
65    fmt.Println(fun01(1), fun02(2))
66    // 5 + 1 - 2 + 10、  5 + 1 - 2 + 10 - 6
67    fmt.Println(fun01(10), fun02(6))
68    // 重置生命周期
69    fun03, fun04 := cal(10)
70    fmt.Println(fun03(1), fun04(2))
71 }
72 
73 // 函数
74 func main() {
75 
76    // 闭包
77   closePackDemo()
78   fmt.Println("======================")
79   closePackDemo2()
80   fmt.Println("======================")
81   closePackDemo3()
82 }

  执行结果如下:

9、递归

递归:函数内部调用函数自身

  ·递归是函数调用自身;

  ·必须先定义函数退出条件,若无退出条件,递归将成为死循环;

  ·go语言递归函数很可能会产生一大堆goroutine,也可能会出现栈空间内存溢出问题。

 1 package main
 2 
 3 import (
 4    "fmt"
 5    "strings"
 6 )
 7 
 8 // 递归
 9 // 斐波那契数列  F(0)=0,F(1)=1, F(n)=F(n - 1)+F(n - 2)(n ≥ 2
10 func fibonacci(n int) int {
11    if n == 0 {
12       return 0
13    }
14    if n == 1 || n == 2 {
15       return 1
16    }
17    return fibonacci(n-1) + fibonacci(n-2)
18 }
19 // 阶乘 n * (n-1) *...* 1
20 func factorial(n int) int {
21    if n == 1 {
22       return 1
23    }
24    return n * factorial(n-1)
25 }
26 
27 // 函数
28 func main() {
29 
30    // 递归应用
31    fibonacci := fibonacci(5)
32    factorial := factorial(5)
33    fmt.Printf("fibonacci: %v, factorial: %v\n", fibonacci, factorial)
34 }

  执行结果如下:

10、defer语句

  defer语句会将其后跟随的语句进行延迟处理。延迟处理的语句按defer定义的逆序进行执行。先被defer的语句后执行,后被defer的语句先执行。

有点像栈的先进后出,defer最先定义的语句最后执行。

-> defer特性

  1、关键字 defer 用于注册延迟调用

  2、调用直到 return 前才被执行,可以用来做资源清理

  3、多个defer语句,按先进后出的方式执行

  4、defer语句中的变量,在defer声明时就已决定。

 1 package main
 2 
 3 import (
 4    "fmt"
 5    "strings"
 6 )
 7 
 8 // defer延迟处理,常用于
 9 //    1、关闭文件句柄
10 //    2、释放锁资源
11 //    3.数据库连接释放
12 func deferFunc() {
13    fmt.Println("start...")
14    // 关键字 defer 用于注册延迟调用
15    // 多个defer语句,按先进后出的方式执行
16    defer fmt.Println("run01...")
17    defer fmt.Println("run02...")
18    defer fmt.Println("run03...")
19    fmt.Println("end...")
20 }
21 
22 // 函数
23 func main() {
24 
25    // defer延迟执行
26    deferFunc()
27 }

  执行结果如下:

 

11、init函数

  init初始化函数,先于main函数执行。

1、init函数主要特点

  1、init函数先于main函数自动执行,不能被其他函数调用;

  2、init函数没有输入参数、返回值;

  3、每个包可以有多个init函数;包的每个源文件也可以有多个init函数;

  4、不同包的init函数按照包导入的依赖关系决定执行顺序

2、初始化顺序

  变量初始化 -> init() -> main

 1 package main
 2 
 3 import "fmt"
 4 
 5 // 初始化顺序: 变量初始化 -> init() -> main()
 6 // init函数
 7 func init() {
 8     fmt.Println("init()...")
 9 }
10 
11 // 变量初始化
12 var num int = getNum()
13 func getNum() int {
14     fmt.Println("var...")
15     return 100
16 }
17 
18 // main函数
19 func main() {
20     fmt.Println("main...")
21 }

  执行结果如下: 

 

 
posted @ 2023-04-27 10:29  无虑的小猪  阅读(32)  评论(0编辑  收藏  举报