Perl Weekly Challenge: Week 163

Challenge 1:

Sum Bitwise Operators

You are given a list of positive numbers, @n.

Write script to calculate the sum of bitwise & operator for all unique pairs.

Example 1:
Input: @n = (1, 2, 3)
Output: 3

Since (1 & 2) + (2 & 3) + (1 & 3) => 0 + 2 + 1 =>  3.
Example 2:
Input: @n = (2, 3, 4)
Output: 2

Since (2 & 3) + (2 & 4) + (3 & 4) => 2 + 0 + 0 =>  2.

In Raku we can solve this with a one-liner.

@*ARGS.combinations(2).map({ [+&] $_ }).sum.say;

(Full code on Github.)

@*ARGS are the arguments passed on the command line. These should be a list of positive numbers though I'm not actually checking that. We apply .combinations(2) to them to get unique pairs. Then .map() is used to apply bitwise and to each pair. (bitwise and is +& in Raku not just & which always trips me up.) I could have expressed this as $_[0] +& $_[1] but decided to use the hyperoperator [+&] instead. It just reads better IMO. At this point, we have a list of results of each and operation. These are totalled with .sum(). I could have used [+] but this allows me to continue chaining

In Perl, we have to be a bit more verbose as we are missing .sum() and .combinations() but I already had replacement functions for them and armed with those, the core part of the Perl solution is also one line.

say sum([map { $_->[0] & $_->[1] } combinations(\@ARGV, 2) ]);

(Full code on Github.)

Challenge 2:

Summations

You are given a list of positive numbers, @n.

Write a script to find out the summations as described below.

Example 1:
Input: @n = (1, 2, 3, 4, 5)
Output: 42

    1 2 3  4  5
      2 5  9 14
        5 14 28
          14 42
             42

The nth Row starts with the second element of the (n-1)th row.
The following element is sum of all elements except first element of previous row.
You stop once you have just one element in the row.
Example 2:
Input: @n = (1, 3, 5, 7, 9)
Output: 70

    1 3  5  7  9
      3  8 15 24
         8 23 47
           23 70
              70

This one took me a little while to understand but ultimately proved to be quite straightforward to solve. According to the spec, the nth row begins with the (n-1)th element of the previous row. So we start the script with all the elements of the input except the first. I've called this new array @previous but in hindsight @current would have been more appropriate.

my @previous = @n[1 .. *];

There is also a variable to store the running total.

my $total;

Now as long as there is atleast one element left in @previous...

while @previous.elems {

We initalize $total to the value of the first element.

    $total = @previous[0];

And set up an array to hold the next line. It is initialized with $total.

    my @next = ( $total );

Then for each of the remaining elements in @previous, we add its value to $total and add the new value of $total to @next.

    for 1 ..^ @previous.elems -> $i {
        $total += @previous[$i];
        @next.push($total);
    }

Finally, we make the new value of @previous equal to all the elements of @next except the first one.

    @previous = @next[1 .. *];
}

After this loop finishes, $total contains the final answer so we print it.

say $total;

(Full code on Github.)

For once, Perl has everything we need to translate the Raku code above. The syntax isn't even particularly ungainly compared to the Raku version.

my @previous = @n[1 .. $#n];
my $total;

while (scalar @previous) {
    $total = $previous[0];
    my @next = ( $total );

    for my $i (1 .. scalar @previous - 1) {
        $total += $previous[$i];
        push @next, $total;
    }

    @previous = @next[1 .. $#next];
}

say $total;

(Full code on Github.)