有些人怀疑Prolog程序使用移动图来移动3个堆栈上的块

时间:2013-05-08 16:16:50

标签: prolog

我正在学习Prolog使用Ivan Bratko的书:人工智能编程我发现一些问题试图理解如何使用图表来决定如何移动块并按顺序排列它们。

这是与程序必须执行的操作相关的图像:

enter image description here

正如您在上一张图片中所见,可以使用以下许多可接受的移动来移动块A,B,C:

  • 仅当块位于堆栈顶部时才能移动
  • 可以在地面上移动(在空白堆栈上)
  • 可以在另一个块上移动(在包含其他块的另一个堆栈的顶部)

因此,这些可接受的移动会在图形中生成一个状态与另一个状态之间可能转换的图形,如下所示:

enter image description here

所以,正如您可以在上一个图表中看到的那样,我可以使用3个子列表的列表来表示情况。

每个子列表代表一个堆栈,我可以根据之前的约束放置块

因此,例如,上一个图的中心节点的情况可以表示为:

[[A], [B], [C]]

因为每个堆栈包含一个块

由左上角的节点表示的情况,其中我将一个节点堆叠在其他块下方 C,A,B可以表示为:

[[C,A,B], [], []]

因为所有块都在一个堆栈中(第一个)

现在,我必须写一个谓词 s(情境1,情境2),如果情境1和情境1是街区世界的两个表示(如前所述),那么这是正确的,并且有一个合法的举动在Situazion1和Situazion2之间允许。

所以我可以使用一系列事实代表下面的事情(这个事情显示在我老师的幻灯片上,而不是在Bratko的书上:

s([[A|RA],B,C],[RA,[A|B],C]).
s([[A|RA],B,C],[RA,B,[A|C]]).
…

我用这种方式解释它:我有2个堆栈:

情境1 = [A | RA],B,C] 其中RA是没有块A的第一个堆栈的其余部分(在这种情况下它是无效的,因为这是我的情况每个堆栈中有一个块)

情境2 = [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 谓词是如何工作的。

在书上说:

  

可以根据以下规则对后继关系进行编程:如果有两个堆栈,则情境2是情境1的成功者:   情境1中的Stack1和Stack2以及Stack1的顶部块都可以   在Stack2上移动

直观地说这对我来说并不难,因为如果我可以在3个堆叠中的一个顶部占据一个块并且我可以将它放在另一个堆栈的顶部(也是一个void堆栈),那么移动是合法的,这与我把一块放在地上的情况相对应)

但是我发现很多人很难理解以前的 s / 2 谓词代码:

s(Stacks, [Stack1, [Top1|Stack2] | OtherStacks]) :-

                                 del([Top1|Stack1], Stacks, Stacks1),
                                 del(Stack1, Stacks1, OtherStacks).

我有一些难以理解如何阅读它以及使用 del / 3谓词时究竟做什么以及究竟代表什么 Stacks,Stack1,Stack2,Stacks1 变量< / p>

2 个答案:

答案 0 :(得分:3)

你问,以下是什么意思:

s(Stacks, [Stack1, [Top1|Stack2] | OtherStacks]) :-
                             del([Top1|Stack1], Stacks, Stacks1),
                             del(Stack1, Stacks1, OtherStacks).

我们可以这样阅读:s/2当然是一个“步骤”(或“移动”或“继承者”)关系。我们首先得到Stacks堆栈列表。第二个参数是我们的结果,在它们中以某种方式移动一个块。

由于del是回溯谓词,因此s也是如此。因此,它将逐一产生结果。这里的声明性阅读将会有所帮助。 :)

我们读取了这个主体:A = [Top1|Stack1]中有StacksStacks1变为A,其中Stacks被删除。如果我们有三个Stacks1,我们将有两个A和另一个堆栈Top1,其中第一个 - Stack1 - 元素显然是其中的顶部块stack(和A是树桩,其余的是堆栈)。 IOW,我们在三个堆栈中选择一些堆栈Stacks1,其余的是ATop1上的顶部区域为Stack1

到下一行。 :)如果我们要从两个Stacks1删除树桩OtherStacks,我们会得到Stacks1。这只是胡说八道,我们知道树桩不属于Top1。所以这显然是一个错误(“拼写错误”)。

我们如何纠正它?我们的目标是将Stacks1放在 del(A,Stacks1,B), Result = [ Stack1 , % a stump [Top1 | A] | % move the top block B]. 中的一个堆栈上,让我们这样做:

A = Stack2, B = OtherStacks

这是我们的第二个参数,直到重命名变量: % del(Stack1, Stacks1, OtherStacks). % ERROR del(Stack2, Stacks1, OtherStacks). % CORRECT 。所以,正确的第二行是

A, B, C
就像在CapelliC的答案中一样。

CapelliC是对的,命名很可怕,:)它很容易误输。 {{1}}要好得多。

答案 1 :(得分:1)

在纠正s / 2中的单身人士后,获得

s(Stacks, [Stack1, [Top1|Stack2] | OtherStacks]) :-
    del([Top1|Stack1], Stacks, Stacks1),
    del(Stack2, Stacks1, OtherStacks).

我参加考试

?- s([[a,b,c],[],[]],R).
R = [[b, c], [a], []] .