This is my response to The Weekly Challenge #244.
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)
#! /usr/bin/env raku
unit sub MAIN (*@int where @int.elems > 0 && all(@int) ~~ Int, # [1]
:v(:$verbose));
my $index = 0;
my @output; # [2]
for @int -> $int # [3]
{
my $count = @int.grep( * < $int).elems; # [4]
say ": For index = { $index++ }, count of elements less $int is $count."
if $verbose;
@output.push: $count; # [5]
}
say "(" ~ @output.join(", ") ~ ")"; # [6]
[1] At least one element, all of which must be integers.
[2] The result will end up here.
[3] Iterate over the input values. Note that the index value is used by verbose mode only.
[4] Get the number of values (.elems
) in the
array that are less than the current one, using grep
.
See
docs.raku.org/routine/grep
for more information about grep
.
[5] Add the count (from [4]) to the output array.
[6] Pretty print the result.
Running it:
$ ./count-smaller 8 1 2 2 3
(4, 0, 1, 1, 3)
$ ./count-smaller 6 5 4 8
(2, 1, 0, 3)
$ ./count-smaller 2 2 2
(0, 0, 0)
Looking good.
With verbose mode:
$ ./count-smaller -v 8 1 2 2 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.
(4, 0, 1, 1, 3)
$ ./count-smaller -v 6 5 4 8
: For index = 0, count of elements less 6 is 2.
: For index = 1, count of elements less 5 is 1.
: For index = 2, count of elements less 4 is 0.
: For index = 3, count of elements less 8 is 3.
(2, 1, 0, 3)
$ ./count-smaller -v 2 2 2
: For index = 0, count of elements less 2 is 0.
: For index = 1, count of elements less 2 is 0.
: For index = 2, count of elements less 2 is 0.
(0, 0, 0)
Let us have a go at a oneliner version, using map
:
say "(" ~ @int.map({ @int.grep({ $_ < $_ }).elems }).join(", ") ~ ")";
## #1 #2 #2 #1
See
docs.raku.org/routine/map
for more information about map
.
The idea is that the grepped values (#2) should be lower than the current
limit/value (#1). But this will not work, as $_
will refer to
the grepped one only - and not to values from different scopes.
We can remedy that by saving the outermost $_
(the one from
map
) in a variable:
#! /usr/bin/env raku
unit sub MAIN (*@int where @int.elems > 0 && all(@int) ~~ Int);
say "(" ~ @int.map({ my $map = $_; @int.grep({ $_ < $map }).elems }).join(", ") ~ ")";
Running this gives the expected result:
$ ./count-smaller-oneliner-my 2 2 2
(0, 0, 0)
$ ./count-smaller-oneliner-my 8 1 2 2 3
(4, 0, 1, 1, 3)
$ ./count-smaller-oneliner-my 6 5 4 8
(2, 1, 0, 3)
But... it is not elegant.
We can use a *
(whatever star) (and get rid of the closure brackets)
for one of the original $_
instances instead, thus removing the
conflict:
#! /usr/bin/env raku
unit sub MAIN (*@int where @int.elems > 0 && all(@int) ~~ Int);
say "(" ~ @int.map({ @int.grep( * < $_ ).elems }).join(", ") ~ ")";
Running it gives the expected result, again:
$ ./count-smaller-oneliner 8 1 2 2 3
(4, 0, 1, 1, 3)
$ ./count-smaller-oneliner 6 5 4 8
(2, 1, 0, 3)
$ ./count-smaller-oneliner 2 2 2
(0, 0, 0)
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
#! /usr/bin/env raku
unit sub MAIN (*@nums where @nums.elems > 0 && all(@nums) ~~ Int, # [1]
:v(:$verbose));
my $group = 0; # [2]
my $total = 0; # [3]
for @nums.combinations(1..Inf) -> @candidate # [4]
{
my $max = @candidate.max; # [5]
my $min = @candidate.min; # [6]
my $sum = $max ** 2 * $min; # [7]
$total += $sum; # [8]
say ": Group { ++$group }: ({ @candidate.join(",") }) => \
square(max({ @candidate.join(",") })) * min({ @candidate.join(",") }) \
=> { $max ** 2 } * $min => $sum" if $verbose;
}
say $total; # [9]
[1] As in «Count Smaller».
[2] Used by verbose mode only.
[3] The answer (grand total) is built up here.
[4] Get all the combinations of the input, which is exactly what we are after. Well, not absolutely all. The first one is an empty list, so we use an array slice to start at the second one (with index 1).
See
docs.raku.org/routine/combinations
for more information about combinations
.
[5] Compute the maximum,
[6] the minimum,
[7] and the sum (using the formulae given in the challenge).
[8] Add this sum to the grand total.
[9] Print the grand total.
Running it:
$ ./group-hero 2 1 4
141
Looking good.
With verbose mode:
$ ./group-hero -v 2 1 4
: 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
141
And that's it.