This is my response to The Weekly Challenge #396.
Input: @order = (3, 1, 4, 2, 5)
@friends = (1, 2, 4)
Output: (1, 4, 2)
ID 1 finished 2nd.
ID 4 finished 3rd.
ID 2 finished 4th.
Example 2:
Input: @order = (5, 4, 3, 2, 1)
@friends = (1, 3, 5)
Output: (5, 3, 1)
ID 5 finished 1st.
ID 3 finished 3rd.
ID 1 finished 5th.
Example 3:
Input: @order = (2, 4, 1, 3)
@friends = (1, 2, 3, 4)
Output: (2, 4, 1, 3)
ID 2 finished 1st.
ID 4 finished 2nd.
ID 1 finished 3rd.
ID 3 finished 4th.
Example 4:
Input: @order = (4, 1, 3, 2)
@friends = (3)
Output: (3)
Example 5:
Input: @order = (1, 3, 2, 6, 5, 4)
@friends = (1, 2, 3)
Output: (1, 3, 2)
ID 1 finished 1st.
ID 3 finished 2nd.
ID 2 finished 3rd.
#! /usr/bin/env raku
unit sub MAIN ($order where $order ~~ /^ <[\d\s]>+ $/,
$friends where $friends ~~ /^ <[\d\s]>+ $/,
:v(:$verbose));
my @order = $order.words>>.UInt;
die "Order does not have 1 as lowest value"
unless @order.min == 1;
die "Order does not have the size as highest value"
unless @order.max == @order.elems;
die "Order has duplicates" if @order.repeated;
my @friends = $friends.words>>.UInt;
die "Friends is not inside order"
if @friends.min < 1 || @friends.max > @order.max;
die "Friends is not sorted" unless [<] @friends;
my %position = @order.antipairs;
my @result = @friends.sort: { %position{$_} };
if $verbose
{
for @result -> $id
{
say ": ID $id finished { ordinal(%position{$id} + 1) }.";
}
}
say "({ @result.join(", ") })";
sub ordinal(Int $n)
{
given $n
{
when 1 { "1st" }
when 2 { "2nd" }
when 3 { "3rd" }
default { $_ ~ "th" }
}
}
[3+5] The order and friends strings must contain digits and spaces only, with at least one character.
[7] Get the order values, and coerce the lot into unsigned integers (to
get rid of the pesky IntStr type that does not exactly come out
nicely in print).
[9] Ensure that the lowest value of the order is 1,
[12] that the highest value is the same as the size,
[15] and that there are no duplicates (i.e. repeated
values).
See docs.raku.org/routine/repeated for more information about repeated.
[17] As [7], but this time for the friends.
[19] Ensure that the friends values are inside the outer borders set by the order.
[22] Ensure that the values are sorted.
[24]
We need the mapping between array index and the value, which
we can get with pairs. We actually need them swapped (from value to
index), and can use antipairs for that.
See docs.raku.org/routine/pairs for more information about pairs.
See docs.raku.org/routine/antipairs for more information about antipairs.
[25] Sort the friends, by their finishing order.
[35] Pretty print the result.
[37] Used by verbose mode only. Note the given/when/default
structore.
Running it:
$ ./finishing-order "3 1 4 2 5" "1 2 4"
(1, 4, 2)
$ ./finishing-order "5 4 3 2 1" "1 3 5"
(5, 3, 1)
$ ./finishing-order "2 4 1 3" "1 2 3 4"
(2, 4, 1, 3)
$ ./finishing-order "4 1 3 2" "3"
(3)
$ ./finishing-order "1 3 2 6 5 4" "1 2 3"
(1, 3, 2)
Looking good.
With verbose mode:
$ ./finishing-order -v "3 1 4 2 5" "1 2 4"
: ID 1 finished 2nd.
: ID 4 finished 3rd.
: ID 2 finished 4th.
(1, 4, 2)
$ ./finishing-order -v "5 4 3 2 1" "1 3 5"
: ID 5 finished 1st.
: ID 3 finished 3rd.
: ID 1 finished 5th.
(5, 3, 1)
$ ./finishing-order -v "2 4 1 3" "1 2 3 4"
: ID 2 finished 1st.
: ID 4 finished 2nd.
: ID 1 finished 3rd.
: ID 3 finished 4th.
(2, 4, 1, 3)
$ ./finishing-order -v "4 1 3 2" "3"
: ID 3 finished 3rd.
(3)
$ ./finishing-order -v "1 3 2 6 5 4" "1 2 3"
: ID 1 finished 1st.
: ID 3 finished 2nd.
: ID 2 finished 3rd.
(1, 3, 2)
0 < p < q < n - 1arr[0…p] is strictly increasing, arr[p…q] is strictly decreasing,
and arr[q…n - 1]Input: @arr = (1, 3, 5, 4, 2, 6, 8)
Output: true
Choose p = 2 and q = 4
$arr[0..2] = (1, 3, 5) (strictly increasing)
$arr[2..4] = (5, 4, 2) (strictly decreasing)
$arr[4..6] = (2, 6, 8) (strictly increasing)
Valid indices: 0 < p (2) < q (4) < n-1 (6).
Example 2:
Input: @arr = (1, 4, 4, 2, 1, 3, 5)
Output: false
The first segment (1, 4, 4) contains duplicate adjacent values, so it is not strictly increasing.
No valid p and q exist to satisfy all three strictly monotonic conditions.
Example 3:
Input: @arr = (2, 10, 3, 1, 9)
Output: true
Choose p = 1 and q = 3.
$arr[0..1] = (2, 10) (strictly increasing)
$arr[1..3] = (10, 3, 1) (strictly decreasing)
$arr[3..4] = (1, 9) (strictly increasing)
Example 4:
Input: @arr = (1, 2, 3, 4, 3, 2, 1)
Output: false
This is a standard mountain array (strictly increasing, then strictly decreasing).
There is no second valley (q) that transitions back into a strictly increasing third segment before reaching the end of the array.
Example 5:
Input: @arr = (1, 3, 6, 4, 2, 7, 8, 4)
Output: false
Note that most of the program, lines [11-37], is verbose mode.
File: trionic-array#! /usr/bin/env raku
unit sub MAIN (*@arr where @arr.elems > 0 && all(@arr) ~~ Int,
:v(:$verbose));
my $n = @arr.elems;
my @dir = (@arr[0 .. $n - 2] Z<=> @arr[1 .. $n - 1])
.map: { $_ < 0 ?? "u" !! $_ > 0 ?? "d" !! "=" };
my $m = @dir.join.match(/^ $<p>=<[u]>+ $<d>=<[d]>+ <[u]>+ $/);
if $verbose
{
if $m
{
my $p = $m<p>.chars;
my $q = $p + $m<d>.chars;
say ": p = $p, q = $q";
say ": (@arr[0 .. $p].join(", ")) is strictly increasing";
say ": (@arr[$p .. $q].join(", ")) is strictly decreasing";
say ": (@arr[$q .. $n - 1].join(", ")) is strictly \
increasing";
}
else
{
my $p-max = @dir.join.match(/^<[u]>*/).chars;
my $q-min = $n - 1 - @dir.join.match(/<[u]>*$/).chars;
say ": Increasing prefix ends at index $p-max: \
(@arr[0 .. $p-max].join(", "))";
say ": Increasing suffix starts at index $q-min: \
(@arr[$q-min .. $n - 1].join(", "))";
say ": No p and q satisfy the conditions";
}
}
say $m.defined;
[3] Ensure integers only, and at least one element.
[6] The size.
[8]
We generate two views of the array, one starting at the index 0 and ending
one short of the end, and the other at index 1. Then we compare two and two
(neighbouring) values with the prefix zip operator Z applied to <=>.
Then we map the result
from the numeric values -1, 0, 1 (the value of the enums) to "u", "=", "d" (as -1
would cause problem for the regex in [11]).
See
https://docs.raku.org/routine/%26lt%3B%3D%26gt%3B
for more information about the numeric comparison operator <=>.
See docs.raku.org/routine/Z for more information about Z.
[11] Join the "u", "=", "d" values to a single string, and look for the three
patters with match; first up, then down, and finally up again. The first tow (of the three)
matches are saved by name,
so that we can retrieve the length (and thus infer the value of p and q in [17+18]).
See docs.raku.org/routine/match for more information about match.
[41] Do we have a match? If so, report success. If not, failure.
Running it:
$ ./trionic-array 1 3 5 4 2 6 8
True
$ ./trionic-array 1 4 4 2 1 3 5
False
$ ./trionic-array 2 10 3 1 9
True
$ ./trionic-array 1 2 3 4 3 2 1
False
$ ./trionic-array 1 3 6 4 2 7 8 4
False
Looking good.
With verbose mode:
$ ./trionic-array -v 1 3 5 4 2 6 8
: p = 2, q = 4
: (1, 3, 5) is strictly increasing
: (5, 4, 2) is strictly decreasing
: (2, 6, 8) is strictly increasing
True
$ ./trionic-array -v 1 4 4 2 1 3 5
: Increasing prefix ends at index 1: (1, 4)
: Increasing suffix starts at index 4: (1, 3, 5)
: No p and q satisfy the conditions
False
$ ./trionic-array -v 2 10 3 1 9
: p = 1, q = 3
: (2, 10) is strictly increasing
: (10, 3, 1) is strictly decreasing
: (1, 9) is strictly increasing
True
$ ./trionic-array -v 1 2 3 4 3 2 1
: Increasing prefix ends at index 3: (1, 2, 3, 4)
: Increasing suffix starts at index 6: (1)
: No p and q satisfy the conditions
False
$ ./trionic-array -v 1 3 6 4 2 7 8 4
: Increasing prefix ends at index 2: (1, 3, 6)
: Increasing suffix starts at index 7: (4)
: No p and q satisfy the conditions
False
And that's it.