This is my response to The Weekly Challenge #268.
@x
and @y
.
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
@ints
, with even number of
elements.
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)
#! /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.