Intersection of two lists with duplicate elements - prolog

I am working on defining an intersection predicate, that takes in two lists, allowing duplicated elements. This is what I have so far.
intersection([], _, []).
intersection([H1|T1], L2, [H1|R]) :- m_member(H1, L2), intersection(T1, L2, R).
intersection([_|T1], L2, R) :- intersection(T1, L2, R).
However, in the case of the follows:
intersection([a,b,b,a],[c,b,b,c,e,f], S).
The predicate-call returns [b, b]. I would like to return [b] instead. Any pointers?

A possible solution to obtain the intersection D of [X|A] and B is:
Suppose, as induction hypothesis, that the intersection of A and B is C (without repetition).
Therefore:
if X is member of C or X is not member of B, then D is equal to C;
otherwise, D is equal to [X|C].
% inter(++Set1, ++Set2, -Set3)
inter([], _, []).
inter([X|A], B, D) :-
inter(A, B, C),
( ( memberchk(X, C)
; \+ memberchk(X, B) )
-> D = C
; D = [X|C] ).
Example:
?- inter([a,b,b,a], [c,b,b,c,e,f], S).
S = [b].

Related

How to access unique pairs from a list in Prolog?

I have a list L = [a, b, c] and I want to pick all unique non-repeating pairs: [a, b], [a, c], [b, c]. If I simply use the member function, it, of course, picks every permutation, so I have to have a predicate
pick_pair(List, X, Y) :-
member(X, List),
member(Y, List),
\+ X = Y.
And to access the members I gather all the pairs into a list by using another predicate
unique_pairs(List, Result) :-
findall([X, Y], pick_pair(List, X, Y), Result).
and only then I acess the resulting list, but it generates [[a, b], [a, c], [b, a], [b, c], [c, a], [c, b]]. I tried to get rid of the pairs that are just a reverse of pairs that were already there by list_to_set but [a, b] and [b, a] do not unify on default, so they're considered to be not equal and therefore belong to the set. I would somehow need to overload the unification for that function or something like that.
My question is:
Can we just access pairs in a list? Something like my_pairs(X, Y, L) which would assign the pair elements directly to X and Y. And if there is no such predicate, how can we make a list of the unique pairs so that we can access its elements by using member(X, List)?
The problem is, by the way, equivalent to getting all the combinations of length two.
Can use a custom variant of select:
unique_pairs(L, E1, E2) :-
select_forward(E1, L, L0),
member(E2, L0).
select_forward(E, [H|T], F) :-
select_forward_(T, H, E, F).
select_forward_(T, H, H, T).
select_forward_([H|T], _, E, F) :-
select_forward_(T, H, E, F).
Result in swi-prolog:
?- unique_pairs([a,b,c], E1, E2).
E1 = a,
E2 = b ;
E1 = a,
E2 = c ;
E1 = b,
E2 = c ;
false.
Generating combinations of m things taken n at a time is easy in Prolog:
combination( 0 , _ , [] ) .
combination( N , [X|Xs] , [X|Ys] ) :- N > 0, N1 is N-1, combination(N1,Xs,Ys).
combination( N , [_|Xs] , Ys ) :- N > 0, combination(N,Xs,Ys).
Once you have that, your pick_pair/2 become just:
pick_pair( Xs, P ) :- combination(2,Xs,P) .
You can also construct combinations with doing arithmetic: you just need to pass a list of the desired length containing variables -- that's just this:
combination( _ , [] ) .
combination( [X|Xs] , [X|Ys] ) :- combination(Xs,Ys).
combination( [_|Xs] , [Y|Ys] ) :- combination(Xs,[Y|Ys]).
With this implementation, if the second argument is completely uninstantiated, it will generate all combinations of any length from 0 through the length of the source list. And if the first argument is uninstantiated.... it will likely generate a [theoretically] infinite set of results.
With this implementatio, your pick_pair/2 becomes something like
pick_pair( Xs, [P,Q] ) :- combination(Xs, [P,Q] ).
Or maybe
pick_pair( Xs, P ) :- length(P,2), combination(Xs,P).

How to code a program in prolog, that does comparison on graphs

