with Raku

This is my response to The Weekly Challenge #194.

You are given time in the format

Write a script to find the highest digit between

Example 1:

File: digital-clock
`hh:mm`

with one missing digit.
Write a script to find the highest digit between

`0-9`

that makes it valid time.
Example 1:

Input: $time = '?5:00'
Output: 1
Since 05:00 and 15:00 are valid time and no other digits can fit in the
missing place.

Example 2:
Input: $time = '?3:00'
Output: 2

Example 3:
Input: $time = '1?:00'
Output: 9

Example 4:
Input: $time = '2?:00'
Output: 3

Example 5:
Input: $time = '12:?5'
Output: 5

Example 6:
Input: $time = '12:5?'
Output: 9

#! /usr/bin/env raku
subset MissingTime where /^<[012\?]><[0..9\?]>\:<[0..5\?]><[0..9\?]>$/; # [1]
unit sub MAIN (MissingTime $time where $time.comb.Bag<?> == 1); # [2]
die "Illegal time"
if $time.substr(0,1) eq "2" && $time.substr(1,1) ne any <0 1 2 3 ?>; # [3]
given $time # [4]
{
when .substr(0,1) eq "?" && .substr(1,1) < 4 { say "2"; } # "?[0..3]:**" # [5]
when .substr(0,1) eq "?" { say "1"; } # "?[4..9]:**" # [5a]
when .substr(1,1) eq "?" && .substr(0,1) < 2 { say "9"; } # "[0..1]?:**" # [6]
when .substr(1,1) eq "?" { say "3"; } # "2?:**" # [6a]
when .substr(3,1) eq "?" { say "5"; } # "**:?*" # [7]
when .substr(4,1) eq "?" { say "9"; } # "**:*?" # [8]
}

[1] A grammar is ideal here, but I have chosen the lazier
route of using a custom type (with `subset`

).

Feel free to read «lazier» as «crazier».
I use a `subset`

, two `where`

clauses, a `Bag`

, a
`substr`

(ing) and an `any`

junction - just to avoid a grammar.

See
docs.raku.org/language/typesystem#index-entry-subset-subset
for more information about `subset`

.

[2] The custom type allows up to four question marks, and thus we use a
`where`

clause that ensures that there is one and only one of them. We do this
be turning the string into a single letters, and then into a `Bag`

- which is
a hash like structure where the characters are the keys, end the frequency is the value.
Then we look up the question mark, and there should be only one.

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

type.

[3] The subset (in [1]) took care of the first digit (0, 1 or 2), but the second one cannot be higher than 3 if the first one is 2. This check ensures that - or a question mark.

[4] Using `given`

/`when`

, which is
Raku speak for «switch», gives compact code.

[5] The first character is a question mark. The highest value is either 2 or 1 [5a], depending on the second digit.

[6] The second character is a question mark. The highest value is either 9 or 3 [6a], depending on the first digit.

[7] The third digit has 5 as the highest value.

[8] The fourth digit has 9 as the highest value.

Running it:

$ ./digital-clock ?5:00
1
$ ./digital-clock ?3:00
2
$ ./digital-clock 1?:00
9
$ ./digital-clock 2?:00
3
$ ./digital-clock 12:?5
5
$ ./digital-clock 12:5?
9

Looking good.

You are given a string made of alphabetic characters only,

Write a script to determine whether removing only one character can make the frequency of the remaining characters the same.

Example 1:

File: frequency-equalizer
`a-z`

.
Write a script to determine whether removing only one character can make the frequency of the remaining characters the same.

Example 1:

Input: $s = 'abbc'
Output: 1 since removing one alphabet 'b' will give us 'abc' where each
alphabet frequency is the same.

Example 2:
Input: $s = 'xyzyyxz'
Output: 1 since removing 'y' will give us 'xzyyxz'.

Example 2:
Input: $s = 'xzxz'
Output: 0 since removing any one alphabet would not give us string with
same frequency alphabet.

#! /usr/bin/env raku
subset az where /^<[a..z]>+$/; # [1a]
unit sub MAIN (az $s, :v(:$verbose)); # [1]
my @freq = $s.comb.Bag.values.sort; # [2]
say ": Frequency sorted: @freq[]" if $verbose;
@freq.push(@freq.pop - 1); # [3]
say ": Frequency lowered: @freq[]" if $verbose;
say ( [==] @freq ) ?? 1 !! 0; # [4]

[1] Ensure that the input only conains klower case letters (a..z), using a custom type (set up in [1a]).

[2] Get the frequency of the used letters. The `Bag`

gives a hash like
structure (as explained in the first part of this challenge), and we apply
`.values`

to get just the values - or the frequencies. Then we sort
the list, giving us the highest value at the end.

[3] Remove the highest value (with `pop`

that takes the last value
in the list), subtract one, and push the new value back in the list og frequencies.

[4] Now all the frequencies should be the same.
We use `[==]`

to check this. The Reduction Metaoperator `[]`

inserts the given operator (the numeric eqality operator `==`

) between each
value in the list and collapses the whole shabang to a Boolean value. Finally we print
«1» if they are equal, and «0» otherwise.

See
docs.raku.org/language/operators#Reduction_metaoperators for more
information about the Reduction Metaoperator `[]`

.

Running it:

$ ./frequency-equalizer abbc
1
$ ./frequency-equalizer xyzyyxz
1
$ ./frequency-equalizer xzxz
0

Looking good.

With verbode mode:

$ ./frequency-equalizer -v abbc
: Frequency sorted: 1 1 2
: Frequency lowered: 1 1 1
1
$ ./frequency-equalizer -v xyzyyxz
: Frequency sorted: 2 2 3
: Frequency lowered: 2 2 2
1
$ ./frequency-equalizer -v xzxz
: Frequency sorted: 2 2
: Frequency lowered: 2 1
0

And that's it.