checking if two lists have common elements - list

So basically, I have a predicate called common_elements(List1,List2), and the purpose of this predicate is to check if List1 has at least an element that belongs in List2.
Example:
?- common_elements([1,2,3,4,5,6],[6]).
true.
?- common_elements([1,2,3],[2]).
true.
?- common_elements([1,2,3],[6]).
false.
?- common_elements([P1,P2,P3,P4,P5,P6],[P7,P8,P6]).
true.
So for numbers this works well, but if i type variables it unifies the variables instead of checking if they are in the 2nd list.
Example:
?- common_elements([1,2,3],[2]).
true.
?- common_elements([1,2,3],[6]).
false.
?- common_elements([P1,P2,P3,P4,P5,P6],[P7,P8,P6]).
P1 = P7.
So as you can see for numbers it works well, but for some reason if type variables it unifies them instead of just comparing them and dont seem tu understand why.
Program:
common_elements(L1,L2) :- common_elements(L1,L2,[]).
common_elements([],_,AC) :- length(AC,C),
C >= 1.
common_elements([P|_],L2,AC) :- member(P,L2),!,
append(AC,[P],NAC),
common_elements([],L2,NAC).
common_elements([P|R],L2,AC) :- \+ member(P,L2),!,
common_elements(R,L2,AC).

The member/2 predicate will perform unification. Indeed, for example:
?- member(P1, [P2]).
P1 = P2.
You can make use of ==/2 to avoid unification, and thus:
True if Term1 is equivalent to Term2. A variable is only identical to a sharing variable.
So what we can here do is check if a variable is equivalent to another variable with:
membereq(X, [H|_]) :-
X == H.
membereq(X, [_|T]) :-
membereq(X, T).
Then we thus can check:
common_elements([H|_], L2) :-
membereq(H, L2).
common_elements([_|T], L2) :-
common_elements(T, L2).
This then answers the queries with:
?- common_elements([1,2,3,4,5,6],[6]).
true ;
false.
?- common_elements([1,2,3,4,5,6],[6]).
true ;
false.
?- common_elements([1,2,3],[2]).
true ;
false.
?- common_elements([1,2,3],[6]).
false.
?- common_elements([P1,P2,P3,P4,P5,P6],[P7,P8,P6]).
true ;
false.

Using builtins, as you've tagged SWI and ordsets is underappreciated
:- use_module(library(ordsets)).
common_elements(A, B) :-
sort(A, AS), % can omit if using sorted A list
sort(B, BS), % can omit if using sorted B list
ord_intersect(As, Bs).
Some examples:
t1 :-
common_elements([a, b, c], [d, e, f]).
t2 :-
common_elements([a, b, c], [c, d, e]).
t3 :-
common_elements([a, b, c], [_A, _B, _C]).
t4 :-
common_elements([a, b, C], [d, e, C]).
t5 :-
common_elements([a, b, _C], [d, e, _F]).
tests :-
\+ t1,
t2,
\+ t3,
t4,
\+ t5.
Note: Use sort/2 rather than list_to_set/2 because the latter leaves variables in place. Also, use ord_intersect/2 over intersection/3 as the latter can produce unexpected results. If you use many set operations you may find it worthwhile using ordered lists (ordsets) throughout.

Related

Separate list by first occurence of variable in SWI-Prolog without instantiating the variable itself

