Arithmetically Good
with Raku

by Arne Sommer

Arithmetically Good with Raku

[245] Published 13. July 2023.

This is my response to The Weekly Challenge #225.

Challenge #225.1: Max Words

You are given a list of sentences, @list.

A sentence is a list of words that are separated by a single space with no leading or trailing spaces.

Write a script to find out the maximum number of words that appear in a single sentence.

Example 1:
Input: @list = ("Perl and Raku belong to the same family.",
                "I love Perl.",
                "The Perl and Raku Conference.")
Output: 8
Example 2:
Input: @list = ("The Weekly Challenge.",
                "Python is the most popular guest language.",
                "Team PWC has over 300 members.")
Output: 7
File: max-words
#! /usr/bin/env raku

unit sub MAIN (*@list);              # [1]

say @list>>.split(" ")>>.elems.max;  # [2]

[1] A slurpy array collecting all the sentences. Note the missing where @list.elems > 0 clause, thus allowing no input.

[2] Apply split on each sentence (courtesy of the >>. method invocation. We split on a single space, as requested, instead of using the more forgiving words method. Then we use elems on each sentence (which is now a list of words) to get the length. This returns a list of lengths, one for each sentence. And finally we use max to get the highest value.

See docs.raku.org/routine/words for more information about words.

Running it:

$ ./max-words "Perl and Raku belong to the same family." "I love \
    Perl." "The Perl and Raku Conference."
8

$ ./max-words "The Weekly Challenge." "Python is the most popular \
  guest language." "Team PWC has over 300 members."
7

Looking good.

Challenge #225.2: Left Right Sum Diff

You are given an array of integers, @ints.

Write a script to return left right sum diff array as shown below:

@ints = (a, b, c, d, e)

@left  = (0, a, (a+b), (a+b+c))
@right = ((c+d+e), (d+e), e, 0)
@left_right_sum_diff = ( | 0 - (c+d+e) |,
                         | a - (d+e)   |,
                         | (a+b) - e   |,
                         | (a+b+c) - 0 | )
Example 1:
Input: @ints = (10, 4, 8, 3)
Output: (15, 1, 11, 22)

@left  = (0, 10, 14, 22)
@right = (15, 11, 3, 0)

@left_right_sum_diff = ( |0-15|, |10-11|, |14-3|, |22-0|)
                     = (15, 1, 11, 22)
Example 2:
Input: @ints = (1)
Output: (0)

@left  = (0)
@right = (0)

@left_right_sum_diff = ( |0-0| ) = (0)
Example 3:
Input: @ints = (1, 2, 3, 4, 5)
Output: (14, 11, 6, 1, 10)

@left  = (0, 1, 3, 6, 10)
@right = (14, 12, 9, 5, 0)

@left_right_sum_diff = ( |0-14|, |1-12|, |3-9|, |6-5|, |10-0|)
                     = (14, 11, 6, 1, 10)

The formulae in the challenge is wrong; as it has five input values and only four as output. These values should be identical; as shown by the three examples.

The trick here is recognizing the pattern, and also that the right column is the same as the left one, but in the other direction (from the end, and not the beginning). These observations make it easyish.

File: lrsd
#! /usr/bin/env raku

unit sub MAIN (*@int, :v(:$verbose));                 # [1]

my $size = @int.elems;                                # [2]

my @reverse = @int.reverse;                           # [3]
my @left    = (0,);                                   # [4]
my @right   = (0,);                                   # [5]

for 0 .. $size -2 -> $i                               # [6]
{
  @left.push:  sum(@int[0 .. $i]);                    # [7]
  @right.push: sum(@reverse[0 .. $i]);                # [8]
}

@right .= reverse;                                    # [9]

say ":Left: @left[]\n:Right: @right[]" if $verbose;

say (@left Z @right).map({ ( $_[0] - $_[1] ).abs });  # [10]

[1] A slurpy array collecting the values. Note that no arguments is ok.

[2] The number of input elements; which is also the number of output elements.

[3] As the right column is the same as the left one - from the other direction.

[4] The first value is zero.

[5] Ditto.

[6] Iterate over the rest of the values.

[7] The left value to add, using sum to add them all together. Note the use of an array slice (a part of an array) to get the requested values.

See docs.raku.org/routine/sum for more information about sum.

[8] The right (as in non-left; but it is actually the right one, as in non-wrong, as well) value to add.

[9] Reverse the right column.

[10] We use the zipper operator Z to merge the two lists, giving us a list of pair values. Then we use map to reduce each pair to a single value (left minus right, and get rid of the sign with abs). Then print that list.

See docs.raku.org/routine/Z for more information about the infix zip operator Z.

See docs.raku.org/routine/abs for more information about abs.

Running it:

$ ./lrsd 10 4 8 3
(15 1 11 22)

$ ./lrsd 1
(0)

$ ./lrsd 1 2 3 4 5
(14 11 6 1 10)

Looking good, except the missing commas...

With verbose mode:

$ ./lrsd -v 10 4 8 3
:Left: 0 10 14 22
:Right: 15 11 3 0
(15 1 11 22)

$ ./lrsd -v 1
:Left: 0
:Right: 0
(0)

$ ./lrsd -v 1 2 3 4 5
:Left: 0 1 3 6 10
:Right: 14 12 9 5 0
(14 11 6 1 10)

Running it without arguments gives a list containing a zero:

$ ./lrsd 
(0)

$ ./lrsd -v 
:Left: 0
:Right: 0
(0)

We should perhaps get an empty list here...

Let us fix that, and add the commas as well.

File: lrsd-zero
#! /usr/bin/env raku

unit sub MAIN (*@int, :v(:$verbose));

if @int.elems == 0    # [1]
{
  say '()';
  exit;
}

my $size = @int.elems;

my @reverse = @int.reverse;
my @left    = (0,);
my @right   = (0,);

for 0 .. $size -2 -> $i
{
  @left.push:  sum(@int[0 .. $i]);
  @right.push: sum(@reverse[0 .. $i]);
}

@right .= reverse;

say ":Left: @left[]\n:Right: @right[]" if $verbose;

say '(', (@left Z @right).map({ ( $_[0] - $_[1] ).abs }).join(", "), ')';  # [2]

[1] We treat no arguments as a special case.

[2] Note the explicit parens this time, as we use join on the list to add commas. The original program had Raku adding the parens for us automagically on array stringification.

And that's it.