Special Progression
with Raku

by Arne Sommer

Special Progression with Raku

[375] Published 10. December 2025.

This is my response to The Weekly Challenge #351.

Challenge #351.1: Special Average

You are given an array of integers.

Write a script to return the average excluding the minimum and maximum of the given array.

Example 1:
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
File: special-average
#! /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

Challenge #351.2: Arithmetic Progression

You are given an array of numbers.

Write a script to return true if the given array can be re-arranged to form an arithmetic progression, otherwise return false.

A sequence of numbers is called an arithmetic progression if the difference between any two consecutive elements is the same.

Example 1:
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.
File: arithmetic-progression-array
#! /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.