with Raku

This is my response to The Weekly Challenge #282.

You are given a positive integer,

Write a script to return the

A good integer is exactly three consecutive matching digits.

Example 1:

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

You are given an alphabetic string,

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

Example 1:

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