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

How do I change to get the result below?

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

?- alldifferent([a, b, b, c, d]).
false
?- alldifferent([a, b, c, d, e]).
true
false
  • 10,264
  • 13
  • 101
  • 209
aman
  • 87
  • 1
  • 7
  • 1
    By using the search facility.. http://stackoverflow.com/questions/20131904/check-if-all-numbers-in-a-list-are-different-in-prolog – magus Apr 08 '14 at 10:01
  • 1
    The question could be made more interesting by asking what would be the most *efficient* way of calculating this. I'm not sure whether the `length/1`-method is faster/slower than working on a sorted list (with duplicates). – Wouter Beek Apr 08 '14 at 10:15
  • Are you looking for understanding on how to create `alldifferent/1` or are you really specifically wanting to know how it might relate to `allsame/1` (the answer given to you in a different question) and specifically how `allsame/1` could be morphed to be `alldifferent/1`? Finding the solution to a predicate like `alldifferent/1` may not look at all like solving `allsame/1`. Also, you should try to take what you've learned so far and apply it the problem. Start by writing down what you think it means logically for members of a list to be "all different" and then translate that to Prolog. – lurker Apr 08 '14 at 10:52
  • @magus@mbratch thank both of you. i think i figured out the solution. I was just looking for alldifferent/1 predicate then but now, I want to relate it with the allsame/1. Thank you again :) – aman Apr 08 '14 at 11:24

3 Answers3

6

First, lets look at your definition of allsame/1 first. For a given list, you are establishing equalities like so:

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

So you are establishing a chain of equalities. There is another way of expressing this, by referring to a common variable instead:

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

This is most idiomatically expressed by:

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

Or, more prolix, without using the frequently defined 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).

Now, you say, you "want to relate" this to alldifferent/1, which makes the situation even more complex. Note that alldifferent/1 is not a direct negation of your property. That would be:

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

There are two directly consecutive elements that are different.

For completeness, here is a version that avoids redundant answers for queries like notallsame([1,2,3]):

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

There is first a sequence of identical elements, followed by an element that is different.

all//1 and ... //1 are defined elsewhere.

But back to alldifferent/2. The equality-relation is transitive, which permitted us to do some shortcuts, either doing a chain or a star. But being different is not transitive. So we have now to establish the difference relation dif/2 between all possible pairs. In total, we need n2-n/2 many dif/2 goals. Hey, let's be happy that we still can exploit commutativity, otherwise we would have to pay more than twice.

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

This relation establishes dif/2 like so:

 [ 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 ...])

Here is another version, which might look seductively simpler. In fact, it has a certain resemblance to your original program. Has it not?

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

So finally, we might use the following definitions using a higher order definition:

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

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

Should you, for whatever reason, not be able to use dif/2, use the safe ISO Prolog approximation iso_dif/2 instead. It will succeed as often as (safely) possible, it might produce an error, when safe failure is possible, though. Think of alldifferent_bis([_,a,a]).

Community
  • 1
  • 1
false
  • 10,264
  • 13
  • 101
  • 209
0

Here's one way:

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!

Another way:

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

A third way:

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

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

A fourth way:

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

There's more than one way to do it...some more efficient than others.

Nicholas Carey
  • 71,308
  • 16
  • 93
  • 135
0

One more way to do it:

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

SWI-Prolog-style mode declaration:

%! all_different(+Xs:list) is semidet.
Sergii Dymchenko
  • 6,890
  • 1
  • 21
  • 46
  • 1
    `all_different([X,a]), X = a` now succeeds. Either it should fail, or produce an error. – false Apr 08 '14 at 20:08
  • @false It's not intended to be used when the argument is not fully instantiated. I've added mode declaration to show this. – Sergii Dymchenko Apr 08 '14 at 20:24
  • Above declaration rules out `all_different([a,b|_])` but does not rule out `all_different([X,a])`. And, if it would, what about `all_different([+X,+a])`? – false Apr 08 '14 at 22:00
  • @false Both `[X,a]` and `[+X,+a]` are not fully instantiated. The declaration requires the argument to be fully instantiated. – Sergii Dymchenko Apr 08 '14 at 22:08
  • The declaration for `length/2` above only says: The `list` must be `+`. What you imply is: It has to be ground. Please show me where such a thing is required. Further this means that `all_different([+_,-_])` will not be considered by you. – false Apr 08 '14 at 22:38
  • @false According to SWI-Prolog documentation http://www.swi-prolog.org/pldoc/man?section=modes, `+` in declaration means 'fully instantiated', i.e., not contain any variables. – Sergii Dymchenko Apr 08 '14 at 22:58
  • And of course declaration should not be about `length`, but about `all_different` - I just copied this from SWI-Prolog docs and forgot to change. – Sergii Dymchenko Apr 08 '14 at 23:01
  • 1
    The *entire sentence* reads: Argument must be fully instantiated to a term that satisfies the type. And [_] is fully instantiated for `list`. – false Apr 08 '14 at 23:01
  • @false `[_]` is not fully instantiated, it contains a variable. If it would be fully instantiated, we would check if it's a list. `a` is fully instantiated, but is not fully instantiated to a list. – Sergii Dymchenko Apr 08 '14 at 23:17
  • @false I hope I communicated clearly that the predicate is not intended to be used when the argument is not fully instantiated. – Sergii Dymchenko Apr 08 '14 at 23:19
  • 1
    Please read again the sentence: it says: fully instantiated to a term that satisfies the type. Your reading would be expressed as: Ground. – false Apr 08 '14 at 23:24
  • @false 1. 'Fully instantiated' and 'ground' means the same. 2. 'Fully instantiated to a type' means 'fully instantiated and of a specified type'. That's my understanding. Are you disagree with 1 or 2 or both? – Sergii Dymchenko Apr 08 '14 at 23:31
  • HET! Maybe agreement with 1 (under certain circumstances). But never with 2. The type is just `list`. And not `list_of_chars` or whatever. So the elements are not considered. – false Apr 08 '14 at 23:35