This is my response to The Weekly Challenge #249.
Input: @ints = (3, 2, 3, 2, 2, 2)
Output: (2, 2), (3, 3), (2, 2)
There are 6 elements in @ints.
They should be divided into 6 / 2 = 3 pairs.
@ints is divided into the pairs (2, 2), (3, 3), and (2, 2) satisfying
all the conditions.
Example 2:
Input: @ints = (1, 2, 3, 4)
Output: ()
There is no way to divide @ints 2 pairs such that the pairs satisfy
every condition.
This is actually quite easy:
#! /usr/bin/env raku
unit sub MAIN (*@ints where @ints.elems %% 2
&& @ints.elems > 0
&& all(@ints) ~~ Int, # [1]
:v(:$verbose));
my @output; # [2]
my @sorted = @ints>>.Int.sort; # [3]
say ":Sorted: { @sorted.join(",") }" if $verbose;
while @sorted # [4]
{
my $first = @sorted.shift; # [4a]
my $second = @sorted.shift; # [4b]
if $first == $second # [5]
{
@output.push: ($first, $second); # [5a]
say ":Pair: $first,$second" if $verbose;
}
else # [6]
{
say ":Non-pair: $first,$second" if $verbose;
say "()"; # [6a]
exit; # [6b]
}
}
say @output.map({ "($_[0], $_[1])"}).join(", "); # [7]
[1] Ensure that we have an even number of values (with
the divisibility operator %%
), that we have at least 1 (or 2,
rather) and that they are all integers.
See docs.raku.org/routine/%% for more information about the Divisibility Operator %%
.
[2] The result will end up here. We cannot print the pairs as we encounter them, as we may come across an unequal pair later on.
[3] Coerce the input values to integers (i.e. get rid of the
IntStr
type we got courtesy of the command line) and sort them
(numerically).
See docs.raku.org/type/IntStr for more information about the IntStr
type.
[4] As long as we have unfinished business, get the next two values [4a,4b].
[5] Are they equal? If so, add them to the output [5a].
[6] If not, print the empty array [6a] and exit [6b].
[7] Pretty print the result.
Running it:
$ ./equal-pairs 3 2 3 2 2 2
(2, 2), (2, 2), (3, 3)
$ ./equal-pairs 1 2 3 4
()
Looking good.
With verbose mode:
$ ./equal-pairs -v 3 2 3 2 2 2
:Sorted: 2,2,2,2,3,3
:Pair: 2,2
:Pair: 2,2
:Pair: 3,3
(2, 2), (2, 2), (3, 3)
$ ./equal-pairs -v 1 2 3 4
:Sorted: 1,2,3,4
:Non-pair: 1,2
()
The output from the first example as given by the challenge seems random, but I have chosen to print the pairs in increasing order instead.
s
consisting of only the characters
"D
" and "I
".
[0 .. length(s)]
such that
for each character s[i]
in the string:
s[i] == 'I' ⇒ perm[i] < perm[i + 1]
s[i] == 'D' ⇒ perm[i] > perm[i + 1]
Example 1:
Input: $str = "IDID"
Output: (0, 4, 1, 3, 2)
Example 2:
Input: $str = "III"
Output: (0, 1, 2, 3)
Example 3:
Input: $str = "DDI"
Output: (3, 2, 0, 1)
This is even easier:
File: di-string-match
#! /usr/bin/env raku
unit sub MAIN ($s where $s ~~ /^<[ID]>+$/, :v(:$verbose)); # [1]
my @output; # [2]
my @integers = (0 .. $s.chars); # [3]
for $s.comb -> $char # [4]
{
if $char eq "I" # [5]
{
@output.push: @integers.shift; # [5a]
say ":I -> lowest integer { @output.tail }" if $verbose; # [9]
}
else # [6]
{
@output.push: @integers.pop; # [6a]
say ":D -> highest integer { @output.tail }" if $verbose;
}
}
@output.push: @integers[0]; # [7]
say ": -> remaining integer { @output.tail }" if $verbose;
say "({ @output.join(", ") })"; # [8]
[1] Ensure that the string contains only «I» and «D», with at least one.
[2] The output will end up here.
[3] The integers to shuffle around, sorted by the lowest first.
[4] Iterate over the indiviual characters (with comb
).
See docs.raku.org/routine/comb for more information about comb
.
[5] If the character is «I», we take the lowest remaining integer
(with shift
, from the left hand side).
[6] Else (it is an «D») we take the highest remaining
integer (with pop
, from the right hand side).
See docs.raku.org/routine/pop for more information about pop
.
[7] There is one more integer than characters, and we add it to the end.
[8] Pretty print the result.
[9] tail
gives the last (rightmost) entry
See docs.raku.org/routine/tail for more information about tail
.
Running it:
$ ./di-string-match IDID
(0, 4, 1, 3, 2)
$ ./di-string-match III
(0, 1, 2, 3)
$ ./di-string-match DDI
(3, 2, 0, 1)
Looking good.
With verbose mode:
$ ./di-string-match -v IDID
:I -> lowest integer 0
:D -> highest integer 4
:I -> lowest integer 1
:D -> highest integer 3
: -> remaining integer 2
(0, 4, 1, 3, 2)
$ ./di-string-match -v III
:I -> lowest integer 0
:I -> lowest integer 1
:I -> lowest integer 2
: -> remaining integer 3
(0, 1, 2, 3)
$ ./di-string-match -v DDI
:D -> highest integer 3
:D -> highest integer 2
:I -> lowest integer 0
: -> remaining integer 1
(3, 2, 0, 1)
And that's it.