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