with Raku

This is my response to The Weekly Challenge #249.

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

Write a script to divide the given array into equal pairs such that:

Write a script to divide the given array into equal pairs such that:

- Each element belongs to exactly one pair.
- The elements present in a pair are equal.

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:

- Sort the input values (numerically)
- Grab 2 values; abort if they are not equal
- Add the 2 values to the result
- More input values? If so go to 2.
- Print the result.

#! /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.

You are given a string

Find a permutation of the integers

`s`

consisting of only the characters
"`D`

" and "`I`

".
Find a permutation of the integers

`[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.