This is my response to The Weekly Challenge #242.
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])
n x n
binary matrix.
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.