GO 函数式编程 | 青训营笔记

35 阅读3分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 3 天。

函数是Go语言的一等公民,其封装了一系列的指令操作。

1. 函数

函数的定义

func 函数名 (形参列表) (返回值列表) {
    函数体
}
func main() {
    println(add(1, 2))
    println(calculate(1, 2, 3))
}

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

func calculate1(a, b, c int) (int, int) {
    sum := a + b + c
    avg := sum / 3
    return sum, avg
}

func calculate2(a, b, c int) (sum int, avg int) {
    sum = a + b + c
    avg = sum / 3
    return
}

// 可变参数列表
func sum(nums ...int) int {
    sum := 0
    for _, n := range nums {
        sum += n
    }
    //for i := 0; i < len(nums); i++ {
    //    sum += nums[i]
    //}
    return sum
}
  • 参数若类型一致,可以向上面那样声明多个参数
  • 返回值支持多个返回值,在 return 时按顺序赋值返回
  • 若返回值定义了变量名,则 return 后面可以为空
  • Go 的函数没有重载,参数默认值,可选参数
  • 函数也是一种数据类型,可以作为参数传递和返回值

反射获取函数的一些信息:

fmt.Println(reflect.TypeOf(add))
// func(int, int) int
fmt.Println(
    runtime.FuncForPC(
        reflect.ValueOf(add).Pointer(),
    ).Name(),
)
// main.add

2. init 函数

Go 语言的每个源文件中都可以包含一个 init 函数,该函数在 main 函数之前被调用,用以完成一些初始化操作。

  • 一个 Go 源文件的执行顺序:1. 包变量的定义和初始化;2. init 函数;3. main 函数
package main

import "fmt"

var a = variable()

func variable() int {
    fmt.Println("包变量的初始化时执行 variable")
    return 1
}

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

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


// 包变量的初始化时执行 variable
// init
// main
  • import 其他源文件,会按引入顺序依次执行,
package beimport

import "fmt"

var A = variable()

func variable() int {
    fmt.Println("beimport.go#variable")
    return 1
}

func init() {
    fmt.Println("beimport.go#init")
}
package main

import "fmt"
import "awesomeProject/funcdemo/beimport"

var a = variable()

func variable() int {
    fmt.Println("variable")
    return 1
}

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

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

    fmt.Println(beimport.A)
}

// beimport.go#variable
// beimport.go#init
// variable
// init
// main
// 1 

3. 闭包

如果一个函数,访问到了它的外部(局部)变量的值,那么这个函数和他所处的环境,称为闭包

func Accumulator(init int) func(int) int {
    n := init

    return func(x int) int {
        n += x
        return n
    }
}

func main() {
    accumulator := Accumulator(10)
    fmt.Println(accumulator(1)) // 11
    fmt.Println(accumulator(2)) // 13
    fmt.Println(accumulator(3)) // 16
}
  • accumulator := Accumulator(10) 这次调用结束后,一个函数变量被赋值给了 accumulator 并成为了一个闭包
  • 在闭包 accumulator 中,函数 Accumulator 的局部变量 n 逃逸了。n 的生命周期没有随着函数 Accumulator 的作用域的结束而结束。

4. defer

当函数执行到 defer 语句时,不会立即执行该语句;而是将该语句压入一个 defer 栈中。
然后再函数 return 之前,将 defer 栈中压入的语句一条一条弹出来执行。

func main() {
fmt.Println(1)
fmt.Println(2)

defer fmt.Println("defer 1")
defer fmt.Println("defer 2")
defer fmt.Println("defer 3")

fmt.Println(3)
fmt.Println(4)
}

// 1
// 2
// 3
// 4
// defer 3
// defer 2
// defer 1
  • 入栈时,函数的入参也会被一起拷贝好放入栈中
func main() {
    n := 0
    defer fmt.Println(n) // 0
    n = 1
}

可以用指针来接受新的值

func main() {
n := 0
p := &n

defer testDefer(p) // 1
n = 1
}

func testDefer(p *int) {
fmt.Println(*p)
}