by Arne Sommer

# Merged Maximum with Raku

[276] Published 12. February 2024.

This is my response to The Weekly Challenge #256.

#### Fun Fact about 256

256 is the same as:
`2 ** 8`
`2*2*2*2*2*2*2*2`
`[*](2,2,2,2,2,2,2,2)`
`[*](<2 2 2 2 2 2 2 2>)`

## Challenge #256.1: Maximum Pairs

You are given an array of distinct words, `@words`.

Write a script to find the maximum pairs in the given array. The words `\$words[i]` and `\$words[j]` can be a pair one is reverse of the other.

Example 1: ```Input: @words = ("ab", "de", "ed", "bc") Output: 1 There is one pair in the given array: "de" and "ed" ``` Example 2: ```Input: @words = ("aa", "ba", "cd", "ed") Output: 0 ``` Example 3: ```Input: @words = ("uv", "qp", "st", "vu", "mn", "pq")) Output: 2 ```
File: maximum-pairs ```#! /usr/bin/env raku unit sub MAIN (*@words where @words.elems > 0 # [1] && @words.elems == @words.unique.elems, # [1a] :v(:\$verbose)); my \$words = @words.Set; # [2] my \$count = 0; # [3] for \$words.keys.sort -> \$word # [4] { my \$reverse = \$word.flip; # [5] if \$words{\$reverse} # [6] { \$count++; # [6a] say ": + Word \$word has a reverse ({\$reverse})" if \$verbose; } elsif (\$verbose) { say ": Word \$word does not have a reverse" if \$verbose; } } say \$count div 2; # [7] ```

[1] At least one word, and ensure that they are all unique [1a].

[2] Turn the list of words into a `Set`, a read only hash like structure. This makes it easy to look up values (i.e. words).

See docs.raku.org/type/Set for more information about the `Set` type.

[3] The number of matching pairs will end up here. (But see [7].)

[4] Iterate over the words, in alphabetical order.

[5] Reverse the word with `flip`.

[6] Do we have the reveresed word in the Set? if so, increase the counter.

[7] Print the result, divided by two. As we count both elements in a pair, end the challenge wants us to count the pairs. `div` is integer division.

See docs.raku.org/routine/div for more information about `div`.

Running it:

```\$ ./maximum-pairs ab de ed bc 1 \$ ./maximum-pairs aa ba cd ed 0 \$ ./maximum-pairs uv qp st vu mn pq 2 ```

Looking good.

With verbose mode:

```\$ ./maximum-pairs -v ab de ed bc : Word ab does not have a reverse : Word bc does not have a reverse : + Word de has a reverse (ed) : + Word ed has a reverse (de) 1 \$ ./maximum-pairs -v aa ba cd ed : + Word aa has a reverse (aa) : Word ba does not have a reverse : Word cd does not have a reverse : Word ed does not have a reverse 0 \$ ./maximum-pairs -v uv qp st vu mn pq : Word mn does not have a reverse : + Word pq has a reverse (qp) : + Word qp has a reverse (pq) : Word st does not have a reverse : + Word uv has a reverse (vu) : + Word vu has a reverse (uv) 2 ```

Oops. The second excample does not look good, as «aa» is the reverse of itself. But luckily - or unluckily, depending on your point of view, the `div` got rid of the 0.5 that normal division would have given us.

But this is still a problem, if we add more palindromes (words where the reverse is the same as the word itself):

```\$ ./maximum-pairs -v aa bb cc dd : + Word aa has a reverse (aa) : + Word bb has a reverse (bb) : + Word cc has a reverse (cc) : + Word dd has a reverse (dd) 2 ```

Let us fix that.

File: maximum-pairs-palindrome ```#! /usr/bin/env raku unit sub MAIN (*@words where @words.elems > 0 && @words.elems == @words.unique.elems, :v(:\$verbose)); my \$words = @words.Set; my \$count = 0; for \$words.keys.sort -> \$word { my \$reverse = \$word.flip; if (\$word eq \$reverse) # [1] { say ": Word \$word is a palindrome" if \$verbose; } elsif \$words{\$reverse} # [1a] { \$count++; say ": + Word \$word has a reverse ({\$reverse})" if \$verbose; } elsif (\$verbose) { say ": Word \$word does not have a reverse" if \$verbose; } } say \$count div 2; ```

[1] If the word is a palindrome, ignore it. If not, ... [1a].

The palindromic example works now:

```\$ ./maximum-pairs-palindrome -v aa bb cc dd : Word aa is a palindrome : Word bb is a palindrome : Word cc is a palindrome : Word dd is a palindrome 0 ```

Another example, showing all the three word types:

