Monotonically Reshaped
Raku

by Arne Sommer

Monotonically Reshaped Raku

[224] Published 14. February 2023.

This is my response to The Weekly Challenge #204.

Challenge #204.1: Monotonic Array

You are given an array of integers.

Write a script to find out if the given array is Monotonic. Print 1 if it is otherwise 0.

An array is Monotonic if it is either monotone increasing or decreasing.

Example 1:
Input: @nums = (1,2,2,3)
Output: 1
Example 2:
Input: @nums (1,3,2)
Output: 0
Example 3:
Input: @nums = (6,5,5,4)
Output: 1

This is easy, using the Reduction Metaoperator [].

File: monotonic-array
#! /usr/bin/env raku

unit sub MAIN (*@nums where @nums.elems >= 2 && all(@nums) ~~ /^<[0..9]>*$/);
                             # [1]
if    [<=] @nums { say 1; }  # [2]
elsif [>=] @nums { say 1; }  # [3]
else             { say 0; }  # [4]

[1] Ensure at least two elements (as that should be the minimum for an array), and the values must be non-negative integers.

[2] The increasing rule: All the values (except the last one) must be less than or equal (<=) to the next one for this to be satisfied. The Reduction Metaoperator [] applies the operator (the content of the brackets) to the elements in the array. In this case [<=] (1, 2, 3) is the same as 1 <= 2 <= 3. The end result is a single Booolean value, thus the Reduction part of the name.

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

[3] The decreasing rule.

[4] Not Monotonic.

Running it:

$ ./monotonic-array 1 2 2 3
1

$ ./monotonic-array 1 3 2
0

$ ./monotonic-array 6 5 5 4
1

Looking good.

Verbose mode is missing in this program, as there really isn't anything to be verbose about.

Challenge #204.2: Reshape Matrix

You are given a matrix (m x n) and two integers (r) and (c).

Write a script to reshape the given matrix in form (r x c) with the original value in the given matrix. If you can’t reshape print 0.

Example 1:
Input: [ 1 2 ]
       [ 3 4 ]

       $matrix = [ [ 1, 2 ], [ 3, 4 ] ]
       $r = 1
       $c = 4

Output: [ 1 2 3 4 ]
Example 2:
Input: [ 1 2 3 ]
       [ 4 5 6 ]

       $matrix = [ [ 1, 2, 3 ] , [ 4, 5, 6 ] ]
       $r = 3
       $c = 2

Output: [ [ 1, 2 ], [ 3, 4 ], [ 5, 6 ] ]

        [ 1 2 ]
        [ 3 4 ]
        [ 5 6 ]
Example 3:
Input: [ 1 2 ]

       $matrix = [ [ 1, 2 ] ]
       $r = 3
       $c = 2

Output: 0

We need a way of specifying a matrix on the command line, and the compact format I used in Ordered Search for Raku (the program search-matrix2), my response to challenge 111, is equally suitable here. Use spaces between the values, and | between the rows. The three examples can be specified by the following strings: "1 2 | 3 4", "1 2 3 | 4 5 6" and "1 2".

File: reshape-matrix
#! /usr/bin/env raku

unit sub MAIN (Str :m(:$matrix) = "1 2 | 3 4",           # [1]
               Int :$r where $r > 0 = 1,                 # [2]
               Int :$c where $c > 0 = 4,                 # [2]
                   :v(:$verbose));

my @all   = $matrix.split(/<[\s|]>+/);                   # [3]
my $count = @all.elems;                                  # [4]

say ": All: { @all.raku }" if $verbose;
say ": Count: $count"      if $verbose;

if $r * $c == $count                                     # [5]
{
  my @result;                                            # [6]

  for ^$r -> $row-id                                     # [7]
  {
    my $start-index = $row-id * $c;
    my $stop-index  = $start-index + $c - 1;
    
    say ": Row $row-id [ index $start-index .. $stop-index ]" if $verbose;
    @result.push: @all[$start-index .. $stop-index];     # [8]
  }

  say @result.raku;                                      # [9]
}
else                                                     # [10]
{
  say 0;                                                 # [10]
}

[1] A named argument (the leading :) for the matrix («--matrix»; and the «-m» shortcut), as well as a default value.

[2] The r and c values, also as named arguments.

[3] We do not need the two dimentional matrix at all, so we unwrap the input as a plain (one dimentional) array with this regex. If you do want the two dimensions, this will do the trick: $matrix.split("|")>>.words>>.List;.

[4] The number of elements.

[5] We can only reshape the matrix if the number of values in both versions (input and output) are equal.

[6] The result (the rows in the new matrix) will end up here.

