开发者生态
morning
Prolog 编码恐怖
2026-05-17
1 阅读
RohanAdwankar
Prolog 编码恐怖 我似乎听到了低声呼喊,“恐怖!恐怖!” (约瑟夫·康拉德,黑暗之心)你为什么在这里?作为一名 Prolog 程序员,您可能有叛逆的性格。在许多情况下,这会引导您远离整个行业目前试图解决问题的方式。专注于超越的事物。本页的目的是向您展示在哪些情况下追随这种连续趋势可能不是一个好主意,因为成本很高,而且没有任何好处。少量规则足以编写出色的 Prolog 代码。破坏它们将导致程序在一种或多种方面存在缺陷。视频:恐怖:失去解决方案 终止且效率可接受的 Prolog 程序可能在两个主要方面存在缺陷: 它报告错误的答案。它未能报告预期的解决方案。这些情况中哪一个更糟糕?想想看!假设一个程序仅在第一种方面有缺陷。您可以采取什么措施来仍然获得正确的结果?然后,假设一个程序仅在第二种情况下有缺陷。您有什么选择可以以某种方式仍然获得所有预期的解决方案?第二种使程序有缺陷的主要方法是使用不纯且非单调的语言结构。例如 !/0 、 (->)/2 和 var/1 。声明性的解决方法是使用干净的数据结构、像 dif/2 这样的约束和像 if_/3 这样的元谓词。恐怖:全局状态 作为初学者,您会很想在 Prolog 中修改全局数据库。这会在程序中引入隐式依赖关系。我所说的“隐式”是指您的程序中没有任何内容强制执行这些依赖性。例如,如果您以与预期不同的顺序使用此类谓词,它们可能会意外失败或产生奇怪的结果。以这种方式使程序有缺陷的主要方法是使用像assertz/1和retract/1这样的谓词。声明性的解决方法是使用谓词参数或半上下文表示法来线程化状态。恐怖:不纯的输出 作为初学者,您有时会想在系统终端上打印答案,而不是让顶层报告它们。例如,您的程序可能包含如下代码:solve :-solution(S),format("thesolutionis:~q\n",[S])。这种方法的一个主要缺点是您无法轻松推断此类输出,因为它仅发生在系统终端上,并且不能作为程序中的 Prolog 术语使用。因此,您不会为此类输出编写测试用例,从而增加了引入破坏此类谓词的更改的可能性。另一个严重的缺点是这会阻止您将代码用作真正的关系。为了受益于关系的完整通用性,请使用 Prolog 代码描述解决方案,并让顶层进行打印:solution(S) :-constraint_1(S) 等。有时,您可能需要特殊格式。在这种情况下,您仍然可以以纯粹的方式描述输出,例如使用非终结符 format_//2 。这使得测试用例易于编写。可怕之处:低级语言结构 一些 Prolog 程序员可能认为没有理由使用更新的语言结构。例如,CLP(FD) 约束仅广泛应用了大约 20 年,这对于 Prolog 来说是一个相对较新的发展。如果您认为低级结构对您很有帮助,为什么还要学习更新的材料呢?数以百万计的学生没有得到较低层次结构的良好服务,这一事实不必让您担心。不幸的是,坚持低级结构会付出高昂的代价:它使语言变得更难教、更难学、更难理解。它要求学生基本上同时学习声明性和操作性语义,这在几乎所有情况下都太多了。让 Prolog 变得比必要的更难教的主要方法是向初学者介绍算术的低级谓词,例如 (is)/2 、 (=:=)/2 和 (>)/2 。一种声明性的出路是教授约束。请参阅声明性整数算术。恐怖阶乘 要查看其中一些缺陷的示例,请查看恐怖阶乘:horror_factorial(0, 1) :- !。 Horror_factorial(N, F) :- N > 0,N1 为 N - 1,horror_factorial(N1, F1),F 为 N*F1。观察发布最常见查询时丢失解决方案的恐惧:?-horror_factorial(N, F)。 N = 0,F = 1。没有 !/0 的版本几乎同样可怕:horror_factorial(0, 1)。 Horror_factorial(N, F) :- N > 0,N1 为 N - 1,horror_factorial(N1, F1),F 为 N*F1。低级语言结构的恐怖盛行:?-horror_factorial(N, F)。 N=0,F=1; catch: error(instantiation_error,'(is)'/2) 如果您接受这一点,您将受到使用过时语言结构的限制。将关系误认为函数。不关心最一般的查询。通过使用不纯的结构来防止声明式调试。出路:纯洁 要停止恐怖,就保持纯洁