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

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

Related

checking if two lists have common elements

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.

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

DLV list composition

I was wondering if there is a way in DLV for creating a list with the elements of all predicates that are true in a rule. For example, if I have the following predicates
foo(a, b).
foo(a, c).
foo(a, e).
foo(b, c).
The result I am looking for should be new predicates where the first element is the first parameter of foo and the second parameter should contain a list with all the elements associated to the first parameter. Empirically:
bar(a, [b,c,e]).
bar(b, [c]).
I know there is a way of getting these results (plus many more) with the following code:
bar(A, [X]) :- foo(A, X).
bar(A, P ) :- bar(A, P0),
foo(A, X),
not #member(X, P0),
#insLast(P0, X, P).
But I would like to know if there is a way of preventing the generation of all possible lists of size from 1 to N (being N the number of elements of the final list). I would like to do it for two reasons: (1) reduce computational cost (2) prevent discarding all unnecessary predicates.
If the computational cost was not a problem, which may be the case, I was thinking of the following changes in order to keep only the predicates with the largest lists:
tmp_bar(A, [X], 1) :- foo(A, X).
tmp_bar(A, P, L) :- tmp_bar(A, P0, L0),
foo(A, X),
not #member(X, P0),
#insLast(P0, X, P),
L = L0 + 1.
bar(A, P) :- tmp_bar(A, P, L),
max_list(A, L).
max_list(A, L) :- foo(A, _),
#max{X: tmp_bar(A, P, X)} = L.
However, this starts to get complicated and is showing all of the lists of maximum size and not only one of them. How do I get rid of all but one? I tried generating bar(A,P) only in case their is no other bar(A, _) but I get "rule is not safe". Also tried counting the number of occurrences and similar problems appear...
Most importantly, is it possible to get the results I expect all at once without that many tricks?
Any help is appreciated,
Thanks!
Apparently I found a solution to the problem by adding elements in a particular order. What I do is to add the element at the end of the list only if its smaller than the last element of the current list. I was dealing with names rather than numbers so I though this wasn't possible).
Here is the code:
tmp_bar(A, [X], 1) :- foo(A, X).
tmp_bar(A, P, L) :- tmp_bar(A, P0, L0),
foo(A, X),
#last(P0, Y),
Y < X,
#insLast(P0, X, P),
L = L0 + 1.
bar(A, P) :- tmp_bar(A, P, L),
max_list(A, L).
max_list(A, L) :- foo(A, _),
#max{X: tmp_bar(A, P, X)} = L.
Hope it helps someone else in the future.

prolog term without a functor

I am facing a prolog problem regarding List and Term. Then my question is how to write a predicate
transform([a,b],X)
will return X = (a,b) Or vice versa
This is weird with me because I've never faced such term before. I tried with the built in =.. but
=..((a,b,c,d),X)
returns X=[',',a,(b,c,d)] which makes me deeply disappoint.
Thank you.
Check something like this:
transform([A], A):-
A=..[_].
transform([A,B], (A,B)):-
B=..[_].
transform([A,B,C|Tail], L):-
L=..[',',A,T],
transform([B,C|Tail], T).
The first clause is only needed if you want transform([Item], Item).
?- transform([a,b], X).
X = (a, b)
?- transform([a,b,c,d,e,f], X).
X = (a, b, c, d, e, f)
?- transform(L, (a,b,c,d,e,f,g))
L = [a, b, c, d, e, f, g]
Note that the term you are building does have a functor, it is ','/2, and it is shown with the parenthesis you are seeing.

Split a list in prolog

suppose i have this list in prolog:
[-0.791666666666667-[]-[predicate(a,b,c,d)]-[predicate_2(p,e,q,d,g)]]
there is way to split this in:
-0.791666666666667, [], [predicate(a,b,c,d)], [predicate_2(p,e,q,d,g)] ???
Split means have different pice of the list.
Maybe:
X = -0.791666666666667 Y = [] Z = [predicate(a,b,c,d)] etc...
Or another solution can be replace - with "," so it become a list with different elements?
You can use pattern matching. Similar to the way you use it on lists ([H|T]):
split(A, R) :- split(A, R, []).
split(A-B, R, Acc) :- split(A, R, [B|Acc]), !.
split(H, [H|T], T).
I'm using accumulator, because something like a-b-c is split by A-B into a-b and c.
EDIT: If you know you have 4 terms, you can use something like
split(A-B-C-D, A, B, C, D).
Trivial:
to_list([A-B-C-D], [A,B,C,D]).
Usage:
?- to_list([-0.791666666666667-[]-[predicate(a,b,c,d)]-[predicate_2(p,e,q,d,g)]],
L).
L = [-0.791667, [], [predicate(a, b, c, d)], [predicate_2(p, e, q, d, g)]].
Or do the pattern matching inline, it's rather wasteful to write a predicate for such a task, which seems rather one-off to me.