Primary Five
with Raku

by Arne Sommer

Primary Five with Raku

[191] Published 10. July 2022.

This is my response to The Weekly Challenge #172.

Challenge #172.1: Prime Partition

You are given two positive integers, $m and $n.

Write a script to find out the Prime Partition of the given number. No duplicates allowed.

For example,
Input: $m = 18, $n = 2
Output: 5, 13 or 7, 11

Input: $m = 19, $n = 3
Output: 3, 5, 11

$m is the number to partition, and $n is the number of primes we want in the partition. See e.g. mathworld.wolfram.com/PrimePartition.html for a detailed explanation.

File: prime-partition
#! /usr/bin/env raku

unit sub MAIN (Int $m where $m > 0, Int $n where $n > 0, :a($all)); # [1]

my $primes := (2 .. $m).grep( *.is-prime );                         # [2]

for $primes.combinations($n) -> @combination                        # [3]
{
  my $sum = @combination.sum;                                       # [4]

  if $sum == $m                                                     # [5]
  {
    say @combination.join(", ");                                    # [6]
    last unless $all;                                               # [7]
  }
}

[1] Specify the two values. Also note the «-all», which will be discussed in [7].

[2] Get the primes, up to (and including, even if that is excessive) the original number. (But this will give us the number itself, if we go for $n = 1.)

[3] Then we use combinations($n) to get all the possible combinations of values in the original list - of size $n, and iterate over them.

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

[4] Get the sum of all the values.

[5] Do we have a match?

[6] If so, print the primes.

[7] And we are done, unless we want the program to print all the matches.

Running it:

$ ./prime-partition 18 2
5, 13

$ ./prime-partition 19 3
3, 5, 11

Use the «-a» command line option to get all the answers, and not just the first one («first» as in the first chosen by the program):

$ ./prime-partition -a 18 2
5, 13
7, 11

Looking good.

We can try with a prime, which cannot be partitioned:

$ ./prime-partition 19 4
$ ./prime-partition -a 19 4

Unless you consider a partition of 1 to be a partition:

$ ./prime-partition 19 1
19

Challenge #172.2: Five-number Summary

You are given an array of integers.

Write a script to compute the five-number summary of the given set of integers.

You can find the definition and example in the wikipedia page.

File: five-number-summary
#! /usr/bin/env raku

unit sub MAIN (*@integers where all(@integers) ~~ Int, :v($verbose));  # [1]

my @sorted = @integers>>.Int.sort;                                     # [2]

say ": Sorted: @sorted[]" if $verbose;

my @fns =                      # [3]
(
  @sorted[0],                  # [4]
  lower_quartile(@sorted),     # [6]
  median(@sorted),             # [5]
  upper_quartile(@sorted),     # [7]
  @sorted[*-1]                 # [8]
);

sub median (@values)                                                  # [5a]
{
  my $count = @values.elems;

  return @values[$count/2] if $count % 2;                             # [5b]

  return (@values[$count/2 -0.5] + @values[$count/2 +0.5]) / 2;       # [5c]
}

sub lower_quartile (@values)                                          # [6a]
{
  my $count = @values.elems;

  return median(@values[0 .. $count/2 -0.5]);
}
 
sub upper_quartile (@values)                                          # [7a]
{
  my $count = @values.elems;

  return median(@values[$count/2 .. *]);
}
 
say @fns.join(", ");                                                  # [9]

[1] A slurpy argument (*@) to get all the values in one array, combined with an all junction to ensure that they are all integers.

See docs.raku.org/type/Signature#index-entry-slurpy_argument for more information about slurpy arguments.

See docs.raku.org/routine/all for more information about the all Junction.

[2] Sort the values, after coercing them to integers (with >>.Int) as they are strings (or IntStr rather) when received as input from the command line. Sorting numbers as strings is not a good idea:

> say <1 2 11 12>.sort;        # -> (1 2 11 12)
> say "1 2 11 12".words.sort;  # -> (1 11 12 2)

[3] Collect the 5 values

[4] The first value is the one with the lowest value in the array, which is the first one - as the array is sorted.

[5] The third one (yes, I have skipped the second one - as this one should be explained first) is the median. This is the value in the middle of the array. In the case of an odd number of elements, it is the middle value itself [5b]. If we have an even number of elements, we take the average of the two middle values [5c].

[6] There are many ways of computing the lower quartile, according to en.wikipedia.org/wiki/Quartile. I have chosen the one called «method 2», where we compute the median of the first half of the values. In the case of an odd number of elements, the middle one is included.

[7] The upper quartile is the identical to [6a], but we use the second half of the values. The middle value is included here as well, if we have an odd number of elements.

[8] The fifth value is the one with the highest value in the array, which is the last one - as the array is sorted.

[9] Print the 5 values.

Running it:

$ ./five-number-summary 0 0 1 2 63 61 27 13
0, 0.5, 7.5, 44, 63

We got the same result as the wikipedia page.

And that's it.