我们可以合并两个排序列表,如下所示
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_
的定义以使其在所有方向都有效?
答案 0 :(得分:4)
谓词(@<)/2
和(@>=)/2
不是单调的,因此不会展示正确描述“合并”一般意味着什么所需的属性。使用此类谓词的程序可能在特定实例中有效,但通常会产生不正确的结果。
为了向您展示问题的本质,这些谓词违反了以下重要的声明性属性:
添加目标最多可以 reduce ,永不延伸,解决方案集。
这些单调性被这些属性打破,例如:
?- Y @< X. false.
没有解决方案,是吗?特别是,当然 X=1
,Y=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
无法先验知道A
和B
的可能值是什么,因此可以“建议”无可能的解决方案例如,如果您在可能值的域中有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).
现在我们已经限制A
和B
存在于特定的有限值域中,您的谓词可以“反向”工作:
| ?- 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
| ?-