Prolog 不是在写“执行步骤”,而是在定义知识库(Knowledge Base)和逻辑关系,然后让解释器去通过**深度优先搜索(DFS)回溯(Backtracking)**来寻找证明。

这里是一份针对程序员的 Prolog 速成指南:


1. 核心概念:只有三种元素

在 Prolog 里,一切都是 Term(项):

  1. 原子(Atom):常量,小写开头。例如:catjustin'Hello World'
  2. 数字(Number)13.14
  3. 变量(Variable)大写开头或下划线开头。例如:XList_ (匿名变量)。
    • 注意:变量没有“赋值”的概念,只有“绑定(Binding)”的概念。一旦绑定,在这个作用域内不可变。
  4. 结构(Structure/Compound Term)functor(arg1, arg2)。例如:parent(john, mary)

2. 程序的构成:事实与规则

Prolog 程序 = 事实 + 规则。

事实 (Facts)

永远为真的陈述。

human(socrates).
parent(john, mary).
parent(mary, tom).

规则 (Rules)

类似“如果...那么...” (Horn Clauses)
格式:Head :- Body. (如果 Body 成立,则 Head 成立)。

% 如果 X 是 Y 的父母,且 Y 是 Z 的父母,那么 X 是 Z 的祖父母
grandparent(X, Z) :- 
    parent(X, Y), 
    parent(Y, Z).

3. 执行机制:统一 (Unification) 与 回溯

这是你最需要理解的部分。

Unification (=)

这不是赋值,是模式匹配。Prolog 试图让等号两边变得一样。

算术求值 (is)

如果你想算数,必须用 is

回溯 (Backtracking)

当 Prolog 遇到死胡同(fail)时,它会退回到上一个决策点(choice point),尝试另一种可能性。


4. 数据结构:列表 (List)

Prolog 的列表是链表结构。

解构示例:

[a, b, c] 可以写成 [a | [b, c]]。

查询:[H|T] = [1, 2, 3].
结果:H = 1, T = [2, 3].


5. 递归模式(最常用的模板)

Prolog 没有 for 或 while 循环,一切靠递归。

模板:

  1. Base Case(基准情况):通常是空表或 0。
  2. Recursive Step(递归步骤):处理头部,递归尾部。

例子:计算列表长度

% 1. 基准:空表长度为 0
len([], 0).

% 2. 递归:列表长度 = 尾部长度 + 1
len([_|T], N) :- 
    len(T, SubN),  % 先递归算出尾部的长度
    N is SubN + 1. % 再加 1 (注意不能写 N = SubN + 1)

例子:列表拼接 (append).这是 Prolog 最神奇的代码之一,既能拼接,能拆分。

% base: 空表拼接 L,结果就是 L
my_append([], L, L).

% recursive: [H|T] 拼接 L2,结果是 [H|Result]
% 只要 T 和 L2 拼成了 Result
my_append([H|T], L2, [H|Result]) :-
    my_append(T, L2, Result).

6. 程序员常见的坑 (Gotchas)

  1. = vs is
    • X = 3+2 结果是 3+2 (一个表达式结构)。
    • X is 3+2 结果是 5 (数值)。
  2. 单例变量警告 (Singleton Variable)
    • 如果你定义了 member(X, [H|T]) 但没用到 H,Prolog 会警告。
    • 修正:用下划线 _ 代替没用的变量,如 member(X, [_|T])
  3. 截断符 ! (Cut)
    • 告诉 Prolog:“到这里为止,前面的匹配不要再回溯了”。
    • 用于优化(剪枝)或强制互斥(如 max 函数)。

总结练习

用一句话概括 Prolog 编程:“不要告诉电脑怎么做(How),告诉它什么是真的(What),以及定义好递归的基准条件。”