This is my response to The Weekly Challenge #345.
@ints.
Input: @ints = (1, 3, 2)
Output: (1)
Example 2:
Input: @ints = (2, 4, 6, 5, 3)
Output: (2)
Example 3:
Input: @ints = (1, 2, 3, 2, 4, 1)
Output: (2, 4)
Example 4:
Input: @ints = (5, 3, 1)
Output: (0)
Example 5:
Input: @ints = (1, 5, 1, 5, 1, 5, 1)
Output: (1, 3, 5)
It follows from the 4th example that the very first and last values should be considered, even if they are not strictly (or otherwise) higher then their left (for the first one) and right (for the last one) non-existent neighbour.
I have decided to support the challenge text as well, with the aptly named «strictly» command line argument.
File: peak-positions
#! /usr/bin/env raku
unit sub MAIN (*@ints where @ints.elems > 0 && all(@ints) ~~ Int, # [1]
:s(:$strictly), # [2]
:v(:$verbose));
my $i-offset = 0; # [3]
unless $strictly # [4]
{
@ints.unshift: -Inf; # [4a]
@ints.push: -Inf; # [4b]
$i-offset = -1; # [4c]
}
my @indices; # [5]
for 1 .. @ints.end - 1 -> $i # [6]
{
my $peak = @ints[$i -1] < @ints[$i] > @ints[$i +1]; # [7]
say ": index { $i + $i-offset } (@ints[$i -1] < @ints[$i] > \
@ints[$i +1]) : $peak" if $verbose;
@indices.push($i + $i-offset) if $peak; # [8]
}
say "({ @indices.join(",") })"; # [9]
[1] A slurpy array of integers, with at least one element.
[2] Enable strict mode if you want the fourth example to fail.
[3] Index offset. Used to compensate for [4a] and [4b].
[4]
Not strict mode? If so, add a very negative number at the front
(unshift) [4a] and end (push) [4b] of the array, and adjust the
index offset [4c] to compensate for the index shift (pun intended) caused
by the first one (i.e. [4a].
See docs.raku.org/routine/unshift for more information about unshift.
See docs.raku.org/routine/push for more information about push.
[5] The result will end up here.
[6] Iterate over the indices, except the very first and last ones. Note
that this will actually iterate over all the original indices (before
the infinitely negative additions), thus checking all the values. Except
in strict mode, where the first and last elements will not be considered
for inclusion in the result. end gives the index of the last element,
and is shorter (to write) than elems -1.
See docs.raku.org/routine/end for more information about end.
[7] Is the value with the current index a peak value?
[8] If so, add it to the result. (After adjusting the index, if we have used strict mode.)
[9] Pretty print the result.
Running it:
$ ./peak-positions 1 3 2
(1)
5 ./peak-positions 2 4 6 5 3
(2)
$ ./peak-positions 1 2 3 2 4 1
(2,4)
$ ./peak-positions 5 3 1
(0)
$ ./peak-positions 1 5 1 5 1 5 1
(1,3,5)
Looking good.
With verbose mode:
$ ./peak-positions -v 1 3 2
: index 0 (-Inf < 1 > 3) : False
: index 1 (1 < 3 > 2) : True
: index 2 (3 < 2 > -Inf) : False
(1)
$ ./peak-positions -v 2 4 6 5 3
: index 0 (-Inf < 2 > 4) : False
: index 1 (2 < 4 > 6) : False
: index 2 (4 < 6 > 5) : True
: index 3 (6 < 5 > 3) : False
: index 4 (5 < 3 > -Inf) : False
(2)
$ ./peak-positions -v 1 2 3 2 4 1
: index 0 (-Inf < 1 > 2) : False
: index 1 (1 < 2 > 3) : False
: index 2 (2 < 3 > 2) : True
: index 3 (3 < 2 > 4) : False
: index 4 (2 < 4 > 1) : True
: index 5 (4 < 1 > -Inf) : False
(2,4)
$ ./peak-positions -v 5 3 1
: index 0 (-Inf < 5 > 3) : True
: index 1 (5 < 3 > 1) : False
: index 2 (3 < 1 > -Inf) : False
(0)
$ ./peak-positions -v 1 5 1 5 1 5 1
: index 0 (-Inf < 1 > 5) : False
: index 1 (1 < 5 > 1) : True
: index 2 (5 < 1 > 5) : False
: index 3 (1 < 5 > 1) : True
: index 4 (5 < 1 > 5) : False
: index 5 (1 < 5 > 1) : True
: index 6 (5 < 1 > -Inf) : False
(1,3,5)
The fourth example with strict mode:
$ ./peak-positions -v -s 5 3 1
: index 1 (5 < 3 > 1) : False
()
@ints where each element is either a positive
integer or -1.
@seen@ans-1
Input: @ints = (5, -1, -1)
Output: (5, -1)
@seen = (5)
First -1: @ans = (5)
Second -1: @ans = (5, -1)
Example 2:
Input: @ints = (3, 7, -1, -1, -1)
Output: (7, 3, -1)
@seen = (3, 7)
First -1: @ans = (7)
Second -1: @ans = (7, 3)
Third -1: @ans = (7, 3, -1)
Example 3:
Input: @ints = (2, -1, 4, -1, -1)
Output: (2, 4, 2)
Example 4:
Input: @ints = (10, 20, -1, 30, -1, -1)
Output: (20, 30, 20)
Example 5:
Input: @ints = (-1, -1, 5, -1)
Output: (-1, -1, 5)
#! /usr/bin/env raku
unit sub MAIN (*@ints where @ints.elems > 0 && all(@ints) ~~ Int, # [1]
:v(:$verbose));
my @seen; # [2]
my @ans; # [2a]
my $x = 0; # [3]
for @ints -> $int # [4]
{
if $int > 0 # [5]
{
$x = 0; # [5a]
@seen.unshift: $int; # [5b]
}
elsif $int == -1 # [6]
{
if $x < @seen.elems # [7]
{
@ans.push: @seen[$x]; # [7a]
}
else # [8]
{
@ans.push: -1; # [8a]
}
$x++; # [9]
}
else # [10]
{
die "Illegal value $int (legal values: -1,1,2,3,..)"; # [11]
}
say ": $int -> seen: { @seen.join(",") } ans: { @ans.join(",") }"
if $verbose;
}
say "({ @ans.join(",") })"; # [12]
[1] A slurpy arrat of integers, with at least one item. The other restrictions on the input is handled in [5], [6] and [11].
[2] The seen and ans arrays.
[3] The number of already encountered -1s.
[4] Iterate over the input values.
[5] Is it positive? If so, reset the x counter [5a] and add
the value to the front of seen (with unshift).
The rest of the code is just the challenge text translated from English to Raku. I have added code line references in the former.
Running it:
$ ./last-visitor 5 -1 -1
(5,-1)
$ ./last-visitor 3 7 -1 -1 -1
(7,3,-1)
$ ./last-visitor 2 -1 4 -1 -1
(2,4,2)
$ ./last-visitor 10 20 -1 30 -1 -1
(20,30,20)
$ ./last-visitor -- -1 -1 5 -1
Looking good.
With verbose mode:
$ ./last-visitor -v 5 -1 -1
: 5 -> seen: 5 ans:
: -1 -> seen: 5 ans: 5
: -1 -> seen: 5 ans: 5,-1
(5,-1)
$ ./last-visitor -v 3 7 -1 -1 -1
: 3 -> seen: 3 ans:
: 7 -> seen: 7,3 ans:
: -1 -> seen: 7,3 ans: 7
: -1 -> seen: 7,3 ans: 7,3
: -1 -> seen: 7,3 ans: 7,3,-1
(7,3,-1)
$ ./last-visitor -v 2 -1 4 -1 -1
: 2 -> seen: 2 ans:
: -1 -> seen: 2 ans: 2
: 4 -> seen: 4,2 ans: 2
: -1 -> seen: 4,2 ans: 2,4
: -1 -> seen: 4,2 ans: 2,4,2
(2,4,2)
$ ./last-visitor -v 10 20 -1 30 -1 -1
: 10 -> seen: 10 ans:
: 20 -> seen: 20,10 ans:
: -1 -> seen: 20,10 ans: 20
: 30 -> seen: 30,20,10 ans: 20
: -1 -> seen: 30,20,10 ans: 20,30
: -1 -> seen: 30,20,10 ans: 20,30,20
(20,30,20)
$ ./last-visitor -v -- -1 -1 5 -1
: -1 -> seen: ans: -1
: -1 -> seen: ans: -1,-1
: 5 -> seen: 5 ans: -1,-1
: -1 -> seen: 5 ans: -1,-1,5
(-1,-1,5)
And that's it.