This is my response to The Weekly Challenge #278.
$str.
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"
#! /usr/bin/env raku
unit sub MAIN ($str, :v(:$verbose)); # [1]
my @new; # [2]
for $str.words -> $shuffle # [3]
{
$shuffle ~~ /(.*?)(\d+)$/; # [4]
my $word = $0; # [4a]
my $index = $1; # [4b]
say ":Word '$word' at index $index \
{ @new[$index].defined ?? 'REDEFINED' !! ''}"
if $verbose;
@new[$index] = $word; # [5]
}
say @new[1 .. *].join(" "); # [6]
#say @new.grep( *.defined ).join(" "); # [7]
[1] A string, without any constraints.
[2] The new (sorted) list of words will end up here.
[3] Iterate over the words in the sentence.
See docs.raku.org/routine/words for more information about words.
[4] Grab anything before one or more trailing digits (note the non-greedy
pattern .*? so that all the trailing digits are gobbled up by the
greedy digit pattern), and save it [4a]. Then save that number [4b].
[5] Insert the word at the given position (index) in the new array.
[6] Print the content of the array, excluding the undefined value with index zero, with spaces between them.
[7] I'll get back to this later.
Running it:
$ ./sort-string "and2 Raku3 cousins5 Perl1 are4"
Perl and Raku are cousins
$ ./sort-string "guest6 Python1 most4 the3 popular5 is2 language7"
Python is the most popular guest language
$ ./sort-string "Challenge3 The1 Weekly2"
The Weekly Challenge
Looking good.
With verbose mode:
$ ./sort-string -v "and2 Raku3 cousins5 Perl1 are4"
:Word 'and' at index 2
:Word 'Raku' at index 3
:Word 'cousins' at index 5
:Word 'Perl' at index 1
:Word 'are' at index 4
Perl and Raku are cousins
$ ./sort-string -v "guest6 Python1 most4 the3 popular5 is2 language7"
:Word 'guest' at index 6
:Word 'Python' at index 1
:Word 'most' at index 4
:Word 'the' at index 3
:Word 'popular' at index 5
:Word 'is' at index 2
:Word 'language' at index 7
Python is the most popular guest language
$ ./sort-string -v "Challenge3 The1 Weekly2"
:Word 'Challenge' at index 3
:Word 'The' at index 1
:Word 'Weekly' at index 2
The Weekly Challenge
Let us have a go at garbage in:
$ ./sort-string -v "Challenge4 The1 Weekly2"
:Word 'Challenge' at index 4
:Word 'The' at index 1
:Word 'Weekly' at index 2
The Weekly
That was perhaps not obvious. The [1 .. *] array slice stops
at the first undefined value, i.e. @new[3].
The alternate version in [7] will ignore (skip) any undefined entries, and print all the words. You may prefer [6], as in «garbage in, constipation out».
Reuse of word positions is not prevented, but verbose mode will notify you:
$ ./sort-string -v "and2 Raku3 cousins5 Perl1 are4 Ronny3"
:Word 'and' at index 2
:Word 'Raku' at index 3
:Word 'cousins' at index 5
:Word 'Perl' at index 1
:Word 'are' at index 4
:Word 'Ronny' at index 3 REDEFINED
Perl and Ronny are cousins
$word and a character, $char.
$char
with its characters sorted alphabetically. If the $char doesn't
exist then DON'T do anything.
Input: $str = "challenge", $char = "e"
Ouput: "acehllnge"
Example 2:
Input: $str = "programming", $char = "a"
Ouput: "agoprrmming"
Example 3:
Input: $str = "champion", $char = "b"
Ouput: "champion"
Neither the challenge nor the examples say what to do if the character uccurs more than once. I have decided to stop after the very first one.
File: reverse-word
#! /usr/bin/env raku
unit sub MAIN ($word is copy, $char where $char.chars == 1); # [1]
$word = $0.comb.sort.join ~ $1 if $word ~~ /(.*? $char) (.*)/; # [2]
say $word; # [3]
[1] The word, followed by a single character. Note the
is copy trait, so that we can change the variable in [2].
See docs.raku.org/type/Parameter#method_copy for more information about is copy.
[2] First the regexp (the second half): Here we look for the
specified character, in the non-greedy pattern .*?. This will match
up to and including the first occurence of the character. The greedy
pattern .* will gobble up the rest of the string. The if
executes the first part (the assignment) if we did get a match. The
assignment is the first part sorted alphabetically (or rather by Unicode
codepoint value) by splitting the string into separate characters
(comb), sorting them (sort) and gluing them together again
join). Then we slap on the second part.
See docs.raku.org/routine/comb for more information about comb.
[3] Print the result.
Running it:
$ ./reverse-word challenge e
acehllnge
$ ./reverse-word programming a
agoprrmming
$ ./reverse-word champion b
champion
./
Looking good.
Some examples where the character occurs more than once:
$ ./reverse-word programming r
programming
$ ./reverse-word language a
alnguage
$ ./reverse-word challenge l
achllenge
And that's it.