Perl Weekly Challenge 345.

My solutions (task 1 and task 2 ) to the The Weekly Challenge - 345.

Task 1: Peak Positions

Submitted by: Mohammad Sajid Anwar
You are given an array of integers, @ints.

Find all the peaks in the array, a peak is an element that is strictly greater
than its left and right neighbours. Return the indices of all such peak positions.


Example 1
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)

I use the Perl Data Language (PDL) to read the array, and compare each member with that of the array rotated to the right and rotated to the left. Using periodic boundary conditions, this yields a one-liner:

Examples:

perl -MPDL -E '
for(@ARGV){$x=pdl($_);say "$_ -> ", which(($x>$x->rotate(1))&($x>$x->rotate(-1)))}
' '[1 3 2]' '[2 4 6 5 3]' '[1 2 3 2 4 1]' '[5 3 1]' '[1 5 1 5 1 5 1]'

Results:

[1 3 2] -> [1]
[2 4 6 5 3] -> [2]
[1 2 3 2 4 1] -> [2 4]
[5 3 1] -> [0]
[1 5 1 5 1 5 1] -> [1 3 5]

The third example, [5 3 1] is curious, as the first element, 5, has no neighbour to its left. In my solution, I compared it to the last element in the array, 1. Thus, if I change the last element to 5, there would be no solution.

perl -MPDL -E '
for(@ARGV){$x=pdl($_);say "$_ -> ", which(($x>$x->rotate(1))&($x>$x->rotate(-1)))}
' '[5 3 5]'

Results:

[5 3 5] -> Empty[0]

I don’t know if this is the desired result. The alternative is to pad the array on both sides with small enough numbers, which woud be equivalent to ignoring the non-existing neighbors, as in the following two-liner:

perl -MPDL -MPDL::NiceSlice -E '
for(@ARGV){$x=pdl($_);$y=append(append($x(0)-1, $x), $x(-1)-1);
say "$_ -> ",which(($y>$y->rotate(1))&($y>$y->rotate(-1)))-1}
' '[1 3 2]' '[2 4 6 5 3]' '[1 2 3 2 4 1]' '[5 3 1]' '[1 5 1 5 1 5 1]' '[5 3 5]'

Results:

[1 3 2] -> [1]
[2 4 6 5 3] -> [2]
[1 2 3 2 4 1] -> [2 4]
[5 3 1] -> [0]
[1 5 1 5 1 5 1] -> [1 3 5]
[5 3 5] -> [0 2]

I choose this interpretation for the full code:

 1  # Perl weekly challenge 345
 2  # Task 1:  Peak Positions
 3  #
 4  # See https://wlmb.github.io/2025/10/27/PWC345/#task-1-peak-positions
 5  use v5.36;
 6  use feature qw(try);
 7  use PDL;
 8  use PDL::NiceSlice;
 9  
10  die <<~"FIN" unless @ARGV;
11      Usage: $0 A0 A1...
12      to find the local maxima of the arrays An,
13      given by strings of the form
14      "[X0 X1...]" that may be parsed by PDL.
15      FIN
16  for(@ARGV){
17      try {
18          my $ints=pdl($_);
19          # pad with smaller numbers at boundaries
20          my $padded=append(append($ints(0)-1, $ints), $ints(-1)-1);
21          my $indices=which(($padded>$padded->rotate(1))&($padded>$padded->rotate(-1)));
22          $indices -= 1; # remove left boundaries
23          say "$_ -> $indices";
24      }
25      catch($e){warn $e;}
26  }

Examples:

./ch-1.pl '[1 3 2]' '[2 4 6 5 3]' '[1 2 3 2 4 1]' '[5 3 1]' '[1 5 1 5 1 5 1]' '[5 3 5]'

Results:

[1 3 2] -> [1]
[2 4 6 5 3] -> [2]
[1 2 3 2 4 1] -> [2 4]
[5 3 1] -> [0]
[1 5 1 5 1 5 1] -> [1 3 5]
[5 3 5] -> [0 2]

Task 2: Last Visitor

Submitted by: Mohammad Sajid Anwar
You are given an integer array @ints where each element is either a
positive integer or -1.

We process the array from left to right while maintaining two lists:

@seen: stores previously seen positive integers (newest at the front)
@ans: stores the answers for each -1

Rules:

If $ints[i] is a positive number -> insert it at the front of @seen
If $ints[i] is -1:
Let $x be how many -1s in a row we’ve seen before this one.

If $x < len(@seen) -> append seen[x] to @ans

Else -> append -1 to @ans

At the end, return @ans.


Example 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)  # shouldn't it be 7 3?
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)

Note: There is an error in example 2.

I just follow the instructions, which yield a two-liner:

perl -E '
for(@ARGV){$c=0;@a=@s=();for(split " "){unshift(@s, $_),$c=0,next if $_ > 0;
push @a,$c<@s?$s[$c]:-1;++$c;}say "[$_] -> [@a]";}
' -- '5 -1 -1' '3 7 -1 -1 -1' '2 -1 4 -1 -1' '10 20 -1 30 -1 -1' "-1 -1 5 -1"

Results:

[5 -1 -1] -> [5 -1]
[3 7 -1 -1 -1] -> [7 3 -1]
[2 -1 4 -1 -1] -> [2 4 2]
[10 20 -1 30 -1 -1] -> [20 30 20]
[-1 -1 5 -1] -> [-1 -1 5]

The corresponding full code is:

 1  # Perl weekly challenge 345
 2  # Task 2:  Last Visitor
 3  #
 4  # See https://wlmb.github.io/2025/10/27/PWC345/#task-2-last-visitor
 5  use v5.36;
 6  use feature qw(try);
 7  die <<~"FIN" unless @ARGV;
 8      Usage: $0 A0 A1...
 9      where An is a string with space separated numbers, positive
10      or -1. For each run of -1's output the previously seen positive
11      numbers, or -1 if they have been exhausted.
12      FIN
13  for(@ARGV){
14      try {
15          my $count=0; # count of succesive -1's
16          my @answer = my @seen =();
17          my @input = split " ";
18          for(@input){
19              unshift(@seen, $_), $count=0, next if $_ > 0;
20              die "Only positive numbers and -1 are allowed: $_"
21                  unless $_ == -1;
22              push @answer, $count<@seen? $seen[$count]:-1;
23              ++$count;
24          }
25          say "[$_] -> [@answer]";
26      }
27      catch($e){warn $e;}
28  }

Example:

./ch-2.pl '5 -1 -1' '3 7 -1 -1 -1' '2 -1 4 -1 -1' '10 20 -1 30 -1 -1' "-1 -1 5 -1"

Results:

[5 -1 -1] -> [5 -1]
[3 7 -1 -1 -1] -> [7 3 -1]
[2 -1 4 -1 -1] -> [2 4 2]
[10 20 -1 30 -1 -1] -> [20 30 20]
[-1 -1 5 -1] -> [-1 -1 5]

/;

Written on October 27, 2025