Perl Weekly Challenge: Week 364
Challenge 1:
Decrypt String
You are given a string formed by digits and ‘#'.
Write a script to map the given string to English lowercase characters following the given rules.
- Characters 'a' to 'i' are represented by '1' to '9' respectively.
- Characters 'j' to 'z' are represented by '10#' to '26#' respectively.
Example 1
Input: $str = "10#11#12"
Output: "jkab"
10# -> j
11# -> k
1 -> a
2 -> b
Example 2
Input: $str = "1326#"
Output: "acz"
1 -> a
3 -> c
26# -> z
Example 3
Input: $str = "25#24#123"
Output: "yxabc"
25# -> y
24# -> x
1 -> a
2 -> b
3 -> c
Example 4
Input: $str = "20#5"
Output: "te"
20# -> t
5 -> e
Example 5
Input: $str = "1910#26#"
Output: "aijz"
1 -> a
9 -> i
10# -> j
26# -> z
We can parse this format in one line with a regular expression and a little bit of post processing.
We get the input string from the first command-line argument, @*ARGS[0]. then we
.match() that string against a regular expressiion that identifies either two digits
followed by a '#' or a single digit. Each choice is wrapped in <( ... )> to "group" it
for further use. Notice that in the first group, the '#' is outside; this is because we
want to match it but not include it in the results. .match() will return a List of
all groups found, each of which will be a number from 1 to 26. We traverse through this list
with .map() and add each number to 0x60 (decimal 96) which is the ASCII code
for '`' the character before 'a'. This code is converted back to a character from 'a' to
'z' with chr(). The list of characters is then .join()ed back into a string and output
with .say().
@*ARGS[0].match(/ <(\d\d)>\# || <(\d)> /,:g).map({ chr(0x60 + $_) }).join(q{}).say
We can do the same thing just as compactly in Perl.
say join q{}, map { chr(0x60 + $_) } grep {$_} shift =~ /(\d\d)\#|(\d)/g
Challenge 2:
Goal Parser
You are given a string,
$str.Write a script to interpret the given string using
Goal Parser.The Goal Parser interprets “G” as the string “G”, “()” as the string “o”, and “(al)” as the string “al”. The interpreted strings are then concatenated in the original order.
Example 1
Input: $str = "G()(al)"
Output: "Goal"
G -> "G"
() -> "o"
(al) -> "al"
Example 2
Input: $str = "G()()()()(al)"
Output: "Gooooal"
G -> "G"
four () -> "oooo"
(al) -> "al"
Example 3
Input: $str = "(al)G(al)()()"
Output: "alGaloo"
(al) -> "al"
G -> "G"
(al) -> "al"
() -> "o"
() -> "o"
Example 4
Input: $str = "()G()G"
Output: "oGoG"
() -> "o"
G -> "G"
() -> "o"
G -> "G"
Example 5
Input: $str = "(al)(al)G()()"
Output: "alalGoo"
(al) -> "al"
(al) -> "al"
G -> "G"
() -> "o"
() -> "o"
This is another simple application of regular expressions.
First we have to make a copy of the input because it is immutable in Raku.
my $result = $str;
Then we successively apply a substitution to $result for all instances of each of the tokens mentioned in the spec. Well, we can skip "G" because its' input is the same as it's output.
$result ~~ s:g/\(\)/o/;
$result ~~ s:g/\(al\)/al/;
The spec does not clarify what should be done if a character sequence other than these
are encountered so we will just leave them as-is and print $result.
$result.say;
This works and is quick and easy but I wondered if I could avoid making multiple passes
over $result. I remembered that Raku also has S/// which performs substitutions on
a copy of the string and returns the result. This makes it possible to chain together
multiple substitutions via given like this. The second (and subsequent) calls to S///
have to be wrapped in parantheses.
say S:g/\(al\)/al/ given (S:g/\(\)/o/ given @*ARGS[0])
Later it struck me that I could also chain the .subst() method with the same effect and
much better readability. So my final version looks like this:
@*ARGS[0].subst(/\(al\)/, "al", :g).subst(/\(\)/, "o", :g).say
For Perl I didn't know if I could create a one-liner. It doesn't have .subst() and
s/// returns the number of substitutions made in scalar context so chaining substitutions
would not be possible. Or would it? Rummaging through the documentation revealed that
s/// has an /r flag I wasn't aware of which "Return[s] substitution and leave the original string untouched." In other words, it acts like Rakus' S/// operator. So we can meka a
translation of my second Raku version like thia:
say shift =~ s/\(\)/o/gr =~ s/\(al\)/al/gr