Missing Matrix
with Raku

by Arne Sommer

Missing Matrix with Raku

[262] Published 12. November 2023.

This is my response to The Weekly Challenge #242.

Challenge #242.1: Missing Members

You are given two arrays of integers.

Write a script to find out the missing members in each other arrays.

Example 1:
Input: @arr1 = (1, 2, 3)
       @arr2 = (2, 4, 6)
Output: ([1, 3], [4, 6])

(1, 2, 3) has 2 members (1, 3) missing in the array (2, 4, 6).
(2, 4, 6) has 2 members (4, 6) missing in the array (1, 2, 3).
Example 2:
Input: @arr1 = (1, 2, 3, 3)
       @arr2 = (1, 1, 2, 2)
Output: ([3])

(1, 2, 3, 3) has 2 members (3, 3) missing in the array (1, 1, 2, 2).
Since they are same, keep just one.

(1, 1, 2, 2) has 0 member missing in the array (1, 2, 3, 3).

File: missing-members
#! /usr/bin/env raku

unit sub MAIN (Str $arr1, $arr2);  # [1]

my @arr1 = $arr1.words>>.Int;      # [2]
my @arr2 = $arr2.words>>.Int;      # [2]

die "@arr1: Must be integers only" unless all(@arr1) ~~ Int;  # [3]
die "@arr2: Must be integers only" unless all(@arr2) ~~ Int;  # [3]

my @missing2 = (@arr1 (-) @arr2).keys.sort;                   # [4]
my @missing1 = (@arr2 (-) @arr1).keys.sort;                   # [4]

print "(";                                                    # [5]
print "[{ @missing2.join(", ") }]" if @missing2.elems;        # [6]
print ", " if @missing2.elems && @missing1.elems;             # [7]
print "[{ @missing1.join(", ") }]" if @missing1.elems;        # [6]
say ")";                                                      # [5]

[1] Specify the two arrays as space separated strings.

[2] Coerce the values to Integers, getting rid of the pesky IntStr type. Note that the coercion is lazy, so we get the error message from [3] if any of the values are not integer coersion friendly. (Note that «3.14» will be coerced to «3» and thus allowed by [3].)

[3] Ensure that we get integers only.

[4] Use the Set Difference Operator (-) on the two lists. The result is a Set, so we slap on .keys to get the actual values (which you will not get with .values). They come in an undefined order, so we apply .sort to get a sorted result.

See docs.raku.org/language/setbagmix#Set_operators_that_return_a_QuantHash and scroll down to the set difference operator (-).

[5] Print the outermost parens.

[6] Print none, one or two sets of missing members, enclosed in brackets.

[7] print a comma, but only if we have two sets of missing members.

Running it:

$ ./missing-members "1 2 3" "2 4 6"
([1, 3], [4, 6])

$ ./missing-members "1 2 3 3" "1 1 2 2"
([3])

Looking good.

Non numeric values are catched:

$ ./missing-members "1 2 3 3" "1 1 2 2 a"
@arr2: Must be integers only
  in sub MAIN at ./missing-members line 9
  in block <unit> at ./missing-members line 1

Non-integer numeric values are silently coerced to integers:

$ ./missing-members "1 2 3 3.14" "1 1 2 2"
([3])

Challenge #242.2: Flip Matrix

You are given n x n binary matrix.

Write a script to flip the given matrix as below.

1 1 0
0 1 1
0 0 1

a) Reverse each row

0 1 1
1 1 0
1 0 0

b) Invert each member

1 0 0
0 0 1
0 1 1
Example 1:
Input: @matrix = ([1, 1, 0], [1, 0, 1], [0, 0, 0])
Output: ([1, 0, 0], [0, 1, 0], [1, 1, 1])
Example 2:
Input: @matrix = ([1, 1, 0, 0], [1, 0, 0, 1], [0, 1, 1, 1], [1, 0, 1, 0])
Output: ([1, 1, 0, 0], [0, 1, 1, 0], [0, 0, 0, 1], [1, 0, 1, 0])