I have following source-code:
split_list(List, L, [R1|RN], Condition) :-
find_first(List, R1, Condition),
append(L, [R1|RN], List),
forall(member(X, L),
not(call(Condition, X))).
find_first([H|_], H, Condition) :- call(Condition, H), !.
find_first([_|T], X, Condition) :- find_first(T, X, Condition).
This Prolog program splits a list List into two lists L and [R1|RN]. R1 is the first element of List which satisfies the predicate Condition. L contains all elements in List before R1. L does not contain any element satisfying Condition. RN contains all elements which follow R1 in List.
My aim now is to write some predicate, which separates some list [a,b,c,D,d,f,e,f,d] into two lists [a, b, c] and [D, d, f, e, f, d] without instantiating the variable D.
I just tried the call:
split_list([a,b,c,_,d,f,e,f,d], L, R, var).
but this produces much solutions by instantiating _ by a or b or c and so on. How can I solve it?
From the looks of it, this is a more useful find_first (with argument order changed to be more sensible):
% P means previous, R means remainder
find_first_succ(Cond, L, P, [H|T]) :-
find_first_succ_(L, Cond, P, [H|T]).
find_first_succ_([H|T], Cond, [], [H|T]) :-
call(Cond, H), !.
find_first_succ_([H|T], Cond, [H|P], R) :-
find_first_succ_(T, Cond, P, R).
Result in swi-prolog:
?- find_first_succ(var, [a,b,c,_,d,f,e,f,d], P, R).
P = [a, b, c],
R = [_, d, f, e, f, d].
So, you don't need that problematic append.
I found some solution:
split_list(List, L, [R1|RN], Condition) :- member(R1, List), append(L, [R1|RN], List), call(Condition, R1).

Remove duplicate from a list but not returning two same results in SWI-Prolog?

duplicate([],[]).
duplicate([A|B],[A|B1]) :- not(member(A,B)), duplicate(B,B1).
duplicate([A|B],List) :- member(A,B), duplicate(B,List).
I wrote this predicate to remove duplicate from the list, but when I test it,
?- duplicate([a,b,c,a,d,c,b,a,e,f],N).
N = [d, c, b, a, e, f] ;
N = [d, c, b, a, e, f] ;
false.
Is there a way to just keep one result only, not two same results? (so it will only return one list).
Also, I am not allowed to use operators that modify the backtracking search, such as the cut operator !, the negation operators not, +, or the if-then-else operator with -> and ;
It would be grateful if someone could help me . :D
The actual reason for receiving more than one answer is the goal member(A,As). It produces multiple answers for duplicates in As.
?- member(a, [a,a]).
true
; true.
There are several ways out.
memberchk/2 or once/1
memberchk/2 is defined as
memberchk(X, Xs) :-
once(member(X, Xs)).
This removes alternate answers. But then, it may remove otherwise valid solutions too. Consider:
?- memberchk(X, [a,b]), b = X.
false.
?- b = X, memberchk(X, [a,b]), b = X.
b = X.
So memberchk/2 is sensitive to the precise instantiation, which makes it a very brittle, impure predicate.
But it has one good point: It sticks to just one answer for
?- memberchk(a, [a,a]).
true.
So what would be ideal is a definition that is both pure and sticking to the first element. Enter
memberd/2
memberd(X, [X|_Ys]).
memberd(X, [Y|Ys]) :-
dif(X, Y),
memberd(X, Ys).
In this definition, the recursive rule is only of relevance if the list element is different. Thus this rule will never apply to memberd(a, [a,a,a]).
Another problem in your definition is not(member(A,B)) which only behaves as intended, if A and B are sufficiently instantiated. Your definition fails for:
duplicate([a,X],[a,b]). although there is a solution: X = b.
Rather replace it by non_member/2.
Alternatively, in case you are interested in the most efficient solution, consider library(reif) available
for
SICStus and
SWI which leads to:
list_nub([], []).
list_nub([X|Xs], Ys0) :-
if_(memberd_t(X, Xs), Ys0 = Ys1, Ys0 = [X|Ys1]),
list_nub(Xs, Ys1).
Here's one way to remove all duplicates, not the most efficient but I think it's quite easy to understand the intention.
rm_duplicates(In, Out) :-
exclude(has_duplicate(In), In, Out).
has_duplicate(List, Case) :-
dif(I, J),
nth0(I, List, Case),
nth0(J, List, Case).
If you mean to make a list into a set:
list_to_set(List, Set).
It's documented: list_to_set/2

Gathering results of methods in one List with Prolog

