Perl Weekly Challenge: Week 341

Challenge 1:

Broken Keyboard

You are given a string containing English letters only and also you are given broken keys.

Write a script to return the total words in the given sentence can be typed completely.

Example 1
Input: $str = 'Hello World', @keys = ('d')
Output: 1

With broken key 'd', we can only type the word 'Hello'.
Example 2
Input: $str = 'apple banana cherry', @keys = ('a', 'e')
Output: 0
Example 3
Input: $str = 'Coding is fun', @keys = ()
Output: 3

No keys broken.
Example 4
Input: $str = 'The Weekly Challenge', @keys = ('a','b')
Output: 2
Example 5
Input: $str = 'Perl and Python', @keys = ('p')
Output: 1

We can solve this as a one-liner in Raku.

We take the string provided in the first command-line argument and separate into words with .words(). Then we filter those words with .grep(), matching each one against a regular expression which contains the second command-line argument (representing the broken keys) split into individual characters with .comb() and interpolated into the regular expression with @(). This acts as a custom character class. We use the i (case-insnsitive) flag so we have to use the explicit name of the match operator, m, If a word doesnot_ match this regular expression it means it has no broken keys. We count the number of such words with .elems() and print the result with .say().

@*ARGS[0].words.grep({ $_ !~~ m:i/ @(@*ARGS[1].comb) / }).elems.say

(Full code on Github.)

For example 3, @keys is empty. I modeled that in Raku by just having an empty second command-line argument but it seems you can't do that in Perl. So instead I just let the second argument be omitted and there is a line in my solution like this:

my $broken = shift // q{.};

If the second argument is not defined, $broken becomes .. In regular expressions, that will match any character so all words will match.

The rest of it works the same as Raku but with different syntax.

say scalar grep { !/[$broken]/i } split /\s+/, $str;

(Full code on Github.)

Challenge 2:

Reverse Prefix

You are given a string, $str and a character in the given string, $char.

Write a script to reverse the prefix upto the first occurrence of the given $char in the given string $str and return the new string.

Example 1
Input: $str = "programming", $char = "g"
Output: "gorpramming"

Reverse of prefix "prog" is "gorp".
Example 2
Input: $str = "hello", $char = "h"
Output: "hello"
Example 3
Input: $str = "abcdefghij", $char = "h"
Output: "hgfedcbaij"
Example 4
Input: $str = "reverse", $char = "s"
Output: "srevere"
Example 5
Input: $str = "perl", $char = "r"
Output: "repl"

This time, I will start with Perl first. The substitution operator s/// is applied to $str. In the first half, the start of $str up to $char is matched. In the second half, the matched text is reverse()d and the reverse text is substituted for the original. We need to use the e switch for s/// ti allow code in the second half and the x switch to allow whitespace in the first half making it a little more readable.

$str =~ s/(^ .+? $char)/reverse $1/ex;

Then we print out the modified string with say().

say $str;

(Full code on Github.)

In Raku we can do all of this in one line. I saved a few characters by using the first and second command-line arguments as is instead of assigning them to $str and $char. Also because these arguments are immutable, we have to use S/// ... given, the non-destructive version of the substitution operator. One last minor difference to note is that in Raku, strings are reversed with .flip() not .reverse() which is exclusively for lists and arrays.

say S/ (^ .+? $(@*ARGS[1])) /$($0.flip)/ given @*ARGS[0]

(Full code on Github.)