-1

Given some list of integers, I want to calculate the sum of every second element in the list using Prolog ?

E.g.:

[1,2,3,4] => [2+4] = 6
repeat
  • 19,449
  • 4
  • 51
  • 152

3 Answers3

3

There are several ways to accomplish this, but to give you an idea of a simple recursive solution using an accumulator to keep track of the current sum:

% Sets accumulator to 0 for convenience
sum_second(List,Result) :- sum_second(List,0,Result).

sum_second([],A,A).             % Empty list
sum_second([_],A,A).            % One element left in list
sum_second([_,H|T],A,R) :-      % Accumulate sum of second element
    A2 is A+H,
    sum_second(T,A2,R).

Example usage:

?- sum_second([1],R).
R = 0.
?- sum_second([1,2],R).
R = 2.
?- sum_second([1,2,3,4,5,6],R).
R = 12.
SND
  • 1,512
  • 2
  • 16
  • 29
  • 3
    `?- sum_second(Ls, _, _).` shows only a single solution. Certainly we expect a bit more from a relation? – mat Jun 12 '16 at 09:43
  • 3
    Why all the cuts? They aren't necessary and should be avoided unless there's a specific, intentional need for them. Also, you can provide a tidier, predicate to call this one: `sum_second(Ls, R) :- sum_second(Ls, 0, R).` so the caller doesn't have to provide the initial accumulator value. – lurker Jun 12 '16 at 10:15
  • 1
    What is the purpose of the operator " ! . " ? – user3153616 Jun 12 '16 at 10:21
  • I added the cuts to make sure termination occurred explicitly instead of leaving (redundant) choice point(s) which can be confusing for people who are just starting out in Prolog - I know it was for me. But I understand what you mean, I will remove them from the code as they are not required. As for a sum_second/2 for calling predicate, I wanted to write it, but thought it was trivial to this question. Anyways, I will amend my answer and make sure to in the future always include the 'calling' predicate for convenience. Thanks for the critique, it's always welcome. – SND Jun 12 '16 at 11:31
3

While the solution by @SeekAndDestroy is okay, I'd like to add:

  1. Use for declarative integer arithmetics.

    :- use_module(library(clpfd)).
    
  2. Employ first-argument indexing to avoid useless choicepoints.

    list_evens_odds([], [], []).
    list_evens_odds([X|Xs], [X|Es], Os) :-
       list_evens_odds(Xs, Os, Es).
    

That's it!

To sum up every other item (starting with the 2nd), query like so:

?- list_evens_odds([1,2,3,4], _, Zs), sum(Zs, #=, Sum).
Zs = [2,4], Sum = 6.
Community
  • 1
  • 1
repeat
  • 19,449
  • 4
  • 51
  • 152
1

I would use library(aggregate):

sum_every_nth1(L,I,S) :- aggregate(sum(X), P^(nth1(P,L,X), P mod I=:=0), S).

?- sum_every_nth1([1,2,3,4],2,S).
S = 6.

sum_every_nth1/3 is based on the 'relational' behaviour of nth1/3, that binds a position (P in the sample) in a list to an element. So, when called with P free, it binds it to successive indexes. The goal P mod I=:=0 then filters out the indexes not satisfying the requirement (cause failure of the conjunction - a comma means AND).

edit

library(aggregate) has a specific purpose, namely to supply missing aggregation operators available in SQL. To handle multiplication, I would suggest to separate the 'fetching' of elements from the actual operation performed on them:

every_nth1(L,I,Ns) :- findall(X, (nth1(P,L,X),P mod I=:=0), Ns).

mul_every_nth1(L,I,M) :-
    every_nth1(L,I,[N0|Ns]),
    foldl([N,M0,M1]>>(M1 is M0*N),Ns,N0,M).

every_nth1/3 get a list of selected elements, foldl/4 takes care to multiply them.

?- mul_every_nth1([1,2,3,4],2,M).
M = 8.

edit

if you're missing library(yall):

multiply(N,M0,M1) :- M1 is M0*N.

mul_every_nth1(L,I,M) :-
    every_nth1(L,I,[N0|Ns]),
    foldl(multiply,Ns,N0,M).
CapelliC
  • 57,813
  • 4
  • 41
  • 80