2016-07-22 39 views
6

本页面的练习09 http://www.ic.unicamp.br/~meidanis/courses/mc336/2009s2/prolog/problemas/要求创建一个将重复元素打包到子列表中的谓词。`var(A)`和执行顺序

一个直接的解决方案是简单的

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

其中分流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]]).这样的查询。两个解集之间的对应关系是双射的(中的每个pack(A, B)都有一个且仅有一个B),所以必须有更好的解决方案。

之一来解决它的办法是改变计算的顺序,帮助序言挑取决于类型参数的非无限的分支像以下这方面

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),如果不是为什么?

回答

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的文献路过时 ,而大多数这样的解决方案来自dif/2甚至还没有在大多数Prolog系统,当然不是免费的。

+4

纯粹的天才!有了'dif',它现在甚至可以解决疯狂的东西,比如'pack(A,[X,[b]])。 – vasily

+2

要重新审视我以前的所有解决方案。 – vasily

+4

是的。即使在第一个Prolog系统中,“dif/2”也是可用的,然后大部分被实现者忽略,现在在实现中又变得越来越普遍。毕竟,所有方向的推理都是关系编程的主要吸引力之一,所以使用'dif/2'来以一种合理而一般的方式来表达词项不等式。 – mat