Perl Weekly Challenge 240: case folding and the jumping array
This post presents my solutions to the Perl Weekly Challenge 240.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:
- PWC 240 - Task 1 - Raku
- PWC 240 - Task 2 - Raku
- PWC 240 - Task 1 in PostgreSQL PL/Perl
- PWC 240 - Task 2 in PostgreSQL PL/Perl
- PWC 240 - Task 1 in PostgreSQL PL/PgSQL
- PWC 240 - Task 2 in PostgreSQL PL/PgSQL
- PWC 240 - Task 1 in Python
- PWC 240 - Task 2 in Python
Raku Implementations
PWC 240 - Task 1 - Raku Implementation
The first task was about finding out if a given string is an acronym of a list of strings, that means if the first letter of every string is in the array of letters of the given acronyms, without any regard to the case, and in the right order. A first approach can use a loop:sub MAIN( $check-string, *@strings ) {
# the check string must contain the same
# exact number of chars of the number of strings
'False'.say and exit if ( $check-string.comb.elems != @strings.elems );
# check all the letters
my @letters = $check-string.comb;
for 0 ..^ @strings.elems {
'False'.say and exit if ( @letters[ $_ ].fc ne ( @strings[ $_ ].comb )[ 0 ].fc );
}
'True'.say;
}
The idea is to check immediatly if the number of letters match the number of strings, otherwise there is no point in doing a deep comparison. Clearly, the comparison has to be done using the
fc
(fold case) operator in order to properly do a case-insensitive comparison.
Then, there could be a shorter implementation:
sub MAIN( $check-string, *@strings ) {
# the check string must contain the same
# exact number of chars of the number of strings
'False'.say and exit if ( $check-string.comb.elems != @strings.elems );
my @check-letters = $check-string.comb.map( *.fc );
my @letters = @strings.map( ( *.comb )[ 0 ].fc );
'True'.say and exit if ( @letters ~~ @check-letters );
'False'.say;
}
This exploits the smart matching operator that does the right thing when testing arrays.
PWC 240 - Task 2 - Raku Implementation
This task was about producing an array out of aginve input array, jumping to cells that are the result of a given array cell.sub MAIN( *@numbers where { @numbers.grep( * ~~ Int ).elems == @numbers.elems } ) {
my @new;
@new[ $_ ] = @numbers[ @numbers[ $_ ] ] for 0 ..^ @numbers.elems;
@new.join( ', ' ).say;
}
PL/Perl Implementations
PWC 240 - Task 1 - PL/Perl Implementation
Pretty much the same implementation of Raku.CREATE OR REPLACE FUNCTION
pwc240.task1_plperl( text, text[] )
RETURNS boolean
AS $CODE$
my ( $check, $strings ) = @_;
my @check_letters = map { $_ } split( //, $check );
my @letters = map { ( split //, $_ )[ 0 ] } $strings->@*;
return 0 if ( @check_letters != @letters );
for ( 0 .. @check_letters ) {
return 0 if ( CORE::fc( $check_letters[ $_ ] ) ne CORE::fc( $letters[ $_ ] ) );
}
return 1;
$CODE$
LANGUAGE plperl;
Note that I used the
CORE::fc
in order to avoid using an explicit use
that would require to turn the function into untrusted language.
PWC 240 - Task 2 - PL/Perl Implementation
Really simple to implement in PL/Perl, thanks to thereturn_next
function that will append a new result to the output:
CREATE OR REPLACE FUNCTION
pwc240.task2_plperl( int[] )
RETURNS SETOF int
AS $CODE$
my ( $numbers ) = @_;
for ( 0 .. $numbers->@* ) {
return_next( $numbers->[ $numbers->[ $_ ] ] );
}
return undef;
$CODE$
LANGUAGE plperl;
PostgreSQL Implementations
PWC 240 - Task 1 - PL/PgSQL Implementation
I used a nested loop and the internal functionregexp_split_to_array
to get the letters out of a string:
CREATE OR REPLACE FUNCTION
pwc240.task1_plpgsql( c text, s text[] )
RETURNS boolean
AS $CODE$
DECLARE
check_letters text[];
current_index int;
current_letter text;
BEGIN
check_letters := regexp_split_to_array( c, '' );
IF array_length( check_letters, 1 ) <> array_length( s, 1 ) THEN
RETURN FALSE;
END IF;
FOR current_index IN 1 .. array_length( check_letters, 1 ) LOOP
current_letter := ( regexp_split_to_array( s[ current_index ], '' ) )[ 1 ];
IF lower( current_letter ) <> lower( check_letters[ current_index ] ) THEN
RETURN FALSE;
END IF;
END LOOP;
RETURN TRUE;
END
$CODE$
Please consider that in SQL the numbering starts from
1
!
PWC 240 - Task 2 - PL/PgSQL Implementation
Similar to the PL/Perl implementation:CREATE OR REPLACE FUNCTION
pwc240.task2_plpgsql( n int[] )
RETURNS SETOF INT
AS $CODE$
DECLARE
current_index int;
position int;
BEGIN
FOR current_index IN 1 .. array_length( n, 1 ) LOOP
position := n[ current_index ];
IF position = 0 THEN
position := 1;
END IF;
RETURN NEXT n[ position ];
END LOOP;
RETURN;
END
$CODE$
LANGUAGE plpgsql;
I need to use an intermediate variable
position
because, since in SQL numbering starts from 1
, chances are that the index position is a zero and need to be converted appropriately.
Python Implementations
PWC 240 - Task 1 - Python Implementation
Same implementation as above, the function prints and returns the corresponding value.import sys
# task implementation
def main( argv ):
letters = list( map( lambda x: x[ 0 ].casefold(), argv[1:] ) )
check_chars = list( argv[ 0 ] )
if len( check_chars ) != len( letters ):
print( 'False' )
return False
for index in range( len( argv[ 0 ] ) ):
if argv[ 0 ][ index ].casefold() != letters[ index ]:
print( 'False' )
return False
print( 'True' )
return True
# invoke the main without the command itself
if __name__ == '__main__':
main( sys.argv[ 1: ] )
I use the function
casefold
to get the case folding, and a lambda expression to work with map
in a Perl-ish way and get out the first char of every string.
PWC 240 - Task 2 - Python Implementation
In a similar way to the previous implementations, this one creates an array and fills with the jumps into the old one:import sys
# task implementation
def main( argv ):
new_array = []
for index in range( len( argv ) ):
new_array.insert( index, argv[ int( argv[ index ] ) ] )
print( ','.join( new_array ) )
# invoke the main without the command itself
if __name__ == '__main__':
main( sys.argv[ 1: ] )