List 可逆合并

List 可逆合并,list,prolog,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). 合并([A | T],[A | T])。 合并([],[A | T],[A | T])。 合并([A|T

我们可以按如下方式合并两个排序列表

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).
合并([A | T],[A | T])。
合并([],[A | T],[A | T])。
合并([A|T],[B|U],[A|V]):-A@=B,合并([A | T],U,V)。
在一个方向上效果很好。对于这个定义不起作用的是如下的查询
merge_uuA[A,b,c],[A,b,c,d])。
,尽管有一个独特且非常明显的解决方案
A=[d]。
。即使在
merge(A,B,[A,B,c])上迭代,
也会产生相当小的结果集:
A
[A,B,c]
B=[A,B,c]\A
的有序子集

如何更改
合并
的定义以使其在各个方向上都能工作?

谓词
(@=)/2
不是单调的,因此不具有正确描述“合并”一般含义所必需的属性。使用此类谓词的程序可能在特定情况下工作,但通常会产生不正确的结果

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

添加一个目标最多只能减少解决方案集,而不能扩展解决方案集

这些特性打破了这种单调性,例如:

?- Y @< X. false. 什么?我们刚刚被告知没有任何解决办法

换句话说,在处理这些谓词时,我们甚至不能应用最基本的逻辑推理。在我看来,如果您想充分享受逻辑编程的好处,那么最好避免使用此类谓词

约束提供了摆脱这种混乱局面的方法。约束在所有实例化模式中都能正确工作,并允许更通用的程序在所有方向上工作

下面是一个程序,它使用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, 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. 合并([A | T],[A | T])。 合并([],[A | T],[A | T])。 合并([A | T],[B | U],[A | V]):-A#=B,合并([A|T],U,V)。 您引用的示例的整数类比:

?- merge_(A, [1,2,3], [1,2,3,4]). A = [4]. ?-合并(A[1,2,3],[1,2,3,4])。 A=[4]。 所以,这完全符合预期。唯一的缺点是:它要求我们将要推理的实体映射为整数。在实践中,很多程序在任何情况下都会对整数进行推理,所以这通常是可以接受的

类似的推广也适用于其他领域。然而,整数是最常用的领域之一,因此所有广泛使用的Prolog系统都附带CLP(FD)约束,我建议将其作为此类问题的有用起点甚至解决方案

顺便说一下,如果您只使用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, 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. -合并(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]; 等
@马特的回答恰到好处。当将问题限制为整数时,您可以使用CLP(FD)运算符,因为它们了解域(或“可能值的范围”)

不幸的是,
A@
无法事先知道
A
B
的可能值,因此无法“提出”任何可能的解决方案。例如,如果在可能值的域中有一个谓词
valid(X)
X
是真的,那么您可以写,
valid(a),valid(B),a@
。这里有一个人为的例子,说明了如果你知道你关心的可能原子的有限域是什么,这是如何工作的

    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).

您接受的答案要求列表元素为数字。你需要这个来处理一般的原子元素吗?我很想使用一般的原子元素,但现在我很高兴能理解问题所在。