Perl Weekly Challenge 270: no passion this week!

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

The first task was about computing the positions (indexes) of 1 in a matrix, assuming a good one must appear only on a row and column.

sub MAIN() {
    my @matrix =  [1, 0, 0],
                  [0, 0, 1],
                  [1, 0, 0],
    ;

    my @ones;
    for 0 ..^ @matrix.elems -> $row {
		@ones.push: @matrix[ $row ].grep( { $_ == 1 }, :k ).map( { $row, $_, '%02d-%02d'.sprintf( $row, $_ )  } ).flat;
    }


    for @ones -> $current_one {
		$current_one[ 0, 1 ].join( ',' ).say
		    if @ones.grep( {  $current_one[ 2 ] ne $_[ 2 ]
			       && ( $_[ 1 ] == $current_one[ 1 ] || $_[ 0 ] == $current_one[ 0 ] ) } ).elems == 0
    }

}



The idea is to compute all the indexes of 1 found in the matrix, stored into @ones with the column, the row and an identifier. Then I process all the results, ensuring there is not another element with the same identifier in the matrix, in such case the position indicates a good one to display.

PWC 270 - Task 2 - Raku Implementation

The second task was about computing the score to modify an array of given integers so that all the integers become equal to the max value within the array. At each step, I can choose to add 1 unit to a single element or a couple, and depending on the operation I have two different input costs. It is not clear to me if I have to compute the minimum score, I haven’t implemented this logic.

sub MAIN( Int $single,
	  Int $double,
	  *@nums is copy where { @nums.elems == @nums.grep( * ~~ Int ).elems } ) {

    my $current_max = @nums.max;
    my @need_operation = @nums.grep( * < $current_max, :k );
    my $score = 0;

    while ( @need_operation ) {

		if @need_operation.elems == 1 {
		    # single operation
		    @nums[ @need_operation[ 0 ] ] += 1;
		    $score += $single;
		}
		elsif @need_operation.elems > 1 {
		    @nums[ @need_operation[ 0 ] ] += 1;
		    @nums[ @need_operation[ 1 ] ] += 1;
		    $score += $double;
		}

		@need_operation = @nums.grep( * < $current_max, :k );
    }

    $score.say;

}



My implementation is quite simple: I compute the maximum value in the array, and then extract all the elements that are not at the same value. Then, while I’ve more than one element, I apply the double operation, and as last, a single operation. In the case there is to compute the minimum score, not implemented in the above nor in the following solutions, the two costs have to be compared and it is required to decide which operatin costs less to make two changes.

PL/Perl Implementations

PWC 270 - Task 1 - PL/Perl Implementation

Same approach as in Raku, returning a table with the found positions as c column and r row.

CREATE OR REPLACE FUNCTION
pwc270.task1_plperl( int[][] )
RETURNS TABLE( r int, c int )
AS $CODE$

   my ( $matrix ) = @_;
   my @pnes;

   for my $row ( 0 .. $matrix->@* -1 ) {
       for my $column ( 0 .. $matrix->@[ $row ]->@* -1 ) {
       	   next if ( $matrix->@[ $row ]->@[ $column ] != 1 );
	   push @ones, [ $row, $column, sprintf( '%02d-%02d', $row, $column ) ];
       }
   }

   for my $position ( @ones ) {
       if ( grep( { $position->[ 2 ] != $_->[ 2] && ( $position->[ 0 ] == $_->[ 0 ] || $position->[ 1 ] == $_->[ 1 ] ) } @ones ) == 0 ) {
       	  return_next( { r => $position->@[ 0 ], c => $position->@[ 1 ] } );
       }
   }


   return undef;
$CODE$
LANGUAGE plperl;



PWC 270 - Task 2 - PL/Perl Implementation

Same approach as in Raku, a little more verbose because I implement the inner function max.

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

   my ( $single, $double, $nums ) = @_;
   my $score = 0;

   my $max = sub {
      my ( $nums ) = @_;
      my $value = undef;

      for ( $nums->@* ) {
      	  $value = $_ if ( ! $value || $value < $_ );
      }

      return $value;
   };

   my $current_max = $max->( $nums );
   my @need_operation;


   for ( 0 .. $nums->@* - 1 ) {
       push @need_operation, $_ if ( $nums->@[ $_ ] < $current_max );
   }

   while ( @need_operation > 0 ) {
      if ( @need_operation > 1 ) {
         $nums->[ $need_operation[ 0 ] ]++;
         $nums->[ $need_operation[ 1 ] ]++;
         $score += $double;
      }
      elsif ( @need_operation == 1 ) {
         $nums->[ $need_operation[ 0 ] ]++;
         $score += $single;
      }

      @need_operation = ();
      for ( 0 .. $nums->@* - 1 ) {
          push @need_operation, $_ if ( $nums->@[ $_ ] < $current_max );
      }
   }

   return $score;

$CODE$
LANGUAGE plperl;



PostgreSQL Implementations

PWC 270 - Task 1 - PL/PgSQL Implementation

I cheated, I use PL/Perl here.

CREATE OR REPLACE FUNCTION
pwc270.task1_plpgsql( matrix int[][] )
RETURNS SETOF int[]
AS $CODE$
   SELECT pwc270.task1_plperl( matrix );
$CODE$
LANGUAGE sql;



PWC 270 - Task 2 - PL/PgSQL Implementation

Again, this week I was not really inspired to do the solutions in SQL, so I call PL/Perl.

