# 第十五章 Golang 函数

## Go语言函数特性

1. 函数分类：
- 普通函数
- 匿名函数
- 方法

2. 函数不能重载，即不允许函数同名
3. 函数不能嵌套函数，但可以嵌套匿名函数
4. 函数可以赋值给变量
5. 函数可以作为参数传递给另一个函数
6. 函数的返回值可以是一个函数
7. 函数传参传递是参数的副本
8. 函数参数可以没有名称

## 函数定义

```go
func function_name([parameter list])[return_type] {
    // 函数体
}
```

示例

```go
package main

import "fmt"

func sum(a int, b int) (ret int) {
    return a + b
}

func main() {
    r := sum(1, 1)
    fmt.Printf("%v", r)
    // 2
}

```

## 函数返回值

没有返回值

```go
func foo(){
    fmt.Println("Hello")
}
```

一个返回值

```go
func sum(a int, b int) (ret int) {
    return a + b
}
```

多个返回值

```go
func foo() (name string, age int) {
    return "Tom", 23
}

// 省略命名
func foo() (string, int) {
    return "Tom", 23
}
```

多值返回常用于函数返回错误
```
value, exists
value, ok
value, err
```

返回值过多（4个以上），通常放在容器中返回

```
同类型 slice
不同类型 map
```

返回值不想使用，可以使用 `_` 丢弃

## 函数参数

参数类型：
- 形参 声明函数时的参数列表
- 实参 调用时传递的参数

特点：

- 参数可以0个或者有多个，需要指定数据类型
- 函数是传值的方式进行传参
- 可以使用变长参数`...`

示例
```go
package main

import "fmt"

// a， b形参
func sum(a int, b int) (ret int) {
    return a + b
}

func main() {
    // 1, 1 实参
    r := sum(1, 1)
    fmt.Printf("%v", r)
    // 2
}

```

注意：

- map、slice、interface、channel数据类型是指针，拷贝的是指针，有可能会改变原始数据

```go
package main

import "fmt"

func foo(arr []int) {
    arr[0] = 100
}

func main() {

    a := []int{1, 2, 3}

    foo(a)

    fmt.Printf("%v", a)
    // [100 2 3]
}

```

变长参数

```go
package main

import "fmt"

func foo(args ...int) {
    for _, value := range args {
        fmt.Printf("%v ", value)
    }
}

func main() {

    foo(1, 2, 3)

    // 1 2 3
}

```


多种参数混合使用

```go
package main

func foo(name string, isMan bool, arr ...int) {

}

func main() {

    foo("Tom", true, 1, 2, 3)

}

```

## 函数类型与函数变量

定义函数类型
```go
// 示例：接收两个参数，返回一个参数
type function_name func(int, int) int
```

示例

```go
package main

import "fmt"

// 定义函数类型
type foo func(int, int) int

func sum(a int, b int) int {
    return a + b
}

func max(a int, b int) int {
    if a > b {
        return a
    } else {
        return b
    }
}

func main() {
    // 声明
    var f foo

    f = sum
    s := f(1, 2)
    fmt.Printf("%v\n", s) // 3

    f = max
    m := f(1, 2)
    fmt.Printf("%v\n", m) // 2

}

```

## 高阶函数

go语言的函数，可以作为函数的参数，也可以作为函数返回值

函数作为参数

```go
package main

import "fmt"

func sayHello(name string) {
    fmt.Printf("Hello %s", name)
}

func foo(name string, fun func(string)) {
    fun(name)
}

func main() {

    foo("Tom", sayHello)
    // Hello Tom

}

```

函数作为返回值
```go
package main

import "fmt"

func sum(a int, b int) int {
    return a + b
}

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

func calc(name string) func(a int, b int) int {
    switch name {
    case "+":
        return sum
    case "-":
        return sub
    default:
        return nil
    }
}

func main() {
    sum := calc("+")
    r := sum(1, 1)
    fmt.Printf("%v", r)
    // 2
}

```


## 匿名函数

语法格式

```go
// 没有函数名称
func (参数列表)(返回值){
    
}
```

示例1

```go
package main

import "fmt"

func main() {
    sum := func(a int, b int) int {
        return a + b
    }

    ret := sum(1, 1)
    fmt.Printf("%v", ret)
    // 2
}

```

示例2

```go
package main

import "fmt"

func main() {
    ret := func(a int, b int) int {
        return a + b
    }(1, 1)

    fmt.Printf("%v", ret)
    // 2
}

```

## 闭包

闭包可以理解成：定义在一个函数内部的函数，本质上是将函数内部和函数外部连接起来的桥梁

```
闭包 = 函数 + 引用环境
```

示例1

