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;