CREATE OR REPLACE FUNCTION
pwc270.task2_plpgsql( s int, d int, nums int[] )
RETURNS int
AS $CODE$
   SELECT pwc270.task2_plperl( s, d, nums );
$CODE$
LANGUAGE sql;



Java Implementations

PWC 270 - Task 1 - PostgreSQL PL/Java Implementation

The approach is similar to the Raku one, but much more verbose because I was running out fantasy!

public class Task1 {

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

    @Function( schema = "pwc270",
	       onNullInput = RETURNS_NULL,
	       effects = IMMUTABLE )
    public static final String[] task1_pljava( int row_size, int[] matrix ) throws SQLException {
		logger.log( Level.INFO, "Entering pwc270.task1_pljava" );

		List<Integer> rows = new LinkedList<Integer>();
		List<Integer> cols = new LinkedList<Integer>();
		List<String> result = new LinkedList<String>();

		// seek for ones
		for( int row = 0; row < ( matrix.length / row_size ) ; row++ )
		    for ( int col = 0; col < row_size; col++ )
			if ( matrix[ row * row_size  + col ] ==  1 ) {
			    rows.add( row  );
			    cols.add( col );
			}

		for ( int i = 0; i < rows.size(); i++ ) {
		    int current_row = rows.get( i );
		    int current_col = rows.get( i );
		    boolean ok      = true;

		    for ( int r = 0; r < rows.size(); r++ )
				if ( r == i )
				    continue;
				else if ( rows.get( r ) == current_row )
				    ok = false;

		    for ( int c = 0; c < cols.size(); c++ )
				if ( c == i )
				    continue;
				else if ( cols.get( c ) == current_col )
				    ok = false;


		    if ( ok )
			result.add( String.format( "Row %d Col %d", current_row, current_col ) );
		}

		String[] to_return = new String[ result.size() ];
		int i = 0;
		for ( String s : result )
		    to_return[ i++ ] = s;

		return to_return;

    }
}



I use two different lists, of the same size, to contain the rows and the columns of the 1 elements found. Then I iterate over the found elements to ensure there are no other ones on the same row or row. Last, I return a string with the specified position of the element. The need to return a String[] is simply to avoid to implement a ResultSetProvider.

PWC 270 - Task 2 - PostgreSQL PL/Java Implementation



public class Task2 {

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

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

		int score = 0;
		List<Integer> nums = Arrays.stream( numbers ).boxed().collect( Collectors.toList() );

		// get the max
		Integer max = nums.stream().max( Integer::compare ).get();

		List<Integer> needs_operation = new LinkedList<Integer>();
		do {
		    needs_operation.clear();
		    IntStream
			.range( 0, nums.size() - 1 )
			.forEach( index -> {
				if ( nums.get( index ) < max )
				    needs_operation.add( index );
			    } );


		    if ( needs_operation.size() == 1 ) {
				score += score_single;
				int index = needs_operation.get( 0 );
				int value = nums.get( index );
				nums.set( index, ++value );
		    }
		    else if ( needs_operation.size() >= 2 ) {
				score += score_double;

				for ( int i = 0; i < 2; i ++ ) {
				    int index = needs_operation.get( i );
				    int value = nums.get( index );
				    nums.set( index, ++value );
				}
		    }

		} while ( ! needs_operation.isEmpty() );

		return score;

    }
}



The idea is always the same as in the other approaches, but this time I use the stream API to get the indexes of the elements that require to be incremented. Then I perform the operation and compute again which elements are still not at the maximum level.

Python Implementations

PWC 270 - Task 1 - Python Implementation

The idea is always the same.

import sys

# task implementation
# the return value will be printed
def task_1( args ):
    row_size = int( args[ 0 ] )
    matrix   = []

    # build the matrix
    current_row = 0
    matrix.append( [] )
    for x in args[ 1: ]:

        if len( matrix[ current_row ] ) >= row_size:
            current_row += 1
            matrix.append( [] )

        matrix[ current_row ].append( int( x ) )


    ones = []
    for r in range( 0, len( matrix ) ):
        for c in range( 0, len( matrix[ r ] ) ):
            if matrix[ r ][ c ] == 1:
                ones.append( [ r, c ] )

    for current in ones:
        found = list( filter( lambda x: x[ 0 ] == current[ 0 ] or x[ 1 ] == current[ 1 ], ones ) )
        if len( found ) == 1:
            print( current )


    return ""

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



The only difference with the other implementations is that I check that the list of elements found with a 1 on a given row/column must be of size ‘1` to indicate the only element itself.

PWC 270 - Task 2 - Python Implementation

Same implementation as in the other cases.

import sys

# task implementation
# the return value will be printed
def task_2( args ):
    single = int( args[ 0 ] )
    double = int( args[ 1 ] )
    nums   = list( map( int, args[ 2: ] ) )

    # compute the current max
    max = None
    for v in nums:
        if not max or v > max:
            max = v

    need_operation = list( filter( lambda x: nums[ x ] < max, range( 0, len( nums ) ) ) )
    score = 0
    while len( need_operation ) > 0:
        if len( need_operation ) > 1 :
            score += double
            nums[ need_operation[ 0 ] ] += 1
            nums[ need_operation[ 1 ] ] += 1
            need_operation = need_operation[ 2: ]
        elif len( need_operation ) == 1:
            score += single
            nums[ need_operation[ 0 ] ] += 1
            need_operation = need_operation[ 2: ]


    return score



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



The article Perl Weekly Challenge 270: no passion this week! has been posted by Luca Ferrari on May 20, 2024