Perl Weekly Challenge: Week 278
Challenge 1:
Sort String
You are given a shuffle string,
$str
.Write a script to return the sorted string.
A string is shuffled by appending word position to each word.
Example 1
Input: $str = "and2 Raku3 cousins5 Perl1 are4"
Output: "Perl and Raku are cousins"
Example 2
Input: $str = "guest6 Python1 most4 the3 popular5 is2 language7"
Output: "Python is the most popular guest language"
Example 3
Input: $str = "Challenge3 The1 Weekly2"
Output: "The Weekly Challenge"
First the input string is split into words with .words()
and each word s further split
into the word itself and its' position with a regular expression. The position becomes a key
in a hash, %order
, and the word is the value.
my %order;
for $str.words -> $w {
my ($word, $pos) = $w.match(/ (.+)(\d+) /).List;
%order{$pos} = $word;
}
Now we just need to sort the keys of the hash, replace the sorted keys with their values, join them back
together into a string with .join()
and print.
%order.keys.sort.map({ %order{$_} }).join(q{ }).say;
The Perl version works the same way.
my %order;
for my $w (split /\s+/, $str) {
my ($word, $pos) = $w =~ /(.+)(\d+)/;
$order{$pos} = $word;
}
say join q{ }, map { $order{$_} } sort keys %order;
Challenge 2:
Reverse Word
You are given a word,
$word
and a character,$char
.Write a script to replace the substring up to and including
$char
with its characters sorted alphabetically. If the1$char
doesn’t exist thenDON'T
do anything.
Example 1
Input: $str = "challenge", $char = "e"
Output: "acehllnge"
Example 2
Input: $str = "programming", $char = "a"
Output: "agoprrmming"
Example 3
Input: $str = "champion", $char = "b"
Output: "champion"
This can be solved in one line with a regular expression. I'll show the Perl version first.
$ARGV[0] =~ s{(.+$ARGV[1])(.+)}{(join q{}, sort split//, $1) . $2}e; say $ARGV[0]
What the spec calls $str
and $char
are brought in from the command-line. The s///
operator
is applied to the first command-line argument ($ARGV[0]
or $word
.) Actually {}
are used as delimiters instead of //.
as the regular expression //
is used within it. In the first half of the s///
, the word is split into two groups of characters; from the beginning of the word to the second command-line argument ($ARGV[1]
or $char
.) and from the character after $ARGV[1]
to the end of the word. In the second part of the s///
, the first group is split into individual characters with split()
, sorted with sort()
and put back together with join()
. The second group is then appended to it The whole thing then replaces the original value of @ARGV[0]
and is printed.
The Raku version gave me some problems. One is that command-line arguments are immutable so s
cannot be
used directly on them. I discovered that Raku also has an S
operator which works on a copy of its input. Also,
as you can see, ||
is being used as delimiters instead of //
. Another is how Raku deals with a regular expression that fails to match. Perl will return an empty string in match variables ($1
, $2
etc.) Raku returns
Nil
which is a separate datatype. This causes an unsightly warning message when you use a string method like .split()
on it. The solution is to replace any Nil
with an empty string using the logical defined-or operator
//
. Incidently this is why ||
is being used as delimiters for S
instead of //
.
All this makes the Raku version uncharacteristically longer than Perl but still one line.
say S| (.+$(@*ARGS[1]))? (.+) | $( ($0 // q{}).split(q{}).sort.join ~ ($1 // q{}) ) | with @*ARGS[0]