Perl Weekly Challenge 243: map and grep to the rescue!

This post presents my solutions to the Perl Weekly Challenge 243.
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 243 - Task 1 - Raku Implementation

The first task was about finding reverse pairs in an array of integers. A reverse pair is a couple of integeres in an array where the leftmost one is greater than the double value of the rightmost one. This can be solved in an easy way by means of a nested loop, but here grep can help solving the problem in a more elegant way.

sub MAIN( Bool :$verbose = True,
			   *@nums where { @nums.elems == @nums.grep( { $_ ~~ Int } ).elems } ) {


    # A reverse pair is a pair (i, j) where: a) 0 <= i < j < nums.length and b) nums[i] > 2 * nums[j].
    my @reverse_pairs;
    for 0 ..^ @nums.elems -> $i {
		@reverse_pairs.push: [ @nums[ $i ], $_ ] for  @nums[ $i + 1 .. * ].grep( { @nums[ $i ] > 2 * $_ } );
    }

    @reverse_pairs.elems.say;
    @reverse_pairs.join( "\n" ).say if $verbose;
}


I iterate on every element of the array using a positional index c$i, then I slice the array as @nums[ $i + 1 .. * ] and grep it for elements smaller than the half of the current value, and push a couple of values into the @reverse_pairs array. Last, I count the number of elements in the array and all it’s done!

PWC 243 - Task 2 - Raku Implementation

This task is about computing the sum of the division of every element of the array for all the remaining elements, including itself.

sub MAIN( *@nums where { @nums.elems == @nums.grep( * ~~ Int ).elems && @nums.elems >= 1 } ) {
    my $sum = 0;

    for @nums -> $current {
		$sum += [+] ( @nums.map( { ( $current / $_ ).Int }  ) );
    }

    $sum.say;
}



I iterate on every element of the array, storing it as $current, and then map the array into the value of the integer division, reducing it by means of the [+] operator.

PL/Perl Implementations

PWC 243 - Task 1 - PL/Perl Implementation

Same implementation as Raku: I push every element grepped out the list of rightmost elements that satisfay the condition, and return the size of the found array.

CREATE OR REPLACE FUNCTION
pwc243.task1_plperl( int[] )
RETURNS int
AS $CODE$

   my ( $nums ) = @_;
   my @pairs;

   for my $i ( 0 .. $nums->@* ) {
       for ( grep( { $nums->[ $i ] > 2 * $_  } $nums->@[ ( $i + 1 ) .. $nums->@* ] ) ) {
           push @pairs, [ $nums->[ $i ], $_ ] if $_;
       }
   }

   return scalar @pairs;
$CODE$
LANGUAGE plperl;



PWC 243 - Task 2 - PL/Perl Implementation

Again, I map the array with the division of the current value with the current array element. In Perl there is not a builtin reduce operator, and instead of loading a module for the sake of summing, I simply loop over all the mapped elements and sum them together.

CREATE OR REPLACE FUNCTION
pwc243.task2_plperl( int[])
RETURNS int
AS $CODE$

   my ( $nums ) = @_;
   my $sum = 0;

   for my $current ( $nums->@* ) {
       for my $value ( map { int( $current / $_ ) } $nums->@* ) {
       	   $sum += $value;
       }
   }

   return $sum;
$CODE$
LANGUAGE plperl;



PostgreSQL Implementations

PWC 243 - Task 1 - PL/PgSQL Implementation

A nested loop to solve the problem. It could have been solved with a query and count, but it is simple enough to be implemented in an imperative way.

CREATE OR REPLACE FUNCTION
pwc243.task1_plpgsql( nums int[] )
RETURNS int
AS $CODE$
DECLARE
	c int := 0;
BEGIN
	FOR i IN 1 .. array_length( nums, 1 ) LOOP
	    FOR j IN i + 1 .. array_length( nums, 1 ) LOOP
	    	IF nums[ i ] > nums[ j ] * 2 THEN
				c := c + 1;
			END IF;
	    END LOOP;
	END LOOP;

	RETURN c;
END
$CODE$
LANGUAGE plpgsql;



PWC 243 - Task 2 - PL/PgSQL Implementation

Again, a nested loop to compute the sum, similarly to the PL/Perl implementation.

CREATE OR REPLACE FUNCTION
pwc243.task2_plpgsql( nums int[] )
RETURNS int
AS $CODE$
DECLARE
	s int := 0;
	current int;
BEGIN
	FOR i in 1 .. array_length( nums, 1 ) LOOP

	    FOREACH current IN ARRAY nums LOOP
	    	    s := s + ( nums[ i ] / current )::int;
	    END LOOP;
	END LOOP;

	RETURN s;
END
$CODE$
LANGUAGE plpgsql;



Python Implementations

PWC 243 - Task 1 - Python Implementation

A nested loop to solve the problem.

import sys

# task implementation
def main( argv ):
    nums = list( map( int, argv ) )
    counter = 0

    for i in range( 0, len( nums ) ):
        for j in range( i + 1, len( nums ) ):
            if nums[ i ] > 2 * nums[ j ]:
                counter = counter + 1;

    print( counter )


# invoke the main without the command itself
if __name__ == '__main__':
    main( sys.argv[ 1: ] )



PWC 243 - Task 2 - Python Implementation

To be more Perl-ish I use a double lambda expression to map and reduce the array on which I need to compute the current sum.

import sys
from functools import reduce

# task implementation
def main( argv ):
    nums = list( map( int, argv ) )
    sum = 0


    for current in nums:
        sum = sum + reduce( lambda a,b: a + b, list( map( lambda x: int( current /x ), nums ) ) )

    print( sum )

# invoke the main without the command itself
if __name__ == '__main__':
    main( sys.argv[ 1: ] )



The article Perl Weekly Challenge 243: map and grep to the rescue! has been posted by Luca Ferrari on November 13, 2023