Search code examples
listprologrule

Prolog: Rules evaluating in an unexpected order


I am currently working on a Prolog program to remove duplicates from a list.

Here is my code:

makeset([First], [First]). % if only one item in list, the solution is itself.
makeset([First, Second], [First, Second]) :-
   dif(First, Second).
makeset([First, Second], [First]) :-
    First = Second.
% if more than two items in list
makeset([First | Tail], FinalList) :-
   member(First, Tail),
   makeset(Tail, FinalList).
makeset([First | Tail], FinalList) :-
   not(member(First, Tail)),
   makeset(Tail, [First |FinalList]).

However, when I try this on a list with more than two items in it, it just returns false. I ran a trace, and found that it keeps using the last two rules even when it gets down to two items in the list. Why is this?

Example query: makeset([3, 2, 4, 5, 1], X). I get back false, but it keeps using the last rule for every evaluation.

Edit: I changed the rules to the following, and it still will not evaluate properly, so the issue has to be with the last rule.

makeset([First], FinalList) :- 
 FinalList = [First]. % if only one item in list, the solution is itself.
makeset([First, Second], FinalList) :- 
 not(First = Second), FinalList = [First, Second].
makeset([First, Second], FinalList) :- 
 First = Second, FinalList = [First].
% if more than two items in list
makeset([First | Tail], FinalList) :- 
 member(First, Tail), makeset(Tail, FinalList).
makeset([First | Tail], FinalList) :- 
 not(member(First, Tail)), makeset(Tail, [First |FinalList]).

How might I fix the final rule?


Solution

  • makeset([First | Tail], [First | FinalList]) :- 
     not(member(First, Tail)), makeset(Tail, FinalList).
    

    You should not make First part of the input to makeset, that prevents the earlier rules from matching. You should only add First to the list after FinalList is unified using the makeset predicate.

    Your solution also does not handle the case when the input list is empty.