替换与模式匹配 · 符号操作的基本动作

求导、化简、解方程,底层都是同一个动作:在表达式树里找到一个模式,把它替换掉。这一章讲清 SymPy 的三件基本兵器——subs(替换)、match(模式匹配)、replace(按模式批量替换),它们是下一章「计算即重写」的砖石。

作者 David更新于 第 4 篇(共 18 篇)

替换与模式匹配 · 符号操作的基本动作

地基搭好了,现在看符号计算最核心的动作。你会发现,无论多花哨的操作,拆到底都是两个字:替换——在表达式树里找到某个部分,换成别的。这一章讲三件实现「替换」的基本兵器。

subs:把符号换成别的

最常用的是 .subs()——把表达式里的某个符号,换成一个值或另一个表达式:

from sympy import *
x, y, z = symbols('x y z')
e = x**2 + 2*x + 1

print(e.subs(x, 3))          # 16              —— 把 x 换成数 3(即求值)
print(e.subs(x, y + 1))      # (y+1)**2 + 2*(y+1) + 1   —— 把 x 换成表达式
print((x + y).subs({x: 1, y: 2}))    # 3       —— 一次换多个

subs 就是「代入」。注意它的本质是树操作:在 ee 的树里,找到所有 xx 叶子,把每个都换成新的子树(数 3、或子树 y+1)。换成表达式时,新子树整个嫁接进去。

更妙的是,subs 不止能换符号,还能换整个子表达式——只要那棵子树在原树里出现:

print((x*y + z).subs(x*y, z))    # 2*z    —— 把子式 x*y 整个换成 z

它在树里找到 x*y 这棵子树,替换成 z,得到 z + z,自动化简成 2*z。这正是化简、变量代换、方程回代的底层机制。

match:在树里认出一个「模式」

subs 要求你精确说出要换什么。但很多时候我们想表达的是一类模式:「任何东西的平方」「sin⁡\sin 括号里任意倍的 xx」。这就需要通配符 Wild 和模式匹配 .match():

a, b = Wild('a'), Wild('b')

print((x**2).match(a**b))          # {a_: x, b_: 2}    —— 认出「a 的 b 次方」
print(sin(2*x).match(sin(a*x)))    # {a_: 2}           —— 认出「sin(a 乘 x)」

Wild('a') 是个通配符,能匹配任意子表达式。.match(pattern) 拿模式去套目标树:套得上,就返回一个字典,告诉你每个通配符匹配到了什么(a→xa\to x,b→2b\to2);套不上返回 None。

模式匹配是符号计算的「眼睛」。 一条数学规则,比如乘积法则 (uv)′=u′v+uv′(uv)' = u'v + uv',左边的 uvuv 就是一个模式(两个任意表达式相乘)。CAS 要应用这条规则,第一步就是用模式匹配在树里认出「这是一个乘积」,并抓出 uu 和 vv 分别是谁。先「认出」,才能「改写」——这就是下一章的主题。

replace:找出所有匹配,一次换掉

match 只看整棵树是否匹配。实战中我们常要在树的各个角落找出所有符合模式的地方,统统替换——这是 .replace():

# 把表达式里所有的 sin 换成 cos
print((sin(x) + sin(y)**2).replace(sin, cos))    # cos(x) + cos(y)**2

# 配合 Wild:把任意「平方」换成「立方」
print((x**2 + y**2).replace(a**2, a**3))         # x**3 + y**3

replace 遍历整棵树,每遇到一处匹配 sin(任意) 或 (任意)**2,就地替换。一条「模式 → 替换」的规则,加上 replace 的遍历,就能把一个变换施加到整个表达式。

三件兵器,一个内核

回头看这三件工具,它们其实是同一件事的三个层次:

工具干什么一句话
subs把指定的东西换掉精确替换
match认出符合模式的树,抓出通配符模式识别
replace找出所有匹配的地方,批量替换模式识别 + 替换

它们共同指向一个内核:一条规则 =「模式(match 的对象)+ 替换(subs 的动作)」;应用规则 = 在树里找到模式,把它换成替换的样子。 求导规则、三角恒等式、化简规则……全都是这个形状。

小结与下一章

符号操作的基本动作是替换:subs(把指定符号 / 子式换成别的,本质是树的嫁接)、match(用 Wild 通配符在树里认出模式,返回匹配字典)、replace(找出所有匹配处批量替换)。三者合起来定义了「规则 = 模式 + 替换」这个符号计算的原子。

现在,最深刻的一章来了。既然求导、化简、解方程都是「按规则替换」,那我们不妨把这个想法推到底:符号计算的全部,是不是就是「反复应用重写规则」?更进一步——「计算」本身,是不是就是这件事? 答案是响亮的「是」,它把符号计算、λ 演算、Lisp 和数理逻辑连成了一体。👉 计算即重写:逻辑计算的本质。

参考文献

  • [1] SymPy 文档.「Manipulation: subs, replace, match, Wild.」https://docs.sympy.org/latest/tutorials/intro-tutorial/manipulation.html
  • [2] Joel S. Cohen.《Computer Algebra and Symbolic Computation: Elementary Algorithms》, ch. 3 —— 结构上的替换与匹配。
  • [3] Bruno Buchberger et al.《Symbolic Computation》—— 模式匹配在 CAS 中的角色。

本页目录