The first task in this challenge started with the words “Greatest English”. When I think of “greatest” and “English”, it should be obvious that my mind immediately jumps to… Ringo Starr. It may be John Lennon’s song, but it was on Ringo’s album.
Anyway, enough Beatles blather. Onward to PWC 264!
Task 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: ''
Approach
I saw this and I figured that I could accomplish this with a single pass through the string by maintaining a hash of the characters we’d seen already, and if we’d already seen the swapped case version of the character, we could add it to a list of “greatest” characters. Once we’d gone through the string, we could just use a max
function to get the greatest character in that last and return it.
Raku
I already knew how to do this in Perl—using the tr
operator—and I figured there would be a corresponding way to do it in Raku. Sure enough, the Str class has a trans
method. In addition, the max
method on the Any class doesn’t care what type the elements are because it uses the smart cmp
operator semantics to find the largest element in the List
.
sub greatestEnglishLetter($str) {
my %seen;
my @greatest;
# find the characters that exist as both
# upper and lower case in the string
for $str.split('', :skip-empty) -> $c {
# note that we've seen the character
%seen{$c} = 1;
# swap the case of the character
my $C = $c.trans(
['a' .. 'z', 'A' .. 'Z'] => ['A' .. 'Z', 'a' .. 'z']
);
# if we've seen the swapped case version of the char,
# add the uppercase version to our greatest hits
@greatest.push: $c.uc if %seen{$C}:exists;
}
# if we found greatest characters,
# return the greater of them
if (@greatest) {
return @greatest.max;
}
# otherwise, return something that
# represents an empty result
return q{''};
$ raku/ch-1.raku
Example 1:
Input: $str = 'PeRlwEeKLy'
Output: L
Example 2:
Input: $str = 'ChaLlenge'
Output: L
Example 3:
Input: $str = 'The'
Output: ''
View the entire Raku script for this task on GitHub.
Perl
The Perl version is a little more compact. We do need to pull in the maxstr
function from List::Util, however. Note that I’m using the non-destructive /r
option on the tr
operator.
use List::Util qw( maxstr );
sub greatestEnglishLetter($str) {
my %seen;
my @greatest;
# find the characters that exist as both
# upper and lower case in the string
foreach my $c ( split //, $str ) {
# note that we've seen the character
$seen{$c} = 1;
# swap the case of the character
my $C = ($c =~ tr/a-zA-Z/A-Za-z/r);
# if we've seen the swapped case version of the char,
# add the uppercase version to our greatest hits
push @greatest, uc($c) if exists $seen{$C};
}
# if we found greatest characters,
# return the greater of them
if (@greatest) {
return maxstr(@greatest);
}
# otherwise, return something that
# represents an empty result
return q{''};
}
View the entire Perl script for this task on GitHub.
Python
Because Python loves to borrow all of Perl’s useful functionality, I knew there had to be a tr
equivalent somewhere… and I found it in the translate
method on the Str
type. There’s even a static maketrans
method on the Str
type that allows you to create a translation table you can pass into translate
. The syntax isn’t as concise as Perl’s (or Raku’s, for that matter), but it wasn’t too bad.
# make a translation table to switch the case of
# English letters
transTable = str.maketrans(
'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ',
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
)
def greatestEnglishLetter(strVar):
seen = {}
greatest = []
# find the characters that exist as both
# upper and lower case in the string
for c in strVar:
# note that we've seen the character
seen[c] = 1
# swap the case of the character
C = c.translate(transTable)
# if we've seen the swapped case version of the char,
# add the uppercase version to our greatest hits
if C in seen:
greatest.append(c.upper())
# if we found greatest characters,
# return the greater of them
if greatest:
return max(greatest)
# otherwise, return something that
# represents an empty result
return "''"
View the entire Python script for this task on GitHub.
Task 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)
Approach
This is just a single loop through the @indices
list to build the @target
list. The “trickiest” part is inserting into the @target
list at arbitrary locations, not just the beginning or the end.
Raku
Fortunately, in Raku there’s an Array routine for that: splice
. It’s supposed to replace elements in an array, but if you specify a zero length for the replacement, it winds up just inserting elements without removing any.
sub targetArray(@source, @indices) {
my @target;
my @explain;
for 0..@indices.end -> $i {
@target.splice(@indices[$i], 0, @source[$i]);
@explain.push: [
@source[$i], @indices[$i], @target.clone
];
}
return @target, @explain;
}
$ raku/ch-2.raku
Example 1:
Input: @source = (0, 1, 2, 3, 4)
@indicies = (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)
@indicies = (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)
@indicies = (0)
Output: (1)
@source @indices @target
1 0 (1)
View the entire Raku script for this task on GitHub.
Perl
For Perl the biggest change is passing around array references rather than arrays.
sub targetArray($source, $indices) {
my @target;
my @explain;
foreach my $i ( 0 .. $#{$indices}) {
splice(@target, $indices->[$i], 0, $source->[$i]);
push @explain, [
$source->[$i], $indices->[$i], [ @target ]
];
}
return \@target, \@explain;
}
View the entire Perl script for this task on GitHub.
Python
In Python, the method for inserting elements into lists at arbitrary locations is named, appropriately enough, insert
.
def targetArray(source, indices):
target = []
explain = []
for i in range(len(indices)):
target.insert(indices[i], source[i])
explain.append([
source[i], indices[i], target.copy()
])
return target, explain
View the entire Python script for this task on GitHub.
Here’s all my solutions in GItHub: https://github.com/packy/perlweeklychallenge-club/tree/master/challenge-264/packy-anderson
Ugh. Notes I want to make for myself to remember later:
In reading laurent_r’s solutions for task 1, I note that rather than using
.split('', :skip-empty)
on the input string, he uses.comb
. Also, rather than making hashes, he uses a Raku Set.