`var(A)`和执行顺序

vas*_*ily 6 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).
Run Code Online (Sandbox Code Playgroud)

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).
Run Code Online (Sandbox Code Playgroud)

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

这个解决方案失败的地方是查询pack(X, [[a], [b, b]])..2个解集之间的对应关系是双射(对于每个Apack(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)
  ).
Run Code Online (Sandbox Code Playgroud)

这方面有两个问题.

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

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

mat*_*mat 4

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

为什么?一探究竟:

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

?- A=a, var(A).
false.
Run Code Online (Sandbox Code Playgroud)

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

怎么样(\=)/2,您认为它可以表达两个术语不同?一探究竟:

?- X \= Y.
假。

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

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

split(A, [B|T], [A], [B|T]) :- A \= B.
Run Code Online (Sandbox Code Playgroud)

只需使用dif/2

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

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

?- pack(X, [[a], [b, b]]).
X = [a, b, b] ;
false.
Run Code Online (Sandbox Code Playgroud)

请注意,现有的 Prolog 文献已经过时了dif/2,并且大多数此类解决方案都来自大多数 Prolog 系统中甚至不可用的时代,当然在免费系统中也不可用。

  • 纯粹的天才!有了 `dif`,它现在甚至可以解决像 `pack(A, [X, [b]]).` 这样的疯狂问题。 (4认同)
  • 是的。`dif/2` 甚至在第一个 Prolog 系统中就可用,然后基本上被实现者忽略,现在在实现中再次变得越来越普遍。毕竟,全方位推理是关系编程的主要吸引力之一,因此使用“dif/2”以合理且通用的方式表达术语不等式。 (4认同)
  • 将重新审视我以前的所有解决方案。 (2认同)