Perl Weekly Challenge: Week 262

Challenge 1:

Max Positive Negative

You are given an array of integers, @ints.

Write a script to return the maximum number of either positive or negative integers in the given array.

Example 1
Input: @ints = (-3, 1, 2, -1, 3, -2, 4)
Output: 4

Count of positive integers: 4
Count of negative integers: 3
Maximum of count of positive and negative integers: 4
Example 2
Input: @ints = (-1, -2, -3, 1)
Output: 3

Count of positive integers: 1
Count of negative integers: 3
Maximum of count of positive and negative integers: 3
Example 3
Input: @ints = (1,2)
Output: 2

Count of positive integers: 2
Count of negative integers: 0
Maximum of count of positive and negative integers: 2

Just looking at this problem I was confident that Raku could solve it in one line and I was right though only if you stretch the definition of one line a bit.

@*ARGS.classify({ if $_ < 0 { q{n} } elsif $_ > 0 { q{p} } else { q{0} } }, :into(my %a)); max(%a<n>.elems, %a<p>.elems).say

(Full code on Github.)

The mainstay of this code is the .classify() method. This takes a subroutine which we provide and runs it on each element in @ints. Its' output is used as a key into the hash provided by the :into parameter. In our case the subroutine returns 'n' if the element is negative and 'p' if it is positive. (We also have to allow for the element being 0 even though the spec doesn't mention it.) So our hash %a will have two keys (three counting '0' but we're ignoring that,) 'n' and 'p'. Then all we have to do is find which of those keys was bigger using .max() and .elems() and print the result with .say().

Perl doesn't have .classify() so we just loop through @ints and increment either $positive or $negative depending on whether the element is positive or negative. We don't need to worry about 0 in this method.

my $positive = 0;
my $negative = 0;

for my $n (@ints) {
    if ($n > 0) {
        $positive++;
    } elsif ($n < 0) {
        $negative++;
    }
}

say 0+($positive > $negative) ? $positive : $negative;

(Full code on Github.)

Challenge 2:

Multiply by Two

You are given an array of integers, @ints and an integer $k.

Write a script to return the number of pairs (i, j) where

a) 0 <= i < j < size of @ints
b) ints[i] == ints[j]
c) i x j is divisible by k
Example 1
Input: @ints = (3,1,2,2,2,1,3) and $k = 2
Output: 4

(0, 6) => ints[0] == ints[6] and 0 x 6 is divisible by 2
(2, 3) => ints[2] == ints[3] and 2 x 3 is divisible by 2
(2, 4) => ints[2] == ints[4] and 2 x 4 is divisible by 2
(3, 4) => ints[3] == ints[4] and 3 x 4 is divisible by 2
Example 2
Input: @ints = (1,2,3) and $k = 1
Output: 0

This one could possibly be a one-liner too but it seemed simpler and easier to spread it out over multiple lines.

Given @ints...

@ints

...first we get a list of all the indices of its' elements with .keys().

    .keys

We find all the combinations of 2 indices with .combinations(2).

    .combinations(2)

Using .grep() we filter out the combination where the elements at both indices are equal and the product of those elements (which we find with the [*] operator) is divisible by $k (which we find with the %% operator.)

    .grep({ @ints[@$_[0]] == @ints[@$_[1]] && ([*] @$_) %% $k })

We count how many suitable combinations were found with .elems().

    .elems

And finally, we print that number with .say().

    .say;

(Full code on Github.)

For Perl we have to provide our own version of combinations() and workarounds are needed for a lack of [*] and %% operators.

say
    scalar
    grep { $ints[$_->[0]] == $ints[$_->[1]] && ($_->[0] * $_->[1]) % $k == 0 }
    combinations([ keys @ints ], 2);

(Full code on Github.)