Perl Weekly Challenge: Week 368

Challenge 1:

Make It Bigger

You are given a given a string number and a character digit.

Write a script to remove exactly one occurrence of the given character digit from the given string number, resulting the decimal form is maximised.

Example 1
Input: $str = "15456", $char = "5"
Output: "1546"

Removing the second "5" is better because the digit following it (6) is
greater than 5. In the first case, 5 was followed by 4 (a decrease),
which makes the resulting number smaller.
Example 2
Input: $str = "7332", $char = "3"
Output: "732"
Example 3
Input: $str = "2231", $char = "2"
Output: "231"

Removing either "2" results in the same string here. By removing a "2",
we allow the "3" to move up into a higher decimal place.
Example 4
Input: $str = "543251", $char = "5"
Output: "54321"

If we remove the first "5", the number starts with 4. If we remove the
second "5", the number still starts with 5. Keeping the largest possible
digit in the highest place value is almost always the priority.
Example 5
Input: $str = "1921", $char = "1"
Output: "921"

The .indices() method will find each position of an occurrence of $char within $str. We store these in the array @positions.

my @positions = $str.indices($char);

By default we will select the last occurrence of $char for deletion until we find something better.

my $delete = @positions[*-1];

"Something better" in this case means an occurence of $char which has a larger digit in the next position. This loop finds the first such occurrence if any and assigns it to delete.

for @positions -> $pos {
    my $next = $str.substr($pos + 1, 1);
    if  $next > $char {
        $delete = $pos;
        last;
    }
}

Because $str is immutable, we have to copy it into a new string.

my $result = $str;

Then we delete the character at index $delete using .substr-rw().

$result.substr-rw($delete, 1) = q{};

And print the final result.

say $result;

(Full code on Github.)

For Perl, we need a replacement for .indices() but that it pretty easy to implement. The function shown below indices() takes two parameters; the string to be searched, and the character to search for.

sub indices($str, $char) {

We create a list to hold the positions found.

    my @positions;

And a variable that will store the current position within $str. It defaults to -1 for reasons we shall see.

    my $pos = -1;

Now in an infinite loop...

    while(1) {

...we use index() to find the position pf first occurrence of $char in $str starting from position $pos + 1. (So in the first iteration, starting from position 0). The reason we start 1 past the current position is so we don't keep finding the same occurrence over and over again. If an occurrence was found, its' position becomes the new value of $pos.

        $pos = index $str, $char, $pos + 1;

If an occcurrence was not found, index() will return -1 and this means we have reached the end of our search and we break out of the loop with last().

        if ($pos == -1) {
            last;
        }

$pos is appended to the @positions list.

        push @positions, $pos;
    }

And finally @positions is returned.

    return @positions;
}

Now we can translate the Raku code to Perl pretty straightforwardly. Actually, it's a little simpler because we don't have to worry about immutability.

my @positions = indices($str, $char);
my $delete = $positions[-1];

for my $pos (@positions) {
    my $next = substr $str, $pos + 1, 1;
    if  ($next > $char) {
        $delete = $pos;
        last;
    }
}

substr $str, $delete, 1, q{};
say $str;

(Full code on Github.)

Challenge 2:

Big and Little Omega

You are given a positive integer $number and a mode flag $mode. If the mode flag is zero, calculate little omega (the count of all distinct prime factors of that number). If it is one, calculate big omega (the count of all prime factors including duplicates).

Example 1
Input: $number = 100061
    $mode = 0
Output: 3

Prime factors are 13, 43, 179. Count is 3.
Example 2
Input: $number = 971088
    $mode = 0
Output: 3

Prime factors are 2, 2, 2, 2, 3, 20231. Count of distinct numbers is 3.
Example 3
Input: $number = 63640
    $mode = 1
Output: 6

Prime factors are 2, 2, 2, 5, 37, 43. Count including duplicates is 6.
Example 4
Input: $number = 988841
    $mode = 1
Output: 2
Example 5
Input: $number = 211529
    $mode = 0
Output: 2

We have dealt with prime factors several times before in these challenges so I already had a function coded up to calculate them called factorize() which I used most recently in PWC 241. Now solving this challenge is easy.

If the mode is 0, we use factorize() to find the prime factors of $number. then get the unique ones with .unique(), count them with .elems() and print the result with .say().

if $mode == 0 {
    factorize($number).unique.elems.say;

If the mode is 1, the same process is followed except we can omit .unique().

} else {
    factorize($number).elems.say;
}

(Full code on Github.)

For Perl, as well as factorize(), we need a replacement unique() which I also had readily available.

if ($mode == 0) {
    say scalar unique(factorize($number));
} else {
    say scalar factorize($number);
}

(Full code on Github.)