函数是一个可以重复功能单元, 简单来说就是一个执行特定任务的代码块, 是 GO 中的一等公民, 你可以把它当做参数,也可以当做返回值;

go 函数的一些特点

我们先来简单看一下 go 中函数的特点

多返回值

1
2
3
func swap(a, b int) (int, int) {
return b, a
}

命名返回值

1
2
3
4
5
func rectProps(length, width float64) (area, perimeter float64) {
area = length * width
perimeter = (length + width) * 2
return // 不需要明确指定返回值
}

可变参数

1
2
3
4
5
6
7
func sum(nums ...int) int {
total := 0
for _, num := range nums {
total += num
}
return total
}

高阶函数

1
2
3
4
5
6
7
func apply(nums []int, op func(int) int) []int {
result := make([]int, len(nums))
for i, num := range nums {
result[i] = op(num)
}
return result
}

闭包

1
2
3
4
5
6
7
func counter() func() int {
var i int
return func() int {
i++
return i
}
}

defer

1
2
3
4
5
func readFile(filename string) {
file, _ := os.Open(filename)
defer file.Close()
// 处理文件
}

函数作为值和类型

1
type binaryOperation func(a, b int) int

匿名函数和立即执行函数表达式

1
2
3
4
5
6
7
8
9
10
11
12
func main() {
// 匿名函数 以及 立即执行匿名函数
func(a int, b int) {
max := 0
if a > b {
max = a
} else {
max = b
}
fmt.Printf("max: %v\n", max)
}(1, 2)
}

go 中函数的定义和使用

函数在使用前先要进行定义, 就像是乐高积木中的 1x1 1x2 的基础颗粒,它是构成软件大厦的基本模块.函数通过重复调用实现了代码的复用.

函数的定义

  • func :定义函数的关键字
  • function_name: 函数名称
  • params_list: 参数列表
  • return_types: 返回值
  • 函数体: 函数要实现的功能代码集合体
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    func function_name( [params list] ) [return_types]
    {
    函数体
    }
    //egs:
    //1. define add sum
    func sum(a,b int) int{
    return a + b
    }
    //2. define get max
    func max(a,b int) int{
    if a > b {
    return a
    }
    return b
    }

函数的调用

1
2
s := sum(1,2)
max := max(1,4)

函数的返回值

函数可以有 0 或者多个 返回值, 返回只需要指定数据类型, 返回值的返回通过函数体中的 return 关键字指定

  • return 如果你在函数体中指明了返回值名称, 那么 return 后面跟不跟名称都行.
  • 如果返回值有名称的话, 那么必须使用括号包裹, 多个返回值使用 , 进行分割
  • 命名的返回值是预先生命好的, 在函数内部可以直接使用. 无需再次声明.但是不能与函数名重复
  • return 后面可以跟表达式,但是不能出现 赋值表达式
1
2
3
4
5
6
7
8
9
10
11
//eg:
func noReturn(){}
func oneReturn()(ref int){
return 99
}
func twoReturn()(one,two int){
one := 1
two := 2
return
}

函数类型 和 函数变量

在 Go 语言中,函数不仅是可执行的代码块,还是一种类型。这意味着你可以像使用其他类型(如 int,string 等)一样使用函数。这种能力让 Go 语言异常强大和灵活。让我们来看一下这两个概念:函数类型和函数变量。

函数类型

每个函数都有一个类型,该类型由函数的签名定义,即它的参数和返回值。例如:

1
2
3
func add(a int, b int) int {
return a + b
}

在这个例子中,函数 add 的类型是 (int, int) -> int,这意味着这个函数接受两个 int 类型的参数并返回一个 int 类型的值。

你甚至可以定义一个与该函数签名相同的新类型:
type AddFuncType func(int, int) int

函数变量

我们刚才提到, 函数也是一种类型, 那么函数也就可以像其他的数据类型一样,将函数赋值给一个变量:

1
2
3
4
var addFunc AddFuncType = add
// 或者更简单
// addFunc := add

现在,addFunc就是一个函数变量 : result := addFunc(1,2)

