PROLOG Sum of a list created from facts - list

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.

Related

Prolog predicate to multiply two lists (representing unary numbers)

I want to write a code that multiplies lists representing a number, like:
?- times([1,1,1], [1,1], Res).
Res = [1,1,1,1,1,1].
times([], _, []). % base case
times([_|T], Lis, [Lis|H]) :-
times(T, Lis, H).
I already have the code above and it kinda does what I want but not really. For example when asking:
?- times([1,1,1], [1,1], Res)
Res = [[1,1],[1,1],[1,1]].
The idea is there, but I just don't know how to fix that, I understand why it's happening (I'm adding a list as head), so I just wondered if anybody could help me.
Thanks in advance.
[Lis|H] will use Lis as first element, regardless whether Lis is a list or not. You should take a look at append/3 [swi-doc] for example to append two lists:
times([], _, []).
times([_|T], Lis, R) :-
append(Lis, H, R),
times(T, Lis, H).

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.

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.

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.

how to count odd and even number with using PROLOG

question is:
when we key in mem([1,2,3,4,5]).
we will get the output as bellow:
odd=3
even=2
my coding is like that but cannot run. can help me check where is my mistake??
mem(X,[X|L]).
mem(X,[element|L]):-
mem([X,L]).
count([],L,L).
count([X|H],L1,L2):-
write(even),
X%2=0,nl,
write(odd),
X%2>=1,nl,
count([H],[X|L1],L2).
thanks for your helping.
The procedures you have written do two different things and don't actually belong together. mem/2 is equivalent to the usually builtin member/2 except that your definition contains an error: in the second clause element is an atom instead of a variable so it will not match other elements of the list. The usual definition is
member(X, [X|_]).
member(X, [_|L]) :- member(X, L).
Note that this definition will not only test if a term is an element of a list but can even be use to generate a list.
What exactly are you trying to do in count/3: split the list into two lists, one containing odd and the other containing even; or count the number of odd and even elements? The splitting could be done with something like:
count([], [], []).
count([X|L], O, E) :- X rem 2 =/= 0, count(L, [X|O], E).
count([X|L], O, E) :- X rem 2 =:= 0, count(L, O, [X|E]).
Note that =/= /2 and =:= / 2 force evaluation of arguments as arithmetic expressions while = /2 attempts to unify its arguments.
Counting the number of odds and evens can be done in a similar fashion, and is left as an exercise for the reader. :-)