by Arne Sommer

# Digital Frequalizer with Raku

[214] Published 11. December 2022.

This is my response to The Weekly Challenge #194.

## Challenge #194.1: Digital Clock

You are given time in the format `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 ```
File: digital-clock ```#! /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.

## Challenge #194.2: Frequency Equalizer

You are given a string made of alphabetic characters only, `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. ```
File: frequency-equalizer ```#! /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.