Show HN:Wyrm – 通过触摸解代数,基于开源健全性引擎构建
摘要
Wyrm 是一个用 TypeScript 编写的开源符号代数引擎,为 iOS 和 Android 上基于手势的代数应用提供支持。它通过条件重写规则在构建时保证健全性,允许用户通过触摸来解方程。
查看缓存全文
缓存时间: 2026/07/10 21:13
dicroce/wyrm_math
来源:https://github.com/dicroce/wyrm_math
wyrm-math
一个精确的、条件性完备的符号代数引擎,用于构建操作型数学界面——用户通过拖拽项越过等号、点击幂次进行展开,或从两项中提取公因式等方式求解方程。
核心不变式:合法操作可行,非法操作不可行。 方程从不会被验证——它们仅通过重写规则进行变换,因此每个可达状态在构造上都是完备的。而完备性是条件性的:仅在特定条件下有效(如除以 b 要求 b ≠ 0)或可能引入增根的操作(如两边同时乘方、平方)并不会被禁止——其条件会成为一等公民、可见的假设(Assumption),并随方程一同传播。
纯 TypeScript,零依赖,零 DOM——可在 Node、浏览器、Web Worker、原生 WebView 等任何环境中运行。
wyrm-math 是 Wyrm Math 的底层引擎,这是一款面向 iOS 和 Android 的手势代数应用——请尝试在线演示或获取应用 (https://dicroce.github.io/wyrm/home.html)。引擎采用 MIT 许可证;应用是项目自我维持的方式。
import {
parseEquation, Derivation,
enumerateMoves, ruleById, layoutNode, exprToString,
} from "wyrm-math";
const d = new Derivation(parseEquation("2x + 3 = 11"));
// 用户当前可以合法执行哪些操作?
const moves = enumerateMoves(d.current);
// 将 3 拖过等号(UI 选择一个 Move;引擎保证该操作合法——枚举时已进行前置条件检查):
const move = moves.find((m) => m.ruleId === "move-term-across")!;
d.apply(ruleById(move.ruleId), move.location, move.params);
console.log(exprToString(d.current.equation)); // 2x = 11 + -3
// 以任意方式渲染:layoutNode 从静态度量表中返回带有位置、id 键的框和字形(无需测量字体)。
const layout = layoutNode(d.current.equation);
内部模块
公共 API 位于 src/index.ts,组织为十个文档化的分组——读起来就像目录:
| 分组 | 功能 |
|---|---|
| 表达式树 | 具有稳定节点 ID 的不可变 AST。N 元 Sum/Product;无减法或除法节点(a − b 表示为 Sum(a, Neg(b));除法是一个带有分子/分母列表的 Fraction)。智能构造函数维护结构不变性。 |
| 精确算术 | 基于 bigint 的 Rational。任何地方都不使用浮点数——√2 是未定义点,而非 1.4142。 |
| 求值 | truthValue(equation, env) 在采样点上精确判定任意关系(= < ≤ > ≥),若一侧未定义则返回 undefined。 |
| 解析与打印 | parseEquation("2x + 3 = 11") ⇄ exprToString——通过属性测试保证往返一致性。支持隐式乘法、分数、幂、根式;拒绝小数(引擎是精确的)。 |
| 判定与假设 | 状态的基本单位是 { assumptions, equation }。限制(Restrictions)(可能丢失解的操作:如 b ≠ 0),扩展(Extensions)(可能增加解的操作:携带原方程作为义务,通过 checkSolution 解决),固定(Pinned)(用户假设场景)。被解除的假设会被记录,但永不删除。 |
| 规则与推导 | Rule.apply 是方程发生变化的唯一方式。推导日志是仅追加的树:撤销操作移动指针,废弃分支仍保持活跃,情况分支和析取分支会分叉为活跃的兄弟节点。 |
| 内置规则 | 约 25 条规则,涵盖线性方程、合并同类项、分配律、分数、指数法则、不等式(符号感知、关系翻转)和二次方程(x² = 9 分支为 x = ±3;零积性质)。每条规则都附带属性测试,验证其在假设下保持解集不变。 |
| 操作枚举 | enumerateMoves(judgment) 返回每个合法的操作,并附带手势锚点(handle、dropTarget)。对所有规则都是完备的(有限规则集)。固定 x = 0 后,所有除以 x 的操作会自动消失。 |
| 布局几何 | layoutNode 将树映射为带有位置、id 键的框和字形(分数堆叠、上标、根号),使用静态度量表。hitTest 是几何查询。子树的几何布局在平移+缩放意义下与上下文无关——这正是基于 id 键的动画成为可能的原因。 |
| 规则编写工具包 | 保留 ID 的重建、修复不变量的拼接操作、差异簿记以及假设生命周期查询,用于编写新规则。 |
ARCHITECTURE.md 深入解释了不变量和契约。
设计承诺
- 精确性。 所有算术都是基于
bigint的有理数。表达式未定义的点(除以零、无理根)被视为未定义,绝不近似。引擎级完备性契约是在两侧都定义时判断真值。 - 稳定 ID。 每个节点都有一个 ID;操作会保留未改动子树的 ID。这是命中测试和动画的基础:渲染器可以跨重写匹配节点,并刚性移动它们。
- 条件性完备。 对于常规规则和产生限制的规则,属性测试在满足结果判定假设的替换上拒绝采样,并断言真值保持不变。对于产生扩展的规则,检查弱化为一个方向(解不会丢失),通过
checkSolution覆盖增加解的义务。 - 析取。 分支规则返回多个结果,这些结果的解集合集等于原解集(
x² = 9⇒x = 3或x = −3);推导树将所有分支作为活跃、可导航的状态保存。
开发
pnpm install
pnpm test # vitest + fast-check(属性测试是此项目的灵魂)
pnpm typecheck
pnpm build # 输出 dist/(ESM + d.ts)
引擎必须保持无 DOM:tsconfig.json 不包含 DOM lib,且 test/boundary.test.ts 会扫描源码中是否有浏览器全局变量。
许可证
MIT
相似文章
Show HN: 我用Swift构建了LangGraph
Swarm是一个用于构建代理工作流和多智能体系统的Swift框架,具备类型安全的工具调用、持久化检查点以及对多种LLM提供商的支持。
Show HN: Starglyphs - 基于欧拉路径的星座谜题游戏
一位独立开发者制作了 Starglyphs,这是一款基于欧拉路径的程序化生成星座谜题游戏,灵感来自《龙腾世纪:审判》的星象仪小游戏。网页版已上线,Steam 和移动版本正在开发中。
Scheme 是一个 Hoot
作者分享了学习 Scheme 并使用 Hoot 将其编译为 WebAssembly 的经验,虽然遇到了稳定性问题,但成功在浏览器中运行了物理模拟。
Show HN: Inkwash——一款水彩素描应用及技术解析
Inkwash 是一款基于 WebGL2 的水彩素描应用,可模拟颜料流动与纸张交互,由 Claude Fable 5 生成。本文解释了实现逼真水彩效果的浮点纹理与着色器技术管线。
Show HN: Y – 一个基于Electron的可塑编码代理桌面应用
Y是一个可塑的、以聊天为先的桌面应用,能够并行运行像Claude Code和Codex这样的本地编码代理,并具备自我修改的UI功能。