# Perl Weekly Challenge 238: running sums and multiplications (and Python for the very first time!)

This post presents my solutions to the Perl Weekly Challenge 238.
I keep doing the Perl Weekly Challenge in order to mantain my coding skills in good shape, as well as in order to learn new things, with particular regard to Raku, a language that I love.
This week, I solved the following tasks:
The PL/Perl implementations are very similar to a pure Perl implementation, even if the PostgreSQL environment could involve some more constraints. Similarly, the PL/PgSQL implementations help me keeping my PostgreSQL programming skills in good shape.

# Raku Implementations

## PWC 238 - Task 1 - Raku Implementation

The first task was about computing the running sum of an array of integers, defined as the sum of the up-to-nth element.

``````sub MAIN( *@nums where { @nums.grep( * ~~ Int ).elems == @nums.elems } ) {
my @running-sum;
for 0 ..^ @nums.elems -> \$index {
@running-sum[ \$index ] = [+] @nums[ 0 .. \$index ];
}

@running-sum.join( ', ' ).say;
}

``````

## PWC 238 - Task 2 - Raku Implementation

The second task was about sorting an array of integers by the value, assuming the value has to be a single digit. If the current element was more than one digit, the digits have to be multiplied together unless a single digit element is found. Then, all the elements with the same number of multiplication steps have to be sorted together and an higher level than those with less steps.

``````sub MAIN( *@nums where { @nums.grep( { \$_ ~~ Int && \$_ > 0 } ).elems == @nums.elems } ) {
my %steps;
for @nums {
my \$step-counter = 0;
my \$value = \$_;
while ( \$value > 9 ) {
\$value = [*] \$value.comb;
\$step-counter++;
}

%steps{ \$step-counter }.push: \$_;
}

my @running-sort.push: | %steps{ \$_ }.sort for %steps.keys.sort;
@running-sort.join( ', ' ).say;

}

``````

The idea is to keep an hash, named `%steps` keyed by the number of required steps to reduce the value to a single digit, and then store the current value into the number of steps. Then, it is only a matter of sorting the hash by its keys and the corresponding array by their values.

# PL/Perl Implementations

## PWC 238 - Task 1 - PL/Perl Implementation

Same as the Raku implementation.

``````CREATE OR REPLACE FUNCTION
RETURNS SETOF int
AS \$CODE\$
my ( \$nums ) = @_;

for my \$index ( 0 .. \$nums->@* - 1 ) {
my \$sum = 0;

\$sum += \$_ for ( \$nums->@[ 0 .. \$index ] );
return_next( \$sum );
}

return undef;
\$CODE\$
LANGUAGE plperl;

``````

## PWC 238 - Task 2 - PL/Perl Implementation

Same idea as per the Raku implementation.

``````CREATE OR REPLACE FUNCTION
RETURNS SETOF int
AS \$CODE\$
my ( \$nums ) = @_;

my \$steps = {};

# utility function to reduce a number
# does only one pass so that I can counter
# how many passes are required
my \$reduce = sub {
my ( \$number ) = @_;
return \$number if ( \$number <= 9 );

my \$value = 1;
for my \$digit ( split( '', \$number ) ) {
\$value *= \$digit;
}

return \$value;
};

for ( \$nums->@* ) {
my \$step_counter = 0;
my \$value = \$_;

while ( \$value > 9 ) {
\$value = \$reduce->( \$value );
\$step_counter++;
}

push \$steps->{ \$step_counter }->@*, \$_;
}

for my \$key ( sort keys \$steps->%* ) {
return_next( \$_ ) for ( sort \$steps->{ \$key }->@* )
}

return undef;
\$CODE\$
LANGUAGE plperl;

``````

Here I use the `\$reduce` utility function to perform a single step of multiplication in the case the number has more than one digit. Performign a single step makes it possible to count how many times a reduction happened.

# PostgreSQL Implementations

## PWC 238 - Task 1 - PL/PgSQL Implementation

Here I use a window function to perform the sum.

``````CREATE OR REPLACE FUNCTION
RETURNS TABLE( v int, s int )
AS \$CODE\$

SELECT v, sum( v ) OVER ( ORDER BY v )
FROM unnest( nums ) v;
\$CODE\$
LANGUAGE sql;

``````

The `sum` function is both an aggregate and a window function: when used as a window function it provides the sum of the elements over a defined window, that in our case is just the window order by the value.

## PWC 238 - Task 2 - PL/PgSQL Implementation

Similar to the Raku approach, but I use a temporary table as an hash to store the values, the multiplications and the steps.

