🧩 附章 函数式语言是怎样工作的
📄 一句话概括

模式匹配四行 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 秒实验

  1. 用 match/case 把一段 if/elif 的判断链重写一遍,看少几行。
  2. 把一段递归改成循环,比较两者在 10000 个元素上的表现。

常见误区

  1. 以为递归比循环高级。深度一超就崩,这不是高级。
  2. 以为模式匹配只是 switch。它能同时判结构并绑定变量。
自测三问(点开对答案) 1. 模式匹配比 if 强在哪? —— 把判断结构和绑定变量合成一步,不用再按下标取。 2. 递归的硬限制是多少? —— 默认 1000 层;900 个元素能算,1100 个抛 RecursionError。 3. 什么时候该用递归? —— 数据结构本身是递归的(树、链表、JSON)时最自然。

相关笔记