Perl Weekly Challenge 171: numbers and refenreces
It is sad that, after more than two years of me doing Raku, I still don’t have any production code project to work on. Therefore, in order to keep my coding and Raku-ing (is that a term?) knowdledge, I try to solve every Perl Weekly Challenge tasks.In the following, the assigned tasks for Challenge 171.
and for the sake of some Perl 5, let’s do some stuff also in PostgreSQL Pl/Perl:
Last, the solutions in PostgreSQL PL/PgSQL:
PWC 171 - Task 1
This task was about to compute the first 20 odd abundant numbers, those that have a sum of divisors greater than the number itself.sub is-abundant( Int:D $n ) {
my @divisors = 1;
@divisors.push: $_ if $n %% $_ for ( 2 .. $n / 2 );
return @divisors.sum > $n;
}
sub MAIN() {
my @abundant = lazy gather {
take $_ if $_ !%% 2 && is-abundant( $_ ) for ( 1 .. Inf );
};
@abundant[ 0 .. 20 ].join( "\n" ).say;
}
I decided to create an
is-abundant
function that extracts all the divisors, put them int the @divisors
array and sum them, returning if the sum if greater or not than the original number.
Then, in the
MAIN
, I lazy gather
over all numbers and provide only those odd and that are abundant.
PWC 171 - Task 2
I’m not sure I’ve correctly understood the task. The task was requiring you to make a function that accepts two functions and returns the result of invoking them in a nested way.sub compose( Sub:D $f, Sub:D $g ) {
return sub (*@a) {
"F( G( { @a } ) )".say;
$f( $g( @a ) )
};
}
sub MAIN() {
my $f = sub (*@a) {
"F( { @a } )".say;
return @a;
};
my $g = sub (*@a) {
"G( { @a } )".say;
return @a;
};
my $h = compose( $f, $g );
$h( 'PWC 171' );
}
I produced two function,
$f
and $g
that both slurp their arguments and return them unmodified. Of course, in real like, such functions are going to do something.
Then compose
accepts two defined Sub
references and invokes them in a nested way.
PWC 171 - Task 1 in PostgreSQL PL/Perl
A translation of the Raku solution:CREATE OR REPLACE FUNCTION
pwc171.task1_plperl( int )
RETURNS SETOF INT
AS $CODE$
my ($limit) = @_;
my $is_abundant = sub {
my ( $n ) = @_;
my @divisors = ( 1 );
for ( 2 .. $n / 2 ) {
next if $n % $_ != 0;
push @divisors, $_;
}
my $sum = 0;
$sum += $_ for ( @divisors );
return $sum > $n;
};
for ( 1 .. 99999 ) {
next if $_ % 2 == 0;
if ( $is_abundant->( $_ ) ) {
$limit--;
return_next( $_ );
}
last if $limit <= 0;
}
return undef;
$CODE$
LANGUAGE plperl;
The
$is_abundant
code references implements the function that sums all divisors of the given odd number and returns true if the number is abundant.
Then, the main loop does a return_next
to append a new number to the result set in the case it is abundant.
PWC 171 - Task 2 in PostgreSQL PL/Perl
Assuming I understood the tast, theimplementation is just a craftedSELECT
:
CREATE OR REPLACE FUNCTION
pwc171.task2_f( text )
RETURNS TEXT
AS $CODE$
elog( DEBUG, "F( $_[0] )" );
return $_[0];
$CODE$
LANGUAGE plperl;
CREATE OR REPLACE FUNCTION
pwc171.task2_g( text )
RETURNS TEXT
AS $CODE$
elog( DEBUG, "G( $_[0] )" );
return $_[0];
$CODE$
LANGUAGE plperl;
/*
testdb=> select * from pwc171.task2_plperl( 'pwc171.task2_f', 'pwc171.task2_g', 'Hello World' );
DEBUG: Query [SELECT pwc171.task2_f( pwc171.task2_g( 'Hello World' ) ) AS compose;]
DEBUG: G( Hello World )
DEBUG: F( Hello World )
task2_plperl
--------------
Hello World
*/
CREATE OR REPLACE FUNCTION
pwc171.task2_plperl( text, text, text )
RETURNS TEXT
AS $CODE$
my $compose = sub {
my $query = sprintf( "SELECT %s( %s( '%s' ) ) AS compose;",
$_[ 0 ],
$_[ 1 ],
$_[ 2 ] );
elog( DEBUG, "Query [$query]" );
my $result_set = spi_exec_query( $query );
return $result_set->{ rows }[ 0 ]->{ compose };
};
return $compose->( @_ );
$CODE$
LANGUAGE plperl;
I wrote a couple of functions,
f
and g
just to use to test the machinery.
The main function build a crafted nested
SELECT
that invokes all the functions in the right order, and then returns the result outside. Why using an anonymous code reference? Well, because if you want to transform that into a set returning function is easier!
PWC 171 - Task 1 in PostgreSQL PL/PgSQL
Quite same approach as in the PL/Perl solution:CREATE OR REPLACE FUNCTION
pwc171.task1_plpgsql( l int DEFAULT 20 )
RETURNS SETOF INT
AS $CODE$
DECLARE
s int := 0;
i int;
d int;
BEGIN
FOR i IN 2 .. 99999 LOOP
IF i % 2 = 0 THEN
CONTINUE;
END IF;
s := 0;
FOR d in 2 .. ( i / 2 ) LOOP
IF i % d = 0 THEN
s := s + d;
END IF;
END LOOP;
IF s > i THEN
RETURN NEXT i;
l := l - 1;
IF l = 0 THEN
RETURN;
END IF;
END IF;
END LOOP;
RETURN;
END
$CODE$
LANGUAGE plpgsql;
The only different with the Perl and Raku solutions is that I don’t care about divisors, but keep only the sum of them while processing them.
PWC 171 - Task 2 in PostgreSQL PL/PgSQL
Easy enough to be done with a simple query:CREATE OR REPLACE FUNCTION
pwc171.task2_plpgsql( f text DEFAULT 'pwc171.task2_f',
g text DEFAULT 'pwc171.task2_g',
v text DEFAULT 'PWC 171' )
RETURNS SETOF TEXT
AS $CODE$
BEGIN
RETURN QUERY
EXECUTE format( 'SELECT * FROM %s( %s( $$%s$$ ) )', f, g, v );
END
$CODE$
LANGUAGE plpgsql;
The
EXECUTE
accepts a dynamically built query, built on top of format()
(a kind of printf
) that does the SELECT
of the nested functions. Note the usage of dollar quoting $$
around the function argument to escape it in already quoted string.