with Raku

This is my response to The Weekly Challenge #242.

You are given two arrays of integers.

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

Example 1:

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])

You are given

Write a script to flip the given matrix as below.

`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.