Perl Weekly Challenge: Week 340

Challenge 1:

Duplicate Removals

You are given a string, $str, consisting of lowercase English letters.

Write a script to return the final string after all duplicate removals have been made. Repeat duplicate removals on the given string until we no longer can.

A duplicate removal consists of choosing two adjacent and equal letters and removing them.

Example 1
Input: $str = 'abbaca'
Output: 'ca'

Step 1: Remove 'bb' => 'aaca'
Step 2: Remove 'aa' => 'ca'
Example 2
Input: $str = 'azxxzy'
Output: 'ay'

Step 1: Remove 'xx' => 'azzy'
Step 2: Remove 'zz' => 'ay'
Example 3
Input: $str = 'aaaaaaaa'
Output: ''

Step 1: Remove 'aa' => 'aaaaaa'
Step 2: Remove 'aa' => 'aaaa'
Step 3: Remove 'aa' => 'aa'
Step 4: Remove 'aa' => ''
Example 4
Input: $str = 'aabccba'
Output: 'a'

Step 1: Remove 'aa' => 'bccba'
Step 2: Remove 'cc' => 'bba'
Step 3: Remove 'bb' => 'a'
Example 5
Input: $str = 'abcddcba'
Output: ''

Step 1: Remove 'dd' => 'abccba'
Step 2: Remove 'cc' => 'abba'
Step 3: Remove 'bb' => 'aa'
Step 4: Remove 'aa' => ''

This is a simple one-liner in Raku. First we get the input from the first command-line argument and assign it to $_. This will save some typing in the rest of the code. Then we use a regular expression to match a character followed by another instance of the same character and using the s/// operator "substitute" them with an empty string or in other words, delete them. We repeatedly do this in an otherwise empty while-loop. The loop will end when there are no more substitutions to be made. We them print whatever is left in $_ with .say() Because we are not explicitly stating which variable is the topic of .say(), we have to provide a semicolon after the while block to prevent ambiguity.

$_ = @*ARGS[0]; while s/(.)$0// {}; .say

(Full code on Github.)

The Perl version is a one-liner too and works in the same way as Raku. It is actually a little shorter whch doesn't happen too often.

$_ = shift; while (s/(.)\1//) {} say

(Full code on Github.)

Challenge 2:

Ascending Numbers

You are given a string, $str, is a list of tokens separated by a single space. Every token is either a positive number consisting of digits 0-9 with no leading zeros, or a word consisting of lowercase English letters.

Write a script to check if all the numbers in the given string are strictly increasing from left to right.

Example 1
Input: $str = "The cat has 3 kittens 7 toys 10 beds"
Output: true

Numbers 3, 7, 10 - strictly increasing.
Example 2
Input: $str = 'Alice bought 5 apples 2 oranges 9 bananas'
Output: false
Example 3
Input: $str = 'I ran 1 mile 2 days 3 weeks 4 months'
Output: true
Example 4
Input: $str = 'Bob has 10 cars 10 bikes'
Output: false
Example 5
Input: $str = 'Zero is 0 one is 1 two is 2'
Output: true

Yet another one-liner in Raku. As in challenge 1, first we get the input from the first command-line argument. We find all the numbers with .match() and convert them from match objects back to numbers with the hyper operator ».Int. This results in an array of numbers, @n. This is compared to a numerically sorted version of itself. If the comparison is is True, we print that using .say() otherwise we print False.

my @n = @*ARGS[0].match(/(\d+)/,:g)».Int; (@n ~~ @n.sort({$^a <=> $^b})).say

(Full code on Github.)

I tried writing a one-liner in Perl, but it was a little too long to justify the name. Instead look at this:

Like the Raku version, we start by assigning the numbers matched from the input into an array, @n.

my @n = shift =~ /(\d+)/g;

We set up a variable to hold the result. Initially, this will be true.

my $result = true;

Because we are using modern Perls new true and false builtin we have to add these lines to the top of the script.

use builtin qw/ true false /;
no warnings qw/ experimental::builtin /;

Instead of comparing this array with a sorted version of itself, we look through all of its elements starting from the second and compare each element to the one before it.

for my $i (1 .. scalar @n - 1) {

If it is lesser, the array is not strictly increasing so we set $result to false and stop processing.

    if ($n[$i] < $n[$i- 1]) {
        $result = false;
        last
    }
}

Depending on the value of $result, we print true or false.

say $result ? "true" : "false";

(Full code on Github.)