Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

prolog

clpfd

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!

like image 688
Robert Hung Avatar asked Apr 07 '16 07:04

Robert Hung


People also ask

How do you sum an element in a list in Prolog?

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.

How do you remove even numbers from a list in Prolog?

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 .

How do you check if a number is even in Prolog?

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

How do you split a list into head and tail in Prolog?

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.


2 Answers

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.
like image 200
Ulukai Avatar answered Sep 30 '22 13:09

Ulukai


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.
like image 26
repeat Avatar answered Sep 30 '22 13:09

repeat