Perl Weekly Challenge 268: arrays and slices

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

The first task was to find the magic number between two equally sized arrays of integers. The magic n umber is the difference between two elements of the arrays so that from the first array you can obtain the second one, even if in a different order. Essentially, it means that the sorted arrays have values with a consistent difference.

sub MAIN(  :@left,  :@right where { @left.elems == @right.elems }  ) {
    my @sorted-left  = @left.sort;
    my @sorted-right = @right.sort;

    my @diffs;
    for 0 ..^ @sorted-left.elems -> $i {
		my $current = @sorted-left[ $i ] - @sorted-right[ $i ];
		@diffs.push: $current if ! @diffs.grep( $current );
    }

    @diffs[ 0 ].say and exit  if @diffs.elems == 1;
    'Impossible'.say;
}



I first sort the arrays, then I iterate and compute the $current difference between sorted cells, and add it the list of @diffs if not already inserted. In the end, if the list of @diffs has only one element, that is the magic number, otherwise the magic number does not exist.

PWC 268 - Task 2 - Raku Implementation

The second task was about sorting a given input array of integers by sorting its two elements slices.

sub MAIN( *@nums where { @nums.elems == @nums.grep( * ~~ Int ).elems  && @nums.elems %% 2 }  ) {
    my @sorted;
    for @nums.sort.rotor( 2 ) -> @couple {
		@sorted.push: @couple.reverse.flat;
    }

    @sorted.join( " " ).say;
}



I use the rotor that automatically splits the array in sub elements of two parts, and sort the subarray in the way required, then pushing as a flat list into the output result @sorted.

PL/Perl Implementations

PWC 268 - Task 1 - PL/Perl Implementation

The same implementation as in Raku.

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

   my ( $left, $right ) = @_;

   die 'Array must have the same length!' if ( $left->@* != $right->@* );

   my @diffs;
   for my $index ( 0 .. $left->@* - 1 ) {
       my $d = $left->@[ $index ] - $right->@[ $index ];
       push @diffs, $d if ( ! grep( { $_ == $d } @diffs ) );
   }

   return $diffs[ 0 ] if ( @diffs == 1 );
   return undef;

$CODE$
LANGUAGE plperl;



PWC 268 - Task 2 - PL/Perl Implementation

Same implementtion as in Raku, but since I don’t have the rotor, I extract the couple of elements and sort them manually.

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

   my ( $nums ) = @_;

   $nums = [ sort $nums->@* ];
   my @sorted;
   for my $index ( 0 .. $nums->@* - 1 ) {
       next if $index % 2 != 0;
       push @sorted, reverse sort( $nums->@[ $index ], $nums->@[ $index + 1 ] );
   }

   return [ @sorted ]	;

$CODE$
LANGUAGE plperl;



PostgreSQL Implementations

PWC 268 - Task 1 - PL/PgSQL Implementation

A single query, with a CTE, can solve the problem.

CREATE OR REPLACE FUNCTION
pwc268.task1_plpgsql( l int[], r int[] )
RETURNS int
AS $CODE$

   WITH sorted_left AS ( SELECT v, row_number()  OVER ( order by 1 ) as r  FROM unnest( l ) v  )
   , sorted_right AS ( SELECT v, row_number()  OVER ( order by 1 ) as r  FROM unnest( r ) v  )
   , diffs AS (
    SELECT l.v - r.v as d
    FROM sorted_left l, sorted_right r
    WHERE l.r = r.r
   )
   SELECT d
   FROM diffs d
   GROUP BY d
   HAVING count(*) = ( SELECT array_length( l, 1 ) );


$CODE$
LANGUAGE sql;



I use the row_number() function to get the position of every sorted element, so that I can compute the differences between elements with the same index (i.e., position), and then I select only the difference that appears the exact number as the length of the array.

PWC 268 - Task 2 - PL/PgSQL Implementation

Here I cheated, I call the PL/Perl implementation.

CREATE OR REPLACE FUNCTION
pwc268.task2_plpgsql( nums int[] )
RETURNS SETOF int
AS $CODE$
   SELECT pwc268.task2_plperl( nums );
$CODE$
LANGUAGE sql;



Java Implementations

PWC 268 - Task 1 - PostgreSQL PL/Java Implementation

The idea is the same as in PL/Perl approach: I sort the lists of integers and compute all the differences, returning the found value if there is only one.

public class Task1 {

    private final static Logger logger = Logger.getAnonymousLogger();

    @Function( schema = "pwc268",
	       onNullInput = RETURNS_NULL,
	       effects = IMMUTABLE )
    public static final Integer task1_pljava( int[] left, int[] right ) throws SQLException {
		logger.log( Level.INFO, "Entering pwc268.task1_pljava" );

		List<Integer> l = new LinkedList<Integer>();
		List<Integer> r = new LinkedList<Integer>();

		for ( int i : left )
		    l.add( i );
		for ( int i : right )
		    r.add( i );

		Collections.sort( l );
		Collections.sort( r );
		List<Integer> diffs = new LinkedList<Integer>();

		for ( int i = 0; i < l.size(); i++ ) {
		    int current = l.get( i ) - r.get( i );
		    if ( ! diffs.contains( current ) )
				diffs.add( current );
		}

		if ( diffs.size() == 1 )
		    return diffs.get( 0 );
		else
		    return null;


    }
}



PWC 268 - Task 2 - PostgreSQL PL/Java Implementation

Same approach as in PL/Perl: extract the single couple of elements of every slice and manually sort them by means of the ternary operator.

public class Task2 {

    private final static Logger logger = Logger.getAnonymousLogger();

    @Function( schema = "pwc268",
	       onNullInput = RETURNS_NULL,
	       effects = IMMUTABLE )
    public static final int[] task2_pljava( int[] nums ) throws SQLException {
		logger.log( Level.INFO, "Entering pwc268.task2_pljava" );

		List<Integer> sorted = new LinkedList<Integer>();
		for ( int i : nums )
		    sorted.add( i );
		Collections.sort( nums );

		int[] result = new int[ nums.length ];
		int index = 0;

		for ( int i = 0; i < sorted.size(); i++ ) {
		    int left = sorted.get( i );
		    int right = sorted.get( ++i );

		    result[ index++ ]   = ( left > right ? left : right );
		    result[ index++ ] = ( left > right ? right : left );
		}

		return result;
    }
}



Python Implementations

PWC 268 - Task 1 - Python Implementation

Same approach as in PL/Perl, but since there is no common way to pass two different arrays on the command line, I use the | as a separator.

def task_1( args ):
    left = []
    right = []

    left  = list( map( int, args[ 0 : args.index( "|" ) ] ) )
    right = list( map( int, args[ args.index( "|" ) + 1 : ] ) )

    left.sort()
    right.sort()

    diffs = []
    for index in range( 0, len( left ) ):
        d = left[ index ] - right[ index ]
        if not d in diffs:
            diffs.append( d )

    if len( diffs ) == 1:
        return diffs[ 0 ]
    else:
        return None



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



PWC 268 - Task 2 - Python Implementation

Same approach already implemented in PL/Java.

def task_2( args ):
    sorted = list( map( int, args ) )
    sorted.sort()

    result = []

    for i in range( 0, len( sorted ) ):
        if i % 2 != 0:
            continue

        left  = sorted[ i ]
        right = sorted[ i + 1 ]

        result.append( left if left > right else right )
        result.append( left if right > left else right )

    return result


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



The article Perl Weekly Challenge 268: arrays and slices has been posted by Luca Ferrari on May 8, 2024