## Andrew Shitov Weekly Review: Challenge - 273

Tuesday, Jun 18, 2024| Tags: Raku

# Raku Solutions Weekly Review

## Note from the Reviewer

This week’s review has been generated by `ChatGPT`, an `AI` language model. The `AI` has analyzed the submitted solutions for both tasks and grouped them based on the methods and features used. It also includes the `AI`'s own solutions for each task. While every effort has been made to ensure the accuracy and clarity of the review, please feel free to reach out with any questions or feedback. We hope you find the insights and analyses helpful! (This note was `AI-generated` too.)

## Task 1: Percentage of Character

This week’s challenge was to write a program that returns the percentage, nearest whole, of a given character in a given string. The problem is straightforward and involves simple string manipulation, but the solutions submitted by various participants showcase a variety of approaches in Raku. Let’s group these solutions based on the features and methods they used.

## 1. Using `grep`

Several participants used the `grep` method to count the occurrences of the character in the string. The `grep` method filters elements of a list based on a given condition.

Documentation for `grep`.

``````# Luca Ferrari
sub MAIN( Str \$string, Str \$char where { \$char.chars == 1 } ) {
( \$string.comb.grep( * ~~ \$char ).elems / \$string.comb.elems * 100 ).Rat.round.say;
}

# Joelle Maslak
sub MAIN(Str:D \$str where \$str.chars > 0, Str:D \$char where \$char.chars == 1) {
my \$charcount = \$str.comb.grep(\$char);
printf("Percentage of times character apears in string %.0f%%
", 100.0*\$charcount/\$str.chars);
}

# Ulrich Rieke
my \$count = \$word.comb.grep( {\$_ eq \$needle} ).elems;
say (( \$count / \$word.chars ) * 100).round;
``````

## 2. Using `Bag`

Another popular approach was to use the `Bag` method, which is particularly useful for counting occurrences of elements in a list. `Bag` is a multi-set that tracks the number of occurrences of each element.

Documentation for `Bag`

``````# Arne Sommer
unit sub MAIN (\$str where \$str.chars > 0, \$char where \$char.chars == 1);
say ( (100 * \$str.comb.Bag{\$char} / \$str.chars) + 0.5).int;

# BarrOff
sub percentage-of-character(Str \$str, Str \$char --> Int) {
return 0 unless \$str.contains(\$char);
ceiling(100 * Bag(\$str.comb){\$char} ÷ \$str.chars)
}

# Jan Krnavek
sub percentage-of-character (\$str,:\$char) {
\$str.comb
andthen .Bag
andthen .{\$char}/.total
andthen 100 * \$_
andthen .round
}
``````

## 3. Using `comb` and Basic Arithmetic

Several solutions relied on the `comb` method to split the string into characters and then used basic arithmetic to calculate the percentage. `comb` splits a string into a list of substrings matching a given pattern.

Documentation for `comb`

``````# Andrew Shitov
my @tests = perl => 'e', java => 'a', python => 'm', ada => 'a', ballerina => 'l', analitik => 'k';
for @tests -> \$pair {
say (100 * \$pair.key.comb(\$pair.value) / \$pair.key.comb).round;
}

sub percentage-of-character(\$str,\$c) {
my \$count = \$str.comb(\$c).elems;
round(100 * \$count / \$str.chars)
}

# Laurent Rosenfeld
sub percent (\$str, \$char) {
my \$count = 0;
for \$str.comb -> \$ch {
\$count++ if \$ch eq \$char;
}
return ((\$count * 100) / \$str.chars).round;
}
``````

## 4. Using `indices`

A few participants used the `indices` method, which returns the indices of the matching elements, to count occurrences and calculate the percentage. `indices` returns the positions of all matches of a pattern in a string, and the count of these indices gives the number of occurrences.

cumentation for `indices`

``````# Mark Anderson
sub perc-of-char(\$str, \$char) {
round 100 * \$str.indices(\$char) / \$str.chars
}

# Bruce Gray
sub task1 ( \$str, \$char --> UInt ) {
my UInt \$count = +\$str.comb(\$char);
return round( 100 * \$count / \$str.chars );
}
``````

## 5. Using `classify`

Jaldhar H. Vyas used the `classify` method, which categorizes elements based on a given condition. `classify` returns a hash where the keys are the results of the classification routine and the values are lists of elements that fall into each category.

Documentation for `classify`

``````# Jaldhar H. Vyas
sub MAIN(Str \$str, Str \$chr) {
\$str.comb.classify({ \$_ }, into => my %freq);
say %freq{\$chr}:exists ?? (%freq{\$chr}.elems / \$str.chars * 100).round !! 0;
}
``````

## 6. Mixed Approaches and Advanced Techniques

Some participants used a combination of methods and advanced techniques to solve the problem, adding robustness and flexibility to their solutions.

``````# Robert Ransbottom
my @w = \$word.comb;
(100 × @w.grep( \$letter) ÷ @w).round;
}

