Perl Weekly Challenge: Week 311
Challenge 1:
Upper Lower
You are given a string consists of english letters only.
Write a script to convert lower case to upper and upper case to lower in the given string.
Example 1
Input: $str = "pERl"
Output: "PerL"
Example 2
Input: $str = "rakU"
Output: "RAKu"
Example 3
Input: $str = "PyThOn"
Output: "pYtHoN"
This time I'm going to start with Perl as we can solve this challenge with one regularexpression.
$_ = shift; s/(.)/uc $1 eq $1 ? lc $1: uc $1/ge; say
We get the input from the command-line. The first command-line argument is removed with shift()
and placed into $_
. The s///
operator works on $_
. In the first part, a character in $_
is matched
and captured. The /g
or global flag to s///
makes the capture occur for every character. Strictly speaking,
I only needed to match letters but changing the case of non-letter characters is harmless and making the regular'
expression stricter would only have made it longer for no good reason so I stuck with .
. The /e
flag allows
us to execute code in the second half of s///
. There we use the ternary operator, ?:
to test if a captured
character is upper-case by testing it for equality with a known upper-case version of it created with uc()
.
If it is uppercase, the character is converted to lower-case with lc()
. If it is not, i.e if it is lower-case,
it is converted to upper-case. The results of the substitution are output with say
which also operates on $_
.
The Raku version is just a little bit longer. It uses .subst()
, the method version of s///
to join all steps
into one statement.
@*ARGS.shift.subst(/(.)/, { $0.uc eq $0 ?? $0.lc !! $0.uc }, :g).say
Challenge 2:
Group Digit Sum
You are given a string, $str, made up of digits, and an integer, $int, which is less than the length of the given string.
Write a script to divide the given string into consecutive groups of size $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.
Example 1
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"
The first step in my Raku solution is assigning $str
to a new variable. This
is going to be repeatedly modified which we can't do to $str
because, as a function
argument, it is immutable.
my $sum = $str;
Now while the lenth of $sum
is greater than $int
...
while $sum.chars > $int {
...we split $sum
into groups of $Int
characters with .comb()
. For some reason $int
kept being interpreted as
a String
so I had to specifically cast it to an Int
with .Int()
. Using .map()
each of these groups are in turn split into individual digits and added together with .comb()
and .sum()
All those summed groups are
then concatenated together with .join()
. The result is then assigned back to $sum
.
$sum = $sum.comb($int.Int).map({ $_.comb.sum }).join;
}
When $sum
has shrunk to the required length, we print it out with say()
.
say $sum;
This is the Perl version.
my $sum = $str;
while (length $sum > $int) {
We have to provide our own sum()
function and workaround the lack of .comb($int)
by using a regular expression.
$sum = join q{}, map { sum( split // ) } ($sum =~ /(\d{1, $int})/g);
}
say $sum;