This is my response to The Weekly Challenge #235.
Input: @ints = (0, 2, 9, 4, 6)
Output: true
Removing ONLY 9 in the given array makes it strictly increasing order.
Example 2:
Input: @ints = (5, 1, 3, 2)
Output: false
Example 3:
Input: @ints = (2, 2, 3)
Output: true
#! /usr/bin/env raku
unit sub MAIN (*@ints where all(@ints) ~~ Int, :v(:$verbose)); # [1]
# ( say 'true'; exit ) if [<] @ints; # [2]
for 0 .. @ints.end -> $index # [3]
{
my @copy = @ints.clone; # [4]
@copy.splice($index,1); # [5]
say ":Index $index: remove value @ints[$index] -> @copy[]" if $verbose;
( say 'true'; exit ) if [<] @copy; # [6]
}
say 'false'; # [7]
[1] One or more integers.
[2] Are they sorted already?
This is easy to ascertain with the Reduction Metaoperator [] in combination
with <
(the "less than" operator). If so, we are done.
(Note that I have commented out this line of code as this situation will
be catched by the very first iteration of the loop [3].)
See
docs.raku.org/language/operators#Reduction_metaoperators for more
information about the Reduction Metaoperator []
.
[3] Iterate over the indices in the input. (Note that
for ^@ints.elems -> $index
would work just as well).
[4] Get a copy (with clone
), so
that we can change it (the copy), without messing up the original.
See
docs.raku.org/routine/clone
for more information about clone
.
[5] Remove the (as in "one") element at the given index
(with splice
).
See
docs.raku.org/routine/splice
for more information about splice
.
[6] Have we succeeded? If so, say so and exit.
[7] Not succeeded by now? Then we have failed. Say so
Running it:
$ ./remove-one 0 2 9 4 6
true
$ ./remove-one 5 1 3 2
false
$ ./remove-one 2 2 3
true
Looking good.
With verbose mode:
$ ./remove-one -v 0 2 9 4 6
:Index 0: remove value 0 -> 2 9 4 6
:Index 1: remove value 2 -> 0 9 4 6
:Index 2: remove value 9 -> 0 2 4 6
true
$ ./remove-one -v 5 1 3 2
:Index 0: remove value 5 -> 1 3 2
:Index 1: remove value 1 -> 5 3 2
:Index 2: remove value 3 -> 5 1 2
:Index 3: remove value 2 -> 5 1 3
false
$ ./remove-one -v 2 2 3
:Index 0: remove value 2 -> 2 3
true
Input: @ints = (1, 0, 2, 3, 0, 4, 5, 0)
Ouput: (1, 0, 0, 2, 3, 0, 0, 4)
Example 2:
Input: @ints = (1, 2, 3)
Ouput: (1, 2, 3)
Example 3:
Input: @ints = (0, 3, 0, 4, 5)
Ouput: (0, 0, 3, 0, 0)
This task is ideally suited for
gather
/take
.
See my Raku Gather,
I Take article or
docs.raku.org/syntax/gather take for more information about
gather
/take
.
#! /usr/bin/env raku
unit sub MAIN (*@ints where all(@ints) ~~ Int); # [1]
my $seq := gather # [2]
{
for @ints -> $int # [3]
{
take $int; # [4]
take $int if $int == 0; # [5]
}
}
say "({ $seq[^@ints.elems].join(", ") })"; # [6]
[1] Zero or more integers.
[2] Set up a sequence with gather
.
[3] Iterate over the input values.
[4] Let each value pass through (to the output sequence) with take
.
[5] Add an extra zero (with take
), if the input value is zero.
[6] Collect as many values from the output sequence, as we have in
the input, and pretty print them. The sequence is lazy, so the program
will not pass through (generate with take
) any more values than
requested.
Running it:
$ ./duplicate-zeros 1 0 2 3 0 4 5 0
(1, 0, 0, 2, 3, 0, 0, 4)
$ ./duplicate-zeros 1 2 3
(1, 2, 3)
$ ./duplicate-zeros 0 3 0 4 5
(0, 0, 3, 0, 0)
Looking good.
And that's it.