Greatest Target
with Raku

by Arne Sommer

Greatest Target with Raku

[284] Published 12. April 2024.

This is my response to The Weekly Challenge #265.

Challenge #265.1: Greatest English Letter

You are given a string, $str, made up of only alphabetic characters [a..zA..Z].

Write a script to return the greatest english letter in the given string.

A letter is greatest if it occurs as lower and upper case. Also letter ‘b’ is greater than ‘a’ if ‘b’ appears after ‘a’ in the English alphabet.

Example 1:
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: ''
File: greatest-english-letter
#! /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.)

Challenge #265.2: Target Array

You are given two arrays of integers, @source and @indices. The @indices can only contains integers 0 <= i < size of @source.

Write a script to create target array by insert at index $indices[i] the value $source[i].

Example 1:
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)
File: target-array
#! /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.