加载失败
这篇文章用 Pokémon(宝可梦)里的技能、属性和配队规则来演示 Prolog(逻辑编程语言)的基础,把事实和规则写成关系后,再通过查询得到答案。评论区重点补充了 Prolog 的核心机制:查询结果本身就是 term,失败时看到的 `false` 往往只是搜索穷尽,而不是程序报错。不少讨论把它和 SQL(结构化查询语言)的 query planner、以及 Datalog(更像数据库递归推理的逻辑语言)对比,争论声明式写法和实际搜索成本之间的张力。另一些人顺着这个题材联想到 Scryer Prolog(一个开源 Prolog 实现)、Web Prolog(面向分布式/并发的 Prolog 扩展)和用 Prolog 做游戏规则、元编程、甚至法律/税务知识图谱的项目。
有评论解释,Prolog 里的输出不是普通终端文本,而是可继续求值的 Prolog term。`x ; y ; false` 可以理解成“还能得到 x 或 y,或者没有更多答案”,所以 `false` 只是搜索穷尽的信号。另有人指出,若引擎通过索引或其他优化直接命中答案,就不会再留下需要继续搜索的分支,因此也未必打印 `false`。这也被用来说明 Prolog 的 `;`、term 和查询结果本来就是同一套逻辑表示,体现了 declarative programming 的思路。
反对者认为 Prolog 最大的问题不是语法,而是性能和行为太依赖搜索顺序、优化模式和隐式回溯。简单问题可以优雅地写成谓词,但复杂一点就可能从几秒变成几天,因为引擎会在大量组合里盲搜。有人拿 SQL query planner 做类比,认为 SQL 也需要理解底层执行计划;但另一方强调 SQL 至少有更多局部优化手段,而 Prolog 的 `cut`(`!`)一旦不够用,就很难继续在同一范式内补救。也有人反驳说,Prolog 的机制本身并非不可理解,关键在于问题建模和 engine 的实现质量。
[来源1] [来源2] [来源3] [来源4] [来源5] [来源6]
几条评论把文章里的例子直接拉到 Datalog(面向数据库推理的逻辑语言)来比较。Datalog 同样能表达这些查询,语法很像 Prolog,但它依赖 least fixed point 语义,通常更可预测,也更像 SQL 的递归查询。有人指出,Datalog 不 Turing-complete 往往被视为优点,因为它更容易保证终止和性能稳定;如果真需要更强表达力,还可以看 Flix、Formulog、Soufflé 这类 Datalog-ish 系统。评论还提到这些系统对 atom order、search order 的依赖更少,所以在这类任务上更可控。
不少人从 Pokémon 例子延伸到游戏规则建模:Prolog 很适合 adventure、RPG、strategy 和桌游,因为 clauses 可以表示规则,facts 可以表示状态。简单 AI 对手甚至只需要递归 backtracking search;更复杂的状态修改则可以把 `assert/retract` 之类操作 reify 成事实,方便回溯和规划。评论还提到 Scryer Prolog(一个开源 Prolog 实现)讨论区里的 metaprogramming 思路、factgraph.pl(把 IRS Fact Graph 用 Prolog 表达的项目)以及 Web Prolog(面向分布式/并发的 Prolog 扩展)。还有人惊讶于 Erlang 的早期实现居然是用 Prolog 写出来的。
很多人只是觉得这种用 Pokémon 讲 Prolog 的方式很顺手,尤其能把“前向/后向查询”讲得直观。有人开玩笑说,能把儿童游戏机制讲到这种粒度,Prolog 简直像“adult programming language”。也有人表示自己不熟 Pokémon 也能跟上,还有人顺手挑出代码里的 typo、问有没有同样易懂的 Odin 教程,或者想把同样的方法搬到 Magic: The Gathering。整体上这是一次高接受度的教学型讨论,偏轻松但信息量不低。
[来源1] [来源2] [来源3] [来源4] [来源5] [来源6] [来源7] [来源8] [来源9] [来源10]
Prolog: 一种逻辑编程语言,用事实、规则和查询来表达问题,核心机制是搜索与回溯。
backtracking: 回溯搜索;在一个分支失败后撤回选择,继续尝试其他可能解。
unification: 合一/统一匹配;把变量、常量和结构项匹配起来并建立约束的过程。
cut operator(`!`): Prolog 的剪枝运算符,用来阻止部分回溯、控制搜索范围。
Datalog: 一种接近数据库递归查询的逻辑语言,通常采用固定点语义,行为更可预测。