by Arne Sommer

# Equal DI with Raku

[269] Published 26. December 2023.

This is my response to The Weekly Challenge #249.

## Challenge #249.1: Equal Pairs

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:
1. Each element belongs to exactly one pair.
2. The elements present in a pair are equal.
Example 1: ```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:

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

File: equal-pairs ```#! /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.

## Challenge #249.2: DI String Match

You are given a string `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.