This is my response to The Weekly Challenge #351.
Input: @ints = (8000, 5000, 6000, 2000, 3000, 7000)
Output: 5250
Min: 2000
Max: 8000
Avg: (3000+5000+6000+7000)/4 = 21000/4 = 5250
Example 2:
Input: @ints = (100_000, 80_000, 110_000, 90_000)
Output: 95_000
Min: 80_000
Max: 110_000
Avg: (100_000 + 90_000)/2 = 190_000/2 = 95_000
Example 3:
Input: @ints = (2500, 2500, 2500, 2500)
Output: 0
Min: 2500
Max: 2500
Avg: 0
Example 4:
Input: @ints = (2000)
Output: 0
Min: 2000
Max: 2000
Avg: 0
/pre>
Example 5:
Input: @ints = (1000, 2000, 3000, 4000, 5000, 6000)
Output: 3500
Min: 1000
Max: 6000
Avg: (2000 + 3000 + 4000 + 5000)/4 = 14000/4 = 3500
#! /usr/bin/env raku
unit sub MAIN (*@ints where @ints.elems > 0 && all(@ints) ~~ Int, # [1]
:v(:$verbose));
my @sorted = @ints.sort; # [2]
my $min = @sorted[0]; # [3]
my $max = @sorted[*-1]; # [4]
if $verbose
{
say ":Min $min, Max: $max";
say ":Sorted { @sorted.join(",") }";
}
@sorted.shift while @sorted && @sorted[0] == $min; # [5]
@sorted.pop while @sorted && @sorted[*-1] == $max; # [6]
say ":Sorted w/o min/max: { @sorted.join(",") }" if $verbose;
say @sorted.elems ?? @sorted.sum / @sorted.elems !! 0; # [7]
[1] Ensure an array of at least one integer.
[2] Sort the values, lowest first (by default).
[3] The minimum value (at index 0), to get rid of.
[4] The maximum value (at the rightmost index), also to get rid of.
[5] Get rid of the lowest value, once and for all
duplicates (if any) with shift. Pun intended.
See docs.raku.org/routine/shift for more information about shift.
[6] Ditto, for the highest value with pop.
See docs.raku.org/routine/pop for more information about pop.
[7] Print the average. But beware of an empty array so that we do not divide by zero. As example 3 and 4 would have us do otherwise.
Running it:
$ ./special-average 8000 5000 6000 2000 3000 7000
5250
$ ./special-average 100_000 80_000 110_000 90_000
95000
$ ./special-average 2500 2500 2500 2500
0
$ ./special-average -2000
0
$ ./special-average 1000 2000 3000 4000 5000 6000
3500
Looking good.
With verbose mode:
$ ./special-average -v 8000 5000 6000 2000 3000 7000
:Min 2000, Max: 8000
:Sorted 2000,3000,5000,6000,7000,8000
:Sorted w/o min/max: 3000,5000,6000,7000
5250
$ ./special-average -v 100_000 80_000 110_000 90_000
:Min 80_000, Max: 110_000
:Sorted 80_000,90_000,100_000,110_000
:Sorted w/o min/max: 90_000,100_000
95000
$ ./special-average -v 2500 2500 2500 2500
:Min 2500, Max: 2500
:Sorted 2500,2500,2500,2500
:Sorted w/o min/max:
0
$ ./special-average -v 2000
:Min 2000, Max: 2000
:Sorted 2000
:Sorted w/o min/max:
0
$ ./special-average -v 1000 2000 3000 4000 5000 6000
:Min 1000, Max: 6000
:Sorted 1000,2000,3000,4000,5000,6000
:Sorted w/o min/max: 2000,3000,4000,5000
3500
Input: @num = (1, 3, 5, 7, 9)
Output: true
Already AP with common difference 2.
Example 2:
Input: @num = (9, 1, 7, 5, 3)
Output: true
The given array re-arranged like (1, 3, 5, 7, 9) with common
difference 2.
Example 3:
Input: @num = (1, 2, 4, 8, 16)
Output: false
This is geometric progression and not arithmetic progression.
Example 4:
Input: @num = (5, -1, 3, 1, -3)
Output: true
The given array re-arranged like (-3, -1, 1, 3, 5) with common
difference 2.
Example 5:
Input: @num = (1.5, 3, 0, 4.5, 6)
Output: true
The given array re-arranged like (0, 1.5, 3, 4.5, 6) with common
difference 1.5.
#! /usr/bin/env raku
unit sub MAIN (*@num where @num.elems > 0 && all(@num) ~~ Numeric, # [1]
:v(:$verbose));
my @sorted = @num.sort; # [2]
my @difference; # [3]
for 0 .. @sorted.end - 1 -> $i # [4]
{
@difference.push: @sorted[$i+1] - @sorted[$i]; # [5]
}
if $verbose
{
say ":Sorted: { @sorted.join(", ") }";
say ":Differences: { @difference.join(", ") }";
}
say [==] @difference; # [6]
[1] At least one number, of the Numeric persuation.
See docs.raku.org/routine/Numeric for more information about Numeric.
[2] Sort the values, so that we can compare them.
[3] The difference between all the neighbouring values will end up here.
[4] Iterate over the index of the left value to compare.
[5] Compute the difference to the next (right) value, and add it to the difference list.
[6] Are all the differeces equal? We
use the Reduction Metaoperator [] with == to compute this in
one go.
See docs.raku.org/language/operators#Reduction_metaoperators for more information about the Reduction Metaoperator [].
Running it:
$ ./arithmetic-progression-array 1 3 5 7 9
True
$ ./arithmetic-progression-array 9 1 7 5 3
True
$ ./arithmetic-progression-array 1 2 4 8 16
False
$ ./arithmetic-progression-array 5 -1 3 1 -3
True
$ ./arithmetic-progression-array 1.5 3 0 4.5 6
True
Looking good.
With verbose mode:
$ ./arithmetic-progression-array -v 1 3 5 7 9
:Sorted: 1, 3, 5, 7, 9
:Differences: 2, 2, 2, 2
True
$ ./arithmetic-progression-array -v 9 1 7 5 3
:Sorted: 1, 3, 5, 7, 9
:Differences: 2, 2, 2, 2
True
$ ./arithmetic-progression-array -v 1 2 4 8 16
:Sorted: 1, 2, 4, 8, 16
:Differences: 1, 2, 4, 8
False
$ ./arithmetic-progression-array -v 5 -1 3 1 -3
:Sorted: -3, -1, 1, 3, 5
:Differences: 2, 2, 2, 2
True
$ ./arithmetic-progression-array -v 1.5 3 0 4.5 6
:Sorted: 0, 1.5, 3, 4.5, 6
:Differences: 1.5, 1.5, 1.5, 1.5
True
It does not really matter for the short examples we are given, but we should perhaps optimise the last part (i.e. [5] and [6]) so that we jump out if we encounter a different difference. So to speak. (Consider a million element list where the lowest values are 1, 2 and 4.)
File: arithmetic-progression
#! /usr/bin/env raku
unit sub MAIN (*@num where @num.elems > 0 && all(@num) ~~ Numeric,
:v(:$verbose));
my @sorted = @num.sort;
my $difference; # [1]
say ":Sorted: { @sorted.join(", ") }";
for 0 .. @sorted.end - 1 -> $i
{
my $d = @sorted[$i+1] - @sorted[$i]; # [2]
if $i == 0 # [3]
{
$difference = $d; # [3a]
say ":Initial difference $d" if $verbose;
}
else # [4]
{
if $difference != $d # [5]
{
say False; # [5a]
exit; # [5b]
}
}
}
say True; # [6]
[1] The (global) difference is now a single value.
[2] The current difference.
[3] Initialise the global difference manually for the first value (index 0).
[4] The rest of the iterations;
[5] A different difference than the expected one? Report failure [5a] and be gone [5b].
[6] Failure to fail means success. Say so.
Running it gives the expected result, here with a verbose mode that is slightly less verbose than the original one:
$ ./arithmetic-progression -v 1 3 5 7 9
:Sorted: 1, 3, 5, 7, 9
:Initial difference 2
True
$ ./arithmetic-progression -v 9 1 7 5 3
:Sorted: 1, 3, 5, 7, 9
:Initial difference 2
True
$ ./arithmetic-progression -v 1 2 4 8 16
:Sorted: 1, 2, 4, 8, 16
:Initial difference 1
False
$ ./arithmetic-progression -v 5 -1 3 1 -3
:Sorted: -3, -1, 1, 3, 5
:Initial difference 2
True
$ ./arithmetic-progression -v 1.5 3 0 4.5 6
:Sorted: 0, 1.5, 3, 4.5, 6
:Initial difference 1.5
True
Elegant it ain't...
This one-linerish-y version is elegant, though. But we are back in the land of inefficiency:
File: arithmetic-progression-map
#! /usr/bin/env raku
unit sub MAIN (*@num where @num.elems > 0 && all(@num) ~~ Numeric);
my @sorted = @num.sort;
my @difference = (0 .. @sorted.end - 1).map: @sorted[$_+1] - @sorted[$_]; # [1]
say [==] @difference;
[1] A programming guru near you may have told you that it is rude
to use map on indices, but I do not subsscribe to that school of thought.
See docs.raku.org/routine/map for more information about map.
Even shorter:
File: arithmetic-progression-onelinerish
#! /usr/bin/env raku
unit sub MAIN (*@num where @num.elems > 0 && all(@num) ~~ Numeric);
say [==] (0 .. @num.end - 1).map({ (@num.sort)[$_+1] - (@num.sort)[$_] }); # [1]
[1] Twice sorted is twice bitten, or something...
Running it gives the expected result.
$ ./arithmetic-progression-onelinerish 1 3 5 7 9
True
$ ./arithmetic-progression-onelinerish 9 1 7 5 3
True
$ ./arithmetic-progression-onelinerish 1 2 4 8 16
False
$ ./arithmetic-progression-onelinerish 5 -1 3 1 -3
True
$ ./arithmetic-progression-onelinerish 1.5 3 0 4.5 6
True
And that's it.