(5-3)函数:函数的高级用法

5.3  函数的高级用法

在本章前面的内容中,已经讲解了Go语言函数的基本用法。在本节的内容中,将进一步讲解函数的高级用法,包括匿名函数(闭包)、递归函数等知识。

5.3.1  匿名函数

在 Go 语言中,可以使用匿名函数(Anonymous Function,也称为闭包Closure)创建没有名称的函数,使其成为第一类对象,可以像普通函数一样使用。创建匿名函数语法格式如下所示:

func(parameters) returnType {
    // 函数体
}
  1. parameters:是匿名函数的参数列表,可以为空或者有多个参数,用逗号分隔;
  2. returnType:是匿名函数的返回值类型,也可以为空。

匿名函数可以直接赋值给变量,形成一个函数变量。函数变量可以像普通函数一样被调用,也可以作为参数传递给其他函数,或者在函数中返回。

实例5-6:使用匿名函数(源码路径:Go-codes\5\niming.go

实例文件niming.go的具体实现代码如下所示。

func main() {
    // 定义匿名函数并将其赋值给变量 f
    f := func(x, y int) int {
        return x + y
    }

    // 调用函数变量 f
    result := f(3, 4)
    fmt.Println(result) // 输出:7

    // 匿名函数作为参数传递给函数
    funcAsParameter(f)

    // 匿名函数作为返回值
    g := returnFunc()
    result2 := g(3, 4)
    fmt.Println(result2) // 输出:12
}

// 函数接受一个函数变量作为参数
func funcAsParameter(f func(int, int) int) {
    result := f(5, 6)
    fmt.Println(result) // 输出:11
}

// 函数返回一个匿名函数
func returnFunc() func(int, int) int {
    return func(x, y int) int {
        return 2 * (x + y)
    }
}

在上述代码中,首先定义了一个匿名函数 f ()并将其赋值给函数变量。然后调用了函数 f ()的变量,并将其作为参数传递给另一个函数。最后,我们定义了一个函数returnFunc(),它返回一个匿名函数,并将其赋值给另一个函数变量,然后我们调用了这个函数变量。执行后会输出:

7
11
14

(1)将匿名函数用作回调函数

在 Go 语言中,匿名函数可以作为回调函数传递给其他函数,用于在某个事件发生时进行回调操作。举个例子,假设有一个函数 forEach()可以遍历整数切片,并将每个元素作为参数传递给回调函数进行处理,那么我们可以使用匿名函数作为回调函数,在每个元素上执行自定义的操作,比如打印元素的平方。

func forEach(nums []int, f func(int)) {
    for _, num := range nums {
        f(num)
    }
}

func main() {
    nums := []int{1, 2, 3, 4, 5}

    forEach(nums, func(num int) {
        fmt.Println(num * num)
    })
}

在上述代码中,我们使用匿名函数 func(num int) { fmt.Println(num * num) } 作为回调函数传递给 forEach 函数,当 forEach 函数遍历切片中的每个元素时,都会执行匿名函数中定义的操作,即打印元素的平方。执行上述代码将输出:

1
4
9
16
25

(2)使用匿名函数实现操作封装

在 Go 语言中,可以使用匿名函数实现封装操作。可以将一些操作封装在匿名函数中,并将其作为参数传递给另一个函数,这个函数再执行匿名函数中的操作。例如下面是一个例子,其中我们将对一个整数切片进行排序,但是我们可以在排序之前使用匿名函数实现对整数的修改。

实例5-7:使用匿名函数对整数进行修改(源码路径:Go-codes\5\sort.go

实例文件sort.go的具体实现代码如下所示。

import (
	"fmt"
	"sort"
)
func main() {
	nums := []int{3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5}

	// 使用匿名函数对整数进行修改
	for i := range nums {
		func(i int) {
			nums[i] *= 2
		}(i)
	}

	// 使用 sort 函数对整数切片进行排序
	sort.Ints(nums)

	fmt.Println(nums)
}

在上述代码中,首先使用匿名函数将整数修改为原来的两倍。这个匿名函数需要一个参数 i,并将整数切片中索引为 i 的元素乘以 2。然后我们使用 for 循环遍历整数切片,并将每个元素的索引作为参数传递给匿名函数。在匿名函数中,我们使用闭包的方式访问了 nums 切片。最后,我们使用标准库的 sort.Ints 函数对整数切片进行排序,并输出排序后的结果。执行后会输出:

[2 2 4 6 6 8 10 10 10 12 18]

在上述例子中,很多读者不知道哪个是匿名函数。在例子中,匿名函数是作为参数传递给函数numbersOperate()的。具体来说,这行代码中的匿名函数是:

func(numbers []int) {
    for i, n := range numbers {
        numbers[i] = operate(n)    }

}(numbers)

这个匿名函数实现了对数组numbers进行操作的逻辑,通过函数operate()对每个元素进行处理,并将处理结果存回到 numbers 数组中。它被直接传递给函数numbersOperate(),并在函数内部被调用。

5.3.2  递归函数

在 Go 语言中,递归函数指的是函数可以调用自身的一种特殊函数。递归函数是一种重要的编程技巧,用于解决一些需要反复处理的问题,例如树形结构的遍历、排序算法的实现等。

递归函数的基本思想是将一个大问题分解为若干个小问题,并且这些小问题与大问题具有相同的解法。这样一来,我们可以在函数内部不断调用自身来解决子问题,直到达到最小的子问题,然后将子问题的解合并起来,得到最终问题的解。递归函数有两个重要的部分:基本情况和递归情况。基本情况是指递归函数中的停止条件,一旦满足了该条件,递归就会停止,开始返回值。而递归情况是指递归函数中继续调用自身的情况,通过不断调用自身来处理子问题。

Go 语言中的递归函数使用和其他语言类似,在使用时需要注意以下三点:

  1. 基线条件:递归函数必须要有一个基线条件,也就是结束递归的条件,否则就会进入死循环。
  2. 递归条件:递归函数需要有一个递归条件,也就是在调用自身之前需要满足的条件。
  3. 函数调用:递归函数调用自身,需要注意函数的参数和返回值的传递。

实例5-8:使用递归函数计算阶乘(源码路径:Go-codes\5\factorial.go

实例文件factorial.go的具体实现代码如下所示。

// 计算阶乘的递归函数
func factorial(n int) int {
    if n == 0 {
        return 1
    } else {
        return n * factorial(n-1)
    }
}

func main() {
    // 计算 5 的阶乘
    fmt.Println("Factorial of 5:", factorial(5))

    // 计算 10 的阶乘
    fmt.Println("Factorial of 10:", factorial(10))
}

在上述代码中定义了一个递归函数 factorial(),用于计算输入的整数的阶乘。在 main 函数中,调用 factorial 函数分别计算了 5 和 10 的阶乘,并将结果打印出来。基本的计算过程是:在调用 Factorial(3) 函数时,会依次调用函数Factorial(2)、函数Factorial(1)和函数Factorial(0),直到函数Factorial(0)达到基本情况(即返回 1),然后逐层返回值,最终得到 Factorial(3) 的结果为 6。在如图5-8所示的流程图中,箭头表示函数调用关系,框内的数字表示每个函数的参数和返回值。

            ┌───────────────────┐
            │   Factorial(3)    │
            └───────────────────┘
                        │
                        ▼
            ┌───────────────────┐
            │   Factorial(2)    │
            └───────────────────┘
                        │
                        ▼
            ┌───────────────────┐
            │   Factorial(1)    │
            └───────────────────┘
                        │
                        ▼
            ┌───────────────────┐
            │   Factorial(0)    │
            └───────────────────┘
                        │
                        ▼
                   1 (base case)     

执行后会输出:

Factorial of 5: 120
Factorial of 10: 3628800

5.3.3  方法

在 Go 语言中,方法是一种特殊类型的函数,它与某个类型关联在一起,可以被该类型的值或指针调用。方法的语法与函数类似,但是需要在函数名前面加上接收者声明,接收者可以是值或指针类型。Go 语言中的方法分为两种:值方法和指针方法,具体说明如下:

  1. 值方法是针对该类型的值进行操作的方法,它的接收者声明中使用的是该类型的值。
  2. 指针方法是针对该类型的指针进行操作的方法,它的接收者声明中使用的是该类型的指针。

Go 语言中的方法可以像函数一样拥有返回值,也可以是没有返回值的。

举个例子,假设我们有一个结构体类型叫做 Person,定义如下:

type Person struct {
    Name string
    Age int
}

我们可以为这个类型定义一个方法,用于打印这个人的信息:

func (p Person) PrintInfo() {
    fmt.Printf("Name: %s, Age: %d\n", p.Name, p.Age)
}

这个方法的接收者是 Person 类型的值,它的名称为 PrintInfo。该方法可以通过以下方式调用:

p := Person{Name: "Alice", Age: 30}
p.PrintInfo()  // 输出: Name: Alice, Age: 30

如果我们想要修改这个人的信息,就需要定义一个指针方法:

func (p *Person) ChangeName(name string) {
    p.Name = name
}

这个方法的接收者是 Person 类型的指针,它的名称为 ChangeName。该方法可以通过以下方式调用:

p := &Person{Name: "Alice", Age: 30}
p.ChangeName("Bob")
p.PrintInfo()  // 输出: Name: Bob, Age: 30

在这个例子中,我们首先创建了一个指向 Person 的指针,然后调用了方法ChangeName(),将人名从 Alice 改为了 Bob。最后,我们又调用了方法PrintInfo(),打印了这个人的信息。

注意:有关指针的知识将在本书后面的内容中进行讲解。

  • 29
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

码农三叔

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值