`var(A)`和执行顺序

时间:2016-07-22 13:39:18

标签: list prolog prolog-dif logical-purity

此页面上的练习09 http://www.ic.unicamp.br/~meidanis/courses/mc336/2009s2/prolog/problemas/要求创建一个谓词,将重复的元素打包到子列表中。

直接的解决方案很简单

pack([], []).
pack([H|T], [I|U]) :-
    split(H, T, I, P),
    pack(P, U).

其中split为split(Head, Tail, HeadGroup, Rest)定义为

split(A, [], [A], []).
split(A, [B|T], [A], [B|T]) :- A \= B.
split(A, [A|T], [A|U], B) :- split(A, T, U, B).

工作正常,几乎与上述网页上提供的示例解决方案一致。

此解决方案失败的地方是pack(X, [[a], [b, b]]).等查询。两个解集之间的对应关系是双射的(对于A中的每个pack(A, B),只有一个B,因此必须有更好的解决方案。

解决它的一种方法是改变评估顺序,帮助prolog根据参数的类型选择非无限分支,如下所示

pack([], []).
pack(A, B) :-
  ( var(A) ->
    A = [H|T],
    B = [I|U],
    pack(P, U),
    split(H, T, I, P)
  ; A = [H|T],                                                                                                                                                                                                                                
    B = [I|U],
    split(H, T, I, P),
    pack(P, U)
  ).

这方面有两个问题。

首先,这是令人难以置信的难看,所以根据参数类型选择规则顺序可能有更好的方法吗?

其次,可能是更复杂的问题,有没有办法在没有var(A)的情况下重写解决方案,如果不是为什么?

1 个答案:

答案 0 :(得分:4)

从声明的角度来看,var/1(\=)/2之类的非单调结构非常有问题

为什么呢?看看:

?- var(A), A=a.
A = a.

?- A=a, var(A).
false.

因此,这打破了交换性的结合,这是我们在实际推理关于逻辑程序时所依赖的核心属性之一。

认为(\=)/2会表示两个术语有什么不同?看看:

?- X \= Y.
false.

没有两个不同的术语,是吗? 对我来说似乎有点奇怪,至少可以这么说,显然这个谓词确实意味着别的东西。

幸运的是,在您的情况下,解决方案非常简单。只需使用纯约束dif/2即表示两个术语不同。有关详细信息,请参阅 。您只需更改一行代码即可使解决方案更加通用。而不是:

split(A, [B|T], [A], [B|T]) :- A \= B.

只需使用dif/2

split(A, [B|T], [A], [B|T]) :- dif(A, B).

通过此更改,您的示例完全按预期工作:

?- pack(X, [[a], [b, b]]).
X = [a, b, b] ;
false.

请注意,现有的Prolog文献过时,大​​多数此类解决方案来自大多数Prolog系统中甚至不能使用dif/2的时间,当然不是免费的