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 `0`s and `1`s 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.