Prolog:使用递归在多个列表上成对兼容

时间:2015-10-09 02:58:09

标签: list recursion prolog

所以我有一个谓词:

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位。

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代码可以是一个很好的练习,很多乐趣,一个有益的活动,正确地获取递归的细节可能很难,特别是新手!

幸运的是,还有另一种方式;一个可以提升你更高级别的,一个更惯用的逻辑编程方式,它可以立即让你做很多的事情。

对于您的" 成对兼容性"问题,请考虑将 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)

修改

首先是好消息:以上两个答案正确!那么"多余的答案"意思?他们好吗?坏? 如果是这样,有多糟糕?怎么会?而且,我们如何应对?让我们找出来吧!

  1. 冗余答案不好,应予以处置。它们也很常见。

  2. 冗余答案没有那么糟糕,比如说,"失去声明性语义"。

  3. 冗余答案 <#34;在找到所有答案后留下无用的选择点&#34;。有关该问题的详细信息,请查看explanations given in this answer

  4. 为什么会这样?要回答这个问题,我们会考虑compatible/1的定义:

    compatible([],_).
    compatible(_,[]).
    compatible([HA|TA],[HB|TB]) :-
        dif(HA,HB),                  % unlike `(\=)/2`, `dif/2` is sound
        compatible(TA,TB).
    

    突出显示了两个罪魁祸首条款。使用普通的第一个参数索引(以及上面的定义),Prolog无法推断某些目标可以确定性地成功。

  5. 我们可以应付吗?如果是这样,怎么样?程序问题 - 程序解决方案,对吧?!是的,但是我们需要选择明智的道路,以免我们 在一些仅仅是有限加速的声明性语义中进行交易 - 就像基于元逻辑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).
    
  6. 现在更好?让我们使用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/2test_recursive/2检查您的列表是否兼容。