Prolog差异列表

时间:2014-01-18 02:54:55

标签: prolog difference-lists

考虑以下程序,一个使用差异列表,另一个不是:

reverse1(List1,R) :- rev1(List1, R-[]).
rev1([], A-A).
rev1([H|T], C-A) :-rev1(T, C - [H|A]).

reverse2(List1,R) :- rev2(List1, R, []).
rev2([], A, A).
rev2([H|T], C, A) :- rev2(T, C, [H|A]).

由于两者都做同样的事情,使用差异列表有什么好处?

3 个答案:

答案 0 :(得分:8)

在给出的示例中,reverse1未使用真差异列表,而只是reverse2的不同表示形式。它们都以相同的方式使用相同的变量。 reverse使用-仿函数来附加它们,reverse2将它们作为单独的参数进行维护。但这两者之间的差别就是这样。算法行为是相同的。

真正的差异列表是一个列表结构,其中包含一个“洞”,如X-T,其中T未实例化(可能在以后的某个时间点),{{1} }包含X例如T)。这里的[a,b,c|T]-T仿函数将“公开的”未实例化变量与包含它的结构相关联。

一个流行且简单的示例是使用差异列表实现-append的传统递归实现可能如下所示:

append

足够简单,执行时间与第一个列表的长度成正比。

使用差异列表,您可以按如下方式实施append([], Ys, Ys). append([X|Xs], Ys, [X|Zs]) :- append(Xs, Ys, Zs).

append

这就是使用差异列表追加列表所需的全部内容。没有递归或其他任何东西。执行时间为append(A-B, B-C, A-C). ,与列表的长度无关。这是一个执行示例:

O(1)

收率:

append([1,2,3|T]-T, [4,5,6|W]-W, DL).

你可以通过在最后一个参数中用空列表“填充”这个洞来得到具体的答案:

DL = [1,2,3,4,5,6|W]-W
T = [4,5,6|W]

你得到:

append([1,2,3|T]-T, [4,5,6|W]-W, L-[]).

答案 1 :(得分:4)

您的示例中的内容是不是差异列表。比较http://en.wikibooks.org/wiki/Prolog/Difference_Lists。差异列表使用将尾部保持为已知且可以直接更改的变量的技巧。因此,它允许将恒定时间附加到列表。但这不是你在你的例子中所做的。

然后查看您的示例,rev1实际上只使用-作为分隔符,就好像它是逗号一样。我认为唯一不同的是,在rev2中,prolog解释器有机会以改善性能的方式索引规则。不知道在这种情况下是否会产生任何不同。从美学角度讲,第二种解决方案对我来说似乎也更加清洁。

答案 2 :(得分:3)

我从未见过使用“不在上下文中”的差异列表,主要上下文是DCG的实现。

这是基于DCG的反向(好吧,我自己写的,但您也可以在Christian链接的页面中找到它。)

reverse3(L,R) :- phrase(rev3(L),R).
rev3([]) --> [].
rev3([H|T]) --> rev3(T),[H].

列出它证明你的reverse2几乎完全相同:

?- listing(rev3).
stackoverflow:rev3([], A, A).
stackoverflow:rev3([D|A], B, E) :-
    rev3(A, B, C),
    C=[D|E].

所有这些定义都有一个问题:它们在“后向”模式下循环,在第一个解决方案后回溯:

?- reverse1(R,[a,b,c]).
R = [c, b, a] ; (^C here)
Action (h for help) ? abort
% Execution Aborted

然后看看正确,高效的库实现是很有趣的:

?- listing(reverse).
lists:reverse(A, B) :-
    reverse(A, [], B, B).

lists:reverse([], A, A, []).
lists:reverse([B|A], C, D, [_|E]) :-
    reverse(A, [B|C], D, E).

这里没有差异列表!

然后,关于你的问题,我会说差异列表的唯一好处是更好地理解Prolog ......