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!
To sum the elements in a list inductively, we add the first element to the sum of the remaining ones. In Prolog we say this: sum([H|T], S) :- sum(T,X), S is H + X. Another common pattern in Prolog is tail-recursion, which is popular because it's easy for the compiler to optimize.
remove_even([El|T], NewT):- El mod 2 =:= 0, remove_even(T, NewT). NewT is the list [E1|T] with even elements removed if E1 is even and NewT is T (the "tail list" of the original list) with the even elements removed from T .
= is for unification and =:= nd is are considered equals to in Prolog. It's as simple as that. Also, whether it's a rule or fact, it always starts so that the code will be: even(N):- mod(N,2) =:= 0.
In Prolog list elements are enclosed by brackets and separated by commas. Another way to represent a list is to use the head/tail notation [H|T]. Here the head of the list, H, is separated from the tail of the list, T, by a vertical bar.
Split your problem into smaller parts. As you already said, you have two different functionalities that should be combined:
even
)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.
Using clpfd and Prolog lambda 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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With