This is my response to The Weekly Challenge #279.
@letters
and @weights
.
@letters
based
on the @weights
.
Input: @letters = ('R', 'E', 'P', 'L')
@weights = (3, 2, 1, 4)
Output: PERL
Example 2:
Input: @letters = ('A', 'U', 'R', 'K')
@weights = (2, 4, 1, 3)
Output: RAKU
Example 3:
Input: @letters = ('O', 'H', 'Y', 'N', 'P', 'T')
@weights = (5, 4, 2, 6, 1, 3)
Output: PYTHON
File: sort-letters
#! /usr/bin/env raku
unit sub MAIN ($letters where $letters.chars > 0, # [1]
*@weight where @weight.elems eq $letters.chars # [2]
&& all(@weight) ~~ UInt); # [2a]
say ($letters.comb Z @weight) # [3]
.sort({ $^a.[1] <=> $^b.[1] }) # [4]
.map( *.[0] ) # [5]
.join; # [6]
[1] Ensure at least one letter, all of them in a single string.
[2] A slurpy array of weights, with one weight for each
letter, and all of them of the UInt
(Unsigned Int) type [2a].
See docs.raku.org/type/UInt for more information about the UInt
type.
[3]
Combine each letter (as individual letters, courtsey
of comb
on the string) with its weight with the Z
zip operator. The result is a list containing a sublist for
each letter/weight combination.
See docs.raku.org/routine/comb for more information about comb
.
See docs.raku.org/routine/Z for more information about Z
.
[4] Sort the list, on the second element (with index 1) of each sublist - the weight.
[5] Then we take the sorted list and pick the first element (with
index 0) of each sublist - the letter - with map
. The result
is a sorted list of letters.
[6] Join the list of letters into a single string, and print it (in [3]).
Running it:
$ ./sort-letters REPL 3 2 1 4
PERL
$ ./sort-letters AURK 2 4 1 3
RAKU
$ ./sort-letters OHYNPT 5 4 2 6 1 3
PYTHON
Looking good.
Note that a weight of zero is perfectly ok, as is gaps:
$ ./sort-letters AURK 2 4 0 3
RAKU
$ ./sort-letters AURK 200 400 100 300
RAKU
Duplicate weights are sorted in the original order, perhaps surprisingly:
$ ./sort-letters AURK 1 1 1 1
AURK
The order is the same if you run the program several times, at least on Rakudo v2024.02.
$str
.
Input: $str = "perl"
Ouput: false
Example 2:
Input: $str = "book"
Ouput: true
Two possible strings "bo" and "ok" containing exactly one vowel each.
Example 3:
Input: $str = "good morning"
Ouput: true
Two possible strings "good " and "morning" containing two vowels each or "good m" and "orning" containing two vowels each.
We do not actually have to split the string at all, just count the vowels. If the number is even, report success, and report failure if it is odd.
File: split-string
#! /usr/bin/env raku
unit sub MAIN ($str where $str.chars > 0); # [1]
say $str.comb.grep( * eq any(<a e i o u>) ).elems %% 2;
######## 2 ## # 3 ############################### # 4 ######
[1] Ensure at least one character in the string.
[2] Turn the string into a list of individual characters.
[3] Keep the vowels only, using grep
on an
any
junction on a list of legal (i.e. english) vowels.
See docs.raku.org/routine/any for more information about any
.
[4] Count the number of vowels. Are they divisible (the %%
operator) by 2, thus an even number?
Running it:
$ ./split-string perl
False
$ ./split-string book
True
$ ./split-string "good morning"
True
Looking good.
But...
$ ./split-string bcd
True
Zero is divisible by 2. And that gives us a false positive.
Let us fix that.
File: split-string-zero
#! /usr/bin/env raku
unit sub MAIN ($str where $str.chars > 0);
my $elems = $str.comb.grep( * eq any() ).elems;
say so $elems && $elems %% 2;
Not quite as elegang as the first version, but the result is correct:
$ ./split-string-zero bcd
False
And that's it.