[7] Iterate over the row numbers (zero based) in the new matrix.

[8] Copy the relevant values, by their indices, and add them as a row to the new matrix.

[9] Print the result, using the builtin raku method.

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

[10] Wrong number of elements (courtesy of [5]), if we get here. Print 0 (error).

Running it:

$ ./reshape-matrix -m="1 2 | 3 4" -r=1 -c=4
[("1", "2"),]

$ ./reshape-matrix -m="1 2 3| 4 5 6" -r=3 -c=2
[("1", "2"), ("3", "4"), ("5", "6")]

$ ./reshape-matrix -m="1 2" -r=3 -c=2
0

As expected.

With verbose mode:

$ ./reshape-matrix -v -m="1 2 | 3 4" -r=1 -c=4
: All: ["1", "2", "3", "4"]
: Count: 4
: Row 0 [ index 0 .. 3 ]
[("1", "2", "3", "4"),]

$ ./reshape-matrix -v -m="1 2 3|4 5 6" -r=3 -c=2
: All: ["1", "2", "3", "4", "5", "6"]
: Count: 6
: Row 0 [ index 0 .. 1 ]
: Row 1 [ index 2 .. 3 ]
: Row 2 [ index 4 .. 5 ]
[("1", "2"), ("3", "4"), ("5", "6")]

$ ./reshape-matrix -v -m="1 2" -r=3 -c=2
: All: ["1", "2"]
: Count: 2
0

We should probably get rid of the quotes, and use brackets (i.e. [ and ]) only:

File: reshape-matrix2
#! /usr/bin/env raku

unit sub MAIN (Str :m(:$matrix) = "1 2 | 3 4",
               Int :$r where $r > 0 = 1,
               Int :$c where $c > 0 = 4,
                   :v(:$verbose));

my @all   = $matrix.split(/<[\s|]>+/);
my $count = @all.elems;

say ": All: { @all.raku }" if $verbose;
say ": Count: $count"      if $verbose;

if $r * $c == $count
{
  my @result;

  for ^$r -> $row-id
  {
    my $start-index = $row-id * $c;
    my $stop-index  = $start-index + $c - 1;
    
    say ": Row $row-id [ index $start-index .. $stop-index ]" if $verbose;
    @result.push: @all[$start-index .. $stop-index];
  }

  say @result.elems == 1
    ?? "[ { @result[0].join(", ") } ]"                                  # [1]
    !! "[ { @result.map({ '[' ~ @_.join(", ") ~ ']' }).join(", ") } ]"; # [2+]
}
else
{
  say 0;
}

[1] Drop the outer brackets if there is only one row.

[2+] Two or more rows, with outer brackets.

Running it:

$ ./reshape-matrix2 -v -m="1 2 | 3 4" -r=1 -c=4
: All: ["1", "2", "3", "4"]
: Count: 4
: Row 0 [ index 0 .. 3 ]
[ 1, 2, 3, 4 ]

$ ./reshape-matrix2 -v -m="1 2 3|4 5 6" -r=3 -c=2
: All: ["1", "2", "3", "4", "5", "6"]
: Count: 6
: Row 0 [ index 0 .. 1 ]
: Row 1 [ index 2 .. 3 ]
: Row 2 [ index 4 .. 5 ]
[ [1 2], [3 4], [5 6] ]

$ ./reshape-matrix -v -m="1 2" -r=3 -c=2
: All: ["1", "2"]
: Count: 2
0

Looking good.

Some more, just for fun:

$ ./reshape-matrix2 -v -m="1 2 3|4 5 6" -r=2 -c=3
: All: ["1", "2", "3", "4", "5", "6"]
: Count: 6
: Row 0 [ index 0 .. 2 ]
: Row 1 [ index 3 .. 5 ]
[ [1 2 3], [4 5 6] ]

./reshape-matrix2 -v -m="1 2 3|4 5 6" -r=1 -c=6
: All: ["1", "2", "3", "4", "5", "6"]
: Count: 6
: Row 0 [ index 0 .. 5 ]
[ 1, 2, 3, 4, 5, 6 ]

$ ./reshape-matrix2 -v -m="1 2 3|4 5 6" -r=6 -c=1
: All: ["1", "2", "3", "4", "5", "6"]
: Count: 6
: Row 0 [ index 0 .. 0 ]
: Row 1 [ index 1 .. 1 ]
: Row 2 [ index 2 .. 2 ]
: Row 3 [ index 3 .. 3 ]
: Row 4 [ index 4 .. 4 ]
: Row 5 [ index 5 .. 5 ]
[ [1], [2], [3], [4], [5], [6] ]

And that's it.