Perl Weekly Challenge: Week 378

Challenge 1:

Second Largest Digit

You are given an alphanumeric string.

Write a script to find the second largest distinct digit in the given string. Return -1 if none found.

Example 1
Input: $str = "aaaaa77777"
Output: -1

The only digit in the given string is 7 and there is no second digit.
Example 2
Input: $str = "abcde"
Output: -1

No numerical digits in the given string.
Example 3
Input: $str = "9zero8eight7seven9"
Output: 8
Example 4
Input: $str = "xyz9876543210"
Output: 8
Example 5
Input: $str = "4abc4def2ghi8jkl2"
Output: 4

We can do this in one line of Raku. The salient part is a call to .match() on the first command-line argument that looks for and catpures digits and returns them in a sequence of Match objects. These are converted to integers with the hyper operator ».Int. They are .sort()ed in descending numeric order and duplicates are removed with.unique(). All this has been wrapped in parentheses so we can treat it as a List. The second value in the list is printed with by the say() at the beginning of the code or if it is not defined (//) the value -1 is printed instead.

say (@*ARGS[0].match(/(\d)/,:g)».Int.sort({ $^b <=> $^a }).unique)[1] //-1

(Full code on Github.)

The Perl version is also one line but longer and more complicated so I have split it up into several lines for this description.

The complication is caused by our lack of .unique(). We work around it by creating a Hash.

my %a;

Digits are matched and captured by a regular expression as in Raku but each digit is treated as a key in the hash. The value of the key is incremented with each occurrence.

for my $i (shift =~ /(\d)/g) {
    $a{$i}++ 
}

When the process is complete, the keys of the hash will be unique digits found in the input. Now the rest of the code can behave the same as in Raku.

say [sort {$b <=> $a} keys %a]->[1] // -1

(Full code on Github.)

Challenge 2:

Sum of Words

You are given three strings consisting of lower case English letters ‘a’ to ‘j’ only. The letter value of a = 0, b = 1, c = 3, etc.

Write a script to find if sum of first two strings return the third string.

Example 1
Input: $str1 = "acb", $str2 = "cba", $str3 = "cdb"
Output: true

$str1 = "acb" = 021
$str2 = "cba" = 210
$str3 = "cdb" = 231
$str1 + $str2 = $str3
Example 2
Input: $str1 = "aab", $str2 = "aac", $str3 = "ad"
Output: true

$str1 = "aab" = 001
$str2 = "aac" = 002
$str3 = "ad"  = 03
Example 3
Input: $str1 = "bc", $str2 = "je", $str3 = "jg"
Output: false

$str1 = "bc" = 12
$str2 = "je" = 94
$str3 = "jg" = 96
Example 4
Input: $str1 = "a", $str2 = "aaaa", $str3 = "aa"
Output: true

$str1 = "a"    = 0
$str2 = "aaaa" = 0000
$str3 = "aa"   = 00
Example 5
Input: $str1 = "c", $str2 = "d", $str3 = "h"
Output: false

$str1 = "c" = 2
$str2 = "d" = 3
$str3 = "h" = 7
Example 6
Input: $str1 = "gfi", $str2 = "hbf", $str3 = "bdhd"
Output: true

$str1 =  "gfi" =  658
$str2 =  "hbf" =  715
$str3 = "bdhd" = 1373

I expected the solution to this challenge to contain more code but surprisingly, it turned out to be one (slightly long) line in both Raku and Perl. Once again, I will split up the line for clarity.

We get the first three command-line arguments as input and assign them to an array for brevity. Then using .map() to iterate over this array, we .trans()form each element from letters to the equivalent numbers.

my @a = @*ARGS[^3].map({ .trans([q{a}..q{j}] => [0..9]) });

Now all we need to do is add the first element to the second and see if it matches the third element. (The answer will be either True or False.)

say @a[0] + @a[1] == @a[2]

(Full code on Github.)

For Perl we use the tr operator (which can be written as y—apparently for sed compatability.) instead of .trans() but to the same effect.

my @a = map { y/a-j/0-9/; $_ } @ARGV[0..2];

Unfortunately as boolean values don't stringify in Perl, we have to provide our own true and false.

say $a[0] + $a[1] == $a[2] ? "true" : "false"

(Full code on Github.)