I'm trying to code a program in prolog that says true if all the paths from a to b are the same size. Example : we have a path from a to b and another from a to c to b, here it's false because there are two paths from a to b with different sizes, the first is 1 and the other is 2. They all must be the same size otherwise it's false.
I started doing this to get the length of each path, but I'm stuck here, I just need to compare if there are two same paths or not, if yes then we compare the two results if they are the same length then true otherwise false, but I don't know how to do it in Prolog :
chemin1(X, Y):-
arete(X,Y).
chemin1(X, Y):-
arete(X,Z),
chemin1(Z,Y).
chemin2(X, Y, N):-
arete(X, Y),
N is 1.
chemin2(X, Y, N):-
arete(X, Z),
N1 is 1,
chemin2(Z, Y, N2),
N is N1+N2.
I'm assuming you have an acyclic directed graph and that a path is represented by a vertex list.
% b
% / \
% a d
% \ / \
% c---e
arete(a, b).
arete(a, c).
arete(b, d).
arete(c, d).
arete(c, e).
arete(d, e).
chemin(X, X, [X]).
chemin(X, Z, [X|Xs]):- arete(X, Y), chemin(Y, Z, Xs).
Examples:
?- chemin(a, d, C).
C = [a, b, d] ;
C = [a, c, d] ;
false.
?- chemin(a, e, C).
C = [a, b, d, e] ;
C = [a, c, d, e] ;
C = [a, c, e] ;
false.
Then, all paths between two vertices X and Y are of the same size, if there are no two paths between vertices X and Y that are of different sizes.
% all_same_size(+X, +Y)
all_same_size(X, Y) :-
not( ( chemin(X, Y, Xs),
chemin(X, Y, Ys),
not( same_size(Xs, Ys) ) ) ).
same_size([], []).
same_size([_|Xs], [_|Ys]) :- same_size(Xs, Ys).
Examples:
?- all_same_size(a, d).
true.
?- all_same_size(a, e).
false.
chemin2(X0,X, N) :-
path(arete, Path, X0,X),
length(Path, N).
allequallength(X0, X) :-
setof(N, chemin2(X0,X, N), [_]).
Using path/4.
With this definition you can also ask a more general question using the facts you indicated:
arete(a, b).
arete(b, d).
arete(b, c).
arete(a, c).
?- allequallength(X0,X).
X0 = X
; X0 = a, X = b
; X0 = a, X = d
; X0 = b, X = c
; X0 = b, X = d.

How to shuffle lists in Prolog while preserving inner order

