Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Intersection and union of 2 lists

Tags:

list

prolog

i'm starting up learning prolog (i use SWI-prolog) and i did a simple exercise in which i have 2 lists and i want to calculate their intersection and union. Here is my code that works pretty well but i was asking myself if there is a better way to do it as i don't like to use the CUT operator.

intersectionTR(_, [], []).
intersectionTR([], _, []).
intersectionTR([H1|T1], L2, [H1|L]):-
    member(H1, L2),
    intersectionTR(T1, L2, L), !.
intersectionTR([_|T1], L2, L):-
    intersectionTR(T1, L2, L).

intersection(L1, L2):-
    intersectionTR(L1, L2, L),
    write(L).


unionTR([], [], []).
unionTR([], [H2|T2], [H2|L]):-
    intersectionTR(T2, L, Res),
    Res = [],
    unionTR([], T2, L),
    !.
unionTR([], [_|T2], L):-
    unionTR([], T2, L),
    !.

unionTR([H1|T1], L2, L):-
    intersectionTR([H1], L, Res),
    Res \= [],
    unionTR(T1, L2, L).
unionTR([H1|T1], L2, [H1|L]):-
    unionTR(T1, L2, L).

union(L1, L2):-
    unionTR(L1, L2, L),
    write(L).

Keep in mind that i want to have just 1 result, not multiple results (even if correct) so running the code with this:

?- intersect([1,3,5,2,4] ,[6,1,2]).

should exit with:

[1,2]
true.

and not with

[1,2]
true ;
[1,2]
true ;
etc...

The same must be valid for union predicate.
As i said my code works pretty well but please suggest better ways to do it.
Thanks

like image 955
andreapier Avatar asked Mar 08 '12 08:03

andreapier


People also ask

How do you find the union of two lists?

To perform the union of two lists in python, we just have to create an output list that should contain elements from both the input lists. For instance, if we have list1=[1,2,3,4,5,6] and list2=[2,4,6,8,10,12] , the union of list1 and list2 will be [1,2,3,4,5,6,8,10,12] .

Which of the following ways can be used to find intersection of 2 lists?

We can also use sets to find the intersection of two lists. To perform the intersection operation on lists by using sets, you can convert the input lists to sets. After that, you can perform the set intersection operation using the intersection() method.

How do you find the intersection of two linked lists in C?

Get count of the nodes in the first list, let count be c1. Get count of the nodes in the second list, let count be c2. Get the difference of counts d = abs(c1 – c2) Now traverse the bigger list from the first node till d nodes so that from here onwards both the lists have equal no of nodes.


2 Answers

Also, not sure why you're dead against cuts, so long as their removal would not change the declaritive meaning of the code, as per your link. For example:

inter([], _, []).

inter([H1|T1], L2, [H1|Res]) :-
    member(H1, L2),
    inter(T1, L2, Res).

inter([_|T1], L2, Res) :-
    inter(T1, L2, Res).

test(X):-
        inter([1,3,5,2,4], [6,1,2], X), !.

test(X).
X = [1, 2].

In the test bit where I call the code, I'm just saying do the intersection but I'm only interested in the first answer. There are no cuts in the predicate definitions themselves.

like image 172
magus Avatar answered Nov 10 '22 12:11

magus


The following is based on my previous answer to Remove duplicates in list (Prolog); the basic idea is, in turn, based on @false's answer to Prolog union for A U B U C.

What message do I want to convey to you?

  • You can describe what you want in Prolog with logical purity.
  • Using if_/3 and (=)/3 a logically pure implementation can be
    • both efficient (leaving behind choice points only when needed)
    • and monotone (logically sound with regard to generalization / specialization).
  • The implementation of @false's predicates if_/3 and (=)/3 does use meta-logical Prolog features internally, but (from the outside) behaves logically pure.

The following implementation of list_list_intersection/3 and list_list_union/3 uses list_item_isMember/3 and list_item_subtracted/3, defined in a previous answer:

