Adding elements of a list in prolog - list

so I'm quite new to prolog and I have to add up all the elements of a list.
listSum([],0).
listSum([Head|Tail], Sum) :- listSum(Tail,TailSum), Sum is Head + TailSum.
The goal is to make this tail-recursive and I was wondering if that was a better way to do it than this
listSum([],0).
listSum(List, Sum) :- listSum(List,Sum,0).
listSum([H|T], Sum, S) :- S1 is S+H, listSum(T, Sum, S1).
listSum([], Sum, S) :- Sum is S.
Or is that perfectly fine to do? Just looking to see if there's any obvious way to improve the above code that I'm missing out on.
Thanks.

This looks perfectly fine to me. You can save yourself some writing by not doing the unnecessary stuff. First, you get 0 from two separate places; it would be enough to get it from one. Also, "output" arguments are supposed to come after all other arguments.
list_sum(L, S) :-
list_sum(L, 0, S).
Then, you don't need the is/2 in the base case:
list_sum([], S, S).
list_sum([X|Xs], S0, S) :-
S1 is S0 + X,
list_sum(Xs, S1, S).
Of course, you could decide to actually save one step in the tail-recursive definition:
list_sum([], 0).
list_sum([X|Xs], S) :-
list_sum(Xs, X, S).
list_sum([], S0, S) :- S is S0.
list_sum([X|Xs], S0, S) :-
S1 is S0 + X,
list_sum(Xs, S1, S).
You should recognize that the second version is a fold:
list_sum([], 0).
list_sum([X|Xs], S) :-
foldl(add, Xs, X, S).
add(X, Y, S) :- S is X+Y.
Or, even directly:
list_sum(List, Sum) :- foldl(add, List, 0, Sum).

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

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

Reversing list in Prolog

I am starting with prolog, and as an exercise I am trying to reverse a list.
For example, inv([1,2,3], S) should give S = [3,2,1].
Below is what a partner and I have already done:
conc([], L, L).
conc([X|L1], L2, [X|L3]) :- conc(L1, L2, L3).
tail([X|Y], S) :- conc([], Y, S).
inv([X|Y], S) :- tail([X|Y], TAIL), inv(TAIL, R_TAIL), conc(R_TAIL, X, S).
inv([], []).
The first three lines work fine. But really don't understand what's going on with the last two, and therefore how to fix it.
I just wanna find the tail (this is working, when I just use the tail function) of the list, reverse it and then append it to the head of the list.
Could you help me guys?
Alternative foldl/4 based solution:
prepend_element(E, L, [E|L]).
inv(List, Reversed) :-
foldl(prepend_element, List, [], Reversed).
Your code doesn't work for me.
Try this way instead:
?- reverse([1,2,3],Xs), write(Xs).
reverse(Xs,Ys) :- reverse(Xs,[],Ys).
reverse([],A,A).
reverse([H|T],R,A) :- reverse(T,[H|R],A).

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