user3517102
user3517102

Reputation: 68

Finding an intersection in Prolog using list predicates

I'm really new to Prolog and I am trying to make an isIntersection that gives me the intersection of two lists and puts the answer in the third list. I cannot use any Prolog list predicates because it's for a class and that's just the rules. This is what I have and I'm having trouble debugging and seeing why this implementation is wrong. Anyone have any ideas?

/* Checks if the item is in the list */
in(Item, [Item|Rest]).
in(Item, [Not|Rest]) :- in(Item, Rest).

/* Makes the intersection list */
isIntersection([], [], []).
isIntersection([H|R], List, [H|Final]) :-
   in(H, List),
   isIntersection(R, List, Final),
   write(H).
isIntersection([Discard|Rest], List, Final) :-
   isIntersection(Rest, List, Final),
   write(Discard).

Upvotes: 2

Views: 282

Answers (3)

Nicholas Carey
Nicholas Carey

Reputation: 74277

I'd go at it something like this, sorting and merging so as to avoid the O(n2) performance:

intersection_of( Xs , Ys , Zs ) :-     % to find the intersection of two sets, we
  sort(Xs,X1)     ,                    % - sort the left  source list, removing duplicates to ensure that it's a set
  sort(Ys,Y1)     ,                    % - sort the right source list, removing duplicates to ensure that it's a set
  merge(Xs,Ys,Z1) ,                    % - merge them to find the common members (an ordered set)
  ( var(Zs) ->                         % - if the result is unbound,
    Zs = Z1 ;                          %   - simply unify the merge result with the result set
    sort(Zs,Z1)                        %   - otherwise, sort the result and match against the merge result
  ) .                                  %

The merge is simple

merge( []     , []     , []     ) .
merge( [_|_]  , []     , []     ) .
merge( []     , [_|_]  , []     ) .
merge( [X|Xs] , [Y|Ys] , [X|Zs] ) :- X =  Y , merge(   Xs  ,    Ys  , Zs ) .
merge( [X|Xs] , [Y|Ys] ,    Zs  ) :- X @< Y , merge(   Xs  , [Y|Ys] , Zs ) .
merge( [X|Xs] , [Y|Ys] ,    Zs  ) :- X @> Y , merge([X|Xs] ,    Ys  , Zs ) .

Upvotes: 1

false
false

Reputation: 10102

Prolog is a very versatile query language, so let's use Prolog to find the problem!

?- isIntersection([a,b],[c,b],Zs).
   false.

This failure is not what we expect. To better localize the problem we might a) generalize the query or b) reduce input size. I will try generalizing it first:

?- isIntersection([a,b],Ys,Zs).
   loops. % ERROR: Out of global stack

Seems we have no luck, but then this query would have to produce infinitely many lists for Ys so it might be OK to loop.

I could continue that way, but why not let Prolog do the thinking for me? I will try all possible lists:

?- length(L,_),append(Xs,Ys,L), isIntersection(Xs,Ys,Zs).
   L = Xs, Xs = Ys, Ys = Zs, Zs = []
;  L = Xs, Xs = [_A], Ys = Zs, Zs = []
;  L = Xs, Xs = [_A, _B], Ys = Zs, Zs = []
;  L = Xs, Xs = [_A, _B, _C], Ys = Zs, Zs = []
;  L = Xs, Xs = [_A, _B, _C, _D], Ys = Zs, Zs = []
; ... .

So for each list length (so far), there is only one solution with Ys and Zs being an empty list... Is there any solution for Ys being larger?

?- length(L,_),Ys = [_|_], append(Xs,Ys,L), isIntersection(Xs,Ys,Zs).
   loops.

So lets take the minimal missing example from above with Ys having one element:

?- isIntersection([],[a],[]).
   false.

With this goal, now look at your code!

But there is another problem (after fixing above):

?- isIntersection([a],[a],Xs).
   Xs = [a]
;  Xs = [].

The rule discards any element! But it should only discard those that are not in List. So:

isIntersection([Discard|Rest], List, Final) :-
   list_without(List,Discard), % maplist(dif(Discard),List)
   isIntersection(Rest, List, Final).

list_without([], _).
list_without([E|Es], F) :-
   dif(E, F),
   list_without(Es, F).

Finally, always keep an eye on negative examples. Many attempts here (incorrectly) succeeds for queries like isIntersection([a],[a],[]).

(Your relation in/2 might better be called element_in/2)

Upvotes: 2

CapelliC
CapelliC

Reputation: 60014

there is only a List that can match your base case, and this simple fact inhibits the whole computation.

Upvotes: 0

Related Questions