# Athanasius
sub find-percentage(\$str, \$orig-char, \$ignore-case) {
my UInt \$count = 0;
my Str  \$char  = \$ignore-case ?? \$orig-char.lc !! \$orig-char;
for \$str.split: '', :skip-empty -> Str \$orig-c {
my Str \$c  = \$ignore-case ?? \$orig-c.lc    !! \$orig-c;
++\$count if \$c eq \$char;
}
return ((\$count / \$str.chars) * 100).round;
}

# Packy Anderson
sub charPercent(\$str, \$char) {
return round(( \$str.comb(\$char).elems / \$str.chars ) * 100);
}
``````

## ChatGPT’s solution

Here you will find a solution to this task generated by ChatGPT:

``````sub percentage-of-character(\$str, \$char) {
my \$count = \$str.comb(\$char).elems;
return round(100 * \$count / \$str.chars);
}

my @tests = (
['perl', 'e', 25],
['java', 'a', 50],
['python', 'm', 0],
['ballerina', 'l', 22],
['analitik', 'k', 13]
);

for @tests -> \$test {
my (\$str, \$char, \$expected) = \$test;
my \$result = percentage-of-character(\$str, \$char);
say "Input: \\$str = \"\$str\", \\$char = \"\$char\"";
say "Output: \$result (Expected: \$expected)";
say \$result == \$expected ?? 'Test Passed' !! 'Test Failed';
say '';
}
``````

When you run the code, you get the following output:

``````\$ raku 1.raku
Input: \$str = "perl", \$char = "e"
Output: 25 (Expected: 25)
Test Passed

Input: \$str = "java", \$char = "a"
Output: 50 (Expected: 50)
Test Passed

Input: \$str = "python", \$char = "m"
Output: 0 (Expected: 0)
Test Passed

Input: \$str = "ada", \$char = "a"
Output: 67 (Expected: 67)
Test Passed

Input: \$str = "ballerina", \$char = "l"
Output: 22 (Expected: 22)
Test Passed

Input: \$str = "analitik", \$char = "k"
Output: 13 (Expected: 13)
Test Passed
``````

## Summary

This week’s challenge highlighted the simplicity and power of `Raku`'s string manipulation capabilities. Most solutions utilized methods like `comb`, `grep`, `Bag`, and `indices` to count occurrences of the given character in the string, followed by calculating the percentage. The variations in implementations demonstrate Raku’s flexibility and the different ways programmers can approach a problem. Whether using concise one-liners or more detailed subroutines, each solution showcases Raku’s expressive syntax and rich standard library. Great job to all participants!

## Task 2: B After A

This week’s second challenge was to write a script that returns true if there is at least one ‘b’, and no ‘a’ appears after the first ‘b’ in the given string. This problem is a bit more complex than the first task and involves string pattern matching and manipulation. Let’s group the solutions based on the features and methods they used.

## 1. Using Regular Expressions

Several participants used regexes to solve the problem. This method allows for a concise solution by leveraging `Raku`'s powerful regex capabilities.

### Explanation of Regexes

Regular expressions are patterns used to match character combinations in strings. `Raku`'s regex syntax is powerful and expressive, making it well-suited for this task.

Documentation for `Regexes`

### Matching Specific Patterns

Some solutions directly match the required pattern using regexes. For instance, checking if there is a `'b'` followed by no `'a'`.

``````# Arne Sommer
unit sub MAIN (\$str where \$str.chars > 0);

# Check if the string contains 'b'
if \$str ~~ /b/ {
# Split the string at the first 'b' and check the part after it
my \$after = \$str.split(/b/, 2)[1];

# If the part after 'b' does not contain 'a', print 'true'
if \$after !~~ /a/ {
say 'true';
exit;
}
}
say 'false';
``````
``````# Luca Ferrari
sub MAIN( Str \$string where { \$string.chars > 0 } ) {
'True'.say and exit if ( \$string ~~ / b / && \$string !~~ / b .* a / );
'False'.ay;
}
``````

### Using Extended Patterns

Other solutions use more complex patterns to ensure that the entire string matches the required conditions.

``````# Laurent Rosenfeld
sub b-after-a (\$str) {
return \$str ~~ /^ <-[b]>* b <-[a]>* \$/ ?? True !! False;
}

