This is my response to The Weekly Challenge #321.
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.
#! /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
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.