I'm trying to print out all possible shuffled variants of two lists in one list while preserving the order.
I need to write a predicate shuffle(L1, L2, L3) which shuffles L1 and L2 and puts the result into L3 while preserving the inner order of L1 and L2.
For example :
?- shuffle([a,b],[1,2],L).
L = [a,b,1,2] ;
L = [a,1,b,2] ;
L = [a,1,2,b] ;
L = [1,a,b,2] ;
L = [1,a,2,b] ;
L = [1,2,a,b]
What I have so far :
shuffle([],[],[]).
shuffle([X|Xs],[Y|Ys],[X,Y|Tail]) :-
shuffle(Xs,Ys,Tail).
shuffle([X|Xs],[Y|Ys],[Y,X|Tail]) :-
shuffle(Xs,Ys,Tail).
This results in :
| ?- shuffle([a,b],[1,2],L).
L = [a,1,b,2] ? ;
L = [a,1,2,b] ? ;
L = [1,a,b,2] ? ;
L = [1,a,2,b]
So I'm missing the cases of "simple append" of L1+L2 and L2+L1...
What is my predicate missing?
We can use dcg for its ease of writing:
shuffle([A|B],[C|D]) --> [A] , shuffle(B,[C|D]).
shuffle([A|B],[C|D]) --> [C] , shuffle([A|B],D).
shuffle(A,[]) --> A.
shuffle([],C) --> C.
shuffle( A, B, C) :- phrase( shuffle(A,B), C).
We either take first card from one non-empty deck or the other, but if one of them is empty we must use all the remaining cards in the non-empty deck at once.
Unfortunately this leaves one extra choice point at the end:
5 ?- shuffle([a,b],[1,2],C).
C = [a, b, 1, 2] ;
C = [a, 1, b, 2] ;
C = [a, 1, 2, b] ;
C = [1, a, b, 2] ;
C = [1, a, 2, b] ;
C = [1, 2, a, b] ;
false.
As for your approach the problem with it was that you tried to take care of two cards at once, and it got complicated. Going by smallest steps can be the easiest.
Here's how you can shuffle two lists while preserving the relative item order.
shuffle([], Xs, Xs).
shuffle([X|Xs], Ys, Zs) :-
shuffle_(Ys, X, Xs, Zs). % use auxiliary predicate shuffle_/4
shuffle_([], X, Xs, [X|Xs]). % do indexing on both lists
shuffle_([Y|Ys], X, Xs, [X|Zs]) :-
shuffle_(Xs, Y, Ys, Zs).
shuffle_([Y|Ys], X, Xs, [Y|Zs]) :-
shuffle_(Ys, X, Xs, Zs).
Sample query using SWI-Prolog:
?- shuffle([a,b], [1,2], Xs).
Xs = [a,1,b,2]
; Xs = [a,1,2,b]
; Xs = [a,b,1,2]
; Xs = [1,a,2,b]
; Xs = [1,a,b,2]
; Xs = [1,2,a,b]. % no useless choice point at the end
#repeat's answer is more elegant and efficient, but, as an alternative:
The unwanted choice-point can be removed using a reusable empty_list_first predicate:
shuffle([A|B], [C|D]) --> [A],
shuffle(B, [C|D]).
shuffle([A|B], [C|D]) --> [C],
{ empty_list_first([A|B], D, A1, D1) },
shuffle(A1, D1).
% Rewritten to prevent needing https://www.swi-prolog.org/pldoc/man?section=basics
%shuffle([], C) --> remainder(C).
shuffle([], C, C, []).
shuffle(A, B, C) :-
empty_list_first(A, B, A1, B1),
phrase(shuffle(A1, B1), C).
empty_list_first([], L2, [], L2).
empty_list_first([H|T], L2, EL1, EL2) :-
empty_list_first_(L2, [H|T], EL1, EL2).
empty_list_first_([], L1, [], L1).
% If neither are empty, keep original order
empty_list_first_([H|T], L1, L1, [H|T]).
Result in swi-prolog:
?- shuffle([a,b], [1,2], C).
C = [a,b,1,2] ;
C = [a,1,b,2] ;
C = [a,1,2,b] ;
C = [1,a,b,2] ;
C = [1,a,2,b] ;
C = [1,2,a,b].
My answer is posted long time after original question but hoping this might prove useful to someone some day. I've taken a different approach to this, might be on the longer side but it works... :)
Since one of the requirements at the class I'm taking to not exceed material learned, some items such as delete and concatenate have been created here as well.
del(X,[X|Xs],Xs).
del(X,[Y|Ys],[Y|Zs]):-
del(X,Ys,Zs).
permutation([],[]).
permutation(Xs,[Z|Zs]):-
del(Z,Xs,Ys),
permutation(Ys,Zs).
conc([],L,L).
conc([X|L1],L2,[X|L3]):-
conc(L1,L2,L3).
is_in_order([],_).
is_in_order([_],_).
is_in_order(Sublist1, Sublist2, Superlist) :-
remove_elements(Superlist, Sublist1, SuperSubList),
list_equal(Sublist2, SuperSubList).
list_equal([], []).
list_equal([X|Xs],[X|Ys]) :-
list_equal(Xs, Ys).
% Remove L1 from L2 and return the resulting list
remove_elements(L, [H|T], R) :-
delete(L, H, R1),
remove_elements(R1, T, R).
remove_elements(L, [], L).
/*Shuffle first creates a concatenated list from both L1 & L2
* It then create permutation for all possible combinations of L1 & L2
* Once done, it scrubs the new lists to filter out the ones that do not
* maintain the original order of L1 & L2
* The result is only the permutations that fullfills the order condition
*/
shuffle(L1,L2,L):-
conc(L1,L2,L3),
permutation(L3, L),
is_in_order(L1, L2, L),
is_in_order(L2, L1, L).

Prolog , Append with no repititions

