tutorial. 数学表示:Formula、Semantic与Notation[math-0001]
Grove的数学接口把“作者写下的公式”“公式表达的数学对象”和“对象如何排版”分开。Formula DSL读入方便的数学文本;Semantic DSL构造可计算对象;Notation DSL描述展示结构。低层计算API接收Semantic值,Formula façade提供自动读取文本的便捷入口。
example. Notation:明确写出排版结构[#71]
这一组Maxwell方程用对齐点、向量箭头和分数构造排版:
◇:align、◇:vec与◇:frac构造的是Notation节点。它们能生成MathML,但不会因此获得向量类型或求导语义。需要证明公式时,应该使用Semantic表达式和计算pass。
example. Semantic:构造可计算的值[#72]
下面的Scheme代码建立多项式。每个构造器都接收Semantic值;数值用精确有理数表示:
@(define x (◆:coordinate 'x))
@(define polynomial
(◆:add
(◆:power x (◆:constant 3))
(◆:multiply (◆:constant 2) x)))
@math[polynomial]◆:coordinate返回一个携带“独立坐标”声明的数学值;◆:constant建立精确常数;◆:power和◆:multiply构造子项;◆:add组合它们。最后的math命令只显示这个值,不执行化简。
表达式的内部term可检查为sexp:
(+ (^ (var x) (num "3")) (* (num "2") (var x)))
它大致是(+ (^ (var x) (num "3")) (* (num "2") (var x)))。来源位置、显示偏好和声明保存在值的旁侧metadata中,不混进关系规则处理的term。
example. Formula:用文本作为另一种入口[#73]
写短公式时,Formula DSL更方便。φ:read读出Semantic值;同一个文本也可以交给φ:simplify这样的便捷包装:
@(define parsed (φ:read "x^3 + 2*x"))
@(define formula-report
(φ:differentiate parsed "x" #:assumptions (φ:assumptions "coordinate(x)")))
@math[(math-report-result formula-report)] Formula reader负责把文本解析一次。φ:differentiate是Formula façade:它将输入和坐标读为Semantic值,再调用核心计算API。若要使用底层API,则明确传入Semantic值和事实:
底层入口也计算同一对象:
核心math-differentiate不解析字符串,也不依赖Formula模块;它只消费数学值。这让文本文法可以独立演进,而计算保持稳定的输入契约。
remark. 可计算和可排版是不同能力[#74]
可以直接把Semantic值交给Notation包装:
括号只影响排版偏好,不修改多项式term。相反,◆:group会把显式分组记录在Semantic值的展示属性中,数学等式仍基于term判断。排版节点不可以传进数学计算API。
使用建议[#75]
在文章中,Formula文本适合简短的手写表达式;Semantic构造器适合要复用、组合或交给低层计算的值;Notation构造器适合纯布局需求。代码示例和输出都是本页构建的一部分,而不是手工维护的副本。