for <aabb abab aaa bbb> ->  \$test {
printf "%-5s => ", \$test;
say b-after-a \$test;
}
``````
``````# Joelle Maslak
sub MAIN(Str:D \$str) {
my \$match = \$str ~~ m/^ <-[ b ]>* 'b' <!before 'a'>/;
say "Output: " ~ (\$match ?? "true" !! " false");
}
``````

## 2. Simplified Logic

Some participants opted for a simplified logic to check if the string contains `'bb'`, which directly solves the problem for the given examples. This approach may not fully generalize but works for the provided test cases.

``````# Andrew Shitov
my @tests = < aabb abab aaa bbb >;
for @tests -> \$test {
say lc ?(\$test ~~ / bb /);
}

sub b-after-a(\$str) {
?(\$str ~~ /bb/)
}
say b-after-a('aabb');
say b-after-a('abab');
say b-after-a('aaa');
say b-after-a('bbb');
``````

## 3. Using String Manipulation and Iteration

Another group of participants used string manipulation and iteration to check the conditions manually. This approach is more explicit and provides control over each step of the process.

Documentation for String Manipulation

### Checking with Iteration

These solutions manually check each character in the string, keeping track of whether a `'b'` has been seen and ensuring no `'a'` appears after it.

``````# Mark Anderson
sub b-after-a(\$str) {
\$str ~~ / ^ <-[b]>* b <-[a]>* b <-[a]>* \$ /
}
sub b-after-a-pos(\$str) {
my \$b = \$str ~~ m:1st / b / || return False;
my \$a-after-b = \$str ~~ m:c(\$b.pos):1st / a /;
my \$b-after-b = \$str ~~ m:c(\$b.pos):1st / b /;
\$b-after-b and not \$a-after-b
}
sub b-after-a-indices(\$str) {
my @a = \$str.indices('a');
my @b = \$str.indices('b');
@b > 1 and all(@a) < @b.head
}
``````

## 4. Using Indices

A few participants used the different index-related methods to find positions of `'a'` and `'b'` in the string and then determined the result based on these positions.

``````# Robert Ransbottom
my @w = \$word.comb;
without my \$b-k = @w.first( 'b', :k) { return False }
with @w[++\$b-k..^@w].first( 'a') { return False }
True;
}

# Roger Bell-West
sub baftera(\$a) {
my \$firstb = index(\$a, "b");
if (!\$firstb.defined) {
return False;
}
my \$lasta = rindex(\$a, "a");
if (!\$lasta.defined) {
return True;
}
return \$lasta < \$firstb;
}

# Packy Anderson
sub bAfterA(\$str) {
my \$seen_b = False;
for \$str.comb -> \$c {
if (\$seen_b) {
if (\$c eq 'a') {
return False;
}
}
elsif (\$c eq 'b') {
\$seen_b = True;
}
}
return \$seen_b;
}
``````

## ChatGPT’s solution

Here you will find a solution to this task generated by `ChatGPT`:

``````sub b-after-a(\$str) {
my \$b-seen = False;
for \$str.comb -> \$c {
if \$c eq 'b' {
\$b-seen = True;
}
elsif \$b-seen && \$c eq 'a' {
return False;
}
}
return \$b-seen;
}

my @tests = (
['aabb', True],
['abab', False],
['aaa', False],
['bbb', True]
);

for @tests -> \$test {
my (\$str, \$expected) = \$test;
my \$result = b-after-a(\$str);
say "Input: \\$str = \"\$str\"";
say "Output: " ~ (\$result ?? 'true' !! 'false') ~ " (Expected: " ~ (\$expected ?? 'true' !! 'false') ~ ")";
say \$result == \$expected ?? 'Test Passed' !! 'Test Failed';
say '';
}
``````

Run the code to see its output:

``````\$ raku 2.raku
Input: \$str = "aabb"
Output: true (Expected: true)
Test Passed

Input: \$str = "abab"
Output: false (Expected: false)
Test Passed

Input: \$str = "aaa"
Output: false (Expected: false)
Test Passed

Input: \$str = "bbb"
Output: true (Expected: true)
Test Passed
``````

## Summary

This week’s challenge showcased a variety of approaches to solve a problem involving string pattern matching and manipulation. The solutions utilized methods like regular expressions, string manipulation, and the `indices` method to determine if a string meets the given conditions. The variations in implementations demonstrate `Raku`'s flexibility and the different ways programmers can approach a problem. Whether using concise regexes or more detailed iteration, each solution highlights `Raku`'s expressive syntax and rich standard library. `Great job to all participants!`

## SO WHAT DO YOU THINK ?

If you have any suggestions or ideas then please do share with us.