Perl Weekly Challenge: I’m The Greatest Target!

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

One thought on “Perl Weekly Challenge: I’m The Greatest Target!

Leave a Reply