💜 想真正擅长编程? 从一门课程开始,100% 免费

《笔记》第十九篇 Go函数递归

第19讲 Go函数递归

本讲基于视频实际讲解内容整理


一、函数组成回顾

老师在讲解递归之前,先回顾了函数的组成部分:

TEXT
func    函数名    (参数列表)    返回类型    { 函数体 }
组成部分 说明 是否可省略
func 关键字 ❌ 不可省略
函数名 调用时使用 ❌ 不可省略(匿名函数除外)
参数列表 入参 ✅ 可以省略
返回类型 返回值类型 ✅ 可以省略
函数体 具体逻辑 ✅ 可以为空

老师强调:参数和返回类型可以省略,但不建议省略,应保持代码清晰。


二、递归概念

递归:函数在函数体内部调用自身。

递归的两个核心要素:

  1. 递归调用:函数调用自身
  2. 终止条件:满足某个条件时停止递归,不再调用自身

⚠️ 如果没有终止条件,递归会无限进行下去,最终导致栈溢出(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,否则结果丢失
参数变化 递归参数必须朝着终止条件方向变化
函数组成 名/参数/返回值,可省略但不建议

八、课后练习

  1. 编写递归函数 countdown(n int),从n递减打印到1
  2. 编写递归函数 sum(n int) int,计算 1+2+...+n 的和
  3. 编写递归函数 fibonacci(n int) int,计算第n个斐波那契数
  4. 故意去掉阶乘函数中的 return,观察输出结果的变化
  5. 用双重循环打印99乘法表
  6. 尝试写一个没有终止条件的递归,观察栈溢出错误