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
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).
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
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).
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.
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).