Average Backspace
with Raku

by Arne Sommer

Average Backspace with Raku

[343] Published 17. May 2025.

This is my response to The Weekly Challenge #321.

Challenge #321.1: Distinct Average

You are given an array of numbers with even length.

Write a script to return the count of distinct average. The average is calculated by removing the minimum and the maximum, then average of the two.

Example 1:
Input: @nums = (1, 2, 4, 3, 5, 6)
Output: 1

Step 1: Min = 1, Max = 6, Avg = 3.5
Step 2: Min = 2, Max = 5, Avg = 3.5
Step 3: Min = 3, Max = 4, Avg = 3.5

The count of distinct average is 1.
Example 2:
Input: @nums = (0, 2, 4, 8, 3, 5)
Output: 2

Step 1: Min = 0, Max = 8, Avg = 4
Step 2: Min = 2, Max = 5, Avg = 3.5
Step 3: Min = 3, Max = 4, Avg = 3.5

The count of distinct average is 2.
Example 3:
Input: @nums = (7, 3, 1, 0, 5, 9)
Output: 2

Step 1: Min = 0, Max = 9, Avg = 4.5
Step 2: Min = 1, Max = 7, Avg = 4
Step 3: Min = 3, Max = 5, Avg = 4

The count of distinct average is 2.
File: distinct-average
#! /usr/bin/env raku

unit sub MAIN (*@ints where @ints.elems > 1  # [1]
                  && @ints.elems %% 2        # [1a]
                  && all(@ints) ~~ Int,      # [1b]
               :v(:$verbose));

my @sorted = @ints.sort;                     # [2]
my @avg;                                     # [3]

while @sorted                                # [4]
{
  my $low  = @sorted.shift;                  # [5]
  my $high = @sorted.pop;                    # [6]
  my $avg  = ($low + $high) / 2;             # [7]

  @avg.push: $avg;                           # [8]

  say ": Low:$low, High:$high, Avg: $avg, Todo: @sorted[]" if $verbose;
}

say ": Avg values:{ @avg.join(",") }" if $verbose;

say @avg.unique.elems;                       # [9]

[1] A slurpy array with at least one element, the number of elements must be even (divisible by 2) [1a], and they must all be integers [1b].

[2] Sorting the values makes it easier to extract the lowest and highest values.

[3] The averages will end up here.

[4] As long as we have more values.

[5] Get (and remove) the lowest value, from the start of the array with shift.

See docs.raku.org/routine/shift for more information about shift.

[6] Get (and remove) the highest value, from the end of the array with pop.

See docs.raku.org/routine/pop for more information about pop.

[7] Compute the average of the two values,

[8] and add it to the average array.

[9] Get rid of duplicate values, if any, with unique, and print the number of remaining elements.

See docs.raku.org/routine/unique for more information about unique.

Running it:

$ ./distinct-average 1 2 4 3 5 6
1

$ ./distinct-average 0 2 4 8 3 5
2

$ ./distinct-average 7 3 1 0 5 9
2

Looking good.

With verbose mode:

$ ./distinct-average -v 1 2 4 3 5 6
: Low:1, High:6, Avg: 3.5, Todo: 2 3 4 5
: Low:2, High:5, Avg: 3.5, Todo: 3 4
: Low:3, High:4, Avg: 3.5, Todo:
: Avg values:3.5,3.5,3.5
1

$ ./distinct-average -v 0 2 4 8 3 5
: Low:0, High:8, Avg: 4, Todo: 2 3 4 5
: Low:2, High:5, Avg: 3.5, Todo: 3 4
: Low:3, High:4, Avg: 3.5, Todo:
: Avg values:4,3.5,3.5
2

$ ./distinct-average -v 7 3 1 0 5 9
: Low:0, High:9, Avg: 4.5, Todo: 1 3 5 7
: Low:1, High:7, Avg: 4, Todo: 3 5
: Low:3, High:5, Avg: 4, Todo:
: Avg values:4.5,4,4
2

Challenge #321.2: Backspace Compare

You are given two strings containing zero or more #.

Write a script to return true if the two given strings are same by treating # as backspace.

Example 1:
Input: $str1 = "ab#c"
       $str2 = "ad#c"
Output: true

For first string,  we remove "b" as it is followed by "#".
For second string, we remove "d" as it is followed by "#".
In the end both strings became the same.
Example 2:
Input: $str1 = "ab##"
       $str2 = "a#b#"
Output: true
Example 3:
Input: $str1 = "a#b"
       $str2 = "c"
Output: false

File: backspace-compare
#! /usr/bin/env raku

unit sub MAIN ($str1 is copy,           # [1]
               $str2 is copy,
               :v(:$verbose));

$str1 ~~ s/.\#// while $str1 ~~ /.\#/;  # [2]
$str2 ~~ s/.\#// while $str2 ~~ /.\#/;  # [2a]

say ": Str1:$str1\n: Str2:$str2" if $verbose;

say ($str1 eq $str2);                   # [3]

[1] We use is copy on the input so that we can change the values later on.

See docs.raku.org/type/Parameter#method_copy for more information about is copy.

[2] As long as we have a (as in any) character followed by a #, remove both with the in-place substitution operator {[C:s///}}.

See docs.raku.org/language/operators#s///_in-place_substitution for more information about s///.

[3] Are the resulting strings equal?

Running it:

$ ./backspace-compare "ab#c" "ad#c"
True

$ ./backspace-compare "ab##" "a#b#"
True

$ ./backspace-compare "a#b" "c"
False

Looking good.

With verbose mode:

$ ./backspace-compare -v "ab#c" "ad#c"
: Str1:ac
: Str2:ac
True

$ ./backspace-compare -v "ab##" "a#b#"
: Str1:
: Str2:
True

$ ./backspace-compare -v "a#b" "c"
: Str1:b
: Str2:c
False

Note that the in-place substitution will cause the string ## to be reduced to an empty string:

$ ./backspace-compare -v "" "##"
: Str1:
: Str2:
True

That is probably ok, as a backspace at the start of a string cannot backspace itself out of the string, and we can just remove it.

But it only works for an even number of them. This is not ok:

$ ./backspace-compare -v "" "#"
: Str1:
: Str2:#
False

$ ./backspace-compare -v "" "###"
: Str1:
: Str2:#
False

Let us fix that:

File: backspace-compare-fixed
#! /usr/bin/env raku

unit sub MAIN ($str1 is copy,
               $str2 is copy,
               :v(:$verbose));

$str1 ~~ s/.\#// while $str1 ~~ /.\#/;
$str2 ~~ s/.\#// while $str2 ~~ /.\#/;

$str1 = "" if $str1 eq "#";   # [1]
$str2 = "" if $str2 eq "#";   # [1]

say ": Str1:$str1\n: Str2:$str2" if $verbose;

say ($str1 eq $str2);

[1] This is only a problem if we end up with a single #, so remove it in that case.

Now it works:

$ ./backspace-compare-fixed -v "" "#"
: Str1:
: Str2:
True

$ ./backspace-compare-fixed -v "" "##"
: Str1:
: Str2:
True

$ ./backspace-compare-fixed -v "" "###"
: Str1:
: Str2:
True

And that's it.