``````CREATE OR REPLACE FUNCTION
RETURNS SETOF int
AS \$CODE\$
DECLARE
current_value int;
digit text;
multiplication int;
step_counter int;
value_to_insert int;
BEGIN
CREATE TEMPORARY TABLE IF NOT EXISTS mul( v int, mul int, steps int DEFAULT 0 );
TRUNCATE mul;

FOREACH current_value IN ARRAY nums LOOP
IF current_value < 9 THEN
INSERT INTO mul( v, mul )
VALUES( current_value, current_value );
CONTINUE;
END IF;

-- if here the number is at least two digits long
step_counter := 0;
value_to_insert := current_value;
WHILE current_value > 9 LOOP
multiplication := 1;
step_counter := step_counter + 1;

FOREACH digit IN ARRAY regexp_split_to_array( current_value::text, '' ) LOOP
multiplication := multiplication * digit::int;
END LOOP;

current_value := multiplication;
END LOOP;

INSERT INTO mul( v, mul, steps )
VALUES( value_to_insert, multiplication, step_counter );

END LOOP;

RETURN QUERY SELECT v
FROM mul
ORDER BY steps ASC, v ASC;
END

\$CODE\$
LANGUAGE plpgsql;

``````

Note how verbose it does result this solution, when compared to PL/Perl and Raku ones. This again emphasizes how PL/PgSQL is probably not the best suited language for number mangling and non-set operations.

## PWC 238 - Task 2 - Another PL/PgSQL Implementation

Defining a function to reduce a single value, it is possible to quickly come up with a single query solution. First of all, the function to reduce a value to a single digit returning the number of steps is:

``````CREATE OR REPLACE FUNCTION pwc238.reduce( n int )
RETURNS int
AS \$CODE\$
DECLARE
current_value int;
step_counter  int;
digit text;
multiplication int;

BEGIN
current_value := n;
step_counter  := 0;

WHILE current_value > 9 LOOP
multiplication := 1;
step_counter   := step_counter + 1;

FOREACH digit IN ARRAY regexp_split_to_array( current_value::text, '' ) LOOP
multiplication := multiplication * digit::int;
END LOOP;

current_value := multiplication;
END LOOP;

RETURN step_counter;
END
\$CODE\$
LANGUAGE plpgsql;

``````

Then, the function to use a single query to get the result is as simple as ordering values by the result of the above function:

``````CREATE OR REPLACE FUNCTION
RETURNS SETOF INT
AS \$CODE\$

SELECT v
FROM unnest( nums ) v
ORDER BY pwc238.reduce( v ), v;

\$CODE\$
LANGUAGE sql
VOLATILE
;

``````

# Python Implementations

## PWC 238 - Task 1 - Python Implementation

Baby steps in my Python knowledge.

``````import sys

def main( argv ):
running_sum = []
current_index = 0
while current_index < len( argv ):
running_sum.insert( current_index, 0 )

for n in argv[ 0 : current_index   ]:
running_sum[ current_index ] += int( n )

current_index += 1

print( ", ".join( map( str, running_sum ) ) )

if __name__ == '__main__':
main( sys.argv[ 1: ] )

``````

The `main` function receives the expected array of numbers, and does the computation of the running sum as for the Raku and PL/Perl implementation. Note how horrible it is to having to `map` strings out of integers due to the need for `join` to use only strings.

## PWC 238 - Task 2 - Python Implementation

The second task was much more verbose than the other languages.

``````import sys
import collections

def reduce( n ):
if n <= 9:
return n

multiplication = 1
for digit in map( int, str( n ) ):
multiplication *= digit

return multiplication

def main( argv ):
steps = {}
for n in map( int, argv ):
current_step = 0
if n > 9:
# need reduction
current_value = n
while current_value > 9:
current_value = reduce( current_value )
current_step += 1

# if the key is not here, create a list
if not str( current_step )  in steps:
steps[ str( current_step ) ] = []

steps[ str(current_step) ].append( n )

# now traverse the dictionary and sort the array
# and print it
for k, v in collections.OrderedDict(sorted(steps.items())).items():
print( ", ".join( map( str, v ) ) )

if __name__ == '__main__':
main( sys.argv[ 1: ] )

``````

The `reduce` function does a single pass in reducing a more than one digit number. The `main` function does all the job. Please note how hard it is placing a list into a dictionary item, seems Python has nothing like autovifification.

The article Perl Weekly Challenge 238: running sums and multiplications (and Python for the very first time!) has been posted by Luca Ferrari on October 9, 2023