Perl Weekly Challenge 141: longer than I thought!

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 141.


And this week, as for the previous PWC, I had time to quickly implement the tasks also on PostgreSQL plpgsql language:

PWC 141 - Task 1

The first task was about producing a script that finds out the first ten integers that have all eight divisors. I decided to make the script parametrizable, so it accepts the number of integers to search for and the number of divisors.

sub MAIN( Int $divisors = 8, Int $count = 10 ) {

    "Searching $count numbers with $divisors divisors...".say;

    my %solutions;

    for $divisors + 1  .. Inf -> $current-number {
        my @intra-solutions = ( 1 .. $current-number ).grep( { $current-number %% $_ } );

        %solutions{ $current-number } = @intra-solutions if @intra-solutions.elems == $divisors;

        last if %solutions.keys.elems >= $count;
    }

    "$_ has $divisors divisors: { %solutions{ $_ }.join( ', ' ) }".say for %solutions.keys.sort;
}



I loop over all the numbers, and for a specific $current-number I produce a list of integers that are candidates to be the divisors. Then I grep the list to see which one is effectively a candidate, therefore @intra-solutions contains all the divisors of the $current-number. If the array of divisors has the wanted size, I push it into the %solutions hash that is indexed by the $current-number and its divisors list. At the end, I do print every number and the list of divisors.

PWC 141 - Task 2

This has been a huge pain to my memory: the task required to produce live numbers, sequences of the given input number where the digits respect the order and are all multiple of a given number.
I did not remember the method to produce the whole range of numbers, than it came: combinations. That made me solve the task in a single row:

sub MAIN( Int $m, Int $n ) {
    $m.comb.combinations( 1 ..^ $m.Str.chars )
           .map( *.join.Int )
           .grep( * %% $n ).unique.sort.say;

}



Let’s see what happens:
  • $m is split by comb into its digits;
  • the result is expanded into all possible combinations. Well, not all the combinations, rather a set limited by the length (minus one) of the length of the original number, i.e., the number of digits;
  • the result is mapped into integers by means of join (e.g., 12 is seen as 1 2 and then joined);
  • and the result is grepped to get only multiple of $n;
  • then I do sort and unique the result set
  • and I print by means of say.

PWC 141 - Task 1 in PostgreSQL plpgsql

The first task is a reimplementation of its version in Raku.

CREATE OR REPLACE FUNCTION
f_find_divisors( divisors int default 8, count int default 10 )
RETURNS SETOF int
AS $CODE$
DECLARE
   current_number  int;
   current_divisor int;
   current_found   int;
BEGIN
    FOR current_number IN 1 .. 999999 LOOP
        IF count = 0 THEN
           EXIT;
        END IF;

        current_found := 0;

        FOR current_divisor IN 1 .. current_number LOOP
            IF current_number % current_divisor = 0 THEN
               current_found := current_found + 1;
            END IF;
        END LOOP;

        IF current_found = divisors THEN
           count := count - 1;
           RETURN NEXT current_number;
        END IF;
    END LOOP;

    RETURN;
END
$CODE$
LANGUAGE plpgsql;



I start looping on a large spectre of numbers, where current_number assumes every single value. I then iterate on all numbers from 1 to current_number, that are all the candidate to be divisors: if a number is a divisor, then I increment a counter named current_found. At the end, if the current_found has the same value required, I do output the number appending it to the result set by means of RETURN NEXT.

PWC 141 - Task 2 in PostgreSQL plpgsql

Here I decided to use a recursive CTE to obtain all possible candidates.

CREATE OR REPLACE FUNCTION
f_live_numbers( m int, n int )
RETURNS SETOF int
AS $CODE$
WITH RECURSIVE
numbers AS ( SELECT unnest( regexp_split_to_array( m::text, '' ) ) AS n )
, combinations( i, v, c ) AS (
SELECT 1, n, n
FROM numbers
UNION
SELECT i + 1, n, c || num.n
FROM combinations, numbers num
WHERE length( c || num.n ) < length( m::text ) - 1
AND num.n IN ( SELECT n FROM numbers WHERE n::int > i )
)

SELECT c::int FROM combinations
WHERE c::int % n = 0;

$CODE$
LANGUAGE sql;



The numbers table materializes all the available digits by means of splitting the incoming number into its own set of digits. The combinations table is the recursive part, and contains an index i, a numeric value v and an appended value c. At each iteration, a new number v is extracted from numbers and appended to the previous one. In order to guarantee ordering, a subselect checks that we only append numbers on the right of the current one from the list of numbers.
Last, we output only those combinations where the value is a multiple of n.

The article Perl Weekly Challenge 141: longer than I thought! has been posted by Luca Ferrari on November 29, 2021