Perl Weekly Challenge: Week 80

Challenge 1:

Smallest Positive Number

You are given unsorted list of integers @N.

Write a script to find out the smallest positive number missing.

Example 1:
Input: @N = (5, 2, -2, 0)
Output: 1
Example 2:
Input: @N = (1, 8, -1)
Output: 2
Example 3:
Input: @N = (2, 0, -1)
Output: 1

I'll start with my Raku solution.

sub MAIN(
    *@N #= a list of integers
) {

Because we are only interested in positive integers, we can safely get rid of any negative elements from @N. We sort the resulting list, @positives, so that the positive integers are in numeric order.

    my @positives =  @N.grep({ $_ >= 0}).sort;

This means that the first element of @positives is the current smallest positive number.

    my $smallest = @positives[0];

Now we go through the rest of the elements in @positives. for 1 .. @positives.elems -> $i {

If the difference between $smallest and the current element is greater than 1, we know we have a missing number. We can add 1 to $smallest and stop because this is the answer.

I added // Inf in there to deal with the edge case of the last element in the list.

        if (@positives[$i] // Inf) - $smallest > 1 {
            $smallest++;
            last;

If it isn't, we just make the current element the new smallest number.

        } else {
            $smallest = @positives[$i];
        }
    }

And once the array has been fully traversed, we can just print $smallest and there's the answer.

    say $smallest;
}

(Full code on Github.)

This is the Perl version of the code above.

my @N = @ARGV;

my @positives = sort grep { $_ >= 0; } @N;
my $smallest = $positives[0];

for my $i (1 .. scalar @positives) {
    if (($positives[$i] // 'inf') - $smallest > 1) {
        $smallest++;
        last;
    } else {
        $smallest = $positives[$i];
    }
}

say $smallest;

(Full code on Github.)

Challenge 2:

Count Candies

You are given rankings of @N candidates.

Write a script to find out the total candies needed for all candidates. You are asked to follow the rules below:

a) You must given at least one candy to each candidate.

b) Candidate with higher ranking get more candies than their mmediate neighbors on either side.

Example 1:
Input: @N = (1, 2, 2)
Explanation:
Applying rule #a, each candidate will get one candy. So total candies needed so far 3. Now applying rule #b, the first candidate do not get any more candy as its rank is lower than it's neighbours. The second candidate gets one more candy as it's ranking is higher than it's neighbour. Finally the third candidate do not get any extra candy as it's ranking is not higher than neighbour. Therefore total candies required is 4.

Output: 4
Example 2:
Input: @N = (1, 4, 3, 2)
Explanation:
Applying rule #a, each candidate will get one candy. So total candies needed so far 4. Now applying rule #b, the first candidate do not get any more candy as its rank is lower than it's neighbours. The second candidate gets two more candies as it's ranking is higher than it's both neighbour. The third candidate gets one more candy as it's ranking is higher than it's neighbour. Finally the fourth candidate do not get any extra candy as it's ranking is not higher than neighbour. Therefore total candies required is 7.

Output: 7

In Raku:

sub MAIN(
    *@N #= a list of integers
) {

I start by giving every candidate (i.e. every element in the array @N one candy as per rule #a. This can be done very concisely thanks to the xx operator.

    my @candies = 1 xx @N.elems;

Then for each element in @N, I look at the neighbors on either side. If the current element is greater than the one on the left, it gets one more candy and ditto if it is greater than the one to its right. Once again I have to check if the element is defined with // to deal with the edge cases of the first and last elements.

    for 0 ..^ @N.elems -> $i {
        if @N[$i] > (@N[$i - 1] // Inf) {
            @candies[$i]++;
        }
        if @N[$i] > (@N[$i + 1] // Inf) {
            @candies[$i]++;
        }
    }

Finally, all that remains is to total up how many candies were given altogether (easy with [+]) and print that total.

    say [+] @candies;
}

(Full code on Github.)

Perl lacks certain amenities that Raku provides.

my @N = @ARGV;

Instead of xx I used map();

my @candies = map{ 1; } @N;

for my $i (0 .. scalar @N - 1) {
    if ($N[$i] > ($N[$i - 1] // 'inf')) {
        $candies[$i]++;
    }
    if ($N[$i] > ($N[$i + 1] // 'inf')) {
        $candies[$i]++;
    }
}

I could have used map() again here instead of [+] but I chose to use a foreach loop instead.

my $total = 0;
foreach (@candies) {
    $total += $_;
}
say $total;

(Full code on Github.)