Perl Weekly Challenge 264: Greatest English Letter

These are some answers to the Week 264, Task 1, of the Perl Weekly Challenge organized by Mohammad S. Anwar.

Spoiler Alert: This weekly challenge deadline is due in a few days from now (on April 14, 2024 at 23:59). This blog post provides some solutions to this challenge. Please don’t read on if you intend to complete the challenge on your own.

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: ''

Greatest English Letter in Raku

I first thought about a regex that could, for example, detect any pair of lowercase / uppercase letter, or vice versa. This turned out to be quite inconvenient.

So I went for a completely different approach: make two lists: one with uppercase letters and one with lowercase letters, except that we store the string’s lower case letters converted as upper case letters. Then we simply look for letters that are common to the two lists (and finally sort them appropriately).

In Raku, we will store the lists in Sets, which are immutable collections of distinct elements in no particular order. One advantage of using a Set is that this removes any duplicate from each list. The second advantage is that we can use the set intersection operator to find letters that are in both lists.

One final note: we use the (Unicode) predefined character classes <:Lu> and <:Ll> to distinguish uppercase from lowercase characters.

sub greatest-eng-let ($in) {
    my $uc = (grep { / <:Lu> / }, $in.comb).Set;
    my $lc = (map { .uc }, grep {/ <:Ll> /}, $in.comb).Set;
    return  ($uc ∩ $lc).keys.sort.tail // "''"; 
}

my @tests = < PeRlwEeKLy ChaLlenge The >;
for @tests -> $test {
    printf "%-15s => ", $test;
    say greatest-eng-let $test;
}

This program displays the following output:

$ raku ./greatest-eng-let.raku
PeRlwEeKLy      => L
ChaLlenge       => L
The             => ''

Greatest English Letter in Perl

Our Perl implementation essentially follows the same approach as the Raku program above: make two lists: one with uppercase letters and one with lowercase letters, except that we store the string’s lower case letters converted as upper case letters. Then we simply look for letters that are common to the two lists (and finally sort them appropriately).

In Perl, our two lists will be stored in hashes. Hashes, as Raku’s Sets, remove duplicates. There is no set intersection operator, but it is quite easy to use a loop or a grep to find common items.

use strict;
use warnings;
use feature 'say';

sub greatest_eng_let {
    my @in = split //, shift;
    my %uc = map { $_ => 1 } grep { $_ le 'Z' } @in; 
    my @common =  grep { exists $uc{$_}}
                 map { uc } grep {$_ gt 'Z'} @in;
    return (sort @common)[-1] // "''"; 
}

my @tests = qw < PeRlwEeKLy ChaLlenge The >;
for my $test (@tests) {
    printf "%-15s => ", $test;
    say greatest_eng_let $test;
}

This program displays the following output:

$ perl ./greatest-eng-let.pl
PeRlwEeKLy      => L
ChaLlenge       => L
The             => ''

Wrapping up

The next week Perl Weekly Challenge will start soon. If you want to participate in this challenge, please check https://perlweeklychallenge.org/ and make sure you answer the challenge before 23:59 BST (British summer time) on April 21, 2024. And, please, also spread the word about the Perl Weekly Challenge if you can.

Leave a comment

About laurent_r

user-pic I am the author of the "Think Perl 6" book (O'Reilly, 2017) and I blog about the Perl 5 and Raku programming languages.