-1

Implement a Prolog Predicate that removes all duplicate elements from a list given in the first argument and returns the result in the second argument position.

false
  • 10,264
  • 13
  • 101
  • 209
  • 2
    https://www.swi-prolog.org/pldoc/man?predicate=sort/2 removes duplicates, as well as sorting. – brebs Sep 29 '22 at 14:03
  • 1
    Otherwise, see https://stackoverflow.com/questions/72720605/prolog-how-to-eliminate-duplicates-of-list-of-elements-without-changing-the-ord – brebs Sep 29 '22 at 14:16
  • 1
    Stack Overflow is not a homework writing service. What have you tried? Show us your source code. What's not working? See also the question https://stackoverflow.com/help/how-to-ask – Nicholas Carey Sep 29 '22 at 17:19
  • See [`list_nub/2`](https://stackoverflow.com/a/55144672/772868) for a pure implementation. – false Sep 29 '22 at 18:24

1 Answers1

0

Consider the possibilities. There's just a few cases:

  • The source list is the empty list ([]). That is a set (, the empty set) by definition. That give us this:

    list_set( [] , [] ) .
    
  • The source list is not empty, and the head of the list is duplicated in the tail of the list. It this case, since the head can be found elsewhere in the list, it's a duplicate and can be discarded. That gives us this:

    list_set( [X|Xs] , Ys ) :-
      member(X,Xs),
      list_set(Xs,Ys)
      .
    
  • Finally, the source list is not empty, and the head of the list is *not duplicated in the tail of the list. In this case, since the head is unique, we can add the head to the result set. That gives us this:

    list_set( [X|Xs] , [X|Ys] ) :-
      \+ member(X,Xs),
      list_set(Xs,Ys)
      .
    

That's all there is to it.

Putting it all together you get:

list_set( []     , []     ) .
list_set( [X|Xs] , Ys     ) :-    member(X,Xs), list_set(Xs,Ys) .
list_set( [X|Xs] , [X|Ys] ) :- \+ member(X,Xs), list_set(Xs,Ys) .

You might notice the two tests using member/2. That can be optimized away by introducing a cut (!/0) to eliminate the choice point between clauses 2 and 3 of the above predicate. Having introduced that, we can eliminate the second member/2, because the only way to get to the third clause is if X is unique:

list_set( []     , []     ) .
list_set( [X|Xs] , Ys     ) :- member(X,Xs), !, list_set(Xs,Ys) .
list_set( [X|Xs] , [X|Ys] ) :-                  list_set(Xs,Ys) .
Nicholas Carey
  • 71,308
  • 16
  • 93
  • 135
  • 1
    Can *double* the performance by using `memberchk` instead of member. Can test that with: `numlist(1, 3_000, NL1), numlist(1, 3_000, NL2), numlist(1, 3_000, NL3), append([NL1, NL2, NL3], L), time(list_set(L, LU)), length(LU, LULen).` – brebs Sep 29 '22 at 17:53