So I am trying to
I am defining the sets with is_a(b, a), is_a(c, a), which for simplicity would look visually something like this:
a
b c
d e f g
I want to give in the list [b, c] and as a result get the list [d, e, f, g]
At the moment when I give in a node or a variable, then it can find everything that is underneath it with this method:
find_nodes(Root, Root) :-
\+ is_a(_, Root).
find_nodes(Root, X) :-
is_a(Node, Root),
find_nodes(Node, X).
Which when run gives me the result I need :
?- find_nodes(b, X).
X = d.
X = e.
But it is not in a list, so I have tried :
?- all_nodes([b, c], X).
all_nodes([], _).
all_nodes([H|T], [R|Res]):-
findall(L, find_nodes(H, L), R),
all_nodes(T, Res).
Which gives me - X = [[d, e], [f, g]|_4040], which consists of lists within lists, but I need just 1 list, that would be X = [d, e, f, g].
What am I doing wrong here?
EDIT
Like #lurker said findall returns a list and adding list to a list will give the result I get right now.
The one thing I also tried was using:
all_nodes([], _).
all_nodes([H|T], [R|Res]):-
find_nodes(H, R),
all_nodes(T, Res).
But well that one does not work either because It only gives me 1 element, which in this case is d and then f.
You can take advantage of the de facto standard findall/4 (*) predicate to solve the problem. This predicate is a variant of the standard findall/3 predicate that allows passing a tail for the list of solutions collected by the predicate. For example:
?- findall(N, (N=1; N=2; N=3), L, [4,5]).
L = [1, 2, 3, 4, 5].
In the following solution, I have renamed predicates and variables for clarity and modified your node leaf predicate:
is_a(a, b).
is_a(a, c).
is_a(b, d).
is_a(b, e).
is_a(c, f).
is_a(c, g).
leaf(Leaf, Leaf) :-
\+ is_a(Leaf, _).
leaf(Node, Leaf) :-
is_a(Node, Child),
leaf(Child, Leaf).
all_nodes([], []).
all_nodes([Node| Nodes], Leaves):-
findall(Leaf, leaf(Node, Leaf), Leaves, Tail),
all_nodes(Nodes, Tail).
Sample calls:
?- all_nodes([b, c], X).
X = [d, e, f, g].
?- all_nodes([a], X).
X = [d, e, f, g].
?- all_nodes([b], X).
X = [d, e].
(*) It's a built-in predicate in GNU Prolog, JIProlog, Lean Prolog, O-Prolog, SICStus Prolog, SWI-Prolog, XSB, and YAP (possibly others).

Why does my Pradicate my_flatten/2 not flatten a nested list structure? (Prolog)

The predicate i wrote flattens a simple list like List=[a,b,c,[d,e],f],
but not a list like List2 = [a,b,[[c,d],e],f]. Here is my code:
my_flatten([], []).
my_flatten([H|T], R):- not(is_list(H)), my_flatten(T, R1), append([H], R1, R).
my_flatten([H|T], R):- is_list(H), my_flatten(T, R1), append(H, R1, R).
My idea is to check whether the head is a list or not and depending on this decision to go through recursion and append the tail to the head.
Am I missing an important point?
Am I missing an important point?
yes, of course. Boris is right that you will learn more if you try to find the problem yourself. Just an hint: you must recurse on the head, when it's a list, to get it flattened.
After that (just add a call in proper place in last clause) and you'll get
?- my_flatten([a,b,[[c,d],e],f], L).
L = [a, b, c, d, e, f] ;
false.

Prolog program to find equality of two lists in any order

