132 Lists
with Raku

by Arne Sommer

132 Lists with Raku

[216] Published 25. December 2022.

This is my response to The Weekly Challenge #196.

Challenge #196.1: Pattern 132

You are given a list of integers, @list.

Write a script to find out subsequence that respect Pattern 132. Return empty array if none found.

Pattern 132 in a sequence (a[i], a[j], a[k]) such that i < j < k and a[i] < a[k] < a[j].

Example 1:
Input:  @list = (3, 1, 4, 2)
Output: (1, 4, 2) respect the Pattern 132.
Example 2:
Input: @list = (1, 2, 3, 4)
Output: () since no susbsequence can be found.
Example 3:
Input: @list = (1, 3, 2, 4, 6, 5)
Output: (1, 3, 2) if more than one subsequence found then return the
  first.
Example 4:
Input: @list = (1, 3, 4, 2)
Output: (1, 3, 2)

File: pattern132
#! /usr/bin/env raku

unit sub MAIN (*@list where @list.elems > 0       # [1]
			 && all(@list) ~~ /^<[1..9]><[0..9]>*$/);

my $end = @list.elems - 1;                        # [2]

for 0 .. $end - 2 -> $i                           # [3]
{
  for $i + 1 .. $end - 1 -> $j                    # [4]
  {
    for $j + 1 .. $end -> $k                      # [5]
    {
      if @list[$i] < @list[$k] < @list[$j]        # [6]
      {
        say "(@list[$i], @list[$j], @list[$k])";  # [6a]
	exit;                                     # [6b]
      }
    }
  }
}

say "()";                                         # [7]

[1] A slurpy array, with at least one element. I have decided to allow posittive integers only, as the examples stick to that subset of the integers mentioned in the challenge.

[2] The index of the last element, as we use it in the three loops below.

[3] The i value can span the entire index range, except the last two - as we have to leave room for j and k.

[4] The j value starts right after i. We stop one index short of the edge, leaving room for k.

[5] The k value takes whatever is left, and iterates over those indices.

[6] Check if the values with the given indices satisfy the Pattern 132 rule. If they do, print the values [6a] and exit [6b]. If not, do nothing. I.e. go on.

[7] We come here if none of the iterations caused a match (and thus a program termination caused by exit). This means a failure, so we print the required empty parens.

Running it:

$ ./pattern132 3 1 4 2
(1, 4, 2)

$ ./pattern132 1 2 3 4
()

$ ./pattern132 1 3 2 4 6 5
(1, 3, 2)

$ ./pattern132 1 3 4 2
(1, 3, 2)

Looking good.

Challenge #196.2: Range List

You are given a sorted unique integer array, @array.

Write a script to find all possible Number Range i.e [x, y] represent range all integers from x and y (both inclusive).

Each subsequence of two or more contiguous integers

Example 1:
Input: @array = (1,3,4,5,7)
Output: [3,5]
Example 2:
Input: @array = (1,2,3,6,7,9)
Output: [1,3], [6,7]
Example 3:
Input: @array = (0,1,2,4,5,6,8,9)
Output: [0,2], [4,6], [8,9]

File: range-list
#! /usr/bin/env raku

unit sub MAIN (*@array where @array.elems > 1         # [1]
		     && all(@array) ~~ /^<[0..9]>+$/  # [2]
		     && [<] @array);                  # [3]

my @ranges;                                           # [4]
my $from = @array.shift.Int;                          # [5]
my $to   = $from;                                     # [6]

while @array                                          # [7]
{
  my $next = @array.shift.Int;                        # [8]
  if $next == $to + 1                                 # [9]
  {
    $to = $next;                                      # [9a]
  }
  else                                                # [10]
  {
    @ranges.push([$from, $to]) if $to > $from;        # [10a]
    $from = $to = $next;                              # [10b]
  }
}

@ranges.push([$from, $to]) if $to > $from;            # [11]

say @ranges.raku;                                     # [12]

[1] At least two elements.

[2] All of them must be non-negative integers, even if the challenge does not explicitly exclude negative values.

[3] The list must be sorted, in ascending order. The Reduction Metaoperator [] is ideal here.

See docs.raku.org/language/operators#Reduction_metaoperators for more information about the Reduction Metaoperator [].

[4] The result (a list of ranges) will end up here.

[5] Get the first value. The trailing .Int coercer gets rid of the pesky IntStr type, which we get courtesy of the command line input.

See docs.raku.org/type/IntStr for more information about the IntStr type.

[8] The last value of the current range. This is initially the same as the start value, giving a range of one value - which is not a range at all.

[7] As long as we have unfinished business (i.e. values yet to be parsed).

[8] Get a new (unparsed) value.

[9] Does the new value fit in as thevery next value in the current range? If so, adjust the upper limit of the range accordingly.

[10] If not, add the current range to the result - if it is a range (e.g. if the start and end values differ) [10a], and initialise the current range with the new start value.

[11] When we are finished parsing the values, add the current range to the result list - if it is a range.

[12] Print the result using the raku method to give a readable output.

See docs.raku.org/routine/raku for more information about the raku method.

$ ./range-list 1 3 4 5 7
[[3, 5],]

$ ./range-list 1 2 3 6 7 9
[[1, 3], [6, 7]]

$ ./range-list 0 1 2 4 5 6 8 9
[[0, 2], [4, 6], [8, 9]]

Looking good. Except the extra brackets (and the trailing comma for the first one), but that is just a presentation quibble...

And that's it.