可逆合并

时间:2016-07-26 10:15:32

标签: list prolog

我们可以合并两个排序列表,如下所示

merge_([A|T], [], [A|T]).
merge_([], [A|T], [A|T]).
merge_([A|T], [B|U], [A|V]) :- A @< B, merge_(T, [B|U], V).
merge_([A|T], [B|U], [B|V]) :- A @>= B, merge_([A|T], U, V).

在一个方向上工作正常。对于此定义不起作用的是像merge_(A, [a, b, c], [a, b, c, d]).这样的查询,尽管有独特且非常明显的解决方案A = [d].。即使迭代merge_(A, B, [a, b, c]).也应该产生相当微不足道的结果:A[a, b, c]B = [a, b, c] \ A的有序子集。

如何更改merge_的定义以使其在所有方向都有效?

2 个答案:

答案 0 :(得分:4)

谓词(@<)/2(@>=)/2 不是单调的,因此不会展示正确描述“合并”一般意味着什么所需的属性。使用此类谓词的程序可能在特定实例中有效,但通常会产生不正确的结果。

为了向您展示问题的本质,这些谓词违反了以下重要的声明性属性:

  

添加目标最多可以 reduce ,永不延伸,解决方案集。

这些单调性被这些属性打破,例如:

?- Y @< X.
false.

没有解决方案,是吗?特别是,当然 X=1Y=0不是的解决方案,对吧?好吧,让我们看看:

?- X=1, Y=0, Y @< X.
X = 1,
Y = 0.

Whaaat?我们刚被告知没有任何解决方案

换句话说,在使用这些谓词时,我们根本无法应用最基本的逻辑推理。在我看来,如果你想充分发挥逻辑程序设计的优势,最好避免使用这些谓词。

约束提供了解决这个烂摊子的方法。约束在所有实例化模式中都能正常工作,并允许更多通用程序在所有方向上工作。

这是一个程序,使用 CLP(FD)约束来描述整数列表的这种合并:

merge_([A|T], [], [A|T]).
merge_([], [A|T], [A|T]).
merge_([A|T], [B|U], [A|V]) :- A #< B, merge_(T, [B|U], V).
merge_([A|T], [B|U], [B|V]) :- A #>= B, merge_([A|T], U, V).

您引用的示例的整数类比:

?- merge_(A, [1,2,3], [1,2,3,4]).
A = [4].

因此,这完全符合预期。唯一的缺点:它要求我们将我们想要推理的权利映射到整数。实际上,在任何情况下,很多程序都会对整数进行推理,所以这通常是可以接受的。

其他域也可以进行类似的概括。但是,整数是最常用的域之一,因此所有广泛使用的Prolog系统都附带了CLP(FD)约束,我建议将其作为这些问题的有用起点甚至解决方案。

顺便说一句,如果您只是使用CLP(FD)约束,那么您引用的另一个示例也可以完全按预期工作:

?- merge_(A, B, [1,2,3]).
A = [1, 2, 3],
B = [] ;
A = [],
B = [1, 2, 3] ;
A = [1],
B = [2, 3] ;
A = [1, 2],
B = [3] ;
etc.

答案 1 :(得分:3)

@mat的回答是现货。当将问题局限于整数时,您可以获得CLP(FD)运算符的好处,因为他们了解域(或“可能值的范围”)。

不幸的是,A @< B无法先验知道AB的可能值是什么,因此可以“建议”无可能的解决方案例如,如果您在可能值的域中有valid(X)的谓词X,则可以写valid(A), valid(B), A @< B, ...。这是一个人为的例子,展示了如果你知道你关心的可能原子的有限域是如何工作的。

    valid(X) :- member(X, [a,b,c,d,e,f,g,h,i,j,k,l,m,n]).

    merge_([A|T], [], [A|T]).
    merge_([], [A|T], [A|T]).
    merge_([A|T], [B|U], [A|V]) :- valid(A), valid(B), A @< B, merge_(T, [B|U], V).
    merge_([A|T], [B|U], [B|V]) :- valid(A), valid(B), A @>= B, merge_([A|T], U, V).

现在我们已经限制AB存在于特定的有限值域中,您的谓词可以“反向”工作:

| ?- merge_(A, [a, b, c], [a, b, c, d]).

A = [d] ? a

no
| ?- merge_(A, B, [a, b, c]).

A = [a,b,c]
B = [] ? a

A = []
B = [a,b,c]

A = [a]
B = [b,c]

A = [a,c]
B = [b]

A = [a,b]
B = [c]

A = [b,c]
B = [a]

A = [b]
B = [a,c]

A = [c]
B = [a,b]

no
| ?-