Related
I am having trouble starting this Prolog program that takes two lists and returns true if all members in the first list are members of the second list, and false otherwise.
Examples:
?- members([a, c], [a, b, c, d])
true
?- members([d, a, c, a], [a, b, c, d, e])
true
?- members([b, e], [a, b, c, d])
false
?- members([], [a, b, c, d])
true
How do I go about doing this? Any help is appreciated.
You can use maplist on this kind of problem since it follows a standard recursive list traversal:
mem(L, X) :- memberchk(X, L).
subset(S, L) :- maplist(mem(L), S).
Results:
| ?- subset([a,c], [a,b,c,d]).
yes
| ?- subset([c,a], [a,b,c,d]).
yes
| ?- subset([e], [a,b,c,d]).
no
| ?- subset([], [a,b,c,d]).
yes
| ?- subset(S, [a,b,c,d]), S=[_|_].
S = [a] ? ;
S = [a,a] ? ;
S = [a,a,a] ? ;
...
Note that the original problem definition does not rule out the case where the subset can have duplicate elements from the superset. If you want to restrict the subsets to having counts of elements less than or equal to the superset, you can use select/3:
subset([], _).
subset([X|Xs], L) :-
select(X, L, L1),
subset(Xs, L1).
Results:
| ?- subset([a,c], [a,b,c,d,e]).
true ? ;
no
| ?- subset([a,f], [a,b,c,d,e]).
no
| ?- subset([a,a], [a,b,c,d,e]).
no
| ?- subset(S, [a,b,c]), S=[_|_].
S = [a] ? ;
S = [a,b] ? ;
S = [a,b,c] ? ;
S = [a,c] ? ;
S = [a,c,b] ? ;
S = [b] ? ;
S = [b,a] ? ;
...
S = [c,b] ? ;
S = [c,b,a] ? ;
no
You'll note that the list [c,b,a] is considered a different list in Prolog versus [a,b,c] so it is a separate solution. If you want to make the lists behave truly as sets, then that's a different solution.
"We tried nothing and we're all out of ideas"
I would suggest you find a predicate that does that, then look at its implementation.
$ swipl
?- subset([a, c], [a, b, c, d]).
true.
?- subset([d, a, c, a], [a, b, c, d, e]).
true.
?- subset([b, e], [a, b, c, d]).
false.
?- subset([], [a, b, c, d]).
true.
It is documented here: http://www.swi-prolog.org/pldoc/doc_for?object=subset/2
You can click on the little yellow circle with the colon and the dash to see how it is implemented: http://www.swi-prolog.org/pldoc/doc/SWI/library/lists.pl?show=src#subset/2
713 subset([], _) :- !.
714 subset([E|R], Set) :-
715 memberchk(E, Set),
716 subset(R, Set).
This definition is about the same as:
maplist([M]>>memberchk(M, Set), Subset)
This definition always succeeds or fails only once. This is by design.
It could also have been implemented like this:
maplist([M]>>member(M, Set), Subset)
And it behaves like the suggestion in the comments:
?- maplist([M]>>member(M, [a,b]), Subset).
Subset = [] ;
Subset = [a] ;
Subset = [a, a] ;
Subset = [a, a, a] ;
Subset = [a, a, a, a] .
?- length(Subset, _), maplist([M]>>member(M, [a,b]), Subset).
Subset = [] ;
Subset = [a] ;
Subset = [b] ;
Subset = [a, a] ;
Subset = [a, b] ;
Subset = [b, a] ;
Subset = [b, b] ;
Subset = [a, a, a] ;
Subset = [a, a, b] .
This question was asked but there are no answers: here. I read the comments and tried to implement in both ways, but there are more problems that I don't understand.
I first tried the easy way that doesn't keep original order:
list_repeated(L, Ds) :-
msort(L, S),
sorted_repeated(S, Ds).
sorted_repeated([], []).
sorted_repeated([X|Xs], Ds) :-
first(Xs, X, Ds).
first([], _, []).
first([X|Xs], X, [X|Ds]) :-
more(Xs, X, Ds).
first([X|Xs], Y, Ds) :-
dif(X, Y),
first(Xs, X, Ds).
more([], _, []).
more([X|Xs], X, Ds) :-
more(Xs, X, Ds).
more([X|Xs], Y, Ds) :-
dif(X, Y),
first(Xs, X, Ds).
Once the list is sorted without removing duplicates, using first and more I add the element to the second argument if it occurs at least twice and skip all consecutive copies of the element.
This is not working properly because if I have:
?- list_duplicates([b,a,a,a,b,b], Ds).
I get answer [a,b] instead of [b,a] and also I get ; false after the answer.
I also tried another way, but this doesn't work because the accumulator is immutable?
list_duplicates(L, Ds) :-
ld_acc(L, [], Ds).
ld_acc([], _, []).
ld_acc([X|Xs], Acc, Ds) :-
( memberchk(X, Acc)
-> Ds = [X|Ds0],
ld_acc(Xs, Acc, Ds0)
; Acc1 = [X|Acc],
ld_acc(Xs, Acc1, Ds)
).
This cannot work because when I check that an element is member of accumulator I remove only one occurrence of each element: if I have three times the same element in the first argument, I am left with two. If I could change the element in the accumulator then I could maybe put a counter on it? In the first version I used different states, first and more, but here I have to attach state to the elements of the accumulator, is that possible?
A plea for purity
When programming in Prolog, a major attraction is the generality we enjoy from pure relations.
This lets us use our code in multiple directions, and reason declaratively over our programs and answers.
You can enjoy these benefits if you keep your programs pure.
Possible solution
As always when describing lists, also consider using DCG notation. See dcg for more information.
For example, to describe the list of duplicates in a pure way, consider:
list_duplicates([]) --> [].
list_duplicates([L|Ls]) -->
list_duplicates_(Ls, L),
list_duplicates(Ls).
list_duplicates_([], _) --> [].
list_duplicates_([L0|Ls], L) -->
if_(L0=L, [L], []),
list_duplicates_(Ls, L).
This uses if_//3 to retain generality and determinism (if applicable).
Examples
Here are a few example queries and answers. We start with simple ground cases:
?- phrase(list_duplicates([a,b,c]), Ds).
Ds = [].
?- phrase(list_duplicates([a,b,a]), Ds).
Ds = [a].
Even the most impure version will be able to handle these situations correctly. So, slightly more interesting:
?- phrase(list_duplicates([a,b,X]), Ds).
X = a,
Ds = [a] ;
X = b,
Ds = [b] ;
Ds = [],
dif(X, b),
dif(X, a).
Pretty nice, isn't it? The last part says: Ds = [] is a solution if X is different from b and a. Note the pure relation dif/2 automatically appears in these residual goals and retains the relation's generality.
Here is an example with two variables:
?- phrase(list_duplicates([X,Y]), Ds).
X = Y,
Ds = [Y] ;
Ds = [],
dif(Y, X).
Finally, consider using iterative deepening to fairly enumerate answers for lists of arbitrary length:
?- length(Ls, _), phrase(list_duplicates(Ls), Ds).
Ls = Ds, Ds = [] ;
Ls = [_136],
Ds = [] ;
Ls = [_136, _136],
Ds = [_136] ;
Ls = [_156, _162],
Ds = [],
dif(_162, _156) ;
Ls = Ds, Ds = [_42, _42, _42] ;
Ls = [_174, _174, _186],
Ds = [_174],
dif(_186, _174) .
Multiple occurrences
Here is a version that handles arbitrary many occurrences of the same element in such a way that exactly a single occurrence is retained if (and only if) the element occurs at least twice:
list_duplicates(Ls, Ds) :-
phrase(list_duplicates(Ls, []), Ds).
list_duplicates([], _) --> [].
list_duplicates([L|Ls], Ds0) -->
list_duplicates_(Ls, L, Ds0, Ds),
list_duplicates(Ls, Ds).
list_duplicates_([], _, Ds, Ds) --> [].
list_duplicates_([L0|Ls], L, Ds0, Ds) -->
if_(L0=L, new_duplicate(L0, Ds0, Ds1), {Ds0 = Ds1}),
list_duplicates_(Ls, L, Ds1, Ds).
new_duplicate(E, Ds0, Ds) -->
new_duplicate_(Ds0, E, Ds0, Ds).
new_duplicate_([], E, Ds0, [E|Ds0]) --> [E].
new_duplicate_([L|Ls], E, Ds0, Ds) -->
if_(L=E,
{ Ds0 = Ds },
new_duplicate_(Ls, E, Ds0, Ds)).
The query shown by #fatalize in the comments now yields:
?- list_duplicates([a,a,a], Ls).
Ls = [a].
The other examples yield the same results. For instance:
?- list_duplicates([a,b,c], Ds).
Ds = [].
?- list_duplicates([a,b,a], Ds).
Ds = [a].
?- list_duplicates([a,b,X], Ds).
X = a,
Ds = [a] ;
X = b,
Ds = [b] ;
Ds = [],
dif(X, b),
dif(X, a).
?- list_duplicates([X,Y], Ds).
X = Y,
Ds = [Y] ;
Ds = [],
dif(Y, X).
I leave the case ?- list_duplicates(Ls, Ls). as an exercise.
Generality: Multiple directions
Ideally, we want to be able to use a relation in all directions.
For example, our program should be able to answer questions like:
What does a list look like if its duplicates are [a,b]?
With the version shown above, we get:
?- list_duplicates(Ls, [a,b]).
nontermination
Luckily, a very simple change allows as to answer such questions!
One such change is to simply write:
list_duplicates(Ls, Ds) :-
length(Ls, _),
phrase(list_duplicates(Ls, []), Ds).
This is obviously declaratively admissible, because Ls must be a list. Operationally, this helps us to enumerate lists in a fair way.
We now get:
?- list_duplicates(Ls, [a,b]).
Ls = [a, a, b, b] ;
Ls = [a, b, a, b] ;
Ls = [a, b, b, a] ;
Ls = [a, a, a, b, b] ;
Ls = [a, a, b, a, b] ;
Ls = [a, a, b, b, a] ;
Ls = [a, a, b, b, b] ;
Ls = [a, a, b, b, _4632],
dif(_4632, b),
dif(_4632, a) ;
etc.
Here is a simpler case, using only a single element:
?- list_duplicates(Ls, [a]).
Ls = [a, a] ;
Ls = [a, a, a] ;
Ls = [a, a, _3818],
dif(_3818, a) ;
Ls = [a, _3870, a],
dif(_3870, a) ;
Ls = [_4058, a, a],
dif(a, _4058),
dif(a, _4058) ;
Ls = [a, a, a, a] ;
etc.
Maybe even more interesting:
What does a list without duplicates look like?
Our program answers:
?- list_duplicates(Ls, []).
Ls = [] ;
Ls = [_3240] ;
Ls = [_3758, _3764],
dif(_3764, _3758) ;
Ls = [_4164, _4170, _4176],
dif(_4176, _4164),
dif(_4176, _4170),
dif(_4170, _4164) .
Thus, the special case of a list where all elements are distinct naturally exists as a special case of the more general relation we have implemented.
We can use this relation to generate answers (as shown above), and also to test whether a list consists of distinct elements:
?- list_duplicates([a,b,c], []).
true.
?- list_duplicates([b,b], []).
false.
Unfortunately, the following even more general query still yields:
?- list_duplicates([b,b|_], []).
nontermination
On the plus side, if the length of the list is fixed, we get in such cases:
?- length(Ls, L), maplist(=(b), Ls),
( true ; list_duplicates(Ls, []) ).
Ls = [],
L = 0 ;
Ls = [],
L = 0 ;
Ls = [b],
L = 1 ;
Ls = [b],
L = 1 ;
Ls = [b, b],
L = 2 ;
Ls = [b, b, b],
L = 3 ;
Ls = [b, b, b, b],
L = 4 .
This is some indication that the program indeed terminates in such cases. Note that the answers are of course now too general.
Efficiency
It is well known in high-performance computing circles that as long as your program is fast enough, its correctness is barely worth considering.
So, the key question is of course: How can we make this faster?
I leave this is a very easy exercise. One way to make this faster in specific cases is to first check whether the given list is sufficiently instantiated. In that case, you can apply an ad hoc solution that fails terribly in more general cases, but has the extreme benefit that it is fast!
So as far as I can tell, you were on the right track with the accumulator, but this implementation definitely works as you want (assuming you want the duplicates in the order they first appear in the list).
list_duplicates(Input,Output) is just used to wrap and initialise the accumulator.
list_duplicates(Accumulator,[],Accumulator) unifies the accumulator with the output when we have finished processing the input list.
list_duplicates(Accumulator,[H|T],Output) says "if the head (H) of the input list is in the rest of the list (T), and is not in the Accumulator already, put it at the end of the Accumulator (using append), then recurse on the tail of the list".
list_duplicates(Accumulator,[_|T],Output) (which we only get to if either the head is not a duplicate, or is already in the Accumulator) just recurses on the tail of the list.
list_duplicates(Input,Output) :-
once(list_duplicates([],Input,Output)).
list_duplicates(Accumulator,[],Accumulator).
list_duplicates(Accumulator,[H|T],Output) :-
member(H,T),
\+member(H,Accumulator),
append(Accumulator,[H],NewAccumulator),
list_duplicates(NewAccumulator,T,Output).
list_duplicates(Accumulator,[_|T],Output) :-
list_duplicates(Accumulator,T,Output).
You could also recurse in list_duplicates(Accumulator,[H|T],Output) with list_duplicates([H|Accumulator],T,Output) and reverse in the wrapper, looking like this:
list_duplicates(Input,Output) :-
once(list_duplicates([],Input,ReverseOutput)),
reverse(ReverseOutput,Output).
list_duplicates(Accumulator,[],Accumulator).
list_duplicates(Accumulator,[H|T],Output) :-
member(H,T),
\+member(H,Accumulator),
list_duplicates([H|Accumulator],T,Output).
list_duplicates(Accumulator,[_|T],Output) :-
list_duplicates(Accumulator,T,Output).
The once call in the wrapper prevents the false output (or in this case, partial duplicate lists due to a lack of guards on the second rule).
I'm trying to make a code that generates all subsets of a set in order.
That is, calling subset([1,2,3], X) should generate
X = [];
X = [1];
X = [2];
X = [3];
X = [1,2];
X = [1,3];
X = [2,3];
X = [1,2,3].
The internal order isn't all that important, only that the smallest subsets are listed first (i.e I don't care if [2,3] comes before [1,2], only that 1, [2] and [3] are before [2,3]).
--
I've tried two approaches thus far. First I tried making the predicate myself...
subset([], []).
subset(List, []).
subset(List, [N]) :-
member(N, List).
subset(List, [N|Rest]) :-
!,
nth0(I, List, N),
findall(E, (nth0(J, List, E), J > I), NewList),
subset2(NewList, Rest).
...but it doesn't even come close to working as intended. Secondly I tried making the powerset (using this subset predicate) and ordering with list_to_ord_set/2, but I couldn't get it to work either.
Help?
Always also consider using DCG notation when describing lists.
For example:
list_sublist(Ls0, Ls) :-
same_length(Ls0, Ls1),
append(Ls, _, Ls1),
phrase(sublist(Ls0), Ls).
sublist([]) --> [].
sublist([L|Ls]) --> ( [] ; [L] ), sublist(Ls).
Sample query:
?- list_sublist([a,b,c], Ls).
Ls = [] ;
Ls = [c] ;
Ls = [b] ;
Ls = [a] ;
Ls = [b, c] ;
Ls = [a, c] ;
Ls = [a, b] ;
Ls = [a, b, c] ;
false.
Another example:
?- list_sublist(Ls, [b,c]).
Ls = [b, c] ;
Ls = [_G511, b, c] ;
Ls = [b, _G514, c] ;
Ls = [b, c, _G517] ;
etc.
Most general case:
?- list_sublist(Xs, Ys).
Xs = Ys, Ys = [] ;
Xs = [_G513],
Ys = [] ;
Xs = Ys, Ys = [_G513]
Xs = [_G513, _G516],
Ys = [] ;
etc.
I've found a not so elegant solution... it requires a cut and some builtins
subset(Xs, Ys) :-
length(Xs, L),
between(0, L, N),
length(Ys, N),
assign(Xs, Ys).
assign(_, []) :- !.
assign([X|Xs], [X|Ys]) :-
assign(Xs, Ys).
assign([_|Xs], Ys) :-
assign(Xs, Ys).
as noted by #Fatalize, we can avoid the cut, just forcing the empty list on first argument of 1^ clause:
assign([], []).
assign([X|Xs], [X|Ys]) :-
assign(Xs, Ys).
assign([_|Xs], Ys) :-
assign(Xs, Ys).
I avoided to swap 2^ and 3^ clauses, so the 'natural' order is still nicely preserved
I am trying to remove duplicates from a list while keeping the rightmost occurrences. E.g.: [1,2,3,1,2] is transformed in [3,1,2]
It's one of my first tries in Prolog and I don't understand what am I doing wrong. It always returns false. This is my code:
%nrap(L:list,E:element,S:integer)
%L - the initial list, list of integers
%E - the element, integer
%S - the result, nrap of E in L, S integer
%flow model: (i,i,o),(i,i,i)
nrap([],_,0).
nrap([H|T],E,S):-
H=E,
nrap(T,E,S1),
S is S1+1.
nrap([H|T],E,S):-
H\=E,
nrap(T,E,S).
%transform(L:list,L2:list,R:list)
%L - the initial list, list of integers
%L2 - copy of the initial list
%R - the resulted list, without duplicates, list of integers
%flow model: (i,i,o),(i,i,i)
transform([],[],[]).
transform([H|T],L2,[H|R]):-
nrap(L2,H,S),
S=1,
transform(T,L2,R).
transform([H|T],L2,R):-
nrap(L2,H,S),
S>1,
transform(T,L2,R).
Shall I be pure or impure? Why even consider sacrificing logical-purity if we can save it easily!
Using memberd_t/3 and if_/3, we define list_rset/2 and its left "twin" list_lset/2:
list_rset([], []). % keep rightmost occurrences
list_rset([E|Es], Rs0) :-
if_(memberd_t(E, Es),
Rs0 = Rs,
Rs0 = [E|Rs]),
list_rset(Es, Rs).
list_lset([], []). % keep leftmost occurrences
list_lset([E|Es], Ls) :-
post_pre_lset(Es, [E], Ls). % uses internal auxilary predicate
post_pre_lset([], _, []).
post_pre_lset([E|Es], Pre, Ls0) :- % 2nd arg: look-behind accumulator
if_(memberd_t(E, Pre),
Ls0 = Ls,
Ls0 = [E|Ls]),
post_pre_lset(Es, [E|Pre], Ls).
Let's run some queries!
?- _Es = [1,2,3,1,2], list_lset(_Es, Ls), list_rset(_Es, Rs).
Ls = [1,2,3], Rs = [3,1,2]. % succeeds deterministically
In above query 1 precedes 2 both at the beginning and at the end of the list [1,2,3,1,2]. What if 1 precedes 2 at the beginning but follows it at the end (e.g., [1,2,3,2,1])?
?- _Es = [1,2,3,2,1], list_lset(_Es, Ls), list_rset(_Es, Rs).
Ls = [1,2,3], Rs = [3,2,1]. % succeeds deterministically
Next, we look at a more general list_rset/2 goal that uses a list containing variables only. Thanks to #PauloMoura for his suggestion!
?- Es = [A,B,C,A,B], list_rset(Es,Rs).
Es = [C,C,C,C,C], Rs = [ C], A=B , B=C
; Es = [B,B,C,B,B], Rs = [C, B], A=B , dif(B,C)
; Es = [C,B,C,C,B], Rs = [ C,B], A=C , dif(B,C)
; Es = [A,C,C,A,C], Rs = [ A,C], dif(A,C), B=C
; Es = [A,B,C,A,B], Rs = [C,A,B], dif(A,B), dif(A,C), dif(B,C).
What's up with the residual goals (above)?
Without sufficient instantiation, dif/2 is not decidable.
To save logical soundness, the execution of the prolog-dif constraints is delayed.
Last, one more use-case: an "input" list Xs that has both variables and ground terms.
?- Es = [A,B,z], list_rset(Es,Rs).
Es = [z,z,z], Rs = [ z], A=B , B=z
; Es = [B,B,z], Rs = [B, z], A=B , dif(B,z)
; Es = [z,B,z], Rs = [ B,z], A=z , dif(B,z)
; Es = [A,z,z], Rs = [A, z], dif(A,z), B=z
; Es = [A,B,z], Rs = [A,B,z], dif(A,B), dif(A,z), dif(B,z).
This is a follow-up to this previous answer... In this answer we use dcg!
We build lset//1 upon memberd_t/3 and if_//3—the dcg analogue of if_/3:
lset([]) -->
[].
lset([X|Xs]) -->
[X],
lset_pre(Xs,[X]).
lset_pre([],_) -->
[].
lset_pre([X|Xs],Pre) -->
if_(memberd_t(X,Pre), [], [X]),
lset_pre(Xs,[X|Pre]).
Same for rset//1:
rset([]) -->
[].
rset([X|Xs]) -->
if_(memberd_t(X,Xs), [], [X]),
rset(Xs).
Some sample queries:
?- _Es = [1,2,3,1,2], phrase(lset(_Es),Ls), phrase(rset(_Es),Rs).
Ls = [1,2,3], Rs = [3,1,2]. % succeeds deterministically
?- _Es = [1,2,3,2,1], phrase(lset(_Es),Ls), phrase(rset(_Es),Rs).
Ls = [1,2,3], Rs = [3,2,1]. % succeeds deterministically
This is easier than you are making it. Since the elements in the "set" have to be in the order of last appearance, you don't need to keep a copy of the list at all: just compare to the remainder of the list (the tail).
If you know that the first list is always going to be ground (all elements are integers, for example), you could write:
list_set([], []).
list_set([X|Xs], Ys0) :-
( memberchk(X, Xs)
-> Ys0 = Ys
; Ys0 = [X|Ys]
),
list_set(Xs, Ys).
memberchk/2 can be used to check if a ground term is in a list of ground terms. It will succeed or fail exactly once.
A more general solution is to pose a constraint that an element should be in the set if it is different from all the elements following it, and be dropped otherwise:
list_set([], []).
list_set([X|Xs], [X|Ys]) :-
maplist(dif(X), Xs),
list_set(Xs, Ys).
list_set([X|Xs], Ys) :-
\+ maplist(dif(X), Xs),
list_set(Xs, Ys).
Here, maplist(dif(X), Xs) means:
X is different from every element in the list Xs (the tail).
and \+ Goal succeeds then Goal does not succeed.
With this defintion:
?- list_set([1,2,3,1,2], S).
S = [3, 1, 2] ;
false.
?- list_set([1,2,3,3,1,1,2], S).
S = [3, 1, 2] ;
false.
?- list_set([A,B,C,A,B],Xs).
Xs = [C, A, B],
dif(A, B),
dif(C, B),
dif(C, A) ;
false.
I need to write a program that finds the intersection of two lists. I can't use cuts and there shouldn't be any duplicate elements in the result list.
This is my code:
intersection([],_,[]).
intersection([X|Xs],Y,[X|Zs]) :-
member(X,Y),
intersection(Xs,Y,Zs).
intersection([_|Xs],Y,Zs) :-
intersection(Xs,Y,Zs).
When I run the following query, I get these answers:
?- intersection([a,b,c,a],[a,v,c],L).
L = [a, c, a] ;
L = [a, c] ; % <---------- this is only answer I want to get
L = [a, a] ;
L = [a] ;
L = [c, a] ;
L = [c] ;
L = [a] ;
L = [].
What can I do? I want to get L = [a,c] and nothing else... Can you help?
In my answer to the related question "Intersection and union of 2 lists" I presented the logically pure predicate list_list_intersectionSet/3. It should fit your requirements to a T!
Here's is a brushed-up version of list_list_intersectionSet/3, which is based on:
monotone conditional if_/3,
meta-predicate tfilter/3,
and the reified test predicates dif/3 and memberd_t/3.
Here we go:
list_list_intersectionSet([] ,_ ,[]).
list_list_intersectionSet([A|As0],Bs,Cs0) :-
if_(memberd_t(A,Bs), Cs0 = [A|Cs], Cs0 = Cs),
tfilter(dif(A),As0,As),
list_list_intersectionSet(As,Bs,Cs).
Let's see it in action!
?- list_list_intersectionSet([a,b,c,a],[a,v,c],L).
L = [a,c].
If by "conjunction" you mean "intersection", you should take a look at the implementation in the SWI-Prolog library(lists) of the predicate intersection/3. It contains cuts, but you can leave them out if you don't mind all the choicepoints.
With it:
?- intersection([a,b,c,a],[a,v,c],I).
I = [a, c, a].
Of course, this doesn't work even in the library predicate, because you need sets with your current definition. (It is enough if only the first argument is a set.)
You can make sets with the sort/2 predicate: if the first argument is a list with repetitions, the second argument will be a sorted list without repetitions, for example:
?- sort([a,b,c,a], S1), intersection(S1, [a,v,c], I).
S1 = [a, b, c],
I = [a, c].
or maybe:
?- sort([a,b,c,a], S1), intersection(S1, [a,v,c,c,a,c], I).
S1 = [a, b, c],
I = [a, c].
?- sort([a,b,c,a,b,c,a,b,c], S1), intersection(S1, [a,v,c,c,a,c], I).
S1 = [a, b, c],
I = [a, c].
If you sort both arguments, you can use a ord_intersection/3 from library(ordsets), implemented in terms of oset_int/3.
?- sort([a,b,c,a], S1), sort([a,v,c,c,a,c], S2), ord_intersection(S1, S2, I).
S1 = [a, b, c],
S2 = [a, c, v],
I = [a, c].
Importantly, oset_int/3 does not use any cuts in its implementation. It however assumes that the first and second arguments are lists of elements sorted by the "standard order of terms" and without duplicates, as done by sort/2.
If for some reason you don't want to use sort/2, you could maybe use an accumulator and check against it before taking an element to the intersection:
my_intersection(Xs, Ys, Zs) :-
my_intersection_1(Xs, Ys, [], Zs).
my_intersection_1([], _, Zs, Zs).
my_intersection_1([X|Xs], Ys, Zs0, Zs) :-
member(X, Ys), \+ member(X, Zs0),
my_intersection_1(Xs, Ys, [X|Zs0], Zs).
my_intersection_1([_|Xs], Ys, Zs0, Zs) :-
my_intersection_1(Xs, Ys, Zs0, Zs).
Of course, the order of the elements in the result will be now reversed. If this is not what you mean by "conjunction", you could for example rewrite the first two clauses of my_intersection_1/4 as:
my_intersection_1([], _, _, []).
my_intersection_1([X|Xs], Ys, Zs0, [X|Zs]) :-
member(X, Ys), \+ member(X, Zs0),
my_intersection_1(Xs, Ys, [X|Zs0], Zs).
The previously shown list_list_intersectionSet/3 restricts the item order in the intersection:
?- list_list_intersectionSet([a,b],[a,b], [a,b]).
true.
?- list_list_intersectionSet([a,b],[a,b], [b,a]).
false.
In this answer we lift that restriction... preserving logical-purity and determinism (for ground cases)!
First, we define none_intersect/2 using Prolog lambdas and
meta-predicate maplist/2.
none_intersect(As,Bs) states that all members in As are different from all members in Bs.
:- use_module(library(lambda)).
none_intersect(As,Bs) :-
maplist(\A^maplist(dif(A),Bs),As).
Next, we define intersection_of_and/3---based on none_intersect/2 (defined above), meta-predicate tpartition/4 and reified term equality (=)/3:
intersection_of_and([],As,Bs) :-
none_intersect(As,Bs).
intersection_of_and([X|Xs],As0,Bs0) :-
tpartition(=(X),As0,[_|_],As), % [_|_] = [X|_]
tpartition(=(X),Bs0,[_|_],Bs), % [_|_] = [X|_]
intersection_of_and(Xs,As,Bs).
intersection_of_and(Xs,As,Bs) states that
all items which occur in both As and Bs also occur in Xs (first clause),
all items in Xs occur in both As and Bs at least once (second clause),
and the list Xs does not contain any duplicates.
intersection_of_and/3 uses a specific argument in order to enable first argument indexing.
Last, we define list_list_intersection/3 which has the argument order that the OP used:
list_list_intersection(As,Bs,Xs) :-
intersection_of_and(Xs,As,Bs).
Let's run some queries! First, the query that the bounty offerer suggested:
?- list_list_intersection([a,b],[a,b], [b,a]).
true.
Next, a similar query with 3 distinct items in 3 lists having 3 different orders:
?- list_list_intersection([a,b,c],[b,a,c], [c,a,b]).
true.
What if some x only occurs in the first/second list?
?- list_list_intersection([a,b,c,x],[b,a,c], [c,a,b]).
true.
?- list_list_intersection([a,b,c],[b,a,c,x], [c,a,b]).
true.
What if some item occurs twice in the first/second list?
?- list_list_intersection([a,b,c],[b,a,c,b], [c,a,b]).
true.
?- list_list_intersection([a,b,c,c],[b,a,c], [c,a,b]).
true.
Last, what if the intersection contains duplicates?
Intersections are not to contain duplicates...
?- list_list_intersection([a,b,c],[b,a,c], [c,c,a,b]).
false. % as expected
Seems like something like this would be the easy way:
intersection( Xs , Ys , Zs ) :-
sort(Xs,X1) , % order and de-dupe the 1st list so as to produce a set
sort(Ys,Y1) , % order and de-dupe the 2nd list so as to produce a set
merge(Xs,Ys,Zs) % merge the two [ordered] sets to produce the result
. % easy!
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 ) .
Or even just this [not-terribly-performant] one-liner:
intersection( Xs , Ys , Zs ) :- setof(Z,(member(Z,Xs),member(Z,Ys)),Zs).
This can be solved by simple set theory:
intersection(A,B,AnB):-
subtract(A,B,AminusB),
subtract(A,AminusB,K),
sort(K,AnB).
For the query:
?- intersection([a,b,c,a],[a,v,c],L).
output is
L = [a, c].
No more answers.