Prolog 序言差异表
考虑以下程序,一个使用差异列表,另一个不使用:Prolog 序言差异表,prolog,difference-lists,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]). 既然两者都做相同的事情,那么使用差异列表的好处是什么?在给出的示例
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]).
既然两者都做相同的事情,那么使用差异列表的好处是什么?在给出的示例中,
reverse1
并没有使用真正的差异列表,而只是reverse2
的不同表示。它们都以相同的方式使用相同的变量reverse
使用-
函子附加它们,并且reverse2
将它们作为单独的参数进行维护。但这就是两者的不同之处。算法行为是相同的
真正的差异列表是一个列表结构,其中有一个“洞”,比如X-T
,其中T
没有被实例化(也许直到稍后的时间点),并且X
包含T
(例如,[A,b,c | T]-T
)。其中的-
函子将“暴露的”未实例化变量与包含该变量的结构相关联
一个流行且简单的示例是使用差异列表实现append
。append
的传统递归实现可能如下所示:
append([], Ys, Ys).
append([X|Xs], Ys, [X|Zs]) :- append(Xs, Ys, Zs).
非常简单,执行时间与第一个列表的长度成正比
使用差异列表,您可以实现如下所示的追加
:
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-[]).
你会得到:
L = [1,2,3,4,5,6]
T = [4,5,6]
W = []
您在示例中看到的不是一个差异列表。比较一下。差异列表使用的技巧是将尾部保持为已知的变量,并且可以直接更改。因此,它允许在列表中附加常量时间。但这不是你在你的例子中所做的
看看您的示例,
rev1
实际上只是使用了-
作为分隔符,就好像它是一个逗号一样。我认为唯一的区别是在rev2
中,prolog解释器有机会以一种提高性能的方式索引规则。不过,我不确定在这种情况下是否会有什么不同。从美学角度讲,第二种解决方案对我来说也更干净。我从未见过使用“上下文外”的差异列表,主要上下文是DCGs实现
这是一个基于DCG的反面(我自己写的,但你也可以在Christian链接的页面上找到)
列出它可以证明您的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…事实上,
列表的第三个和第四个参数:reverse/4
形成了一个差异列表,它开始时是空的(B-B
),并且在每一步上都会延长一个非约束元素,因此,第四个arg表示第三个arg的渐进尾部。:)感谢大家及时的回答。为什么append/3没有定义为:append(I-M,M,I)。
?我对I-M
的理解是,它意味着I是一个列表,谁的尾巴是M
。你的定义和这个更复杂。为什么?@Rolf在Prolog中,头H
尾M
的列表使用
函子表示为。(H,T)
,或者更常见的语法是[H | T]
I-M
对于列表没有语义含义。它只是一个二元函子,-
,有两个参数,I
和M
,或者可以写成,'-'(I,M)
。在上面的回答中,我可以很容易地使用另一个定义为二进制运算符的二元函子,例如+
,而不是-
,并且行为是相同的。
?- 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).