Prolog中的alldifferent / 1谓词

时间:2014-04-08 09:56:26

标签: prolog dcg

allsame([]).
allsame([X]).
allsame([X,X|Z]) :-
   allsame([X|Z]).

如何更改以获得以下结果?

alldifferent(L): The elements in L are all different.

?- alldifferent([a, b, b, c, d]).
false
?- alldifferent([a, b, c, d, e]).
true

3 个答案:

答案 0 :(得分:6)

首先,让我们先看一下allsame/1的定义。对于给定的列表, 你正在建立这样的平等:

[ A,      B,      C,      D ... ]
    A = B,  B = C,  C = D,   ...      chain pattern

所以你正在建立一系列平等。还有另一种表达方式,通过引用一个公共变量来代替:

[ A,      B,      C,      D ... ]
  A = V,  B = V,  C = V,  D = V,  ... star pattern

这最常见地表达为:

allsame_bis(L) :-
   maplist(=(_), L).

或者,更多的增长,不使用经常定义的maplist/2

allsame_ter(L) :-
   allsame_with(L, _).

allsame_with([], _).
allsame_with([X|Xs], V) :-
   X = V,                      % could be moved into the head
   allsame_with(Xs, V).

现在,你说,你"想要关联"这到alldifferent/1,这使情况更加复杂。请注意,alldifferent/1并非直接否定您的财产。那将是:

notallsame(L) :-
   phrase((..., [A,B], {dif(A,B)}, ...), L),
  

有两个直接连续的元素是不同的。

为了完整性,这里有一个版本可以避免notallsame([1,2,3])之类的查询的多余答案:

notallsame_bis(L) :-
   phrase((all(=(A)),[A,B], {dif(A,B)}, ...), L).
  

首先是一系列相同的元素,然后是一个不同的元素。

all//1... //1defined elsewhere

但回到不同的/ 2。平等关系是可传递的,它允许我们做一些快捷方式,无论是做链还是做星。但是不同是不可传递的。所以我们现在要在所有可能的对之间建立差异关系dif/2。总的来说,我们需要 n 2- n / 2许多dif/2目标。嘿,让我们高兴我们仍然可以利用交换性,否则我们将不得不支付两倍以上。

 alldifferent([]).
 alldifferent([X|Xs]) :-
    maplist(dif(X), Xs),
    alldifferent(Xs).

此关系建立dif/2,如此:

 [ A,      B,        C,        D,        E ... ]
       dif(B,A), dif(C,A), dif(D,A), dif(E,A), ... maplist(dif(A),[B,C,D,E ...])
                 dif(C,B), dif(D,B), dif(E,B), ... maplist(dif(B),  [C,D,E ...])
                           dif(D,C), dif(E,C), ... maplist(dif(C),    [D,E ...])
                                     dif(E,D), ... maplist(dif(D),      [E ...])

这是另一个版本,可能看起来更诱人。事实上,它与您的原始程序有一定的相似之处。不是吗?

alldifferent_bis([]).
alldifferent_bis([_]).
alldifferent_bis([A,B|Xs]) :-
   dif(A,B),
   alldifferent_bis([A|Xs]),
   alldifferent_bis([B|Xs]).

最后,我们可以使用a higher order definition使用以下定义:

alldifferent_ter(L) :-
   pairwise(dif,L).

allsame_quater(L) :-
   pairwise(=,L).

如果您因任何原因无法使用dif/2,请使用安全的ISO Prolog近似iso_dif/2。它会尽可能频繁地(安全地)成功,但是当可能发生安全故障时可能会产生错误。想想alldifferent_bis([_,a,a])

答案 1 :(得分:0)

这是一种方式:

all_different( Xs ) :-
  all_different(Xs,[])
  .

all_different( []     , _ ) .  % if the source list is exhausted, success!
all_different( [X|_]  , S ) :- % if the list is non-empty
  member(X,S ) ,               % - if we've already seen X
  ! ,                          % - we cut and fail:
  fail                         % The list has duplicates
  .                            %
all_different( [X|Xs] , S ) :- % otherwise, we simply recurse down,
  all_different( Xs , [X|S] )  % prepending X to the "already seen" list.
  .                            % Easy!

另一种方式:

all_different( Xs ) :- setof( X , member(X,Xs), Xs ) .

第三种方式:

all_different( Xs ) :-
  msort(Xs,S) ,
  check_uniqueness(S)
  .

check_uniqueness([]) .
check_uniqueness([X,X|_]) :- !, fail .
check_uniqueness([_|Xs])  :- check_uniqueness(Xs) .

第四种方式:

all_different( Xs ) :-
  findall( X , (append(Pfx,[X|Sfx],Xs),(member(X,Pfx);member(X,Sfx))) , [] )
  .

有多种方法可以做到这一点......有些方法比其他方式更有效。

答案 2 :(得分:0)

另一种方法:

all_different(Xs) :-
    sort(Xs, Sorted),
    msort(Xs, Sorted).

SWI-Prolog式模式声明:

%! all_different(+Xs:list) is semidet.