Numerous Numbers
with Raku

by Arne Sommer

Numerous Numbers with Raku

[288] Published 11. May 2024.

This is my response to The Weekly Challenge #268.

Challenge #268.1: Magic Number

You are given two arrays of integers of same size, @x and @y.

Write a script to find the magic number that when added to each elements of one of the array gives the second array. Element order is not important.

Example 1:
Input: @x = (3, 7, 5)
       @y = (9, 5, 7)
Output: 2

The magic number is 2.
@x = (3, 7, 5)
   +  2  2  2
@y = (5, 9, 7)
Example 2:
Input: @x = (1, 2, 1)
       @y = (5, 4, 4)
Output: 3

The magic number is 3.
@x = (1, 2, 1)
   +  3  3  3
@y = (5, 4, 4)
Example 3:
Input: @x = (2)
       @y = (5)
Output: 3

File: magic-number
#! /usr/bin/env raku

unit sub MAIN ($x, $y, :v(:$verbose));                  # [1]

my @x = $x.words>>.Int.sort;                            # [2]
my @y = $y.words>>.Int.sort;                            # [2a]

die "Not the same length" unless @x.elems == @y.elems;  # [3]

my @pairs = @x Z @y;                                    # [4]

say ":Pairs: { @pairs.raku }" if $verbose;

my @diff = @pairs.map({ $_[0] - $_[1] });               # [5]

say ":Diff: { @diff.join(", ") }" if $verbose;

say ( [==] @diff ) ?? @diff[0].abs !! 'error';          # [6]

[1] The two arrays as space separated strings.

[2] Turn each string into an array of integers. This will blow up on non-integer input, which is ok. It will also convert non-integer values to integers, which may be not so ok. Use .Numeric instead, and smart match the resulting arrays with e.g. ~~ Int if that conversion bothers you. We sort them, as that is the easiest way to ensure that the differences between the values in the two arrays are optimal.

[3] Ensure that the two arrays have the same length.

[4] Merge (or zip) the arrays together, using the zip operator Z. The result is a list of Pair objects; one value from each list (in order).

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

[5] Reduce the Pairs to a single value each (with map and a simple subtraction of the two values), resulting in an array of differences.

[6] Are the differences the same? If so print it (without a sign, if any). If not, print an error message.

Running it:

$ ./magic-number "3 7 5" "9 5 7"
2

$ ./magic-number "1 2 1" "5 4 4"
3

$ ./magic-number 2 5
3

Looking good.

With verbose mode:

$ ./magic-number -v "3 7 5" "9 5 7"
:Pairs: [(3, 5), (5, 7), (7, 9)]
:Diff: -2, -2, -2
2

$ ./magic-number -v "1 2 1" "5 4 4"
:Pairs: [(1, 4), (1, 4), (2, 5)]
:Diff: -3, -3, -3
3

$ ./magic-number -v 2 5
:Pairs: [(2, 5),]
:Diff: -3
3

The challenge and examples does not state what should happen when we do not have a magic number, but the !! part of [6] handles that:

$ ./magic-number -v "1 2 1" "5 4 42"
:Pairs: [(1, 4), (1, 5), (2, 42)]
:Diff: -3, -4, -40
error

Challenge #268.2: Number Game

You are given an array of integers, @ints, with even number of elements.

Write a script to create a new array made up of elements of the given array. Pick the two smallest integers and add it to new array in decreasing order i.e. high to low. Keep doing until the given array is empty.

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

Round 1: we picked (2, 3) and push it to the new array (3, 2)
Round 2: we picked the remaining (4, 5) and push it to the new array
  (5, 4)
Example 2:
Input: @ints = (9, 4, 1, 3, 6, 4, 6, 1)
Output: (1, 1, 4, 3, 6, 4, 9, 6)
Example 3:
Input: @ints = (1, 2, 2, 3)
Output: (2, 1, 3, 2)
File: number-game
#! /usr/bin/env raku

unit sub MAIN (*@ints where @ints.elems > 1   # [1]
                 && @ints.elems %% 2          # [1a]
                 && all(@ints) ~~ Int,        # [1b]
               :v(:$verbose));

my @sorted = @ints.sort;                      # [2]
my @new;                                      # [3]

while @sorted                                 # [4]
{
  print ":Sorted: { @sorted.join(",")}" if $verbose;

  my $a = @sorted.shift;                      # [5]
  my $b = @sorted.shift;                      # [6]

  say " -> pick $a,$b -> add $b,$a" if $verbose;

  @new.append: $b, $a;                        # [7]
}

say "({ @new.join(", ") })";                  # [8]

[1] At least one element, the number of them must be even [1a], and they must all be integers [1b].

[2] Sort them, as that makes it easier to pick the correct (i.e. lowest) values.

[3] The result will end up here.

[4] As long as we have unfinished business.

[5] Get the lowest remaining value.

[6] And the lowest remaining vaule, after the one above.

[7] Add them to the result, in reverse order. Note the usage of append so that the values are added one at a time to the result, whereas push would have added a new list (with two items) to the result.

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

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

[8] Pretty print the result.

Running it:

$ ./number-game 2 5 3 4
(3, 2, 5, 4)

$ ./number-game 9 4 1 3 6 4 6 1
(1, 1, 4, 3, 6, 4, 9, 6)

$ ./number-game 2 1 3 2
(2, 1, 3, 2)

Looking good.

With verbose mode:

$ ./number-game -v 2 5 3 4
:Sorted: 2,3,4,5 -> pick 2,3 -> add 3,2
:Sorted: 4,5 -> pick 4,5 -> add 5,4
(3, 2, 5, 4)

$ ./number-game -v 9 4 1 3 6 4 6 1
:Sorted: 1,1,3,4,4,6,6,9 -> pick 1,1 -> add 1,1
:Sorted: 3,4,4,6,6,9 -> pick 3,4 -> add 4,3
:Sorted: 4,6,6,9 -> pick 4,6 -> add 6,4
:Sorted: 6,9 -> pick 6,9 -> add 9,6
(1, 1, 4, 3, 6, 4, 9, 6)

$ ./number-game -v 2 1 3 2
:Sorted: 1,2,2,3 -> pick 1,2 -> add 2,1
:Sorted: 2,3 -> pick 2,3 -> add 3,2
(2, 1, 3, 2)

And that's it.