这种特性在你需要将 函数 作为 参数传递 , 或者 从其他函数返回参数的时候很有用;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
type MathFunc func(a, b int) int

func applyMathFunc(mf MathFunc, a int, b int) int {
return mf(a, b)
}

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

func main() {
var mathFunc MathFunc
mathFunc = add

result := applyMathFunc(mathFunc, 5, 7)
fmt.Println("Result:", result) // 输出 "Result: 12"
}

在这个例子中,我们定义了一个函数类型 MathFunc,然后创建了一个 MathFunc 类型的变量 mathFunc,并把 add 函数赋值给了它。然后,我们传递了这个函数变量到 applyMathFunc 函数中。

总的来说,函数类型和函数变量在 Go 语言中提供了巨大的灵活性,使你能够编写更加模块化和可重用的代码。这些特性也是函数式编程在 Go 语言中得以实现的基础。

高阶函数

高阶函数是一般指的是:

  • 接受 一个 或者 或者多个函数作为输入.
  • 输出一个参数.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    package main

    import "fmt"

    func applyFunc(f func(int) int, val int) int {
    return f(val)
    }

    func main() {
    f := func(i int) int {
    return i * 2
    }

    result := applyFunc(f, 4)
    fmt.Println(result) // 输出 8
    }

返回一个函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package main

import "fmt"

func multiplier(factor int) func(int) int {
return func(i int) int {
return i * factor
}
}

func main() {
timesTwo := multiplier(2)
fmt.Println(timesTwo(4)) // 输出 8

timesThree := multiplier(3)
fmt.Println(timesThree(4)) // 输出 12
}

或者定义一个函数, 既能接受函数作为一个参数, 同时也返回一个函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package main

import "fmt"

func compose(f, g func(int) int) func(int) int {
return func(x int) int {
return f(g(x))
}
}

func main() {
addOne := func(x int) int {
return x + 1
}
timesTwo := func(x int) int {
return x * 2
}

composed := compose(addOne, timesTwo)
fmt.Println(composed(2)) // 输出 5 (2*2 + 1)
}

应用场景

高阶函数在 Go 语言中有很多使用场景:

  • Map、Filter 和 Reduce 操作
  • 自定义排序逻辑
  • 中间件设计:在 web 服务中,中间件就是一个函数,它接受一个 HTTP 请求处理函数并返回一个新的处理函数。
  • 事件处理:例如,在 GUI 编程中,你可能会定义一些函数来响应按钮点击等事件。

匿名函数 和 闭包

匿名函数

当你想传递一个功能, 但觉得为期创建一个命名函数太过麻烦, 你可以选择匿名函数.

1
2
3
func (params)returnType{
// 函数体
}

如果你希望理解执行这个函数, 可以直接在括号后面执行:

1
2
3
func(){
fmt.Println("")
}()

或者将匿名函数赋值给变量

1
2
3
add := func(a,b int) int{
return a + b
}

或者就像前面提到的, 作为函数的参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package main

import "fmt"

func operateOnInt(a int, b int, f func(int, int) int) int {
return f(a, b)
}

func main() {
sum := operateOnInt(2, 3, func(a int, b int) int {
return a + b
})
fmt.Println(sum) // 输出 5
}

闭包

在 go 中, 匿名函数经常与闭包一起使用. 闭包是一个函数值, 他引用了函数体之外的变量, 意味着这个函数记住了这些变量的值或引用.
如下面的代码, intSeq 返回了一个匿名函数, 包含一个对外部 i 的引用.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package main

import "fmt"

func intSeq() func() int {
i := 0
return func() int {
i++
return i
}
}

func main() {
// 虽然 匿名函数 被返回了, 但是 匿名函数携带了 i 的状态; 此时 i = 1; 随后每次调用都会递增 1
nextInt := intSeq()

fmt.Println(nextInt()) // 输出 1
fmt.Println(nextInt()) // 输出 2
fmt.Println(nextInt()) // 输出 3
}

你可以看到如何在 Go 中创建闭包,以及如何使用闭包来保存状态。这种特性在需要保存状态或者需要生成多个相似行为但状态不同的函数时非常有用。