```\$ ./maximum-pairs-palindrome -v aa ab bb bc cc cd dd dc cb : Word aa is a palindrome : Word ab does not have a reverse : Word bb is a palindrome : + Word bc has a reverse (cb) : + Word cb has a reverse (bc) : Word cc is a palindrome : + Word cd has a reverse (dc) : + Word dc has a reverse (cd) : Word dd is a palindrome 2 ```

## Challenge #256.2: Merge Strings

You are given two strings, `\$str1` and `\$str2`.

Write a script to merge the given strings by adding in alternative order starting with the first string. If a string is longer than the other then append the remaining at the end.

Example 1: ```Input: \$str1 = "abcd", \$str2 = "1234" Output: "a1b2c3d4" ``` Example 2: ```Input: \$str1 = "abc", \$str2 = "12345" Output: "a1b2c345" ``` Example 3: ```Input: \$str1 = "abcde", \$str2 = "123" Output: "a1b2c3de" ```

Raku has a Zip function `zip` and a corresponding operator `Z` that looks promising. The result is a list of pairs objects, with one value from each input list. But, if the lengths differ, any remaing values will be discarded. E.g.

```> say zip("abc".comb, "12345".comb).raku (("a", "1"), ("b", "2"), ("c", "3")).Seq > say ("abc".comb Z "12345".comb).raku (("a", "1"), ("b", "2"), ("c", "3")).Seq ```

See docs.raku.org/routine/zip for more information about `zip`.

See docs.raku.org/routine/Z for more information about `Z`.

The `roundrobin` function takes care of trailing values:

```> roundrobin("abc".comb, "12345".comb).raku (("a", "1"), ("b", "2"), ("c", "3"), ("4",), ("5",)).Seq > roundrobin("abcde".comb, "123".comb).raku (("a", "1"), ("b", "2"), ("c", "3"), ("d",), ("e",)).Seq ```

See docs.raku.org/routine/roundrobin for more information about `roundrobin`.

Note that the two single values are lists with one element only (shown by the trailing comma). It is not possible at this point to tell which input list the value came from.

File: merge-strings ```#! /usr/bin/env raku unit sub MAIN (\$str1 where \$str1.chars > 0, \$str2 where \$str2.chars > 0); say roundrobin(\$str1.comb, \$str2.comb)>>.join.join; # [1] ```

[1] The `>>.join` joins the Pair objects into two character strings, and the `.join` joins those string together to one single string, ready for printing.

Running it:

```\$ ./merge-strings abcd 1234 a1b2c3d4 \$ ./merge-strings abc 12345 a1b2c345 \$ ./merge-strings abcde 123 a1b2c3de ```

Looking good.

We can do it manually, with `gather`/`take`:

File: merge-strings-gather ```#! /usr/bin/env raku unit sub MAIN (\$str1 where \$str1.chars > 0, \$str2 where \$str2.chars > 0, :v(:\$verbose)); my \$ms := gather { my @str1 = \$str1.comb; my @str2 = \$str2.comb; while (@str1.elems || @str2.elems) # [1] { if @str1.elems # [2a] { say ":Fetching character '@str1[0]' from string 1" if \$verbose; take @str1.shift; # [2] } if @str2.elems # [3a] { say ":Fetching character '@str2[0]' from string 2" if \$verbose; take @str2.shift; # [3] } } } say \$ms.join; ```

[1,2,3] As long as there are more values in the input strings [1], take one from the first [2] if there are more in that one [2a], and take one from the second [3] if there are more in that one [3a].

See my Raku Gather, I Take article or docs.raku.org/language/control#gather/take for more information about `gather`/`take`.

Running it gives the expected result, shown here with verbose mode:

```\$ ./merge-strings-gather -v abcd 1234 :Fetching character 'a' from string 1 :Fetching character '1' from string 2 :Fetching character 'b' from string 1 :Fetching character '2' from string 2 :Fetching character 'c' from string 1 :Fetching character '3' from string 2 :Fetching character 'd' from string 1 :Fetching character '4' from string 2 a1b2c3d4 \$ ./merge-strings-gather -v abc 12345 :Fetching character 'a' from string 1 :Fetching character '1' from string 2 :Fetching character 'b' from string 1 :Fetching character '2' from string 2 :Fetching character 'c' from string 1 :Fetching character '3' from string 2 :Fetching character '4' from string 2 :Fetching character '5' from string 2 a1b2c345 \$ ./merge-strings-gather -v abcde 123 :Fetching character 'a' from string 1 :Fetching character '1' from string 2 :Fetching character 'b' from string 1 :Fetching character '2' from string 2 :Fetching character 'c' from string 1 :Fetching character '3' from string 2 :Fetching character 'd' from string 1 :Fetching character 'e' from string 1 a1b2c3de ```

And that's it.