```go
package main

import (
    "fmt"
)

// 返回一个函数
func add() func(int) int {
    var a int
    return func(b int) int {
        a += b
        return a
    }
}

func main() {
    f := add()
    fmt.Println(f(1)) // 1
    fmt.Println(f(1)) // 2
    fmt.Println(f(1)) // 3

    f2 := add()
    fmt.Println(f2(1)) // 1
    fmt.Println(f2(1)) // 2
    fmt.Println(f2(1)) // 3
}

```

示例2
```go
package main

import (
    "fmt"
    "strings"
)

// 返回一个函数
func makeSuffixFunc(suffix string) func(string) string {
    return func(name string) string {
        if strings.HasSuffix(name, suffix) {
            return name
        } else {
            return name + suffix
        }
    }
}

func main() {
    txtFunc := makeSuffixFunc(".txt")
    jpgFunc := makeSuffixFunc(".jpg")

    fmt.Println(txtFunc("test")) // test.txt
    fmt.Println(jpgFunc("test")) // test.jpg
}

```

示例3

```go
package main

import (
    "fmt"
)

// 返回两个函数
func calc(base int) (func(int) int, func(int) int) {

    add := func(i int) int {
        return base + i
    }

    sub := func(i int) int {
        return base - i
    }

    return add, sub
}

func main() {
    f1, f2 := calc(10)
    fmt.Println(f1(1), f2(2))
    // 11 8
}

```

## 递归

递归函数：函数内部调用函数自身的函数

递归函数特点

- 递归就是自己调用自己
- 必须定义退出条件，否则就会成为死循环
- 递归很可能会出现栈空间内存溢出

示例：阶乘

for循环实现

```go
package main

import "fmt"

// 阶乘
func foo(n int) int {
    ret := 1
    for i := 1; i <= n; i++ {
        ret *= i
    }

    return ret
}

func main() {
    // 5! = 5 x 4 x 3 x 2 x 1
    ret := foo(5)
    fmt.Println(ret)
    // 120
}

```

递归实现

```go
package main

import "fmt"

// 阶乘
func foo(n int) int {
    
    if n == 1 {
        // 退出条件
        return 1
    } else {
        // 自己调用自己
        return n * foo(n-1)
    }
}

func main() {
    // 5! = 5 x 4 x 3 x 2 x 1
    ret := foo(5)
    fmt.Println(ret)
    // 120
}

```

菲波那切数列

计算公式

```
f(n) = f(n-1) + f(n-2) 
且
f(2) = f(1) = 1
```

代码实现

```go
package main

import "fmt"

// 菲波那切数列
func foo(n int) int {
    if n == 1 || n == 2 {
        // 退出条件
        return 1
    } else {
        // 递归表达式
        return foo(n-1) + foo(n-2)
    }
}

func main() {
    // foo(5) = foo(4) + foo(3) = 3 + 2 = 5
    // foo(4) = foo(3) + foo(2) = 2 + 1 = 3
    // foo(3) = foo(2) + foo(1) = 1 + 1 = 2
    // foo(2) = 1
    // foo(1) = 1

    ret := foo(5)
    fmt.Println(ret)
    // 5
}

```

## defer语句

defer语句后面的语句延迟处理，在defer归属的函数即将返回时，将延迟处理的语句按照defer定义的逆序进行执行

也就是说，先定义的defer语句最后执行；后定义的defer语句，先执行

defer特性

- 关键字defer用于注册延迟调用
- 这些调用知道return前才被执行，可以用来做资源清理
- 多个defer语句，按照先进后出的顺序执行
- defer语句中的变量，defer声明时就决定了

defer用途

- 关闭文件句柄
- 锁资源释放
- 数据库连接释放

示例

```go
package main

import "fmt"

func main() {
    fmt.Println("start")

    defer fmt.Println("defer1")
    defer fmt.Println("defer2")
    defer fmt.Println("defer3")

    fmt.Println("end")

    // start
    // end
    // defer3
    // defer2
    // defer1
}

```

## init函数

init函数是特殊函数，会先于main函数执行，实现包级别的`初始化操作`

init函数特点

- init函数先于main函数`自动执行`，不能被其他函数调用
- init函数没有输入参数，没有返回值
- 每个包可以有多个init函数
- 包的每个源文件也可以有多个init函数
- 同一个包的init执行顺序，golang没有明确意义
- 不同胞的init函数按照包导入的依赖关系决定执行顺序


golang初始化顺序

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

示例

```go
package main

import "fmt"

var i int = initVar()

func initVar() int {
    fmt.Println("initVar")
    return 100
}

func init() {
    fmt.Println("init")
}

func main() {
    fmt.Println("main")
}

// initVar
// init
// main

```
