Perl Weekly Challenge 219: building a tree using a stack
This post presents my solutions to the Perl Weekly Challenge 219.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 219 - Task 1 - Raku
- PWC 219 - Task 2 - Raku
- PWC 219 - Task 1 in PostgreSQL PL/Perl
- PWC 219 - Task 2 in PostgreSQL PL/Perl
- PWC 219 - Task 1 in PostgreSQL PL/PgSQL
- PWC 219 - Task 2 in PostgreSQL PL/PgSQL
Raku Implementations
PWC 219 - Task 1 - Raku Implementation
The first task was effectively simple: given a list of integers, sort the squares.sub MAIN( *@n where { @n.grep( * ~~ Int ).elems == @n.elems } ) {
@n.map( { $_ ** 2 } ).sort.join( ', ' ).say;
}
It can be solved with a single line: build an array of squares (using
map
), sort
them and print.
PWC 219 - Task 2 - Raku Implementation
This has been a lot more difficult: given an array of costs for 1, 7 and 30 days, and a list of days (of year), compute the minimum cost of the vacancy.sub MAIN() {
my @days = 1, 5, 6, 7, 9, 15;
# sort the days
@days .= sort;
my %costs;
%costs<1> = 2;
%costs<7> = 7;
%costs<30> = 25;
my @evaluated;
@evaluated.push: { cost => 0, days => @days };
my $current-cost = @days.elems * %costs<1>;
while ( @evaluated.elems > 0 ) {
my %entry = @evaluated.shift;
if ( %entry<days>.elems == 0 ) {
$current-cost = %entry<cost> if ( %entry<cost> < $current-cost );
}
else {
next if ( %entry<cost> >= $current-cost );
my $begin-date = %entry<days>[ 0 ];
for %costs.keys {
my $end-date = $begin-date + $_ - 1;
my @uncovered-days = %entry<days>.grep( * > $end-date );
my $cost = %entry<cost> + %costs{ $_ };
@evaluated.push: { cost => $cost, days => @uncovered-days };
}
}
}
say $current-cost;
}
First of all, I define an hash named
%costs
that is keyed by the number of days and has the cost as a value.
Assuming the daily cost is always the lowest one, I compute the cost of single days and push them as an hash into the @evaluated
array.
Then I loop in seeking for a solution: at every iteration I extract an hash element from the array, %entry
. Such element has the cost and the list of remaining days: if the latter is empty there is nothing more to compute and so I simply evaluate the final cost to see if I’ve found a min.
Otherwise, there are other days to evaluate, so I consider the cost of this %entry
and skip if it is already higher than what I have. Otherwise, I compute the final date of all the three periods of time, and the cost, and add the result to the @evaluated
array, so that I’m building a kind of tree of possibile solutions.
PL/Perl Implementations
PWC 219 - Task 1 - PL/Perl Implementation
The same solution as the Raku implementation, with the only difference that I do an iterativereturn_next
to handle big arrays:
CREATE OR REPLACE FUNCTION
pwc219.task1_plperl( int[] )
RETURNS SETOF int
AS $CODE$
my ( $n ) = @_;
for my $value ( sort { $a <=> $b } map { $_ * $_ } $n->@* ) {
return_next( $value );
}
return;
$CODE$
LANGUAGE plperl;
PWC 219 - Task 2 - PL/Perl Implementation
Same solution as Raku.CREATE OR REPLACE FUNCTION
pwc219.task2_plperl( int[], int[] )
RETURNS int
AS $CODE$
my ( $c, $days ) = @_;
my $costs = {};
$costs->{ 1 } = $c->@[ 0 ];
$costs->{ 7 } = $c->@[ 1 ];
$costs->{ 30 } = $c->@[ 2 ];
my @evaluated;
my $current_cost = ( scalar $days->@* ) * $costs->{ 1 };
push @evaluated, { cost => 0, days => $days };
while ( ( scalar @evaluated ) > 0 ) {
my $entry = shift @evaluated;
if ( $entry->{ days }->@* == 0 ) {
$current_cost = $entry->{ cost } if ( $entry->{ cost } < $current_cost );
}
else {
next if ( $entry->{ cost } >= $current_cost );
my $begin_date = $entry->{ days }->[ 0 ];
for my $duration ( keys $costs->%* ) {
my $end_date = $begin_date + $duration - 1;
my $cost = $entry->{ cost } + $costs->{ $duration };
my @remaining_days = grep { $_ > $end_date } $entry->{ days }->@*;
push @evaluated, { cost => $cost, days => \@remaining_days };
}
}
}
return $current_cost;
$CODE$
LANGUAGE plperl;
PostgreSQL Implementations
PWC 219 - Task 1 - PL/PgSQL Implementation
A single query to do the trick!CREATE OR REPLACE FUNCTION
pwc219.task1_plpgsql( n int[] )
RETURNS SETOF int
AS $CODE$
SELECT v * v
FROM unnest( n ) v
ORDER BY 1
$CODE$
LANGUAGE sql;
PWC 219 - Task 2 - PL/PgSQL Implementation
Here I cheat: I call the PL/Perl implementation because I have no idea about how to translate this to PL/PgSQL in a decent way!CREATE OR REPLACE FUNCTION
pwc219.task2_plpgsql( c int[], days int[] )
RETURNS int
AS $CODE$
SELECT pwc219.task2_plperl( c, days );
$CODE$
LANGUAGE sql;