I wanted to write a Prolog program to find equality of two lists, where the order of elements
doesn't matter. So I wrote the following:
del(_, [], []) .
del(X, [X|T], T).
del(X, [H|T], [H|T1]) :-
X \= H,
del(X, T, T1).
member(X, [X|_]).
member(X, [_|T]) :-
member(X, T).
equal([], []).
equal([X], [X]).
equal([H1|T], L2) :-
member(H1, L2),
del(H1, L2, L3),
equal(T, L3).
But when I give input like equal([1,2,3],X)., it doesn't show all possible values of X. Instead, the program hangs in the middle. What could be the reason?
isSubset([],_).
isSubset([H|T],Y):-
member(H,Y),
select(H,Y,Z),
isSubset(T,Z).
equal(X,Y):-
isSubset(X,Y),
isSubset(Y,X).
Try using predicate that checks if one of sets is a permutation of other set:
delete(X, [X|T], T).
delete(X, [H|T], [H|S]):-
delete(X, T, S).
permutation([], []).
permutation([H|T], R):-
permutation(T, X), delete(H, R, X).
(Predicate taken from http://www.dreamincode.net/code/snippet3411.htm)
?- permutation([1,2,3],[3,1,2]).
true
The actual reason for the non-termination that you observed is this: the following clause does not constrain L2 in any way, shape, or form.
equal([H1|T], L2) :-
member(H1, L2),
del(H1, L2, L3),
equal(T, L3).
So your query ?- equal([1,2,3], X). implies proving the goal member(_, L2) which does not terminate universally. Therefore equal([1,2,3], X) cannot terminate universally, too!
For more information on how to explain non-termination of Prolog code read about failure-slice!
PS. Looking at the termination problem from a different angle, we see that the non-termination is, in fact, a necessary consequence in this case.
Why? Because you do not constrain the number of multiplicities, which makes the solution set infinite in size. The set cannot be represented by a finite number of answers (provided you do not permit delaying goals).
If you don't care about the multiplicities of the list elements,
check for sufficient instantiation with
ground/1,
enforce it with
iwhen/2,
and eliminate duplicates with sort/2 like so:
same_elements(As, Bs) :-
iwhen(ground(As+Bs), (sort(As,Es),sort(Bs,Es))).
Sample use with SWI Prolog 8.0.0:
?- same_elements([a,c,c,b,a,c], [c,b,b,a]).
true.
?- same_elements([a,b,b,a], [b,a,b,e]).
false.
?- same_elements([a,b,b,a], Xs).
ERROR: Arguments are not sufficiently instantiated
Try this:
equal([],[]).
equal([Ha|Ta],[Hb|Tb]) :-
Ha = Hb, lequal(Ta,Tb).
How about:
equal(X, Y) :-
subtract(X, Y, []),
subtract(Y, X, []).
So why does equal([1,2,3], X) not terminate universally with your code?
Let's look at a failure-slice of your code! What are failure slices? Here's the tag info:
A failure-slice is a fragment of a Prolog program obtained by adding some goals false. Failure-slices help to localize reasons for universal non-termination of a pure monotonic Prolog program. They also help to give a lower bound for the number of inferences needed. It is a concrete program-slicing technique.
To create a failure slice:
we insert false goals into the program
while making sure that the fragment does not terminate with above goal.
del(_, [], []) :- false.
del(X, [X|T], T) :- false.
del(X, [H|T], [H|T1]) :- false,
dif(X, H), % note that the OP originally used `X \= H`
del(X, T, T1).
member(X, [X|_]).
member(X, [_|T]) :-
member(X, T).
equal([], []) :- false.
equal([X], [X]) :- false.
equal([H1|T], L2) :-
member(H1, L2), false,
del(H1, L2, L3),
equal(T, L3).
?- equal([1,2,3], _), false. % note that `false` is redundant ...
** LOOPS ** % ... as above `equal/2` cannot succeed.
So... what does above failure slice tell us? It says:
To make the goal equal([1,2,3], X) terminate universally ...
... we must change at least one of the remaining parts (the ones not striked-through)!
I suggest using built-in predicate msort/2, then comparing the lists. It takes O(nlogn) time on SWI Prolog, whereas checking unsorted lists naively element-by-element would take O(n2) time.
lists_equal(List1, List2) :-
msort(List1, Sorted1),
msort(List2, Sorted2),
Sorted1=Sorted2.
Here, sorting lists takes O(nlogn) time, and comparing them takes O(n) time on SWI Prolog, I don't know about other implementations.
Briefly
equal([],[]).
equal([H|T],[H|T1]):-equal(T,T1).