list_list_union([],Bs,Bs).
list_list_union([A|As],Bs1,[A|Cs]) :-
    list_item_subtracted(Bs1,A,Bs),
    list_list_union(As,Bs,Cs).

list_list_intersection([],_,[]).
list_list_intersection([A|As],Bs,Cs1) :-
    if_(list_item_isMember(Bs,A), Cs1 = [A|Cs], Cs1 = Cs),
    list_list_intersection(As,Bs,Cs).

Here's the query you posted as part of your question:

?- list_list_intersection([1,3,5,2,4],[6,1,2],Intersection).
Intersection = [1, 2].                    % succeeds deterministically

Let's try something else... The following two queries should be logically equivalent:

?- A=1,B=3, list_list_intersection([1,3,5,2,4],[A,B],Intersection).
A = 1,
B = 3,
Intersection = [1, 3].
?- list_list_intersection([1,3,5,2,4],[A,B],Intersection),A=1,B=3.
A = 1,
B = 3,
Intersection = [1, 3] ;
false.

And... the bottom line is?

  • With pure code it's easy to stay on the side of logical soundness.
  • Impure code, on the other hand, more often than not acts like "it does what it should" at first sight, but shows all kinds of illogical behaviour with queries like the ones shown above.

Edit 2015-04-23

Neither list_list_union(As,Bs,Cs) nor list_list_intersection(As,Bs,Cs) guarantee that Cs doesn't contain duplicates. If that bothers you, the code needs to be adapted.

Here are some more queries (and answers) with As and/or Bs containing duplicates:

?- list_list_intersection([1,3,5,7,1,3,5,7],[1,2,3,1,2,3],Cs).
Cs = [1, 3, 1, 3].
?- list_list_intersection([1,2,3],[1,1,1,1],Cs).
Cs = [1].

?- list_list_union([1,3,5,1,3,5],[1,2,3,1,2,3],Cs).
Cs = [1, 3, 5, 1, 3, 5, 2, 2]. 
?- list_list_union([1,2,3],[1,1,1,1],Cs).
Cs = [1, 2, 3].
?- list_list_union([1,1,1,1],[1,2,3],Cs).
Cs = [1, 1, 1, 1, 2, 3].

Edit 2015-04-24

For the sake of completeness, here's how we could enforce that the intersection and the union are sets---that is lists that do not contain any duplicate elements.

The following code is pretty straight-forward:

list_list_intersectionSet([],_,[]).
list_list_intersectionSet([A|As1],Bs,Cs1) :-
    if_(list_item_isMember(Bs,A), Cs1 = [A|Cs], Cs1 = Cs),
    list_item_subtracted(As1,A,As),
    list_list_intersectionSet(As,Bs,Cs).

list_list_unionSet([],Bs1,Bs) :-
    list_setB(Bs1,Bs).
list_list_unionSet([A|As1],Bs1,[A|Cs]) :-
    list_item_subtracted(As1,A,As),
    list_item_subtracted(Bs1,A,Bs),
    list_list_unionSet(As,Bs,Cs).

Note that list_list_unionSet/3 is based on list_setB/2, defined here.

Now let's see both list_list_intersectionSet/3 and list_list_unionSet/3 in action:

?- list_list_unionSet([1,2,3,1,2,3,3,2,1],[4,5,6,2,7,7,7],Xs).
Xs = [1, 2, 3, 4, 5, 6, 7].

?- list_list_intersectionSet([1,2,3,1,2,3,3,2,1],[4,5,6,2,7,7,7],Xs).
Xs = [2].

Edit 2019-01-30

Here is an additional query taken from @GuyCoder's comment (plus two variants of it):

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

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

?- list_list_unionSet(Xs,Ys,[a,b]).
   Xs = [], Ys = [a,b]
;  Xs = [], Ys = [a,b,b]
;  Xs = [], Ys = [a,b,b,b]
...

With the old version of list_item_subtracted/3, above queries didn't terminate existentially.

With the new one they do. As the solution set size is infinite, none of these queries terminate universally.

like image 41
repeat Avatar answered Nov 10 '22 11:11

repeat