Perl Weekly Challenge 366: what time is it?
This post presents my solutions to the Perl Weekly Challenge 366.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 366 - Task 1 - Raku
- PWC 366 - Task 2 - Raku
- PWC 366 - Task 1 in PostgreSQL PL/Perl
- PWC 366 - Task 2 in PostgreSQL PL/Perl
Raku Implementations
PWC 366 - Task 1 - Raku Implementation
The first task was to find out if a given list of strings are prefixes of the last word.sub MAIN( *@words ) {
my $string = @words[ * - 1 ];
my @matches;
for 0 ..^ @words.elems - 1 {
my $prefix = @words[ $_ ];
@matches.push: $prefix if $string ~~ / ^ $prefix /;
}
@matches.elems.say;
}
This is quite simple: I iterate over all the words but the last one, and check with a regular expression if any prefix is applicable to the given word. Last, I count how many items I’ve found.
PWC 366 - Task 2 - Raku Implementation
The second task was about finding out how many validHH::MM time strings can be composed when a ? (or more) wildcard is applied.
This can be solved better with a regular expression, but I took an iterative approach.
sub MAIN( Str $str ) {
say 0 and exit if $str !~~ / '?' /;
my ( $hours, $minutes ) = $str.split( ':' );
my ( $left, $right ) = $hours.comb;
my ( @a, @b );
if ( $left eq '?' ) {
@a.push: [ 0, 1, 2 ];
}
else {
@a.push: $left.Int;
}
if ( $right eq '?' ) {
@b.push: $_ for 0 .. 9;
}
else {
@b.push: $right.Int;
}
my @hours;
@hours = ( @a X~ @b )>>.Int.Array.grep( * <= 23 );
@a = ();
@b = ();
( $left, $right ) = $minutes.comb;
if ( $left eq '?' ) {
@a.push: $_ for 0 .. 9;
}
else {
@a.push: $left.Int;
}
if ( $right eq '?' ) {
@b.push: $_ for 0 .. 9;
}
else {
@b.push: $right.Int;
}
my @minutes = ( @a X~ @b )>>.Int.Array.grep( * <= 59 );
my @valid-times;
for @hours -> $h {
for @minutes -> $m {
@valid-times.push: "%02d:%02d".sprintf( $h, $m );
}
}
@valid-times.elems.say;
}
The approach is this:
- I split the given string into the leftmost part (hours) and the rightmost (minutes);
- I split every obtained part into its own digits, and check if there is a wildcard in the leftmost and rightmost digit;
- I use
@aand@bto keep track of valid digits in every part, so that I can compose a@hoursand@minutesarray made by the actual digits substituted where wildcards are found; - I zip
Zthe results to compose the@valid-timesarray and then count how many items I’ve found.
PL/Perl Implementations
PWC 366 - Task 1 - PL/Perl Implementation
Same approach as in Raku, using a regular expression matching.CREATE OR REPLACE FUNCTION
pwc366.task1_plperl( text[], text )
RETURNS int
AS $CODE$
my ( $prefixes, $string ) = @_;
my @matches;
for my $current ( $prefixes->@* ) {
if ( $string =~ / ^ $current /x ) {
push @matches, $current;
}
}
return scalar( @matches );
$CODE$
LANGUAGE plperl;
PWC 366 - Task 2 - PL/Perl Implementation
Same approach as in Raku.CREATE OR REPLACE FUNCTION
pwc366.task2_plperl( text )
RETURNS int
AS $CODE$
my ( $string ) = @_;
my ( $hours, $minutes ) = split ':', $string;
my ( $left, $right ) = split '', $hours;
my @hours;
my ( @a, @b );
if ( $left eq '?' ) {
push @a, $_ for ( 0 .. 2 );
}
else {
push @a, $left;
}
if ( $right eq '?' ) {
push @b, $_ for ( 0 .. 9 );
}
else {
push @b, $right;
}
my @valid_hours;
for my $l ( @a ) {
for my $r ( @b ) {
my $t = sprintf '%02d', $l, $r;
push @valid_hours, $t if ( $t <= 23 );
}
}
@a = ();
@b = ();
( $left, $right ) = split '', $minutes;
if ( $left eq '?' ) {
push @a, $_ for ( 0 .. 5 );
}
else {
push @a, $left;
}
if ( $right eq '?' ) {
push @b, $_ for ( 0 .. 9 );
}
else {
push @b, $right;
}
my @valid_minutes;
for my $l ( @a ) {
for my $r ( @b ) {
my $t = sprintf '%02d', $l, $r;
push @valid_minutes, $t if ( $t <= 59 );
}
}
my @valid_times;
for my $h ( @valid_hours ) {
for my $m ( @valid_minutes ) {
push @valid_times, "$h:$m";
}
}
return scalar @valid_times;
$CODE$
LANGUAGE plperl;