表达式即树 · 符号计算的第一性原理
你眼里的公式 x²+2x+1 是一行字符,计算机眼里却是一棵结构化的树:一个加法节点,挂着三个孩子。这一章用 SymPy 的 srepr、.func、.args 把这棵树剖开——理解了「表达式是树」,你就抓住了整个符号计算的地基。
表达式即树 · 符号计算的第一性原理
符号计算的一切,都建立在一个朴素却深刻的洞察上。这一章只讲这一个洞察,但请务必吃透它——后面每一章都站在它上面。
公式在计算机眼里,是一棵树
你写下 ,看到的是一行数学符号。但计算机不能把它当字符串处理(那样连「加法有结合律」都不知道)。它把这个式子理解成一棵树:
最顶上是一个加法节点,它有三个孩子:(一个幂节点)、(一个乘法节点)、常数 。幂节点又有两个孩子 和 ……一直到叶子——符号()和数字()——为止。这棵树,就是这个公式在计算机里的真身。
用 SymPy 把树剖开
空口无凭。SymPy 的 srepr(structural repr,结构化表示)能把这棵树原样打印出来:
from sympy import *
x = symbols('x')
e = x**2 + 2*x + 1
print(srepr(e))
# Add(Pow(Symbol('x'), Integer(2)), Mul(Integer(2), Symbol('x')), Integer(1))看清楚了吗?Add(Pow(...), Mul(...), Integer(1))——这正是上面那棵树的文字版:一个 Add,套着一个 Pow、一个 Mul 和一个 Integer(1)。你平时看到的漂亮公式 x**2 + 2*x + 1,只是这棵树的「显示皮肤」;它的真身是 Add(Pow(...), ...)。
每个表达式 = 函数 + 参数
这棵树有一个极其规整的结构,规整到可以用一句话概括:
在 SymPy 里,每个表达式都是 函数(参数...) 的形式——.func 给出根节点是什么运算,.args 给出它的孩子。 这个规律递归地贯穿整棵树,直到叶子:
print(e.func) # <class 'sympy.core.add.Add'> 根是「加法」
print(e.args) # (1, x**2, 2*x) 三个孩子
# 递归下去:取第三个孩子 2*x,它又是 函数+参数
child = e.args[2] # 2*x
print(child.func) # <class 'sympy.core.mul.Mul'>
print(child.args) # (2, x)这个 「.func + .args」的统一结构,是符号计算所有算法的立足点:想遍历、想改写一个表达式,你只需递归地处理 func 和每个 arg。一个几行的递归函数,就能走遍任意复杂公式的每个角落。
顺带一提,e.args 是 (1, x**2, 2*x) 而不是你输入的顺序——因为加法可交换,SymPy 会把参数排成一个规范顺序,这样 和 会得到同一棵树。这个「规范化」的思想,下一章还会细讲。
叶子:符号与数
一棵树总会到头。表达式树的叶子(不能再拆的原子)只有两类:符号(如 ,代表未知量)和数(如 )。SymPy 的 .atoms() 能把它们捞出来:
print(e.atoms(Symbol)) # {x} —— 所有符号
print(e.atoms(Number)) # {1, 2} —— 所有数字内部节点是运算(加、乘、幂、sin、积分符号……),叶子是符号和数。任何数学表达式,无论多复杂,都是这样一棵「运算当枝、符号与数当叶」的树。
为什么这件事如此重要
因为符号计算做的一切,本质都是在操作这棵树:
- 求导 = 按求导规则,把树递归地变成另一棵树(和的导数 = 导数的和,乘积法则……)。
- 化简 = 在树里找到可以变简单的部分,替换掉。
- 代入求值 = 在树里找到某个符号,把它换成一个值。
- 解方程 = 把方程的树,变形成
x = 某个表达式的树。
后面所有章节,拆开看都是「对树做变换」。所以请记住这句话——它是本专栏的第一性原理:
公式是树;符号计算,就是操作这棵树。
小结与下一章
任何数学表达式在计算机里都是一棵树:内部节点是运算、叶子是符号与数。SymPy 里每个表达式都遵循 .func(是什么运算)+ .args(孩子们) 的统一结构,递归到底;srepr 能打印真身,.atoms() 能捞出叶子。符号计算的一切,都是对这棵树的操作。
树是「形」,但树的叶子——数——本身也大有讲究:符号计算里的数是精确的(有理数、大整数、根式),永不四舍五入。下一章看这些精确数,以及它们为什么是符号计算的底气。👉 精确数:有理数、大整数与根式。
参考文献
- [1] Joel S. Cohen.《Computer Algebra and Symbolic Computation: Elementary Algorithms》—— 表达式树与递归结构。
- [2] SymPy 文档.「Tutorial: Manipulation — srepr, args, func.」https://docs.sympy.org/latest/tutorials/intro-tutorial/manipulation.html
- [3] Cohen.《...: Mathematical Methods》—— 表达式的完全递归定义。