This is my response to The Weekly Challenge #311.
Input: $str = "pERl"
Output: "PerL"
Example 2:
Input: $str = "rakU"
Output: "RAKu"
Example 3:
Input: $str = "PyThOn"
Output: "pYtHoN"
#! /usr/bin/env raku
unit sub MAIN ($str where $str ~~ /^<[a..z A..Z]>+/); # [1]
say $str.comb.map({ /<[a..z]>/ ?? .uc !! .lc }).join; # [2]
[1] Ensure that we only get lower- and uppercase English letters. I have chosen to assume that this means the plain old ascii letters only, and not other things that may pop up in English dictionaries. See e.g. en.wiktionary.org/wiki/Category:English_terms_spelled_with_Œ.
[2]
Split the word into a list of characters (with comb
), then use
map
to apply uc
on the lowercase letters (i.e. those matching the
regex) to uppercase them and lc
on the rest (i.e. the upercase letters)
to lowercase them. Then we join the characters together to string, and print
it.
See docs.raku.org/routine/uc for more information about uc
.
See docs.raku.org/routine/lc for more information about lc
.
Running it:
$ ./upper-lower pERl
PerL
$ ./upper-lower rakU
RAKu
$ ./upper-lower PyThOn
pYtHoN
Looking good.
This is basically a oneliner, so verbose mode did not make sense.
So let us try something different instead...
... Unicode support.
I have chosen the more catchy «Flip Case» name for this version of the program. The flipping case, so to speak, has been delegated to a «flc» function, making it easy to add it to a module of string functions - should anybody want to do so.
File: flip-case
#! /usr/bin/env raku
unit sub MAIN ($str where $str.chars > 0); # [1]
sub flc ($str) # [2]
{
return $str.comb.map({
if /<lower>/ { .uc } # [3]
elsif /<upper>/ { .lc } # {3a]
else { $_ } # [3b]
}).join;
}
say $str.&flc; # [4]
[1] At least one character. Anything Unicode goes.
[2] The function doing the job.
[3] Does the character have the unicode «lower case letter» property? If so, uppercase it. Does it have the «upper case letter» property? If so lowercase it [3a]. If none of the above, keep the character as is [3b].
[4] Apply the function with the handy .&
«function-as-a-method» syntax.
See
docs.raku.org/language/operators#methodop_.& for more information
about the special procedure invocation syntax .&
.
Running it with some non-English letters:
$ ./flip-case "This is funny!"
tHIS IS FUNNY!
$ ./flip-case "Jordbærsyltetøy"
jORDBÆRSYLTETØY
$ ./flip-case "JordbærsyltetØy"
jORDBÆRSYLTETøY
$ ./flip-case "Die Große Welt"
dIE gROSSE wELT
$ ./flip-case "dIE gROSSE wELT"
Die Grosse Welt
Note that the German (lowercase) letter «ß» is uppercased to «SS». This obviously does not round trip, as shown in the last example.
$str
, made up of digits, and an integer, $int
, which is less
than the length of the given string.
$int
(plus one for
leftovers if any). Then sum the digits of each group, and concatenate all group sums to create a
new string. If the length of the new string is less than or equal to the given integer then return
the new string, otherwise continue the process.
Input: $str = "111122333", $int = 3
Output: "359"
Step 1: "111", "122", "333" => "359"
Example 2:
Input: $str = "1222312", $int = 2
Output: "76"
Step 1: "12", "22", "31", "2" => "3442"
Step 2: "34", "42" => "76"
Example 3:
Input: $str = "100012121001", $int = 4
Output: "162"
Step 1: "1000", "1212", "1001" => "162"
This task can be divided in two:
$int
length
The first part is eminently suited for gather
/take
.
#! /usr/bin/env raku
unit sub MAIN ($str is copy where $str ~~ /^<[0..9]>+/, # [1]
Int $int where $int > 1 # [2a]
&& $str.chars > $int, # [2]
:v($verbose));
while $str.chars > $int # [3]
{
my @parts = partition($str, $int); # [4]
my @sums = @parts>>.comb>>.sum; # [5]
my $sum = @sums.join; # [6]
say ": String: $str Parts: { @parts.join(", ") } -> { @sums.join(", ") }
-> $sum" if $verbose;
$str = $sum; # [7]
}
say $str; # [8]
sub partition ($str is copy, $length) # [9]
{
my @parts = gather # [10]
{
while $str.chars # [11]
{
my $size = min($length, $str.chars); # [12]
take $str.substr(0, $size); # [13]
$str = $str.substr($size); # [14]
}
}
return @parts; # [15]
}
[1] A number, consisting of at least one digit. The
is copy
trait is there so that we can edit the local copy (which
we do in [7]).
See docs.raku.org/type/Parameter#method_copy for more information about is copy
.
[2] The integer, which must be lower than the length of the number in [1]. I have added the «larger than 1» rule [2a]; see the end of the article for why.
[3] As long as the exit condition is not met.
[4] Get the parts of the requested length.
[5] Convert each part of the array into a sum, giving an array of sums.
[6] Join the sums together.
[7] Reset the input string, ready for further iterations of the loop, if required.
[8] Print the result.
[9] The procedure doing the heavy lifting.
[10] We use gather
to collect the return array.
See my Raku Gather, I Take article or docs.raku.org/language/control#gather/take for more information about gather
/take
.
[11] As long as we have unfinished business.
[12] Get the number of characters to grab. The user specified length, or whatever is left if we have not got that many.
[13] Return (to the array) the requested number of digits (with
substr
).
See docs.raku.org/routine/substr for more information about substr
.
[14] Remove the returned part of the string.
[15] Return the result.
Running it:
$ ./group-digit-sum 111122333 3
359
$ ./group-digit-sum 1222312 2
76
$ ./group-digit-sum 100012121001 4
162
Looking good.
With verbose mode:
$ ./group-digit-sum -v 111122333 3
: String: 111122333 Parts: 111, 122, 333 -> 3, 5, 9 -> 359
359
$ ./group-digit-sum -v 1222312 2
: String: 1222312 Parts: 12, 22, 31, 2 -> 3, 4, 4, 2 -> 3442
: String: 3442 Parts: 34, 42 -> 7, 6 -> 76
76
$ ./group-digit-sum -v 100012121001 4
: String: 100012121001 Parts: 1000, 1212, 1001 -> 1, 6, 2 -> 162
162
A size of «1» is a bad idea, as it will give an eternal loop:
$ ./group-digit-sum -v 1001 1
: String: 1001 Parts: 1, 0, 0, 1 -> 1, 0, 0, 1 -> 1001
: String: 1001 Parts: 1, 0, 0, 1 -> 1, 0, 0, 1 -> 1001
: String: 1001 Parts: 1, 0, 0, 1 -> 1, 0, 0, 1 -> 1001
: String: 1001 Parts: 1, 0, 0, 1 -> 1, 0, 0, 1 -> 1001
^C
As will a size of «0». An eternal loop, but without any verbose output:
$ ./group-digit-sum -v 1001 0
^C
So I added the rule enforcing values greater than 1 (in [2a]).
And that's it.