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;