Perl Weekly Challenge: Week 273
Challenge 1:
Percentage of Character
You are given a string,
$str
and a character$char
.Write a script to return the percentage, nearest whole, of given character in the given string.
Example 1
Input: $str = "perl", $char = "e"
Output: 25
Example 2
Input: $str = "java", $char = "a"
Output: 50
Example 3
Input: $str = "python", $char = "m"
Output: 0
Example 4
Input: $str = "ada", $char = "a"
Output: 67
Example 5
Input: $str = "ballerina", $char = "l"
Output: 22
Example 6
Input: $str = "analitik", $char = "k"
Output: 13
Raku has the really handy .classify()
method for sorting a list into a hash. First we need
the list though so we use .comb()
to split $str
into individual characters.
$str.comb.classify({ $_ }, into => my %freq);
Now thanks to .classify()
we have a hash, %freq
whose keys are distinct characters from $str
and
whose values are the occurrences of that character. If $chr
is a key of %freq
(which we determine with .exists()
) we can find the frequency by counting the values of that key with .elems()
. This is divided by the length of $str
(which we determine with .chars()
) and multiplied by 100 to give the percentage occurrence of $chr
in $str
. The spec wants the percentage rounded to the nearest whole number which is easily accomplished with .round()
.
If $chr
did not exist in %freq
, 0 is printed.
say %freq{$chr}:exists ?? (%freq{$chr}.elems / $str.chars * 100).round !! 0;
This is the Perl version.
As we do not have .classify()
we have to emulate it in these two lines. instead of adding occurrences to the hash,
we can just keep a running count of the occurrences.
my %freq;
map { $freq{$_}++; } split //, $str;
We don't have a .round()
method. Rather than write my own I just used the one in the Math::Round module. so we have to include use Math::Round qw/ round /;
at the top of the script.
say exists $freq{$chr} ? round($freq{$chr} / (length $str) * 100) : 0;
Challenge 2:
B After A
You are given a string,
$str
.Write a script to return
true
if there is at least oneb
, and noa
appears after the firstb
.
Example 1
Input: $str = "aabb"
Output: true
Example 2
Input: $str = "abab"
Output: false
Example 3
Input: $str = "aaa"
Output: false
Example 4
Input: $str = "bbb"
Output: true
This one seemed very easy to do with a regular expression; /b[^a]*/
in Perl syntax. First we match the literal character b followed by 0 or more instances of any character except a.
However there is a catch. Take a look at example 3; it should output false because the first b is followed by an a but the way the regular expression was written above, the second b will match. My solution was to use 'back references'. These allow us to refer to earlier matched parts of a regular expression from later parts. b is wrapped in parentheses so it becomes a group. \1
is a reference to that group so that \1[^a]*
is now guaranteed to match the first b followed by any characters except a only.
This is a one-liner that implements the whole solution. I was surprised that shift doesn't return $_
by default as many Perl functions do and I had to explicitly assign it. Perl has many annoying quirks like thia.
$_ = shift; say /(b)\1[^a]*/ ? "true" : "false"
This is the Raku version. Back references start with 0 instead of 1. Negated character chlasses use -
instead of
^
. .match()
does not return true/false so we have to use .so()
to convert it into a Boolean value.
@*ARGS[0].match(/(b)$0<[-a]>*/).so.say