差异列表用于绕过另一个限制:您需要遍历整个列表以“附加”到其末尾(想想一个单链表,其中您有一个指向第一个元素的指针,但没有指向哨兵)。
在代码中:由于列表[a, b, c]
(传统上)是嵌套的 term .(a, .(b, .(c, [])))
,因此在其末尾添加 a意味着在将末尾(中心)d
替换为 之前“剥离”每个术语。因此,要实施:[]
.(d, [])
append/3
append([], L, L).
append([H|T], L, [H|R]) :-
append(T, L, R).
这就是它传统上的实现方式。
这是另一个非常广泛地涵盖该主题的答案。
该答案没有涉及的是,出于效率原因,“返回”列表的库谓词可能会提供谓词的差异列表版本,以防您可能想要附加到列表的末尾。示例是read_pending_codes/3
andread_pending_chars/3
或 . 的四参数版本findall/4
。
DCG 是一种使用差异列表的便捷方式,无需显式传递列表和尾部的两个参数。
实现队列
队列最基本的三个操作:创建一个空队列,推到后面,从前面弹出:
%% 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, Q, Q0)).
%% queue_last(+Queue0, +Last, -Queue)
% Queue0, with Last at its back, is Queue
queue_last(queue(X, Q, [L|Q0]), L, queue(s(X), Q, Q0)).
正如你应该注意到的,它queue_head/3
会让你从前面弹出或推到队列的前面。queue_last/3
只允许你推到后面:一旦你推了一个元素,你就没有(恒定时间)访问队列中它之前的元素。
该queue/3
术语的第一个论点是为了防止 Richard O'Keefe 所谓的“幻觉”变量,即从队列中弹出比已推送到它的元素更多的元素。从上面的谓词中省略第一个参数并看看会发生什么是很有趣的:
?- empty_queue(Q0), queue_head(Q0, H, Q1).
Q0 = queue([H|_G341], [H|_G341]),
Q1 = queue(_G341, [H|_G341]).
而不是失败。