This is my response to The Weekly Challenge #265.
$str
, made up of only alphabetic
characters [a..zA..Z]
.
Input: $str = 'PeRlwEeKLy'
Output: L
There are two letters E and L that appears as lower and upper.
The letter L appears after E, so the L is the greatest english letter.
Example 2:
Input: $str = 'ChaLlenge'
Output: L
Example 3:
Input: $str = 'The'
Output: ''
#! /usr/bin/env raku
unit sub MAIN (Str $str where $str ~~ /^<[a..z A..Z]>+$/, # [1]
:v(:$verbose));
my $h = $str.comb.Bag; # [2]
say ": Bag: { $h.raku }" if $verbose;
for 'Z' ... 'A' -> $letter # [3]
{
say ": Checking $letter" if $verbose;
if $h{$letter} && $h{$letter.lc} # [4]
{
say $letter; # [4a]
exit; # [4b]
}
}
say ""; # [5]
[1] A string containing one or more lower- and uppercase english letters only.
[2]
Turn the string into a list of characters (with
comb
) and that list into a Bag
- a hash like structure
where we can look up the letters easily.
See docs.raku.org/routine/comb for more information about comb
.
See docs.raku.org/routine/Bag for more information about Bag
.
[3] Iterate over uppercase letters, in descending order as we want the first match from the end of the alphabet.
[4] Do we have the letter itself and the lowercase version
(with lc
) in the Bag? If so, print the (uppercase) letter [4a]
and finish [4b].
See docs.raku.org/routine/lc for more information about lc
.
[5] No match; print an empty line.
Running it:
$ ./greatest-english-letter PeRlwEeKLy
L
$ ./greatest-english-letter ChaLlenge
L
$ ./greatest-english-letter The
Looking good.
With verbose mode:
$ ./greatest-english-letter -v PeRlwEeKLy
: Bag: ("P"=>1,"l"=>1,"y"=>1,"K"=>1,"e"=>2,"E"=>1,"R"=>1,"w"=>1,"L"=>1).Bag
: Hash: {:E(1), :K(1), :L(1), :P(1), :R(1), :e(2), :l(1), :w(1), :y(1)}
: Checking Z
: Checking Y
: Checking X
: Checking W
: Checking V
: Checking U
: Checking T
: Checking S
: Checking R
: Checking Q
: Checking P
: Checking O
: Checking N
: Checking M
: Checking L
L
$ ./greatest-english-letter -v ChaLlenge
: Bag: ("g"=>1,"h"=>1,"e"=>2,"C"=>1,"a"=>1,"n"=>1,"L"=>1,"l"=>1).Bag
: Checking Z
: Checking Y
: Checking X
: Checking W
: Checking V
: Checking U
: Checking T
: Checking S
: Checking R
: Checking Q
: Checking P
: Checking O
: Checking N
: Checking M
: Checking L
L
$ ./greatest-english-letter -v The
: Bag: ("h"=>1,"T"=>1,"e"=>1).Bag
: Checking Z
: Checking Y
: Checking X
: Checking W
: Checking V
: Checking U
: Checking T
: Checking S
: Checking R
: Checking Q
: Checking P
: Checking O
: Checking N
: Checking M
: Checking L
: Checking K
: Checking J
: Checking I
: Checking H
: Checking G
: Checking F
: Checking E
: Checking D
: Checking C
: Checking B
: Checking A
Note that we have two lowercase «e»s in the second example,
and the Bag
has that information readily available. (Even if we do
not care here.)
@source
and @indices
. The
@indices
can only contains integers 0 <= i < size of @source
.
$indices[i]
the value $source[i]
.
Input: @source = (0, 1, 2, 3, 4)
@indices = (0, 1, 2, 2, 1)
Output: (0, 4, 1, 3, 2)
@source @indices @target
0 0 (0)
1 1 (0, 1)
2 2 (0, 1, 2)
3 2 (0, 1, 3, 2)
4 1 (0, 4, 1, 3, 2)
Example 2:
Input: @source = (1, 2, 3, 4, 0)
@indices = (0, 1, 2, 3, 0)
Output: (0, 1, 2, 3, 4)
@source @indices @target
1 0 (1)
2 1 (1, 2)
3 2 (1, 2, 3)
4 3 (1, 2, 3, 4)
0 0 (0, 1, 2, 3, 4)
Example 3:
Input: @source = (1)
@indices = (0)
Output: (1)
#! /usr/bin/env raku
unit sub MAIN ($source, $indices, :v(:$verbose)); # [1]
my @source = $source.words>>.Int; # [2]
my @indices = $indices.words>>.UInt; # [3]
die "Note the same number of elems"
unless @source.elems == @indices.elems; # [4]
die "Index too large" if @indices.max > @indices.elems; # [5]
my @target; # [6]
for ^@indices.elems -> $index # [7]
{
say ": Source:@source[$index] Index:@indices[$index]" if $verbose;
@target.splice(@indices[$index], 0, @source[$index]); # [8]
}
say "({ @target.join(",") })"; # [9]
[1] The source array, as a space separated string. Followed by the index array, also as a space separated string.
[2] Convert the source string into an array of integers. Note
that the .Int
coersion will turn non-integer numeric values
into integers. Feel free to consider that a feature, or an arror.
[3] Ditto for the index string, except that we use .UInt
to ensure unsigned integers, as negative indices will not do.
[4] The two arrays must have the same length.
[5] The highest index value (with max
) must be
a valid index in the array(s).
See docs.raku.org/routine/max for more information about max
.
[6] The target values will end up here.
[7] For each index (0 .. <last index in the array>, and not
the content of @indices
).
[8] Use splice
to insert the source value
(@source[$index]
) at index position @indices[$index]
,
moving any values at that index and after to the right. The zero
means that we are to remove no values (i.e. replacing instead
of inserting).
See docs.raku.org/routine/splice for more information about splice
.
[9] Pretty print the result.
Running it:
$ ./target-array "0 1 2 3 4" "0 1 2 2 1"
(0,4,1,3,2)
$ ./target-array "1 2 3 4 0" "0 1 2 3 0"
(0,1,2,3,4)
$ ./target-array "1" "0"
(1)
Looking good.
With verbose mode:
$ ./target-array -v "0 1 2 3 4" "0 1 2 2 1"
: Source:0 Index:0
: Source:1 Index:1
: Source:2 Index:2
: Source:3 Index:2
: Source:4 Index:1
(0,4,1,3,2)
$ ./target-array -v "1 2 3 4 0" "0 1 2 3 0"
: Source:1 Index:0
: Source:2 Index:1
: Source:3 Index:2
: Source:4 Index:3
: Source:0 Index:0
(0,1,2,3,4)
$ ./target-array -v "1" "0"
: Source:1 Index:0
(1)
And that's it.