Search code examples
recursionprolog

Invalid Result in Prolog Program


My program is intended to get rid of repeating elements in a list. I think its correct. I tried to make it work by putting cuts, but it didn't give correct results.

member(X,[X|_]). % If X is Head, it is a member
member(X,[_|T]) :- member(X,T). % If not, Recursively check the tail.

remove_duplicates([],[]).
remove_duplicates([H|T],R):-member(H,T),remove_duplicates(T,R).
remove_duplicates([H|T],[R|REM]):-remove_duplicates(T,REM).

However I am getting results: I = [_G2608, _G2611, _G2614|_G2615] for input remove_duplicates([a,b,a,b,b,c],I).


Solution

  • Here is a version that is both pure and also more efficient in the sense that it does only need constant auxiliary space. The solutions posted so far require in the worst case space proportional to the size of the list in the first argument. But now to correctness:

    ?- remove_duplicates([A,B], Fs).
    

    Here we ask:

    How must A and B look like to result in a list Fs that has no duplicates?

    This question cannot be answered simply by stating the concrete Fs, for this Fs might be [A,B] or [A] should A and B be the same.

    ?- remove_duplicates([A,B],F).
       A = B, F = [B]
    ;  F = [A, B], dif(A, B).
    

    And here is a solution to it. This definition requires the monotonic if_/3 and memberd_truth/3 defined in another answer.

    remove_duplicates([], []).
    remove_duplicates([E|Es], Fs0) :-
       if_( memberd_truth(E, Es) , Fs0 = Fs , Fs0 = [E|Fs] ),
       remove_duplicates(Es, Fs).
    

    Personally, I would prefer a more relational name, like list_unique/2 or list_nub/2 as an allusion towards Haskell.