This is my response to The Weekly Challenge #194.
hh:mm
with one missing digit.
0-9
that makes it valid time.
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.
a-z
.
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.