Split a list in prolog - list

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.

Related

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

PROLOG Sum of a list created from facts

I want to create a list from the facts. And the list should contains only one of the arity in the facts.
For example :
%facts
abc(a, b, c).
abc(d, e, f).
abc(g, h, i).
Sample :
?-lists(A).
A = [a, d, g];
No.
EDIT :
Using the suggestion by Vaughn Cato in the comment, the code become this :
%facts
abc(a, b, c).
abc(d, e, f).
abc(g, h, i).
lists(A) :-
findall(findall(X, abc(X, _, _), A).
The list is created, but how to sum up the list A ?
If sum of list for input from query,
sumlist([], 0).
sumlist([X|Y], Sum) :-
sumlist(Y, Sum1),
Sum is X + Sum1.
But if want to sum the existing list, how to define the predicate?
To sum a list of numbers such as that produced by your definition of lists/1, most Prolog systems (e.g., GNU, SWI) implement sum_list/2 which takes a list of numbers as the first argument and binds their sum in the second:
?- sum_list([1,2,3],Sum).
Sum = 6.
You can also solve it with aggregate_all/3. It eliminates need to build list in memory if you just need a sum.
sum_facts(Template, Arg, Sum) :-
aggregate_all(sum(X), (call(Template), arg(Arg, Template, X)), Sum).
In this example I use a generic call with defined Template:
sum_facts(abc(_, _, _), 1, Sum).
If you will always use it with the first arg of abc/3 this version will be enough:
sum_facts(Template, Arg, Sum) :-
aggregate_all(sum(X), abc(X, _, _), Sum).
As suggested by Vaughn Cato, it's help me a lot by using findall(X,abc(X, _ , _ ),A). to create the list I wanted to.

flatten list by replacing comma with plus operator inside a list in prolog

I am working on a scenario in Prolog (eclipse) wherein I need a list structure to be reformatted.
I have a list of the form:
MyList = [a,b,c].
I was trying to see if I can flatten the list to a single element with all the commas replaced with the + operator.
So my result list would look like:
ResultList = [a+b+c]
which is a single element list. The length of the initial list is arbitrary.
I know prolog is not suited for such operations, but can this be done?
here it is, in standard Prolog. I think there should be no difference with Eclipse:
list_to_op([X,Y|T], [R]) :-
list_to_op(T, X+Y, R).
edit: bug noted by false
list_to_op([X], [X]).
list_to_op([X], R, R+X).
list_to_op([X|T], R, Q) :-
list_to_op(T, R+X, Q).
test:
?- list_to_op([a,b,c],X).
X = [a+b+c] .
The accumulator is required to give the appropriate associativity: the simpler and more intuitive definition
list_to_op1([X], X).
list_to_op1([X|R], X+T) :-
list_to_op1(R, T).
gives
?- list_to_op1([a,b,c],X).
X = a+ (b+c) .
If evaluation order is important, use list_to_op.
edit:
there is a bug: list_to_op([a,b],X) fails.
here the correction, as often happens, it's a simplification:
list_to_op([], R, R).
list_to_op([X|T], R, Q) :-
list_to_op(T, R+X, Q).
This may help
flatten_list(A,[B]) :- flatten_list_inner(A,B).
flatten_list_inner([A],A).
flatten_list_inner([H|T],H+Y) :- flatten_list_inner(T,Y).
The output is slightly different from what you wanted. It is currently [a + (b + c)]
How about this non-recursive version..
list_to_op(L, Res) :-
concat_atom(L, '+', Atom),
Res = [Atom].
?- list_to_op([a,b,c], X).
X = ['a+b+c'].
Edit: This works in Swi-prolog.. not sure about Eclipse.

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.

Custom reverse of a list in Prolog

I am trying to write a predicate that given the following list in Prolog:
[[1,a,b],[2,c,d],[[3,e,f],[4,g,h],[5,i,j]],[6,k,l]]
will produce the following list:
[[6,k,l],[[5,i,j],[4,g,h],[3,e,f]],[2,c,d],[1,a,b]]
As you can see I would like to preserve the order of the elements at the lowest level, to produce elements in the order 1, a, b and NOT b, a, 1.
I would also like to preserve the depth of the lists, that is, lists that are originally nested are returned as such, but in reverse order.
I have managed to achieve the desired order with the following code, but the depth is lost, i.e. lists are no longer nested correctly:
accRev([F,S,T],A,R) :- F \= [_|_], S \= [_|_], T \= [_|_],
accRev([],[[F,S,T]|A],R).
accRev([H|T],A,R) :- accRev(H,[],R1), accRev(T,[],R2), append(R2,R1,R).
accRev([],A,A).
rev(A,B) :- accRev(A,[],B).
I would appreciate help in correcting the code to preserve the correct nesting of lists. Thanks!
Two suggestions. First, here's one (rev_lists/2) which uses a bunch of SWI-PROLOG built-ins:
rev_lists(L, RL) :-
forall(member(M, L), is_list(M)), !,
maplist(rev_lists, L, L0),
reverse(L0, RL).
rev_lists(L, L).
This one works by testing if all elements of a list L are themselves lists (M); if so, it will recursively apply itself (via maplist) over all individual sub-lists, else it will return the same list. This has the required effect.
Secondly, here's rev_lists/2 again, but written such that it doesn't rely on built-ins except member/2 (which is common):
rev_lists(L, RL) :-
reversible_list(L), !,
rev_lists(L, [], RL).
rev_lists(L, L).
rev_lists([], Acc, Acc).
rev_lists([L|Ls], Acc, R) :-
( rev_lists(L, RL), !
; RL = L
),
rev_lists(Ls, [RL|Acc], R).
reversible_list(L) :-
is_a_list(L),
\+ (
member(M, L),
\+ is_a_list(M)
).
is_a_list([]).
is_a_list([_|_]).
It's basically the same strategy, but uses an accumulator to build up reverse lists at each level, iff they are comprised exclusively of lists; otherwise, the same list is returned.