Search code examples
prolog

How to compare two lists in Prolog?


I am trying to write a program which takes a list containing lists, and then compares the lists inside. If any two lists contain the same information, such as them both containing X, then I want to remove X from both lists. How could I go about this?


Solution

  • This seems to do it:

    dedupe(Lists, Result) :-
        append(Lists, Flat),
        findall(E, (select(E, Flat, FlatNoE), \+ memberchk(E, FlatNoE)), Uniques),
        subtract(Flat, Uniques, Unwanted),
        findall(Rs, (member(Ls, Lists), subtract(Ls, Unwanted, Rs)), Result).
    

    e.g.

    ?- dedupe([[1,2,3], [1,2,5,6,1], [4,1,2,1]], R).
    R = [[3], [5, 6], [4]]
    

    The design is:

    • we need to remove duplicates from each sublist
    • duplicates are found by finding all the elements, finding the unique elements, and removing those from all leaving the duplicate elements.
    • All elements are found by flattening the nested lists with append/2.
    • Unique elements are found by taking one element out at a time with select and seeing if that element still exists in the rest of the values with memeberchk.
    • subtract does a list-from-list subtraction to remove the unique elements from all the elements, leaving the Unwanted duplicate elements.
    • The last findall then runs subtract over the sublists, removing the duplicaates from each one.

    It's possible in SWI Prolog to rewrite the last line as:

    maplist({Unwanted}/[Ls, Ls_]>>subtract(Ls, Unwanted, Ls_), Lists, Result).
    

    fwiw. In small testing they have almost identical timing/inferences to solve.