Andrew Shitov Weekly Review: Challenge - 082

Sunday, Oct 25, 2020| Tags: Raku

Raku Solutions Weekly Review


Getting in Touch

Email › Email me (Andrew) with any feedback about this review.

GitHub › Submit a pull request for any issues you may find with this page.

Twitter › Join the discussion on Twitter!

We’d greatly appreciate any feedback you’d like to give.


Welcome to the Raku Review for Week 082 of The Weekly Challenge!. For a quick overview, go through the original tasks and recap of the weekly challenge.


Task 1. Common Factors

In the first task, we had to find common factors of two integer numbers. The solutions submitted can be roughly classified into a few categories:

  • With two greps
  • By intersection of factors
  • Employing the GCD
  • With junctions

Using a row of greps

In this kind of solutions, the range of numbers is first filtered to take the numbers by which $A is divisible, and then the second filter is applied to do the same for $B. The remaining numbers are the numbers in question.

For example, in my solution, we see:

    say ((1 .. ($a max $b)).grep: $a %% *).grep: $b %% *;

Intersection of lists of factors

Here, the first step is to find all the factors for each input number independently and then compute their intersection. Of course, using Raku’s operator (&) for set intersection.

In the program by Arne Sommer:

    my @M-factors = $include-self ?? (1..$M).grep({ $M %% $_ }) !! (1..$M/2).grep({ $M %% $_ });
    my @N-factors = $include-self ?? (1..$N).grep({ $N %% $_ }) !! (1..$N/2).grep({ $N %% $_ });

    my %common = @M-factors (&) @N-factors;

Or the same operator but spellt out as a Unicode symbol as Jaldhar H. Vyas did it:

    sub MAIN(Int $M, Int $N) {
        (factors($M)  factors($N)).keys.sort.join(', ').say;
    }

Simon Proctor demonstrates another example of this approach:

    sub MAIN ( UInt $M, UInt $N ) {
        say "({(fac($M) (&) fac($N)).keys.sort.join(', ')})"
    }

    sub fac( UInt $v ) {
        (1..^$v).grep( $v %% * )
    }

Notice how the whole line of Raku code is interpolated in curly braces inside a double-quoted string.

By the way, in a couple of solutions, finding the factors is optimised to take two factors at once if possible. Examine the take parts of Colin Crain’s solution:

    sub factor (Int $num) {
        gather {
            for (1..$num.sqrt.Int).grep({$num %% $_}) {
                take $_;
                take $num div $_;
            }
        }
    }

With the help of GCD

GCD, or greatest common divisor, is available as a built-in routine in Raku.

Instead of finding the divisors for $A or $B, you can find them for their GCD only, as you can see in the solution submitted by Jan Krňávek:

    sub common-factors( +@a ) {
        my $gcd = [gcd] @a;
        1, 2 ... $gcd
        andthen .grep:  $gcd %% *
    }

Or in Kang-min Liu’s program:

    sub common-factors (Int $a, Int $b) {
        my $x = $a gcd $b;
        return (1..$x).grep(-> $n { $x %% $n });
    }

Mark Andreson expressed the same idea using a WhateverCode block instead of a pointy block with an explicit signature:

    my $gcd = $M gcd $N;
    say (1..$gcd).grep($gcd %% *).join(", ").List;

Philip Hood showed an unexpected way of making some computations directly in the signature of the MAIN function:

    sub MAIN( Int $m = 18, Int $n = 12, $gc = $m gcd $n ) {
        die( "too many args!" ) if @*ARGS.end > 1;
        ( 1 .. $gc ).grep(-> $k { $gc %% $k } ).say;
    }

This trick can probably be used in a Raku Golf contest.

Laurent Rosenfeld added another improvement to check if the found value of GCD is a prime number:

    sub common_factors (Int $a, Int $b) {
        my $gcd = $a gcd $b;
        return (1,) if $gcd == 1;
        return ($gcd,) if $gcd.is-prime;
        return (1..$gcd).grep($gcd %% *).unique;
    }

Using junctions

The next group includes solutions that use junctions.

For example, look at Feng Chang’s code and the use of &:

    (1..min($M,$N)).grep({ ($M & $N) %% $_ }).say;

Markus Holzer reminds us that there’s an alternative form of creating junctions by using the built-in routine all:

    say "({ join ', ', grep all( $N, $M ) %% *, 1 ..^ max $N, $M })"

Bonus slide

The solution that was submitted by Julio de Castro requires special mentioning. A lot of -o fun things happen here:

    sub prefix:<∕>(\num) {
        (1 ... num/2, +num).grep: num %% *
    }

    # intersects and returns result as sorted list
    sub infix:<@∩>(\a, \b) {
        (a  b).keys.sort.list
    }

    sub MAIN(Int \a where * > 0, Int \b where * > 0) {
        say a @ b
    }

Video review

The full review of Task 1 is available on YouTube:

The timestamps for quick access to the review of each solution.

Task 2. Interleave String

This task was understood a bit differently by the participants. The main questions are, first, whether it is possible to only insert $B into $A, or the opposite is also allowed. The second idea is if we can split one or both of the input strings into smaller parts before searching for the result.

Let me not run through the solutions and their types here, but I still want to show you the output of the program by Athanasius. In the ‘Explanation’ section, you clearly see how the result was achieved:

$ raku challenge-082/athanasius/raku/ch-2.raku XXY XXZ XXXXYZ

Challenge 082, Task #2: Interleave String (Raku)

Input:
    $A = "XXY"
    $B = "XXZ"
    $C = "XXXXYZ"

Output: 1

EXPLANATION
    $A =  XX  Y
    $B =    XX Z
    $C =  XXXXYZ

You will find more details about the solutions and other interesting findings there in the video review below.

Video review

The full review of Task 2 is available on YouTube:

The timestamps to the reviews of the individual solutions:


If you want to participate in The Weekly Challenge, please contact us at perlweeklychallenge@yahoo.com.

SO WHAT DO YOU THINK ?

If you have any suggestions or ideas then please do share with us.

Contact with me