This is my response to The Weekly Challenge #365.
Input: $str = "abc", $k = 1
Output: 6
Conversion: a = 1, b = 2, c = 3 -> 123
Digit sum: 1 + 2 + 3 = 6
Example 2:
Input: $str = "az", $k = 2
Output: 9
Conversion: a = 1, z = 26 -> 126
1st sum: 1 + 2 + 6 = 9
2nd sum: 9
Example 3:
Input: $str = "cat", $k = 1
Output: 6
Conversion: c = 3, a = 1, t = 20 -> 3120
Digit sum: 3 + 1 + 2 + 0 = 6
Example 4:
Input: $str = "dog", $k = 2
Output: 8
Conversion: d = 4, o = 15, g = 7 -> 4157
1st sum: 4 + 1 + 5 + 7 = 17
2nd sum: 1 + 7 = 8
Example 5:
Input: $str = "perl", $k = 3
Output: 6
Conversion: p = 16, e = 5, r = 18, l = 12 -> 1651812
1st sum: 1 + 6 + 5 + 1 + 8 + 1 + 2 = 24
2nd sum: 2+4 = 6
3rd sum: 6
I prefer to use the hyphenated task names
($task-name.words.join("-").lc $task-name.words>>.substr(0,1).join.lc
#! /usr/bin/env raku
unit sub MAIN ($str where $str ~~ /^ <[a..z]>+ $/, # [1]
UInt $k where $k > 0, # [2]
:v(:$verbose));
my @digits = $str.comb.map({ $_.ord - 'a'.ord + 1 }); # [3]
my $sum = @digits.join; # [4]
say ": Digits: { @digits.join(", ") } -> $sum" if $verbose;
for 1 .. $k -> $iteration # [5]
{
@digits = $sum.comb; # [6]
$sum = @digits.sum; # [7]
say ": Iteration $iteration: { @digits.join(" + ") } = $sum" if $verbose;
last if $sum < 10; # [8]
}
say $sum; # [9]
[1] The string, with lowercase English letters only. At least one character.
[2] The integer, which I insist must be positive.
[3] Split the string into individual characters with comb).
Then we translate each of them into numbers (a =>1, b => 2, .. , z => 26) with
ord inside a map.
See docs.raku.org/routine/ord for more information about ord.
[4] Join the values to a new string; the initial sum.
[5] As many iterations as requested.
[6] Get the individual digits.
[7] Get the sum of them with sum.
See docs.raku.org/routine/sum for more information about sum.
[8] Bail out if we have reduced the sum to a single digit, as additional iterations would only waste time.
[9] Print the sum.
Running it:
$ ./alphabet-index-digit-sum abc 1
6
$ ./alphabet-index-digit-sum az 2
9
$ ./alphabet-index-digit-sum cat 1
6
$ ./alphabet-index-digit-sum dog 2
8
$ ./alphabet-index-digit-sum perl 3
6
Looking good.
With verbose mode:
$ ./alphabet-index-digit-sum -v abc 1
: Digits: 1, 2, 3 -> 123
: Iteration 1: 1 + 2 + 3 = 6
6
$ ./alphabet-index-digit-sum -v az 2
: Digits: 1, 26 -> 126
: Iteration 1: 1 + 2 + 6 = 9
: Iteration 2: 9 = 9
9
$ ./alphabet-index-digit-sum -v cat 1
: Digits: 3, 1, 20 -> 3120
: Iteration 1: 3 + 1 + 2 + 0 = 6
6
$ ./alphabet-index-digit-sum -v dog 2
: Digits: 4, 15, 7 -> 4157
: Iteration 1: 4 + 1 + 5 + 7 = 17
: Iteration 2: 1 + 7 = 8
8
$ ./alphabet-index-digit-sum -v perl 3
: Digits: 16, 5, 18, 12 -> 1651812
: Iteration 1: 1 + 6 + 5 + 1 + 8 + 1 + 2 = 24
: Iteration 2: 2 + 4 = 6
: Iteration 3: 6 = 6
6
The [8] row prevents silliness like this from running along:
$ ./alphabet-index-digit-sum -v perl 100000000000000
: Digits: 16, 5, 18, 12 -> 1651812
: Iteration 1: 1 + 6 + 5 + 1 + 8 + 1 + 2 = 24
: Iteration 2: 2 + 4 = 6
6
A high $k value can be used to reduce the sum to a single digit,
if that is the goal, without a run time penalty because of [8].
Input: $str = "cat and dog"
Output: 3
Tokens: "cat", "and", "dog"
Example 2:
Input: $str = "a-b c! d,e"
Output: 2
Tokens: "a-b", "c!", "d,e"
"a-b" -> valid (one hyphen between letters)
"c!" -> valid (punctuation at end)
"d,e" -> invalid (punctuation not at end)
Example 3:
Input: $str = "hello-world! this is fun"
Output: 4
Tokens: "hello-world!", "this", "is", "fun"
All satisfy the rules.
Example 4:
Input: $str = "ab- cd-ef gh- ij!"
Output: 2
Tokens: "ab-", "cd-ef", "gh-", "ij!"
"ab-" -> invalid (hyphen not surrounded by letters)
"cd-ef" -> valid
"gh-" -> invalid
"ij!" -> valid
Example 5:
Input: $str = "wow! a-b-c nice."
Output: 2
Tokens: "wow!", "a-b-c", "nice."
"wow!" -> valid
"a-b-c" -> invalid (more than one hyphen)
"nice." -> valid
I have ordered the checks in a way that I feel is optimal, but the verbose output is slightly different from the challenge explanation(s) because of this. That does not really matter.
File: valid-token-counter
#! /usr/bin/env raku
unit sub MAIN ($str, :v(:$verbose)); # [1]
my $valid = 0; # [2]
for $str.words -> $word # [3]
{
if $word ~~ /\d/ # [4]
{
say ": $word -> invalid (digit)" if $verbose;
}
elsif $word ~~ /<[.,!]>./ # [5]
{
say ": $word -> invalid (punctuation not at end)" if $verbose;
}
elsif $word ~~ /"-" .* "-"/ # [6]
{
say ": $word -> invalid (more than one hyphen)" if $verbose;
}
elsif $word ~~ /<[a..z]> "-" <[a..z]>/ # [7]
{
say ": $word -> valid (one hyphen between letters)" if $verbose;
$valid++;
}
elsif $word ~~ / "-" / # [8]
{
say ": $word -> invalid (hyphen not between letters)" if $verbose;
}
else # [9]
{
say ": $word -> valid" if $verbose;
$valid++;
}
}
say $valid;
[1] No restrictions on the input string, so an empty string is ok here.
[2] The number of valid tokens will end up here.
[3] Iterate over the words.
[4] Ignore words containing at least one digit.
[5] Ignore words with at least one character after a punctuation character.
[6] Ignore words with at least two hyphens.
[7] Count the word if it has a letter immediately before and after an hyphen.
[8] Ignore any other word with a hyphen.
[9] Count the word, as it is legal.
Running it:
$ ./valid-token-counter "cat and dog"
3
$ ./valid-token-counter "a-b c! d,e"
2
$ ./valid-token-counter "hello-world! this is fun"
4
$ ./valid-token-counter "ab- cd-ef gh- ij!"
2
$ ./valid-token-counter "wow! a-b-c nice."
2
Looking good.
With verbose mode:
$ ./valid-token-counter -v "cat and dog"
: cat -> valid
: and -> valid
: dog -> valid
3
$ ./valid-token-counter -v "a-b c! d,e"
: a-b -> valid
: c! -> valid
: d,e -> invalid (punctuation not at end)
2
$ ./valid-token-counter -v "hello-world! this is fun"
: hello-world! -> valid (one hyphen between letters)
: this -> valid
: is -> valid
: fun -> valid
4
$ ./valid-token-counter -v "ab- cd-ef gh- ij!"
: ab- -> invalid (hyphen not between letters)
: cd-ef -> valid (one hyphen between letters)
: gh- -> invalid (hyphen not between letters)
: ij! -> valid
2
$ ./valid-token-counter -v "wow! a-b-c nice."
: wow! -> valid
: a-b-c -> invalid (more than one hyphen)
: nice. -> valid
2
Using given/when gives a more compact program, but the file size is higher
due to the extra indentation.
#! /usr/bin/env raku
unit sub MAIN ($str, :v(:$verbose));
my $valid = 0;
for $str.words -> $word
{
given $word # [1]
{
when /\d/ # [2]
{
say ": $word -> invalid (digit)" if $verbose;
}
when /<[.,!]>./
{
say ": $word -> invalid (punctuation not at end)" if $verbose;
}
when /"-" .* "-"/
{
say ": $word -> invalid (more than one hyphen)" if $verbose;
}
when /<[a..z]> "-" <[a..z]>/
{
say ": $word -> valid (one hyphen between letters)" if $verbose;
$valid++;
}
when / "-" /
{
say ": $word -> invalid (hyphen not between letters)" if $verbose;
}
default # [3]
{
say ": $word -> valid" if $verbose;
$valid++;
}
}
}
say $valid;
[1] Inspect the value with given.
See docs.raku.org/syntax/default when for more information about given/when/default.
[2] Smartmatch the value against a regex.
[3] The final else part of the if program corresponds with
default.
Running it gives the expected result. Here is the fourth example:
$ ./valid-token-counter-given -v "ab- cd-ef gh- ij!"
: ab- -> invalid (hyphen not between letters)
: cd-ef -> valid (one hyphen between letters)
: gh- -> invalid (hyphen not between letters)
: ij! -> valid
2
And that's it.