📄 一句话概括
模式匹配四行 case 干掉 if+下标两件事;递归求 1~5 只需 5 层,但默认上限 1000 层,900 个能算、1100 个直接崩。
📝 本节和前面几节的关系
A.7 说不能改东西,这一节说那循环怎么写——靠递归,靠模式匹配。
一、实测:模式匹配
4 个分支:空列表、一个元素、两个元素、多个元素(兜底)。
注意它把【判断】和【取值】合成一步:case [x, y] 既判了长度是 2,又把两个元素绑好了名。
写成 if 要几行?至少 3 行(判长度、取下标、再取下标)。
二、实测:递归的硬限制
递归求 1~5 的和:调用 5 层,结果 15。 默认递归上限 1000 层;900 个元素成功、1100 个元素抛 RecursionError。 这不是风格问题,是能不能跑的问题——所以函数式语言要么优化尾递归、要么把递归藏进库。
三、两种写法怎么选
数据结构本身是递归的(树、链表、JSON)→ 递归最自然。 只是要走一遍线性的东西→ 循环或推导式更省。 判断依据:你在处理的这个东西,是不是【自己包含自己】?
四、动手 Demo:特征6:使用模式匹配和递归来编写循环处理
用 match/case 对四种输入分类。 用递归求和,并试探递归深度上限。
def kind(v):
match v:
case []:
return "空列表"
case [x]:
return "一个元素 %r" % x
case [x, y]:
return "两个元素 %r/%r" % (x, y)
case _:
return "多个元素"
def rsum(xs):
if not xs:
return 0
return xs[0] + rsum(xs[1:])
print(rsum([1, 2, 3, 4, 5])) # 15
print(kind([1, 2])) # 两个元素 1/2
实跑输出:
--------------------------------------------------------------------
特征6:使用模式匹配和递归来编写循环处理
--------------------------------------------------------------------
函数式没有 `for`,**循环靠【递归】和【模式匹配】。**实测两者:
输入 匹配结果
-------------------------------
[] 空列表
[1] 一个元素 1
[1, 2] 两个元素 1/2
[1, 2, 3, 4] 多个元素
匹配的分支数 4 个(含兜底)
递归求和 1~5 15
递归调用层数 5 层
默认递归上限 1000 层
递归 900 个元素 成功 = True
递归 1100 个元素 成功 = False
**模式匹配把【判断+取值】合成一步**,四行 case 干掉了 if+下标两件事。
**递归的代价在这里很硬:超过 1000 层直接抛 RecursionError。**
**所以函数式语言要么优化尾递归、要么把递归交给库**——**这不是风格问题,是能不能跑的问题。**
实测:模式匹配 4 个分支,递归求 1~5 只需 5 层,结果 15。 默认上限 1000 层:900 个能算、1100 个直接抛 RecursionError。 所以递归能不能用,先看深度会不会超——这是硬门槛。
30 秒实验
- 用 match/case 把一段 if/elif 的判断链重写一遍,看少几行。
- 把一段递归改成循环,比较两者在 10000 个元素上的表现。
常见误区
- 以为递归比循环高级。深度一超就崩,这不是高级。
- 以为模式匹配只是 switch。它能同时判结构并绑定变量。
自测三问(点开对答案)
1. 模式匹配比 if 强在哪? —— 把判断结构和绑定变量合成一步,不用再按下标取。 2. 递归的硬限制是多少? —— 默认 1000 层;900 个元素能算,1100 个抛 RecursionError。 3. 什么时候该用递归? —— 数据结构本身是递归的(树、链表、JSON)时最自然。- ☐ A.9 特征7:编译器自动进行类型推断
相关笔记
- 特征5:没有副作用 — 上一条
- 特征7:编译器自动进行类型推断 — 下一条
- OOP具有结构化语言所没有的三种结构 — 另一种组织代码的方式
- 🧩 面向对象是怎样工作的 · 索引