第5章 程序与递归——三看计算机的本质
章节核心概览
本章围绕“程序构造逻辑”与“大规模重复执行解决方案”两大核心,拆解计算机的本质:程序是通过组合与抽象构建的逻辑集合,而递归与迭代则是处理重复任务的高效工具,最终实现“以有限代码应对无限计算场景”的核心目标。
5.1 计算系统与程序
核心定位:计算系统的运行本质是执行程序,程序是连接“用户需求”与“硬件执行”的桥梁,是将抽象逻辑转化为具体计算结果的载体。
关键意义:理解程序的构造原理(组合、抽象)是解锁计算机工作逻辑的基础,为后续复杂程序设计提供思想支撑。
5.2 程序——组合、抽象与构造
程序的核心构建逻辑是“从简单到复杂”:通过基础单元的组合形成功能模块,再通过抽象提取通用逻辑,实现高效复用与灵活扩展。
5.2.1 一种简单的语言——运算组合式
定义:程序构造的基础语言形式,以“运算单元+组合规则”为核心,如“a+b×c”这类遵循语法规则的表达式。
作用:提供最基础的逻辑表达范式,是复杂程序语法与逻辑的“最小单元”。
5.2.2 组合——构造与执行
核心逻辑:将简单运算或功能单元按既定规则拼接(如“运算A+运算B”“条件+执行语句”),形成具备特定功能的程序模块。
执行特征:组合后的程序需遵循“顺序执行”或“规则驱动执行”逻辑,确保每一步操作的准确性与连贯性。
5.2.3 第1种形式的抽象——构造与替换—执行
抽象本质:提取同类任务的“共性逻辑”,构造通用模块(如“求两数之和”的通用函数)。
执行流程:构造通用模块→根据具体需求替换模块中的可变参数→调用模块完成执行,核心价值是减少重复编码,简化程序结构。
5.2.4 第2种形式的抽象——构造与替换—执行
核心差异:与第一种抽象的“参数替换”不同,此形式侧重“逻辑片段替换”,适用于更复杂的场景(如将“数据校验逻辑”作为抽象模块,替换不同场景的校验规则)。
核心优势:进一步提升程序的灵活性,实现“通用框架+定制逻辑”的高效构造模式。
5.2.5 带条件的计算规则及其构造
核心需求:解决“多场景分支”问题,让程序具备“判断能力”。
构造逻辑:定义“条件表达式+分支执行语句”(如“若x>0则执行A,否则执行B”),使程序能根据输入或状态灵活响应,是程序功能从“单一”到“复杂”的关键突破。
5.2.6 关于运算组合式的延伸理解——函数式编程语言与命令式编程语言
编程范式 核心逻辑 典型特征 延伸案例
函数式编程语言 侧重运算组合与无副作用抽象 以函数为核心,避免状态修改 Haskell、Scala
命令式编程语言 侧重指令序列与状态改变 明确“步骤化执行”,如循环、赋值 C、Java
本质关联:两种范式均源于“运算组合式”,只是对“组合”与“抽象”的侧重不同,分别适用于逻辑复用优先(函数式)和执行效率优先(命令式)的场景。
5.3 大规模重复执行规则的程序构造——递归与迭代
当程序需要处理“重复执行同一逻辑”的场景(如遍历、计算序列)时,递归与迭代是两种核心构造方式,二者均能通过有限代码实现无限次重复执行。
5.3.1 递归的感性认识——具有自相似性的重复事物
核心特征:“整体与部分结构相似”,如分形图形(雪花、蕨类植物)、嵌套文件夹结构。
思想迁移:程序中可将复杂重复任务拆解为“与原任务结构一致但规模更小”的子任务,为递归构造提供直观思路。
5.3.2 数学中的递推式与数学归纳法
递推式:通过“初始值+递推关系”定义序列(如斐波那契数列:F(1)=1,F(n)=F(n-1)+F(n-2)),是递归的数学原型。
数学归纳法:通过“验证基础情况(n=1成立)+ 假设n=k成立推导n=k+1成立”,证明无限序列的正确性,为递归的逻辑合理性提供数学支撑。
5.3.3 计算中的递归及递归函数——构造
递归函数定义:函数在自身定义中直接或间接调用自身(如“求n的阶乘”函数:fact(n)=n×fact(n-1))。
构造三要素:
1. 终止条件:明确递归停止的边界(如fact(1)=1),避免无限循环;
2. 递归关系:将原问题拆解为规模更小的同类子问题(如fact(n)依赖fact(n-1));
3. 返回逻辑:子问题结果组合为原问题结果。
5.3.4 两个不同的递归函数(实例解析)
实例1:阶乘计算(线性递归)
python
def fact(n):
if n == 1: # 终止条件
return 1
return n * fact(n-1) # 递归关系:规模递减
特征:递归调用一次只产生一个子问题,执行流程呈线性。
实例2:斐波那契数列(树形递归)
python
def fib(n):
if n <= 2: # 终止条件
return 1
return fib(n-1) + fib(n-2) # 递归关系:一次调用产生两个子问题
特征:递归调用会产生多个子问题,执行流程呈树形,需注意重复计算问题(可通过缓存优化)。
5.3.5 递归的运用——用有限的语句定义对象的无限集合
核心优势:突破“有限代码”的限制,描述无限集合(如“所有自然数”可定义为:1是自然数,自然数+1也是自然数)。
典型场景:无限序列生成、嵌套结构定义(如XML/JSON数据、树结构)。
5.3.6 程序的递归构造——自身调用自身,高阶调用低阶
构造原则:
1. 自身调用自身:确保子问题与原问题类型一致;
2. 高阶调用低阶:复杂问题(高阶)依赖规模更小的问题(低阶)结果,逐步逼近终止条件。
本质:通过“问题拆解+结果聚合”,将复杂任务转化为可重复执行的简单子任务。
5.3.7 递归与迭代/循环的关系
对比维度 递归 迭代(循环)
核心逻辑 问题拆解(分而治之) 循环执行(重复步骤)
代码特征 简洁直观,贴近逻辑描述 步骤明确,需手动控制循环条件
资源消耗 依赖函数调用栈,可能溢出 仅需少量变量,效率更高
适用场景 复杂嵌套问题(如树遍历) 简单重复任务(如累加、遍历数组)
相互转换 多数递归可转化为迭代(需手动维护栈) 迭代均可转化为递归(逻辑更简洁)
5.3.8 关于递归函数的延伸理解
尾递归优化:将递归调用放在函数末尾(如fact_tail(n, acc)),使编译器可复用栈帧,避免栈溢出;
递归与动态规划:递归拆解问题的思路可结合缓存(如备忘录法),解决动态规划中的优化问题(如最长公共子序列);
高阶应用:递归是函数式编程、分治算法、回溯算法的核心思想,是解决复杂问题的“通用工具”。
补充拓展与学习思考
1. 延伸学习资源
视频学习资源目录5(标*者为延伸内容):可重点观看“递归函数优化”“两种编程范式实战”相关视频,强化实践理解。
2. 核心思考题(个人思考与解答)
思考题1:如何判断一个问题是否适合用递归解决?
个人解答:满足“自相似性”(问题可拆解为同类子问题)、“存在明确终止条件”、“子问题规模可逐步缩小”的问题,优先用递归;若子问题重复率高(如斐波那契数列),需结合缓存优化。
思考题2:函数式编程与命令式编程的核心差异对实际开发有何影响?
个人解答:函数式编程更适合并发场景(无状态、无副作用),代码复用率高;命令式编程执行效率更高,更贴近硬件执行逻辑,适合性能敏感场景(如嵌入式开发)。实际开发中可混合使用(如Java中Stream API结合循环)。
思考题3:递归转化为迭代时,核心需要解决什么问题?
个人解答:需手动维护“递归栈”的核心信息(如子问题参数、执行状态),用循环模拟递归的调用与返回流程,确保终止条件与递归关系不变。
3. 学习体会与创新思考
学习体会:本章的核心是“抽象思维”——组合是“横向扩展”,将简单单元拼接为复杂模块;抽象是“纵向提炼”,将重复逻辑转化为通用工具;递归则是“维度突破”,用有限代码应对无限场景。
创新思考:递归的“自相似性”可迁移到生活中的问题解决(如项目管理中的“拆解任务”),而程序构造的“组合+抽象”思想,本质是“化繁为简”的通用逻辑,不仅适用于编程,也适用于其他领域的逻辑构建。