The matrix input (the command line) and the value and size checking are similar to the work done in «matrix-score», the second half of Scored Product with Raku, my response to The Weekly Challenge #218.

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

unit sub MAIN (Str $matrix = "1 1 0 | 1 0 1 | 0 0 0", :v(:$verbose));  # [1]

my @matrix = $matrix.split("|")>>.words>>.Array;                       # [2]
my @rows   = @matrix>>.elems;                                          # [3]

die "Must have at least 1 row" unless @rows.elems >= 1;                # [4]
die "The rows must have the same size" unless [==] @rows;              # [5]
die "Must contain 0 and 1 only" unless all(@matrix[*;*]) eq any(0,1);  # [6]
die "Must have the same number of columns and rows"
  unless @rows.elems == @rows[0];                                      # [7]

my @reverse  = @matrix>>.Int>>.reverse>>.Array;                        # [8]
my @inverted = @reverse>>.&invert;                                     # [9]

say ": Matrix:   { @matrix.raku }"  if $verbose;
say ": Reverse:  { @reverse.raku }" if $verbose;

say @inverted.List.raku;                                               # [10]

multi sub invert (*@bool)                                              # [11]
{
  return |@bool.map( + ! * ).Array;                                    # [11a]
}

[1] The values are separated by spaces, and the rows by «|». Note the default matrix, courtesy of example 1.

[2] Get the two dimentional data structure (i.e. a matrix). The .Array is there to unlazify the rows, as we would get a (lazy) sequence for each otherwise. This does not really matter, but verbose mode looks uncool if we skip it.

[3] The length of each row.

[4] Require at least 1 row.

[5] The rows must have the same size.

[6] The matrix must contain 0s and 1s only, using and all junction on a flattened matrix does the trick; @matrix[*;*].

[7] Require the same number of columns and rows.

[8] Reverse each row. The .Int coercer gets rid of the strings we got from words, and .Array is there to unlazify the result - as in [2].

See docs.raku.org/routine/words for more information about words.

[9] Invert each row. Note the .& syntax that allows us to pretend that a procedure is a method. This is really handy here, as we use >>. to apply it to each element (i.e. row).

[10] Print the result, with the raku method. The trailing .Array is there as .raku print arrays wrapped in "()" and lists in "[]".

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

[11] For each row, use map on (from the right inside the parens) each element (*), negate it (!; turning 1 into False and 0 into True), convert back to a number (0 or 1, with +). The .Array is there to unlazify the result, yet again. The | is the flattening operator, as the result will look unfriendly otherwise.

See docs.raku.org/routine/| for more information about |.

Running it:

$ ./flip-matrix
([1, 0, 0], [0, 1, 0], [1, 1, 1])

$ ./flip-matrix "1 1 0 0 | 1 0 0 1 | 0 1 1 1 | 1 0 1 0"
([1, 1, 0, 0], [0, 1, 1, 0], [0, 0, 0, 1], [1, 0, 1, 0])

Looking good.

With verbose mode:

$ ./flip-matrix -v
: Matrix:   [["1", "1", "0"], ["1", "0", "1"], ["0", "0", "0"]]
: Reverse:  [[0, 1, 1], [1, 0, 1], [0, 0, 0]]
([1, 0, 0], [0, 1, 0], [1, 1, 1])

$ ./flip-matrix -v "1 1 0 0 | 1 0 0 1 | 0 1 1 1 | 1 0 1 0"
: Matrix:   [["1", "1", "0", "0"], ["1", "0", "0", "1"], \
      ["0", "1", "1", "1"], ["1", "0", "1", "0"]]
: Reverse:  [[0, 0, 1, 1], [1, 0, 0, 1], [1, 1, 1, 0], [0, 1, 0, 1]]
([1, 1, 0, 0], [0, 1, 1, 0], [0, 0, 0, 1], [1, 0, 1, 0])

And that's it.