Perl Weekly Challenge: Week 315
Challenge 1:
Find Words
You are given a list of words and a character.
Write a script to return the index of word in the list where you find the given character.
Example 1
Input: @list = ("the", "weekly", "challenge")
$char = "e"
Output: (0, 1, 2)
Example 2
Input: @list = ("perl", "raku", "python")
$char = "p"
Output: (0, 2)
Example 3
Input: @list = ("abc", "def", "bbb", "bcd")
$char = "b"
Output: (0, 2, 3)
Although my solution is a little longer, the essence of it is one line. In fact, not even that much because some of the code on that line is for output presentation purposes only.
say q{(}, @list.keys.grep({ @list[$_].contains($char) }).join(q{, }), q{)};
The heart of it is this: We get the indices of @list
with .keys()
. Then we filter them with .grep()
. The filter is to see
if the element with that index contains $char
with the appropriately named .contains()
. And that's it! The rest of the line is, as I mentioned, just so we can print the filtered list of indices in a nice way.
This is the Perl version. As we don't have .contains()
, I replaced it with index()
. It is actually for finding the position of a substring in a string but it returns -1
if the substring is not found so it can be used for our purpose.
say q{(}, (join q{, }, grep { index ($list[$_], $char) > -1 } keys @list), q{)};
Challenge 2:
Find Third
You are given a sentence and two words.
Write a script to return all words in the given sentence that appear in sequence to the given two words.
Example 1
Input: $sentence = "Perl is a my favourite language but Python is my favourite too."
$first = "my"
$second = "favourite"
Output: ("language", "too")
Example 2
Input: $sentence = "Barbie is a beautiful doll also also a beautiful princess."
$first = "a"
$second = "beautiful"
Output: ("doll", "princess")
Example 3
Input: $sentence = "we will we will rock you rock you.",
$first = "we"
$second = "will"
Output: ("we", "rock")
I'll show the Perl version first this time. This seemed like something that could be pretty easily done
with a regular expression and indeed /$first\s+$second\s+(\w+))/gx
was sufficient to get the correct answers for
examples 1 and 2. (the m
and s
flags are some things I always add as advised by Perl Best Practices but they are not relevant for this purpose.)
Example 3 was a problem though because "we" is both one of the search words and a result. This made the next match start after
the second "we" so "rock" didn't get picked up. I'm not sure this is the best solution but I solved this by adding ?=
(positive
lookahead) to the beginning of the expression. Now it works.
my @output = ($sentence =~ /(?=$first\s+$second\s+(\w+))/gmsx);
This line is just for printing @output
in the same way as the examples.
say q{(}, (join q{, }, map { "\"$_\"" } @output), q{)};
The Raku version gave unexpected difficulty. The equivalent of ?=
in Rakus' revamped regular expression engine is <?after>
. (There is also <?before>
) so I thought I could make the regular expression like this:
/ <?after $first \s+ $second \s+ > (\w+) /
But for some reason you cannot interpolate strings into an <?after>
expression even though my reading of the documentation
suggests you can. I ended up having to put it in a string and interpolate that like this:
my $preceding = '<?after $first \s+ $second \s+ >';
my @output = $sentence.match(/ <$preceding> (\w+) /, :g);
And this works. Weird.