所以我有一个谓词:
compatible([],_).
compatible(_,[]).
compatible([HA|TA],[HB|TB]) :-
HA \= HB,
compatible(TA,TB).
它目前需要两个列表并确定它们是否成对兼容,如果是,则返回true,否则返回false。
例如:
?- compatible([8,2,6,3,67],[7,4,7,4,3]).
true
?- compatible([8,2,6,3,3],[7,4,7,4,3]).
false.
第二次调用返回false,因为3位于两个列表的第5位。
我的问题是如何修改此谓词,以便递归检查2个以上的列表。潜在地,谓词可以检查包含1,2,3,4,5或甚至无限列表的列表列表。如果任何列表彼此不兼容,它将返回false。
因此我可以查看3个列表:
let Y = [[8,2,6,3,67],[7,4,7,4,3],[1,3,42,1,52]]
then...
?- compatible(Y).
true
let Z = [[8,2,6,3,67],[7,4,7,4,3],[1,3,6,1,52]]
then...
?- compatible(Z).
false.
第二次调用失败,因为6位于列表1和3的第3位。
答案 0 :(得分:5)
如果所有子列表的长度相同,并且元素总是整数,则可以根据library(clpfd)提供的两个谓词来定义问题:
compatible(Ls) :-
transpose(Ls, T),
maplist(all_different, T).
使用此定义,您的示例:
?- compatible([[8,2,6,3,67],[7,4,7,4,3],[1,3,42,1,52]]).
true.
?- compatible([[8,2,6,3,67],[7,4,7,4,3],[1,3,6,1,52]]).
false.
如果子列表可以有不同的长度,您应该首先找到最短的,然后将其余部分剪切到该长度。
如果列表的元素可以是任意的Prolog术语,请查看at this question。简而言之:
all_different_terms([]).
all_different_terms([H|T]) :-
maplist(dif(H), T),
all_different_terms(T).
请注意使用dif/2
代替\=/2
。
?- compatible_terms([[a,b,c],[d,e,f],[g,h,i]]).
true.
?- compatible_terms([[a,b,c],[d,e,f],[a,h,i]]).
false.
all_different_terms/1
的定义还应该让您了解如何实现初始建议,重用已定义的谓词compatible/2
:
all_compatible([]).
all_compatible([H|T]) :-
maplist(compatible(H), T),
all_compatible(T).
答案 1 :(得分:4)
编写递归Prolog代码可以是一个很好的练习,很多乐趣,一个有益的活动,但正确地获取递归的细节可能很难,特别是新手!
幸运的是,还有另一种方式;一个可以提升你更高级别的,一个更惯用的逻辑编程方式,它可以立即让你做很多的事情。对于您的" 成对兼容性"问题,请考虑将meta-predicate pairwise/2
与您已有的compatible/1
谓词结合使用!以下是:
?- pairwise(compatible, [[8,2,6,3,67],[7,4,7,4,3],[1,3, 6,1,52]]). false. % finite failure, as expected ?- pairwise(compatible, [[8,2,6,3,67],[7,4,7,4,3],[1,3,42,1,52]]). true % succeeds, as expected ; true % (1st redundant answer) ; true % (2nd redundant answer) ; true % (3rd redundant answer) ; true % (4th redundant answer) ; true % (5th redundant answer) ; true % (6th redundant answer) ; true. % (7th redundant answer)
修改强>
首先是好消息:以上两个答案都正确!那么"多余的答案"意思?他们好吗?坏? 如果是这样,有多糟糕?怎么会?而且,我们如何应对?让我们找出来吧!
冗余答案不好,应予以处置。它们也很常见。
冗余答案没有那么糟糕,比如说,"失去声明性语义"。
冗余答案比 <#34;在找到所有答案后留下无用的选择点&#34;。有关该问题的详细信息,请查看explanations given in this answer。
为什么会这样?要回答这个问题,我们会考虑compatible/1
的定义:
compatible([],_). compatible(_,[]). compatible([HA|TA],[HB|TB]) :- dif(HA,HB), % unlike `(\=)/2`, `dif/2` is sound compatible(TA,TB).
突出显示了两个罪魁祸首条款。使用普通的第一个参数索引(以及上面的定义),Prolog无法推断某些目标可以确定性地成功。
我们可以应付吗?如果是这样,怎么样?程序问题 - 程序解决方案,对吧?!是的,但是我们需要选择明智的道路,以免我们 在一些仅仅是有限加速的声明性语义中进行交易 - 就像基于元逻辑Prolog谓词的许多快速修复一样。
幸运的是,我们可以致电first argument indexing来救援!
compatible([],_). compatible([X|Xs],Ys) :- compatible_(Ys,X,Xs). compatible_([],_,_). compatible_([Y|Ys],X,Xs) :- dif(X,Y), compatible(Xs,Ys).
现在更好?让我们使用compatible/1
的改进定义运行查询:
?- pairwise(compatible, [[8,2,6,3,67],[7,4,7,4,3],[1,3, 6,1,52]]). false. % SAME : finitely fails ?- pairwise(compatible, [[8,2,6,3,67],[7,4,7,4,3],[1,3,42,1,52]]). true. % BETTER: succeeds deterministically
答案 2 :(得分:1)
这是一种非递归的方式。
compatible([],_).
compatible(_,[]).
compatible([HA|TA],[HB|TB]) :-
HA \= HB,
compatible(TA,TB).
pairs(ListOfLists,One,Two):-
select(One,ListOfLists,L1),
select(Two,L1,L2).
test(ListOfLists):-
forall(pairs(ListOfLists,A,B),compatible(A,B)).
否则:
compatible([],_).
compatible(_,[]).
compatible([HA|TA],[HB|TB]) :-
HA \= HB,
compatible(TA,TB).
compatible_one_many(_,[]).
compatible_one_many(One,Many):-
Many=[H|T],
compatible(One,H),
compatible_one_many(One,T).
test_recursive([]).
test_recursive(ListOfLists):-
ListOfLists=[H|T],
compatible_one_many(H,T),
test_recursive(T).
您使用test/2
和test_recursive/2
检查您的列表是否兼容。