Hey I'm trying to append two list with no "double" members
for example
A = [a, b, c]
B = [x, c, q]
then ->
append2(A,B,P)
P= [a,b,c,x,q]
I write this code, but it doesn't work...
not_member(_, []).
not_member(X, [Y|Ys]) :- X \= Y, not_member(X, Ys).
append2(A, [], A).
append2([], A, A).
append2([h1|ls], B, [h1|P]) :- not_member(h1, B), !, append2(ls, B, P).
append2([h1|ls], B, P) :- member(h1, P), append2(ls, B, P).
Thanks for helping :)
Assuming there are no variables in your input lists, but allowing duplicates in each list you may write:
append2(A,B,C):-
findall(Item, append2_item(A,B,Item), C).
append2_item(A,_,ItemA):-
append(HeadA, [ItemA|_], A),
\+ member(ItemA, HeadA).
append2_item(A,B,ItemB):-
append(HeadB, [ItemB|_], B),
\+ member(ItemB, HeadB),
\+ member(ItemB, A).
First clause of append2_item/3 selects (ordered) distinct items from the first list. Second clause of append2_item/3 selects (ordered) distinct items from the second list which are not present in the first list.
append2/3 just collects those elements.
Test case:
?- append2([a,b,c,a],[x,c,q,x],C).
C = [a, b, c, x, q].
Check out the pure code in my answer
to the related question "intersection and union of 2 lists"!
Telling from your requirements, predicate list_list_union/3 is just what you are looking for:
?- list_list_union([a,b,c],[x,c,q],Ls).
Ls = [a,b,c,x,q]. % succeeds deterministically
list_list_union/3 is monotone, so we get sound answers
even when using non-ground terms:
?- As = [_,_,_], Bs = [_,_,_], list_list_union(As,Bs,Ls), As = [a,b,c], Bs = [x,c,q].
As = [a,b,c], Bs = [x,c,q], Ls = [a,b,c,x,q] ; % logically sound result
false.

How to make my relation work

I have the following relation: index(X,N,List).
for example:
index(X,2,[a,b,c]).
X=b
index(b,N,[a,b,c]).
N=2
I don't know how to make my relation to work with the second example. It says that N is not defined well
Here is my code (it works well for the first example).
index(X,1,[X|_]).
index(X,N,[_|Tail]) :- N > 1, N1 is N - 1 , index(X,N1,Tail).
There is a SWI-Prolog built-in nth1/3 that does what you want:
?- nth1(N, [a, b, c], b).
N = 2 ;
false.
Look at its source code:
?- listing(nth1).
lists:nth1(A, C, D) :-
integer(A), !,
B is A+ -1,
nth0_det(B, C, D).
lists:nth1(A, B, C) :-
var(A), !,
nth_gen(B, C, 1, A).
true.
?- listing(nth0_det).
lists:nth0_det(0, [A|_], A) :- !.
lists:nth0_det(1, [_, A|_], A) :- !.
lists:nth0_det(2, [_, _, A|_], A) :- !.
lists:nth0_det(3, [_, _, _, A|_], A) :- !.
lists:nth0_det(4, [_, _, _, _, A|_], A) :- !.
lists:nth0_det(5, [_, _, _, _, _, A|_], A) :- !.
lists:nth0_det(A, [_, _, _, _, _, _|C], D) :-
B is A+ -6,
B>=0,
nth0_det(B, C, D).
true.
?- listing(nth_gen).
lists:nth_gen([A|_], A, B, B).
lists:nth_gen([_|B], C, A, E) :-
succ(A, D),
nth_gen(B, C, D, E).
true.
The variable N has not been instantiated to a numeric type when Prolog attempts to evaluate the goals N > 1 and N1 is N - 1 in the recursive clause defining index/3. This causes the instantiation error you are reporting.
I don't know how to solve your problem directly, but I have two suggestions. The first is to use an accumulator, so that the arithmetic operations in the recursive clause can be evaluated:
get(M,Xs,X) :- get(1,M,Xs,X).
get(N,N,[X|_],X).
get(N,M,[_|Xs],X) :-
L is N + 1,
get(L,M,Xs,X).
For instance:
?- index(N,[a,b],X).
N = 1,
X = a ;
N = 2,
X = b ;
false.
The other is to use a natural number type, so that the index can be constructed via unification:
nat(0).
nat(s(N)) :- nat(N).
get(s(0),[X|_],X).
get(s(N),[_|Y],X) :- get(N,Y,X).
For instance,
?- get(N,[a,b],X).
N = s(0),
X = a ;
N = s(s(0)),
X = b ;
false.
Hopefully this was helpful. Perhaps someone more knowledgeable will come along and give a better solution.

Resources