### 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;
``````

(Full code on Github.)

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;
``````

(Full code on Github.)

#### Challenge 2:

B After A

You are given a string, `\$str`.

Write a script to return `true` if there is at least one `b`, and no `a` appears after the first `b`.

##### 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"
``````

(Full code on Github.)

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
``````

(Full code on Github.)