我正在使用 Ivan Bratko 的书学习 Prolog:人工智能编程,我发现一些问题试图了解如何使用图形来决定如何移动块并按顺序排列它们的练习。
这是与程序必须执行的操作相关的图像:
正如您在上图中所见,块 A、B、C 可以使用一些允许的移动来移动,这些移动是:
- 只有在栈顶的块才能被移动
- 一个方块可以在地面上移动(在一个虚空栈上)
- 一个块可以移动到另一个块上(在另一个包含其他块的堆栈的顶部)
因此,这些可接受的移动会导致图中的一个状态和另一个状态之间可能的转换图,如下所示:
因此,正如您在上图中看到的那样,我可以使用包含 3 个子列表的列表来表示一种情况。
每个子列表代表一个堆栈,我可以根据之前的约束在其中放置块
所以例如上图的中心节点的情况可以表示为:
[[A], [B], [C]]
因为每个堆栈都包含一个块
左上角节点表示的情况,我在其他块 C、A、B 下方堆叠一个节点可以表示为:
[[C,A,B], [], []]
因为所有的块都在一个堆栈中(第一个)
现在,我必须编写一个谓词s(Situation1, Situation2),如果 Situation1 和 Situation1 是块世界的两个表示(如前一个)并且 Situazion1 和 Situazion2 之间允许合法移动,则该谓词 s(Situation1, Situation2) 为 TRUE。
所以我可以在下面使用一系列事实来表示这个东西(这个东西显示在我老师的幻灯片上,而不是 Bratko 的书上:
s([[A|RA],B,C],[RA,[A|B],C]).
s([[A|RA],B,C],[RA,B,[A|C]]).
…
我是这样解释的:我有 2 个堆栈:
Situation1 = [A|RA], B, C]其中 RA 是没有块 A 的第一个堆栈的其余部分(在这种情况下它是无效的,因为这是我在每个堆栈中有一个块的情况)
Situation2 = [RA,[A|B],C]这是另一种情况,第一个堆栈是 void (RA),第二个堆栈是旧的第二个堆栈,顶部是 A 块,第三个堆栈包含只有 C 块
所以它代表了一个合法的过渡......所以我可以声明一系列明确表示所有可能的过渡的事实。
但这不是一个好主意(我可以有很多转换来编码事实)所以在 Bratko 书中以这种方式以编程方式实现 s(Situation1, Situation2) 谓词:
/* BASE CASE: If I delete X from List and X is the HEAD of List, NewList is
the Tail of List
*/
del(X, [X|Tail], Tail).
/* GENERAL CASE: If the head of List is not X then the program have to delete
X in the Tail of List
*/
del(X, [Y|Tail], [Y|Tail1]) :- del(X, Tail, Tail1).
s(Stacks, [Stack1, [Top1|Stack2] | OtherStacks]) :-
del([Top1|Stack1], Stacks, Stacks1),
del(Stack1, Stacks1, OtherStacks).
goal(Situation) :- member([a,b,c], Situation).
solve(N,[N]) :- goal(N).
del/3谓词非常简单(只需从列表中删除 X 项),并没有太多有趣的地方。
我有一些问题要理解计算合法移动的s/2谓词是如何工作的。
书上说:
后继关系可以根据以下规则进行编程:如果有两个堆栈,则 Situation2 是 Situation1 的 SUCCESSOR:Situation1 中的 Stack1 和 Stack2,并且 Stack1 的顶部块可以在 Stack2 上移动
直觉上,这对我来说并不难,因为很明显,如果我可以在 3 个堆栈中的一个顶部取一个块并且我可以将它放在另一个堆栈的顶部(也是一个空堆栈,对应于到我在地上放块的情况)
但是我发现很多难以理解s/2谓词的先前代码在编写时:
s(Stacks, [Stack1, [Top1|Stack2] | OtherStacks]) :-
del([Top1|Stack1], Stacks, Stacks1),
del(Stack1, Stacks1, OtherStacks).
我很难理解如何阅读它以及使用del/3 谓词时究竟做了什么,以及究竟什么代表了Stacks、Stack1、Stack2、Stacks1变量