Perl Weekly Challenge: Week 244
Challenge 1:
Count Smaller
You are given an array of integers.
Write a script to calculate the number of integers smaller than the integer at each index.
Example 1
Input: @int = (8, 1, 2, 2, 3)
Output: (4, 0, 1, 1, 3)
For index = 0, count of elements less 8 is 4.
For index = 1, count of elements less 1 is 0.
For index = 2, count of elements less 2 is 1.
For index = 3, count of elements less 2 is 1.
For index = 4, count of elements less 3 is 3.
Example 2
Input: @int = (6, 5, 4, 8)
Output: (2, 1, 0, 3)
Example 3
Input: @int = (2, 2, 2)
Output: (0, 0, 0)
For the past few months, I have been giving myself the extra challenge of solving these tasks in one line; more often than not, I can do it. I thought this would be one of those times.
my %s=@*ARGS.sort Z=>@*ARGS.keys;say q{(},@*ARGS.map({ %s{$_}}).join(q{, }),q{)};
There are two statements in the line above. The first creates a hash whose keys are
the sorted elements of the command-line input and the values are the indices of those
elements. This is done via the Z=>
operator where Z
takes consecutive elements from each of the arrays that are its' operands and =>
converts them into a Pair
suitable
for insertion into a hash. The second statement iterates through the input with .map()
and for each element, returns the value from %s
for which that element is a key. (The
rest of the statement is just for pretty-printing the results.)
Unfortunately, for example 1, it gives us this:
(4, 0, 2, 2, 3)
Which is not quite right. The problem is that because 2 is repeated in the input. the first time the key %s{2}
gets the proper value 1, but the second time, it is overwritten with 2. In this short piece of code there is no way of saying don't assign a key-value pair if the key already exists.
So I had to rewrite things in a more verbose way.
my %s;
for @int.sort Z=> @int.keys -> $p {
This time we check that a key doesn't already exist before trying to assign a value to it.
unless %s{$p.key}:exists {
%s{$p.key} = $p.value;
}
}
say q{(}, @int.map({ %s{$_} }).join(q{, }), q{)};
And now we get the correct answer.
This is the Perl version. It has to be even more verbose as we don't have Raku
conveniences such as Z=>
and .keys()
.
my %s;
my $index = 0;
for my $elem (sort { $a <=> $b } @int) {
unless (exists $s{$elem}) {
$s{$elem} = $index;
}
$index++;
}
say q{(}, (join q{, }, map { $s{$_} } @int), q{)};
Challenge 2:
Group Hero
You are given an array of integers representing the strength.
Write a script to return the sum of the powers of all possible combinations; power is defined as the square of the largest number in a sequence, multiplied by the smallest.
Example 1
Input: @nums = (2, 1, 4)
Output: 141
Group 1: (2) => square(max(2)) * min(2) => 4 * 2 => 8
Group 2: (1) => square(max(1)) * min(1) => 1 * 1 => 1
Group 3: (4) => square(max(4)) * min(4) => 16 * 4 => 64
Group 4: (2,1) => square(max(2,1)) * min(2,1) => 4 * 1 => 4
Group 5: (2,4) => square(max(2,4)) * min(2,4) => 16 * 2 => 32
Group 6: (1,4) => square(max(1,4)) * min(1,4) => 16 * 1 => 16
Group 7: (2,1,4) => square(max(2,1,4)) * min(2,1,4) => 16 * 1 => 16
Sum: 8 + 1 + 64 + 4 + 32 + 16 + 16 => 141
This time we can do a one-liner.
@*ARGS.combinations(1..@*ARGS.elems).map({$_.max**2*$_.min}).sum.say
First we get all the combinations of the command-line input from single elements to the
entire thing with @*ARGS.combinations(1..@*ARGS.elems)
. Then with .map()
we take each combination and multiply the square (**2
) of the largest (.max()
) element of
the group and multiply it by the smallest (.min()
) element. All these values are
added together with .sum()
and the result is printed with .say()
.
For Perl we need to supply our own functions to replace the missing .combinations()
and .sum()
atleast which I did from code written for previous challenges. We need
.min()
and .max()
too but rather than code direct replacements for them, I wrote
a function called power that does the entire calculation mentioned in the spec.
sub power {
It takes an array reference as a parameter.
my ($arr) = @_;
The array is sorted numerically.
my @sorted = sort { $a <=> $b } @{$arr};
Now the maximum value will be the last element in the sorted array and the minimum value will be the first; we can plug them directly into the power formula.
return $sorted[-1] ** 2 * $sorted[0];
}
In the main code, we first define a variable to hold a running total.
my $total = 0;
Because my version of combinations()
cannot accept a range, we use a for
loop to get all the combinations of one element upto all the elements of @nums
.
for my $i (1 .. scalar @nums) {
Then for each of the combinations of @nums
of a particular length, we get
the power()
using map()
. We add all these powers together using sum()
and add that number to the running total.
$total += sum([ map { power($_) } combinations(\@nums, $i) ]);
}
Finally, we print the total.
say $total;