1
duplicate([],[]).
duplicate([A|B],[A|B1]) :- not(member(A,B)), duplicate(B,B1).
duplicate([A|B],List) :- member(A,B), duplicate(B,List).

I wrote this predicate to remove duplicate from the list, but when I test it,

?- duplicate([a,b,c,a,d,c,b,a,e,f],N).
N = [d, c, b, a, e, f] ;
N = [d, c, b, a, e, f] ;
false.

Is there a way to just keep one result only, not two same results? (so it will only return one list).

Also, I am not allowed to use operators that modify the backtracking search, such as the cut operator !, the negation operators not, +, or the if-then-else operator with -> and ;

It would be grateful if someone could help me . :D

false
  • 10,264
  • 13
  • 101
  • 209
MeiLin Xu
  • 33
  • 4

2 Answers2

3

The actual reason for receiving more than one answer is the goal member(A,As). It produces multiple answers for duplicates in As.

?- member(a, [a,a]).
   true
;  true.

There are several ways out.

memberchk/2 or once/1

memberchk/2 is defined as

memberchk(X, Xs) :-
   once(member(X, Xs)).

This removes alternate answers. But then, it may remove otherwise valid solutions too. Consider:

?-        memberchk(X, [a,b]), b = X.
   false.
?- b = X, memberchk(X, [a,b]), b = X.
   b = X.

So memberchk/2 is sensitive to the precise instantiation, which makes it a very brittle, impure predicate.

But it has one good point: It sticks to just one answer for

?- memberchk(a, [a,a]).
   true.

So what would be ideal is a definition that is both pure and sticking to the first element. Enter

memberd/2

memberd(X, [X|_Ys]).
memberd(X, [Y|Ys]) :-
   dif(X, Y),
   memberd(X, Ys).

In this definition, the recursive rule is only of relevance if the list element is different. Thus this rule will never apply to memberd(a, [a,a,a]).

Another problem in your definition is not(member(A,B)) which only behaves as intended, if A and B are sufficiently instantiated. Your definition fails for: duplicate([a,X],[a,b]). although there is a solution: X = b.

Rather replace it by non_member/2.

Alternatively, in case you are interested in the most efficient solution, consider library(reif) available for SICStus and SWI which leads to:

list_nub([], []).
list_nub([X|Xs], Ys0) :-
   if_(memberd_t(X, Xs),  Ys0 = Ys1, Ys0 = [X|Ys1]),
   list_nub(Xs, Ys1).
false
  • 10,264
  • 13
  • 101
  • 209
0

Here's one way to remove all duplicates, not the most efficient but I think it's quite easy to understand the intention.

rm_duplicates(In, Out) :-
    exclude(has_duplicate(In), In, Out).

has_duplicate(List, Case) :-
    dif(I, J),
    nth0(I, List, Case),
    nth0(J, List, Case).

If you mean to make a list into a set:

list_to_set(List, Set).

It's documented: list_to_set/2

Paul Brown
  • 2,235
  • 12
  • 18
  • 1
    Note that `exclude/3` has an implicit cut which produces incorrect answers like: `B=b,rm_duplicates([a,B],Cs).` succeeds, whereas `rm_duplicates([a,B],Cs).` fails – false Mar 13 '19 at 12:26