This is my response to The Weekly Challenge #275.
$sentence
and list of broken keys @keys
.
Input: $sentence = "Perl Weekly Challenge", @keys = ('l', 'a')
Output: 0
Example 2:
Input: $sentence = "Perl and Raku", @keys = ('a')
Output: 1
Only Perl since the other word two words contain 'a' and can't be typed fully.
Example 3:
Input: $sentence = "Well done Team PWC", @keys = ('l', 'o')
Output: 2
Example 4:
Input: $sentence = "The joys of polyglottism", @keys = ('T')
Output: 2
#! /usr/bin/env raku
unit sub MAIN ($sentence where $sentence.chars > 0, # [1]
*@keys where @keys.elems > 0, # [2]
:v(:$verbose));
my @words = $sentence.words; # [3]
say ": Words: { @words.map({ "'$_'"}).join(", ")}" if $verbose;
my @fully = @words.map({ $_.contains(any(@keys)) ?? 0 !! 1 }); # [4]
say ": Fully: { @fully.join(", ") }" if $verbose;
say @fully.sum; # [5]
[1] Ensure at least one character in the sentence.
[2] Use a slurpy array (the *
prefix) for the keys, and ensure that
we get at least one of them.
[3] Get the individual words (with words
), or rather
what Raku considers words.
See docs.raku.org/routine/words for more information about words
.
[4] Use map
to convert each word into 1 if the word
is fully, and 0 if not. We use contains
to decide this, combined with
an any
junction on all the keys.
See docs.raku.org/routine/contains for more information about contains
.
[5] Add up the ones and zeroes and print the result.
Running it:
$ ./broken-keys "Perl Weekly Challenge" l a
0
$ ./broken-keys "Perl and Raku" a
1
$ ./broken-keys "Well done Team PWC" l o
2
$ ./broken-keys "The joys of polyglottism" T
3
Looking good, except for the last one. It seems that the matching should be case insenstive.
Here is verbose mode, in case you want it, before we tackle the case of the case:
$ ./broken-keys -v "Perl Weekly Challenge" l a
: Words: 'Perl', 'Weekly', 'Challenge'
: Fully: 0, 0, 0
0
$ ./broken-keys -v "Perl and Raku" a
: Words: 'Perl', 'and', 'Raku'
: Fully: 1, 0, 0
1
$ ./broken-keys -v "Well done Team PWC" l o
: Words: 'Well', 'done', 'Team', 'PWC'
: Fully: 0, 0, 1, 1
2
$ ./broken-keys -v "The joys of polyglottism" T
: Words: 'The', 'joys', 'of', 'polyglottism'
: Fully: 0, 1, 1, 1
3
The modified program:
File: broken-keys-lc
#! /usr/bin/env raku
unit sub MAIN ($sentence where $sentence.chars > 0,
*@keys where @keys.elems > 0,
:v(:$verbose));
my @words = $sentence.words;
my @lc_keys = @keys>>.lc; # [1]
say ": Words: { @words.map({ "'$_'"}).join(", ")}" if $verbose;
my @fully = @words.map({ $_.lc.contains(any(@lc_keys)) ?? 0 !! 1 }); # [2]
say ": Fully: { @fully.join(", ") }" if $verbose;
say @fully.sum;
[1] Convert the entire array of words to lowercase with
>>.lc
.
See docs.raku.org/routine/lc for more information about lc
.
[2] Convert each word to lowercase before the comparison.
Running it gives the correct result for all four examples, shown here with verbose mode:
$ ./broken-keys-lc -v "Perl Weekly Challenge" l a
: Words: 'Perl', 'Weekly', 'Challenge'
: Fully: 0, 0, 0
0
$ ./broken-keys-lc -v "Perl and Raku" a
: Words: 'Perl', 'and', 'Raku'
: Fully: 1, 0, 0
1
$ ./broken-keys-lc -v "Well done Team PWC" l o
: Words: 'Well', 'done', 'Team', 'PWC'
: Fully: 0, 0, 1, 1
2
$ ./broken-keys-lc -v "The joys of polyglottism" T
: Words: 'The', 'joys', 'of', 'polyglottism'
: Fully: 0, 1, 1, 0
2
$str
, where each character is
either a letter or a digit.
Input: $str = 'a1c1e1'
Ouput: 'abcdef'
shift('a', 1) => 'b'
shift('c', 1) => 'd'
shift('e', 1) => 'f'
Example 2:
Input: $str = 'a1b2c3d4'
Output: 'abbdcfdh'
shift('a', 1) => 'b'
shift('b', 2) => 'd'
shift('c', 3) => 'f'
shift('d', 4) => 'h'
Example 3:
Input: $str = 'b2b'
Output: 'bdb'
Example 4:
Input: $str = 'a16z'
Output: 'abgz'
The challenge does not say what to do if the replacement character is not a letter, so I have chosen to ignore that problem.
File: replace-digits
#! /usr/bin/env raku
unit sub MAIN ($str where $str ~~ /^<[a..z]> <[a..z 0..9]>*$/); # [1]
$str.comb.map( *.&do-magic ).join.say; # [2]
sub do-magic ($letter) # [3]
{
state $prev = ""; # [4]
return ($prev.ord + $letter).chr if $letter eq any('0' .. '9'); # [5]
$prev = $letter; # [6]
return $letter; # [7]
}
[1] EWnsure that we get one letter (lower case only, as in the examples), followed by zero or more letters (also lower case only) or digits.
[2] Split the string into individual characters (with comb
) apply
the custom procedure to each value (with map
), join the result together
to a new string, and print the result.
[3] The procedure, taking one character as input.
[4] The previous character, as a state
variable so that
we keep the value between calls.
See
docs.raku.org/syntax/state for
more information about the variable declarator state
.
[5] If we get a digit, return a modified version of the previous character (that we have saved in the state variable).
[6] We have a letter if we get here. Save it for later.
[7] Return the letter unchanged.
Running it:
$ ./replace-digits a1c1e1
abcdef
$ ./replace-digits a1b2c3d4
abbdcfdh
$ ./replace-digits b2b
bdb
$ ./replace-digits a16z
abgz
Looking good.
And that's it.