Andrew Shitov Weekly Review: Challenge - 083

Sunday, Nov 8, 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 083 of The Weekly Challenge!. For a quick overview, go through the original tasks and recap of the weekly challenge.


Task 1. Words Length

The first task this week is quite simple. And as always in such cases, more people submitted their solutions :-) The task is to find the total number of characters in the phrase excluding its first and the last words. Not quite clear if we should also ignore punctuation, but that’s a separate question.

In Raku, we can use the words method that splits the phrase into words, after which take a slice to extract the elements with indices from 1 to *-2. After that point, different approaches are possible: either get the lengths of all the words and add them up or join the words and then take the total length in one go.

The first branch gives solutions similar to this one:

$s.words[1 .. *-2]>>.chars.sum.say;

The second approach leads to the following code:

$S.words[1..*-2].join.chars;

Alternatively, you can remove those non-wanted words and spaces using a regex, and then take the lengths of what’s left:

$_ = $str;
s:g/^ \s* \S+ | \S+ \s* $ | \s+//;
say $_.chars;

Or you can map the words and exploit side effects of executing a code block. This example is a good start to study the facilities for parallel computing that Raku offers. Think about how to add race and atomic operations to this program:

my $stringlen = 0 ;
@words[1..$arraylen - 2].map( {$stringlen += $_.chars } ) ;
say $stringlen ;

An alternative approach is to find the positions of all spaces and use a simple formula to find the length of the words we need:

sub words-length (Str $s) {
    my @w = $s.trim.indices(' ');
    return @w.elems == 0 ?? 0 !! @w.tail - @w.head - @w.elems + 1;
}

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. Flip Array

In the second task, you had to take an array of positive integers. For some of them, you can alter the sign. In the end, add the numbers up to get the minimum possible but non-negative sum. The solution with the minimum sum and the minimum number of sign flips wins.

On a bigger scale, we can split the solutions into three groups. In the first, we simply try to find all possible combinations of + and -. In the second, we split the numbers into two groups, one of which comes with +, while another with -.

The first type of solution is easily implemented with the help of binary representation of the numbers from 0 to 2n - 1, where n is the size of input data. Zero bits can represent a +, and set bits would stand for -. Having the bits in an array, you can use Raku’s Z operator to multiply data items:

my $sum = [+] @bits>>.subst(0, -1) Z* @a;

Splitting the items into two groups can be done with testing all combinations:

for @arr.combinations($k) -> $c {
    my $new_sum = $base_sum - 2 * $c.sum;
    if 0 <= $new_sum < $min {
        $min = $new_sum;
        $pick = $c;
    }
}

Note that in this fragment, the minus sign is indirectly applied by subtracting the values twice.

There’s also a completely different approach with recursion. Here is the idea:

sub do_it (@left, @right is copy) {
    my $current = @right.shift;

    # . . .

    do_it((@left,  $current).flat, @right);
    do_it((@left, -$current).flat, @right);
}

As you see, on each step, two branches are tested: in one, the next item is added with a +, while in the other with -.

From the other interesting solutions, let me highlight the usage of Raku’s built-ins. [Using classify routine:

my &less-than-zero = *.grep: * < 0;

say +less-than-zero               # find and count all negative numbers of
    ( [X] map { +$_, -$_ }, @A )  # all possible candidates
        .classify( *.sum )        # grouped by sum
        .grep( *.key > -1 )       # filtered where sum is positive
        .sort( *.key )            # sorted by sum
        .head.value               # closest to zero
        .min( &less-than-zero )   # the one with the least flips

And with the reduce function:

sub cmpair($cap) {
    return -> @a, @b {
    (@b.sum <= $cap) ?? max(@a,(@b.sum,-@b.elems)) !! @a;
    }
}

say -reduce(cmpair(@*ARGS.sum/2), (0,0), |@*ARGS.combinations).[1];

Explore the rest of the code in the below video review!

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