在 Prolog 中使用差异列表附加元素的问题

问题描述

我在 this answer 中找到了这个 Prolog 代码,它使用差异列表实现了一个队列:

MANIFEST.in

做这样的事情它按预期工作:

%% empty_queue(-Queue)
% make an empty queue
empty_queue(queue(0,Q,Q)).

%% queue_head(?Queue,?Head,?Queue0)
% Queue,with Head removed,is Queue0
queue_head(queue(s(X),[H|Q],Q0),H,queue(X,Q0)).

%% queue_last(+Queue0,+Last,-Queue)
% Queue0,with Last at its back,is Queue
queue_last(queue(X,[L|Q0]),L,queue(s(X),Q0)).

我明白了

...,empty_queue(Q),queue_last(Q,999,Q_),writeln(Q_),....

如果我用这个片段观察 queue(s(0),[999|_3076],_3076) 的值,也很有趣:

Q

我明白了:

empty_queue(Q),writeln(Q),writeln(Q)

我想应该是这样的,因为差异导致空列表,所以它们有点等价。

问题是,在命令之后

queue(0,_3750,_3750)
queue(0,[999|_3758],[999|_3758])

我不能重复使用 queue_last(Q,Q_) 来创建 Q,例如:

Q__

因为 empty_queue(Q),888,Q__) 的绑定失败。

queue_last(queue(X,Q0)).

我该如何解决这个问题?有一些解决方法吗? (总是使用差异列表)

解决方法

我无法重复使用 Q 来创建 Q__

那是因为您必须使用称为 Q_ 的“线程化”新结构。旧的 Q 是一个燃烧器,必须丢弃。它不再正确描述“差异列表”。

?- empty_queue(Q1),queue_last(Q1,999,Q2),queue_last(Q2,888,Q3).

Q1 = queue(0,[999,888|_14714],888|_14714]),% Useless
Q2 = queue(s(0),[888|_14714]),% Burnt
Q3 = queue(s(s(0)),_14714).       % Correct,valid

empty_queue(Q1) 调用之后,这是 Q1

queue
├── arg 0: 0
├── arg 1: ----+---> <empty cell #1>
|              |
└── arg 2: ----+

queue_last(Q1,Q2) 调用之后,这是 Q1Q2

Q1(无效)

queue
├── arg 0: 0
├── arg 1: ----+---->[|]
|              |     / \
|              |  999  <empty cell #2>
|              |
└── arg 2: ----+

Q2(有效)

queue
├── arg 0: s(0)
├── arg 1: --------->[|]
|                    / \
|                 999  <empty cell #2>
|                          ^
|                          |
└── arg 2: ----------------+

queue_last(Q2,Q3) 调用之后,这是 Q1Q2Q3

Q1(无效)

queue
├── arg 0: 0
├── arg 1: ----+---->[|]
|              |     / \
|              |  999   [|]
|              |       /   \
└── arg 2: ----+    888    <empty cell #3>

Q2(无效)

queue
├── arg 0: s(0)
├── arg 1: --------->[|]
|                    / \
|                 999  [|]<------------------+
|                      /  \                  |
|                   888   <empty cell #3>    |
|                                            |
└── arg 2: ----------------------------------+

Q3(有效)

queue
├── arg 0: s(s(0))
├── arg 1: --------->[|]
|                    / \
|                 999  [|]
|                      /  \              
|                   888   <empty cell #3>
|                                ^
|                                | 
└── arg 2: ----------------------+
,

Prolog 变量不能重新分配。你不能重复使用它们。我不知道调用变量“燃烧”是否有很大帮助,它们没有被燃烧,它们绑定到一个具体的值。

不要使用“write”和朋友,除非你正在做一些复杂的打印风格的调试。在顶层尝试一下,无论如何您都会打印出所有内容。以下是如何使用此队列实现。请注意,我使用了 Q0Q1Q2 等,因为一旦有多个下划线,我就无法计算下划线。

Enqueueing a,然后是队列末尾的 b

?- empty_queue(Q0),queue_last(Q0,a,Q1),b,Q2).
Q0 = queue(0,[a,b|_15096],b|_15096]),Q1 = queue(s(0),[b|_15096]),Q2 = queue(s(s(0)),_15096).

入队 a,然后是 b,然后弹出你入队的第一个值(FIFO 顺序):

?- empty_queue(Q0),queue_head(Q2,Popped,Q3).
Q0 = queue(0,b|_17772],b|_17772]),[b|_17772]),_17772),Popped = a,Q3 = queue(s(0),[b|_17772],_17772).

在前面推两次,然后弹出(LIFO 顺序):

?- empty_queue(Q0),queue_head(Q1,x,Q0),y,_21688,_21688),Q1 = Q3,[x|_21688],[y,x|_21688],Popped = y.

我在您的问题下方的评论中链接的答案(这里又是:https://stackoverflow.com/a/31925828/14411997)详细解释了这是如何工作的。它还包含指向其他相关问答等的链接。