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

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

Expected Result:

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

What I know how to remove all 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). 

What gives me:

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

And I also know how to sum all the squares of 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 figure out how to put the two together. I think I may have gone wrong, but I'm not sure how to determine the right relationship to make sense.

Thanks!

+5
source share
4 answers

Divide your problem into smaller parts. As you said, you have two different functions that should be combined:

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

So, firstly, use different predicate names for different functions:

 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 the third predicate, you can now combine the next two small steps:

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

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

This is the result for your example:

 sumsq_even([1,3,5,2,-4,6,8,-7], Sum). S = 120. 
+2
source

You can perform both tasks (filtering an even number and summing them):

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

A predicate query 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 indicated 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 if_ / 3 argument is done with = / 3, as defined here .

+2
source

Using and write:

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

An example of the query specified by OP:

 ?- zs_sumevensq([1,3,5,2,-4,6,8,-7], S). S = 120. 
+2
source

Once you have mastered the basics, you may be interested in learning about the built-in. The aggregate library provides an easy way to process lists using the / 2 element as an access element to the list of elements:

 sumsq_even(Ints, Sum) :- aggregate(sum(C), I^(member(I, Ints), (I mod 2 =:= 0 -> C is I*I ; C = 0)), Sum). 
0
source

Source: https://habr.com/ru/post/1246606/


All Articles