第19讲 Go函数递归
本讲基于视频实际讲解内容整理
一、函数组成回顾
老师在讲解递归之前,先回顾了函数的组成部分:
TEXT
func 函数名 (参数列表) 返回类型 { 函数体 }
| 组成部分 | 说明 | 是否可省略 |
|---|---|---|
| func | 关键字 | ❌ 不可省略 |
| 函数名 | 调用时使用 | ❌ 不可省略(匿名函数除外) |
| 参数列表 | 入参 | ✅ 可以省略 |
| 返回类型 | 返回值类型 | ✅ 可以省略 |
| 函数体 | 具体逻辑 | ✅ 可以为空 |
老师强调:参数和返回类型可以省略,但不建议省略,应保持代码清晰。
二、递归概念
递归:函数在函数体内部调用自身。
递归的两个核心要素:
- 递归调用:函数调用自身
- 终止条件:满足某个条件时停止递归,不再调用自身
⚠️ 如果没有终止条件,递归会无限进行下去,最终导致栈溢出(stack overflow)。
三、99乘法表实现
老师在课程中通过99乘法表演示了循环的使用,这是理解递归的基础:
3.1 双重循环实现
Go
package main
import "fmt"
func main() {
// 双重循环打印99乘法表
for i := 1; i <= 9; i++ {
for j := 1; j <= i; j++ {
fmt.Printf("%d×%d=%d\t", j, i, i*j)
}
fmt.Println()
}
}
输出:
TEXT
1×1=1
1×2=2 2×2=4
1×3=3 2×3=6 3×3=9
...
1×9=9 2×9=18 ... 9×9=81
3.2 用函数封装
Go
func printMultiplicationTable() {
for i := 1; i <= 9; i++ {
for j := 1; j <= i; j++ {
fmt.Printf("%d×%d=%d\t", j, i, i*j)
}
fmt.Println()
}
}
四、递归的实现
4.1 递归的基本结构
Go
func recursiveFunc(param) returnType {
if 终止条件 {
return 基准结果
}
// 递归调用(参数要朝着终止条件变化)
return recursiveFunc(变化后的param)
}
4.2 示例:递归打印数字
老师在课程中演示了一个递归案例:当 i < 10 时递归调用:
Go
package main
import "fmt"
// 递归打印1到9
func printNumber(i int) {
if i >= 10 {
return // 终止条件
}
fmt.Println(i)
printNumber(i + 1) // 递归调用,参数+1
}
func main() {
printNumber(1)
}
输出:
TEXT
1
2
3
4
5
6
7
8
9
4.3 示例:递归计算阶乘
Go
func factorial(n int) int {
if n <= 1 {
return 1 // 终止条件
}
return n * factorial(n-1) // 递归调用
}
func main() {
fmt.Println(factorial(5)) // 120
}
执行过程:
TEXT
factorial(5)
= 5 * factorial(4)
= 5 * 4 * factorial(3)
= 5 * 4 * 3 * factorial(2)
= 5 * 4 * 3 * 2 * factorial(1)
= 5 * 4 * 3 * 2 * 1
= 120
五、递归的注意事项
5.1 必须有终止条件
Go
// ❌ 错误:没有终止条件,无限递归
func badRecursion(i int) {
fmt.Println(i)
badRecursion(i + 1) // 永远不会停止!
}
5.2 递归调用后必须有return接收返回值
老师在课程中特别强调了一个常见错误:递归调用后忘记return,导致结果不正确。
Go
// ❌ 错误:忘记return,递归调用的结果被丢弃
func factorial(n int) int {
if n <= 1 {
return 1
}
factorial(n - 1) // 忘记return!结果丢失
// 实际返回的是0(int的零值),而不是阶乘结果
}
// ✅ 正确:递归调用前要有return
func factorial(n int) int {
if n <= 1 {
return 1
}
return n * factorial(n - 1) // 有return!
}
关键要点:递归调用本身也是一个函数调用,它的返回值需要通过
return传回给调用者。如果忘记return,递归调用的结果就被丢弃了。
5.3 参数要朝着终止条件变化
Go
// ✅ 正确:参数每次递增,朝着 i >= 10 的终止条件靠近
func printNumber(i int) {
if i >= 10 {
return
}
printNumber(i + 1) // i每次+1,最终会>=10
}
// ❌ 错误:参数不变,永远不会到达终止条件
func printNumber(i int) {
if i >= 10 {
return
}
printNumber(i) // i不变,无限递归!
}
六、编程拓展话题
老师在课程中还拓展讨论了一些编程相关的话题:
| 话题 | 内容 |
|---|---|
| 机器码 | 计算机直接执行的二进制指令 |
| 汇编语言 | 机器码的助记符表示,比机器码可读 |
| C++之父 | Bjarne Stroustrup 创造了C++语言 |
| 编程语言层次 | 机器码 → 汇编 → C/C++ → 高级语言 |
这些话题帮助同学们理解编程语言的发展脉络和底层原理。
七、本讲小结
| 知识点 | 要点 |
|---|---|
| 递归定义 | 函数调用自身 |
| 终止条件 | 递归必须有终止条件,否则栈溢出 |
| 99乘法表 | 双重循环实现 |
| 忘记return | 递归调用后必须有return,否则结果丢失 |
| 参数变化 | 递归参数必须朝着终止条件方向变化 |
| 函数组成 | 名/参数/返回值,可省略但不建议 |
八、课后练习
- 编写递归函数
countdown(n int),从n递减打印到1 - 编写递归函数
sum(n int) int,计算 1+2+...+n 的和 - 编写递归函数
fibonacci(n int) int,计算第n个斐波那契数 - 故意去掉阶乘函数中的
return,观察输出结果的变化 - 用双重循环打印99乘法表
- 尝试写一个没有终止条件的递归,观察栈溢出错误