First, it's easier to read a code written with consistent naming:
union([], A, A) :- !.
union([A|B], C, D) :-
memberchk(A, B), !,
union(B, C, D).
union([A|B], C, [A|D]) :-
union(B, C, D).
What does it do? Given two lists A
and B
, it produces the result with a prefix of all elements of A
not present in B
, and then B
itself. So, union([1,2,3],[],[1,2,3])
. And also union([1,2,3],[2],[1,3,2])
. You see that order is preserved here. So if you want to compare lists regardless of their order, you should write a special, additional predicate for that: union([1,2,3],[],X), regardless(X,[3,2,1])
should work then.
Same with the duplicates - your set equality predicate should disregard any. union
as presented above OTOH is not about sets, but about lists. There are many different lists representing the same set, but as lists they are considered different.
In your 25
you hit the issue of duplicates: union([a,a],[a],X)
produces X=[a]
. Again, as set it is the same as [a,a]
, but as lists they are different.
One strategy for coping with this is to represent sets as strictly increasing lists - if your elements have order well defined for them. Then we can write union
such that given two sets according to that definition, it will also produce a set, and it will work efficiently at that:
union([],X,X):-!.
union(X,[],X):-!.
union([A|B],[C|D],[H|T]):-
A @< C -> H=A, union(B,[C|D],T) ;
C @< A -> H=C, union([A|B],D,T) ;
H=A, union(B,D,T).
We will have to define make_set/2
predicate here too. Even better (in some respects) is representing sets (again, of comparable elements) by self-balancing binary trees.