by Arne Sommer

# Changingly Good with Raku

[303] Published 16. August 2024.

This is my response to The Weekly Challenge #282.

## Challenge #282.1: Good Integer

You are given a positive integer, `\$int`, having `3 or more digits`.

Write a script to return the `Good Integer` in the given integer or `-1` if none found.

A good integer is exactly three consecutive matching digits.

Example 1: ```Input: \$int = 12344456 Output: "444" ``` Example 2: ```Input: \$int = 1233334 Output: -1 ``` Example 3: ```Input: \$int = 10020003 Output: "000" ```

Off we go, without careful reading of the challenge...

File: good-integer-regexp ```#! /usr/bin/env raku unit sub MAIN (UInt \$int where \$int > 99); # [1] say \$int ~~ /(.) {} :my \$c=\$0; <?after \$c ** 3> / # [2] ?? \$0.Str x 3 # [2a] !! -1; # [2b] ```

[1] An unsigned integer, with a value of 100 or above.

[2] This «three identical characters in a row» regexp is reused (slightly modified) from my recent The Nine Billion Names of God with Raku article. We capture only one of them (the characters, with the parens), so have to print three of that one with the string repetition operator `x`). Note the stringification courtesy of `Str`, as working with a match object does not really work out here [2a]. Or `-1` on failure.

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

Running it:

```\$ ./good-integer-regexp 12344456 444 \$ ./good-integer-regexp 1233334 333 \$ ./good-integer-regexp 10020003 000 ```

Oops. The second one is wrong. The regexp matches three or more, and not exactly three as prescribed by the challenge.

A second try, and a considerable longer program to boot.

File: good-integer ```#! /usr/bin/env raku unit sub MAIN (UInt \$int where \$int > 99, :v(:\$verbose)); my \$matching := gather # [1] { my @digits = \$int.comb; # [2] my \$current = @digits.shift; # [3] my \$count = 1; # [4] while @digits # [5] { my \$next = @digits.shift; # [6] if \$current eq \$next # [7] { \$count++; # [7a] } else # [8] { take \$current x \$count; # [8a] \$current = \$next; # [8b] \$count = 1; # [8c] } } take \$current if \$current; # [9] } for \$matching -> \$candidate # [10] { say ":Considering candidate '\$candidate'" if \$verbose; if \$candidate.chars == 3 # [11] { say \$candidate; # [11a] exit; # [11b] } } say -1; # [12] ```

[1] Using `gather` (here) and `take` (in [8a] and [9]) to set up a partial string feed works pretty well.

[2] Turn the input into an array of individual digits.

[3] Get the first digit.

[4] The size of the partial string starts out at 1.

[5] As long as we have additional digits to process.

[6] Get the next one.

[7] Identical to the previous one? If so, count it.

[8] If not, return (with `take`) the old value [8a], save the new value [8b] and reset the counter [8c].

[9] Return the last value on loop termination. The test is actually not really needed.

[10] Iterate over the partial strings.

[11] Does it have the required length? If so, print it [11a] and exit [11b].

[12] We have failed to find a match if we reach this part. Say so.

Running it:

```\$ ./good-integer 12344456 444 \$ ./good-integer 1233334 -1 \$ ./good-integer 10020003 000 ```

Looking good.

With verbose mode:

```\$ ./good-integer -v 12344456 :Considering candidate '1' :Considering candidate '2' :Considering candidate '3' :Considering candidate '444' 444 \$ ./good-integer -v 1233334 :Considering candidate '1' :Considering candidate '2' :Considering candidate '3333' :Considering candidate '4' -1 \$ ./good-integer -v 10020003 :Considering candidate '1' :Considering candidate '00' :Considering candidate '2' :Considering candidate '000' 000 ```

One can argue that «000» is not an integer. It is easy to amend the program to support that notion, but I will refrain from doing so.

## Challenge #282.2: Changing Keys

You are given an alphabetic string, `\$str`, as typed by user.

Write a script to find the number of times user had to change the key to type the given string. Changing key is defined as using a key different from the last used key. The `shift` and `caps lock` keys won’t be counted.

Example 1: ```Input: \$str = 'pPeERrLl' Ouput: 3 p -> P : 0 key change P -> e : 1 key change e -> E : 0 key change E -> R : 1 key change R -> r : 0 key change r -> L : 1 key change L -> l : 0 key change ``` Example 2: ```Input: \$str = 'rRr' Ouput: 0 ``` Example 3: ```Input: \$str = 'GoO' Ouput: 1 ```

File: changing-keys ```#! /usr/bin/env raku unit sub MAIN (\$str where \$str ~~ /^<[a..z A..Z]>+\$/, # [1] :v(:\$verbose)); my @letters = \$str.comb; # [2] my \$first = @letters.shift; my \$count = 0; while @letters { my \$second = @letters.shift; my \$change = \$first.lc ne \$second.lc; # [3] say ": \$first -> \$second : { +\$change } key change" if \$verbose; \$count++ if \$change; # [4] \$first = \$second; # [5] } say \$count; # [6] ```

[1] Ensure english letters only, lower- and uppercase.

[2] This and the loop is almost a copy of the one in the first half of the challenge.

[3] Compare the lowercase versions (`lc`) of the strings.

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

[4] Increase the counter if the letters differ.

[5] Set up for the next iteration.

[6] Print the result.

Running it:

```\$ ./changing-keys pPeERrLl 3 \$ ./changing-keys rRr 0 \$ ./changing-keys GoO 1 ```

Looking good.

With verbose mode:

```\$ ./changing-keys -v pPeERrLl : p -> P : 0 key change : P -> e : 1 key change : e -> E : 0 key change : E -> R : 1 key change : R -> r : 0 key change : r -> L : 1 key change : L -> l : 0 key change 3 \$ ./changing-keys -v rRr : r -> R : 0 key change : R -> r : 0 key change 0 \$ ./changing-keys -v GoO : G -> o : 1 key change : o -> O : 0 key change 1 ```

And that's it.