Creating a predicate in Prolog that sums the squares of only the even numbers in a list
Asked Answered
H

4

6

I'm trying to figure out how to create a predicate in prolog that sums the squares of only the even numbers in a given list.

Expected output:

?- sumsq_even([1,3,5,2,-4,6,8,-7], Sum).

Sum = 120 ;

false.

What I know how to do is to remove all the odd numbers from a list:

sumsq_even([], []).
sumsq_even([Head | Tail], Sum) :-
    not(0 is Head mod 2),
    !,
    sumsq_even(Tail, Sum).
sumsq_even([Head | Tail], [Head | Sum]) :-  
    sumsq_even(Tail, Sum).

Which gives me:

Sum = [2, -4, 6, 8]

And I also know how to sum all the squares of the numbers in a list:

sumsq_even([], 0)
sumsq_even([Head | Tail], Sum) :-
    sumsq_even(Tail, Tail_Sum),
    Sum is Head * Head + Tail_Sum.

But I can't seem to figure out how to connect these two together. I'm thinking I may have gone the wrong way about it but I'm not sure how to define proper relationships to get it to make sense.

Thanks!

Headdress answered 7/4, 2016 at 7:24 Comment(1)
Ideally, your Prolog programs are pure relations. This means that they should be usable in all directions, also for example in the most general case. For instance, we would also like to obtain answers for ?- sumsq_even(Ls, Sum).. Check out the answers provided by @repeat and @tas for this generality.Vietcong
W
3

Split your problem into smaller parts. As you already said, you have two different functionalities that should be combined:

  • remove odd numbers from a list (even)
  • sum all the squares of the numbers in a list (sumsq)

So, in the first place, use different predicate names for different functionalities:

even([], []).
even([Head | Tail], Sum) :-
    not(0 is Head mod 2),
    !,
    even(Tail, Sum).
even([Head | Tail], [Head | Sum]) :-  
    even(Tail, Sum).

sumsq([], 0).
sumsq([Head | Tail], Sum) :-
    sumsq(Tail, Tail_Sum),
    Sum is Head * Head + Tail_Sum.

In a third predicate you can now combine the two subsequent smaller steps:

sumsq_even(List, Sum) :-
    even(List, Even_List),
    sumsq(Even_List, Sum).

In this rule, first the (input) list is reduced to even elements (Even_List) and after that the sum of the squares are calculated.

This is the result for your example:

sumsq_even([1,3,5,2,-4,6,8,-7], Sum).
S = 120.
Welt answered 7/4, 2016 at 10:7 Comment(0)
N
2

You can actually do both tasks (filtering the even number and summing them up) at once:

:- use_module(library(clpfd)).

nums_evensumsq([],0).
nums_evensumsq([X|Xs],S0) :-
    X mod 2 #= 0,
    nums_evensumsq(Xs,S1),
    S0 #= S1 + X * X.
nums_evensumsq([X|Xs],S) :-
    X mod 2 #= 1,
    nums_evensumsq(Xs,S).

Querying the predicate gives the desired result:

   ?- nums_evensumsq([1,3,5,2,-4,6,8,-7],S).
S = 120 ? ;
no

You can write it even shorter using if_/3 as defined here:

nums_evensumsq([],0).
nums_evensumsq([X|Xs],S0) :-
    nums_evensumsq(Xs,S1),
    Y #= X mod 2,
    if_(Y = 0, S0 #= S1 + X * X, S0 #= S1).

Note that the comparison in the first argument of if_/3 is done with =/3 as defined here.

Noun answered 7/4, 2016 at 14:10 Comment(2)
A detail is actually a bit off... What about summing up squares?Syncopation
@repeat: How embarrassing. It was right in front of my eyes. Thanks for the hint, I corrected my code.Noun
S
2

Using and Prolog write:

:- use_module(library(clpfd)).
:- use_module(library(lambda)).

zs_sumevensq(Zs, S) :-
   maplist(\Z^X^(X #= Z*Z*(1-(Z mod 2))), Zs, Es),
   sum(Es, #=, S).

Sample query as given by the OP:

?- zs_sumevensq([1,3,5,2,-4,6,8,-7], S).
S = 120.
Syncopation answered 6/5, 2016 at 9:6 Comment(0)
R
0

Once you mastered the basics, you could be interested to learn about builtins. Library aggregate, provides a simple way to handle lists, using member/2 as list elements 'accessor':

sumsq_even(Ints, Sum) :-
  aggregate(sum(C), I^(member(I, Ints), (I mod 2 =:= 0 -> C is I*I ; C = 0)), Sum).
Rodenticide answered 7/4, 2016 at 17:22 Comment(1)
There's no need to use if-then-else here. C = 0 doesn't contribute anything to the sum!Syncopation

© 2022 - 2024 — McMap. All rights reserved.