2
votes

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).
3
@WillNess: The highlighting is inconsistent and thus irritating. Single-letter variables are colored differently, in is highlighted differently. Brief: That is no help at all! - false
@false I usually prefer it, in spite of the inconsistency, because the comments get dimmer, and we get some cheerful colors in the code. - Will Ness
@WillNess: maybe we could convince them to change? Nut then this would need a lot of lawyering. Also it is a bit controversial as to the precise syntax to employ. - false
@false the usual response for these kind of requests from "the meta community" is, "program it yourself" (I think I got such, once, so I prefer not to try anymore :) :) - and I wouldn't know where to start, doing it myself). I suspect the lang-prolog spec is non-existent and lang-python or lang-perl is matched and used instead. - Will Ness
@WillNess: Are you aware of the split into MSO and MSE? It seems you only posted on the olde MSO which is now MSE. The new MSO has no longer total rep. Just your SO rep is shown. So downvotes do not seem to have an effect. Go Help center -> Meta Stack Overflow. - false

3 Answers

2
votes

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).
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 = [_G682],
Ys = Zs, Zs = [] ;
L = Xs, Xs = [_G682, _G685],
Ys = Zs, Zs = [] ;
L = Xs, Xs = [_G682, _G685, _G688],
Ys = Zs, Zs = [] ;
L = Xs, Xs = [_G682, _G685, _G688, _G691],
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)

0
votes

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

0
votes

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] , [Z|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 ) .