With Perl Weekly Challenge 337 being Smaller Than Current and an Odd Matrix, I figured I’d dig into my collection and pull out a track called Odd Ones on the Kanno Yōko (菅野 よう子) Cowboy Bebop soundtrack mini-album, Vitaminless.
So with the music out of the way, let’s do some odd programming and knock off PWC337.
Task 1: Smaller Than Current
You are given an array of numbers, @num1
.
Write a script to return an array, @num2
, where $num2[i]
is the count of all numbers less than or equal to $num1[i]
.
Example 1
Input: @num1 = (6, 5, 4, 8)
Output: (2, 1, 0, 3)
index 0: numbers <= 6 are 5, 4 => 2
index 1: numbers <= 5 are 4 => 1
index 2: numbers <= 4, none => 0
index 3: numbers <= 8 are 6, 5, 4 => 3
Example 2
Input: @num1 = (7, 7, 7, 7)
Output: (3, 3, 3, 3)
Example 3
Input: @num1 = (5, 4, 3, 2, 1)
Output: (4, 3, 2, 1, 0)
Example 4
Input: @num1 = (-1, 0, 3, -2, 1)
Output: (1, 2, 4, 0, 3)
Example 5
Input: @num1 = (0, 1, 1, 2, 0)
Output: (1, 3, 3, 4, 1)
Approach
There are multiple ways we could do this. Back in college many moons ago, I’d loop over the list, and inside the loop I would loop over the list again, skipping over the current number, and compare it to the current number, counting the ones that are less than or equal, like this:
count = array[4] of integer;
for i := 0 to 4 do
begin
count[i] := 0;
for j := 0 to 4 do
begin
if j != i and num[j] <= num[i] then
count[i] := count[i] + 1;
end;
end;
But lately I’ve been really enamored of Multisets. We can count how many instances of any particular number using a multiset, and then sum the values for keys less than or equal to the current value, and subtract 1 because there will always be at least 1 instance of the current key.
Raku
Part of me hoped that I could get this down to a one-liner, but I couldn’t make it happen. But I was able to get the meat of the work to happen on line 8. $bag.keys.grep({ $_ <= @num1[$i] })
produces a list of keys where the key is less than or equal to $i
, and we then pass that back into $bag{ }
to get a slice of the values of the map for those keys. Summing the values of these keys will include the current value, so we subtract 1 from the total to get the final count.
sub smallerThan(@num1) {
my $bag = @num1.Bag;
my @out;
for 0..@num1.end -> $i {
@out[$i] = $bag{ $bag.keys.grep({ $_ <= @num1[$i] }) }.sum - 1;
}
@out
}
View the entire Raku script for this task on GitHub.
$ raku/ch-1.raku
Example 1:
Input: @arr = (6, 5, 4, 8)
[2, 1, 0, 3]
Output: (2, 1, 0, 3)
Example 2:
Input: @arr = (7, 7, 7, 7)
[3, 3, 3, 3]
Output: (3, 3, 3, 3)
Example 3:
Input: @arr = (5, 4, 3, 2, 1)
[4, 3, 2, 1, 0]
Output: (4, 3, 2, 1, 0)
Example 4:
Input: @arr = (-1, 0, 3, -2, 1)
[1, 2, 4, 0, 3]
Output: (1, 2, 4, 0, 3)
Example 5:
Input: @arr = (0, 1, 1, 2, 0)
[1, 3, 3, 4, 1]
Output: (1, 3, 3, 4, 1)
Perl
I like reading other people’s solutions to the challenge, and Matthias Muth has been doing wonderful solutions using List::MoreUtils’ frequency
to essentially create bags/multisets.
I tried doing sum(values %bag{grep { $_ <= $num1[$i] } keys %bag }) - 1
like I had in line 8 of the Raku solution, but I found myself getting the error Type of arg 1 to values must be hash or array (not key/value hash slice)
, so I had to assign the hash slice to a temporary variable, %bag2
.
use List::AllUtils qw( sum );
use List::MoreUtils qw( frequency );
sub smallerThan(@num1) {
my %bag = frequency @num1;
my @out;
foreach my $i ( 0 .. $#num1 ) {
my %bag2 = %bag{grep { $_ <= $num1[$i] } keys %bag };
$out[$i] = sum(values %bag2) - 1;
}
@out
}
View the entire Perl script for this task on GitHub.
Python
I realized when I wrote the Python solution that I could have been using push
in the Raku/Perl solutions to add the values to the output array.
def smaller_than(num1):
bag = Counter(num1)
out = []
for i in range(len(num1)):
out.append(
sum(bag[key] for key in bag.keys() if key <= num1[i]) - 1
)
return out
View the entire Python script for this task on GitHub.
Elixir
With Elixir, we’re writing a recursive function smaller_than/3
to process each of the items in num1
. We probably could have done it with nested Enum.map_reduce/3
statements, but I think this looks cleaner.
def smaller_than([], _, out), do: out
def smaller_than([i | rest], bag, out) do
{_, count} = Enum.map_reduce(Map.keys(bag), 0,
fn k, count ->
cond do
k <= i ->
{k, count + Map.get(bag, k)}
true ->
{k, count}
end
end
)
smaller_than(rest, bag, out ++ [count - 1])
end
def smaller_than(num1) do
bag = Enum.frequencies(num1)
smaller_than(num1, bag, [])
end
View the entire Elixir script for this task on GitHub.
Task 2: Odd Matrix
You are given row
and col
, also a list of positions in the matrix.
Write a script to perform action on each location (0-indexed) as provided in the list and find out the total odd valued cells.
For each location (r, c), do both of the following:
a) Increment by 1 all the cells on row r.
b) Increment by 1 all the cells on column c.
Example 1
Input: $row = 2, $col = 3, @locations = ([0,1],[1,1])
Output: 6
Initial:
[ 0 0 0 ]
[ 0 0 0 ]
Apply [0,1]:
Increment row 0:
Before After
[ 0 0 0 ] [ 1 1 1 ]
[ 0 0 0 ] [ 0 0 0 ]
Increment col 1:
Before After
[ 1 1 1 ] [ 1 2 1 ]
[ 0 0 0 ] [ 0 1 0 ]
Apply [1,1]:
Increment row 1:
Before After
[ 1 2 1 ] [ 1 2 1 ]
[ 0 1 0 ] [ 1 2 1 ]
Increment col 1:
Before After
[ 1 2 1 ] [ 1 3 1 ]
[ 1 2 1 ] [ 1 3 1 ]
Final:
[ 1 3 1 ]
[ 1 3 1 ]
Example 2
Input: $row = 2, $col = 2, @locations = ([1,1],[0,0])
Output: 0
Initial:
[ 0 0 ]
[ 0 0 ]
Apply [1,1]:
Increment row 1:
Before After
[ 0 0 ] [ 0 0 ]
[ 0 0 ] [ 1 1 ]
Increment col 1:
Before After
[ 0 0 ] [ 0 1 ]
[ 1 1 ] [ 1 2 ]
Apply [0,0]:
Increment row 0:
Before After
[ 0 1 ] [ 1 2 ]
[ 1 2 ] [ 1 2 ]
Increment col 0:
Before After
[ 1 2 ] [ 2 2 ]
[ 1 2 ] [ 2 2 ]
Final:
[ 2 2 ]
[ 2 2 ]
Example 3
Input: $row = 3, $col = 3, @locations = ([0,0],[1,2],[2,1])
Output: 0
Initial:
[ 0 0 0 ]
[ 0 0 0 ]
[ 0 0 0 ]
Apply [0,0]:
Increment row 0:
Before After
[ 0 0 0 ] [ 1 1 1 ]
[ 0 0 0 ] [ 0 0 0 ]
[ 0 0 0 ] [ 0 0 0 ]
Increment col 0:
Before After
[ 1 1 1 ] [ 2 1 1 ]
[ 0 0 0 ] [ 1 0 0 ]
[ 0 0 0 ] [ 1 0 0 ]
Apply [1,2]:
Increment row 1:
Before After
[ 2 1 1 ] [ 2 1 1 ]
[ 1 0 0 ] [ 2 1 1 ]
[ 1 0 0 ] [ 1 0 0 ]
Increment col 2:
Before After
[ 2 1 1 ] [ 2 1 2 ]
[ 2 1 1 ] [ 2 1 2 ]
[ 1 0 0 ] [ 1 0 1 ]
Apply [2,1]:
Increment row 2:
Before After
[ 2 1 2 ] [ 2 1 2 ]
[ 2 1 2 ] [ 2 1 2 ]
[ 1 0 1 ] [ 2 1 2 ]
Increment col 1:
Before After
[ 2 1 2 ] [ 2 2 2 ]
[ 2 1 2 ] [ 2 2 2 ]
[ 2 1 2 ] [ 2 2 2 ]
Final:
[ 2 2 2 ]
[ 2 2 2 ]
[ 2 2 2 ]
Example 4
Input: $row = 1, $col = 5, @locations = ([0,2],[0,4])
Output: 2
Initial:
[ 0 0 0 0 0 ]
Apply [0,2]:
Increment row 0:
Before After
[ 0 0 0 0 0 ] [ 1 1 1 1 1 ]
Increment col 2:
Before After
[ 1 1 1 1 1 ] [ 1 1 2 1 1 ]
Apply [0,4]:
Increment row 0:
Before After
[ 1 1 2 1 1 ] [ 2 2 3 2 2 ]
Increment col 4:
Before After
[ 2 2 3 2 2 ] [ 2 2 3 2 3 ]
Final:
[ 2 2 3 2 3 ]
Example 5
Input: $row = 4, $col = 2, @locations = ([1,0],[3,1],[2,0],[0,1])
Output: 8
Initial:
[ 0 0 ]
[ 0 0 ]
[ 0 0 ]
[ 0 0 ]
Apply [1,0]:
Increment row 1:
Before After
[ 0 0 ] [ 0 0 ]
[ 0 0 ] [ 1 1 ]
[ 0 0 ] [ 0 0 ]
[ 0 0 ] [ 0 0 ]
Increment col 0:
Before After
[ 0 0 ] [ 1 0 ]
[ 1 1 ] [ 2 1 ]
[ 0 0 ] [ 1 0 ]
[ 0 0 ] [ 1 0 ]
Apply [3,1]:
Increment row 3:
Before After
[ 1 0 ] [ 1 0 ]
[ 2 1 ] [ 2 1 ]
[ 1 0 ] [ 1 0 ]
[ 1 0 ] [ 2 1 ]
Increment col 1:
Before After
[ 1 0 ] [ 1 1 ]
[ 2 1 ] [ 2 2 ]
[ 1 0 ] [ 1 1 ]
[ 2 1 ] [ 2 2 ]
Apply [2,0]:
Increment row 2:
Before After
[ 1 1 ] [ 1 1 ]
[ 2 2 ] [ 2 2 ]
[ 1 1 ] [ 2 2 ]
[ 2 2 ] [ 2 2 ]
Increment col 0:
Before After
[ 1 1 ] [ 2 1 ]
[ 2 2 ] [ 3 2 ]
[ 2 2 ] [ 3 2 ]
[ 2 2 ] [ 3 2 ]
Apply [0,1]:
Increment row 0:
Before After
[ 2 1 ] [ 3 2 ]
[ 3 2 ] [ 3 2 ]
[ 3 2 ] [ 3 2 ]
[ 3 2 ] [ 3 2 ]
Increment col 1:
Before After
[ 3 2 ] [ 3 3 ]
[ 3 2 ] [ 3 3 ]
[ 3 2 ] [ 3 3 ]
[ 3 2 ] [ 3 3 ]
Final:
[ 3 3 ]
[ 3 3 ]
[ 3 3 ]
[ 3 3 ]
Approach
The approach for this is fairly straightforward: we generate a 2D-array/matrix of with the number of columns/rows specified, and then we increment the rows/columns as listed in the @locations
array. Most of the work is going to be reproducing the progress display.
Raku
This gave me a chance to show off some neat Raku features:
- Zip metaoperator – applies a given infix operator to pairs taken one left, one right, from its arguments. The example shows it being used with the infix
~
string concatenation operator, which is exactly what I needed to do on line 6. - List repetition operator (
xx
) – returns a Sequence of$a
repeated and evaluated$b
times ($b
is coerced toInt
). On line 22 I’m using it to make columns for the empty matrix. - On line 49 I was writing
for @locations -> @location
and then using@location[0]
and@location[1]
for the row and column, but then I realized that I could replace@location
with the list($row, $col)
, and I could get much more readable code.
# take two displayMatrix() strings and display them side-by-side
sub displayTwo($display1, $display2) {
# split the two strings and join each row together
my @display = $display1.split(/\n/) Z~ $display2.split(/\n/);
@display.join("\n")
}
sub displayMatrix($label, @matrix) {
my $width = (@matrix[0].elems + 2) * 2 + 1;
my $display = sprintf "%-*s\n", $width, $label;
for 0 .. @matrix.end -> $r {
my @row = @matrix[$r];
$display ~= "[ " ~ @row.join(" ") ~ " ] \n";
}
$display
}
sub emptyMatrix($row, $col) {
my @matrix;
for 1..$row -> $r { @matrix.push([ 0 xx $col ]); }
@matrix;
}
sub incrementRow($row, @matrix is copy) {
for 0..@matrix[0].end -> $col { @matrix[$row][$col]++; }
@matrix
}
sub incrementCol($col, @matrix is copy) {
for 0..@matrix.end -> $row { @matrix[$row][$col]++; }
@matrix
}
sub countOdd(@matrix) {
my $count = 0;
for 0..@matrix.end -> $row {
for 0..@matrix[0].end -> $col {
$count++ if @matrix[$row][$col] % 2;
}
}
$count
}
sub oddMatrix($row, $col, @locations) {
my @matrix = emptyMatrix($row, $col);
my $display = displayMatrix("Initial:", @matrix);
for @locations -> ($row, $col) {
$display ~= "\nApply [$row,$col]:\n";
$display ~= "Increment row $row:\n";
my $before = displayMatrix("Before", @matrix);
@matrix = incrementRow($row, @matrix);
my $after = displayMatrix("After", @matrix);
$display ~= displayTwo($before, $after);
$display ~= "Increment col $col:\n";
$before = displayMatrix("Before", @matrix);
@matrix = incrementCol($col, @matrix);
$after = displayMatrix("After", @matrix);
$display ~= displayTwo($before, $after);
}
$display ~= "\n";
return
countOdd(@matrix),
$display ~ displayMatrix('Final:', @matrix);
}
View the entire Raku script for this task on GitHub.
$ raku/ch-2.raku
Example 1:
Input: $row = 2, $col = 3, @locations = ([0,1],[1,1])
Output: 6
Initial:
[ 0 0 0 ]
[ 0 0 0 ]
Apply [0,1]:
Increment row 0:
Before After
[ 0 0 0 ] [ 1 1 1 ]
[ 0 0 0 ] [ 0 0 0 ]
Increment col 1:
Before After
[ 1 1 1 ] [ 1 2 1 ]
[ 0 0 0 ] [ 0 1 0 ]
Apply [1,1]:
Increment row 1:
Before After
[ 1 2 1 ] [ 1 2 1 ]
[ 0 1 0 ] [ 1 2 1 ]
Increment col 1:
Before After
[ 1 2 1 ] [ 1 3 1 ]
[ 1 2 1 ] [ 1 3 1 ]
Final:
[ 1 3 1 ]
[ 1 3 1 ]
Example 2:
Input: $row = 2, $col = 2, @locations = ([1,1],[0,0])
Output: 0
Initial:
[ 0 0 ]
[ 0 0 ]
Apply [1,1]:
Increment row 1:
Before After
[ 0 0 ] [ 0 0 ]
[ 0 0 ] [ 1 1 ]
Increment col 1:
Before After
[ 0 0 ] [ 0 1 ]
[ 1 1 ] [ 1 2 ]
Apply [0,0]:
Increment row 0:
Before After
[ 0 1 ] [ 1 2 ]
[ 1 2 ] [ 1 2 ]
Increment col 0:
Before After
[ 1 2 ] [ 2 2 ]
[ 1 2 ] [ 2 2 ]
Final:
[ 2 2 ]
[ 2 2 ]
Example 3:
Input: $row = 3, $col = 3, @locations = ([0,0],[1,2],[2,1])
Output: 0
Initial:
[ 0 0 0 ]
[ 0 0 0 ]
[ 0 0 0 ]
Apply [0,0]:
Increment row 0:
Before After
[ 0 0 0 ] [ 1 1 1 ]
[ 0 0 0 ] [ 0 0 0 ]
[ 0 0 0 ] [ 0 0 0 ]
Increment col 0:
Before After
[ 1 1 1 ] [ 2 1 1 ]
[ 0 0 0 ] [ 1 0 0 ]
[ 0 0 0 ] [ 1 0 0 ]
Apply [1,2]:
Increment row 1:
Before After
[ 2 1 1 ] [ 2 1 1 ]
[ 1 0 0 ] [ 2 1 1 ]
[ 1 0 0 ] [ 1 0 0 ]
Increment col 2:
Before After
[ 2 1 1 ] [ 2 1 2 ]
[ 2 1 1 ] [ 2 1 2 ]
[ 1 0 0 ] [ 1 0 1 ]
Apply [2,1]:
Increment row 2:
Before After
[ 2 1 2 ] [ 2 1 2 ]
[ 2 1 2 ] [ 2 1 2 ]
[ 1 0 1 ] [ 2 1 2 ]
Increment col 1:
Before After
[ 2 1 2 ] [ 2 2 2 ]
[ 2 1 2 ] [ 2 2 2 ]
[ 2 1 2 ] [ 2 2 2 ]
Final:
[ 2 2 2 ]
[ 2 2 2 ]
[ 2 2 2 ]
Example 4:
Input: $row = 1, $col = 5, @locations = ([0,2],[0,4])
Output: 2
Initial:
[ 0 0 0 0 0 ]
Apply [0,2]:
Increment row 0:
Before After
[ 0 0 0 0 0 ] [ 1 1 1 1 1 ]
Increment col 2:
Before After
[ 1 1 1 1 1 ] [ 1 1 2 1 1 ]
Apply [0,4]:
Increment row 0:
Before After
[ 1 1 2 1 1 ] [ 2 2 3 2 2 ]
Increment col 4:
Before After
[ 2 2 3 2 2 ] [ 2 2 3 2 3 ]
Final:
[ 2 2 3 2 3 ]
Example 5:
Input: $row = 4, $col = 2, @locations = ([1,0],[3,1],[2,0],[0,1])
Output: 8
Initial:
[ 0 0 ]
[ 0 0 ]
[ 0 0 ]
[ 0 0 ]
Apply [1,0]:
Increment row 1:
Before After
[ 0 0 ] [ 0 0 ]
[ 0 0 ] [ 1 1 ]
[ 0 0 ] [ 0 0 ]
[ 0 0 ] [ 0 0 ]
Increment col 0:
Before After
[ 0 0 ] [ 1 0 ]
[ 1 1 ] [ 2 1 ]
[ 0 0 ] [ 1 0 ]
[ 0 0 ] [ 1 0 ]
Apply [3,1]:
Increment row 3:
Before After
[ 1 0 ] [ 1 0 ]
[ 2 1 ] [ 2 1 ]
[ 1 0 ] [ 1 0 ]
[ 1 0 ] [ 2 1 ]
Increment col 1:
Before After
[ 1 0 ] [ 1 1 ]
[ 2 1 ] [ 2 2 ]
[ 1 0 ] [ 1 1 ]
[ 2 1 ] [ 2 2 ]
Apply [2,0]:
Increment row 2:
Before After
[ 1 1 ] [ 1 1 ]
[ 2 2 ] [ 2 2 ]
[ 1 1 ] [ 2 2 ]
[ 2 2 ] [ 2 2 ]
Increment col 0:
Before After
[ 1 1 ] [ 2 1 ]
[ 2 2 ] [ 3 2 ]
[ 2 2 ] [ 3 2 ]
[ 2 2 ] [ 3 2 ]
Apply [0,1]:
Increment row 0:
Before After
[ 2 1 ] [ 3 2 ]
[ 3 2 ] [ 3 2 ]
[ 3 2 ] [ 3 2 ]
[ 3 2 ] [ 3 2 ]
Increment col 1:
Before After
[ 3 2 ] [ 3 3 ]
[ 3 2 ] [ 3 3 ]
[ 3 2 ] [ 3 3 ]
[ 3 2 ] [ 3 3 ]
Final:
[ 3 3 ]
[ 3 3 ]
[ 3 3 ]
[ 3 3 ]
Perl
Really, the only interesting thing in the Perl implementation is my use of List::AllUtils’ pairwise
to accomplish what I did with Raku’s Zip metaoperator. Everything else is stock Raku → Perl translation.
use List::AllUtils qw( pairwise );
# take two displayMatrix() strings and display them side-by-side
sub displayTwo($display1, $display2) {
# split the two strings and join each row together
my @d1 = split /\n/, $display1;
my @d2 = split /\n/, $display2;
join("\n", pairwise { $a . $b } @d1, @d2) . "\n";
}
sub displayMatrix($label, @matrix) {
my $width = (scalar(@{$matrix[0]}) + 2) * 2 + 1;
my $display = sprintf "%-*s\n", $width, $label;
foreach my $r (0 .. $#matrix) {
my $row = $matrix[$r];
$display .= "[ " . join(" ", @$row) . " ] \n";
}
$display
}
sub emptyMatrix($row, $col) {
my @matrix;
foreach my $r (1 .. $row) { push @matrix, [ (0) x $col ]; }
@matrix;
}
sub incrementRow($row, @matrix) {
foreach my $col ( 0 .. $#{$matrix[0]} ) {
$matrix[$row][$col]++;
}
@matrix
}
sub incrementCol($col, @matrix) {
foreach my $row ( 0 .. $#matrix ) { $matrix[$row][$col]++; }
@matrix
}
sub countOdd(@matrix) {
my $count = 0;
foreach my $row ( 0 .. $#matrix ) {
foreach my $col ( 0 .. $#{$matrix[0]} ) {
$count++ if $matrix[$row][$col] % 2;
}
}
$count
}
sub oddMatrix($row, $col, @locations) {
my @matrix = emptyMatrix($row, $col);
my $display = displayMatrix("Initial:", @matrix);
foreach my $location ( @locations ) {
my ($row, $col) = @$location;
$display .= "\nApply [$row,$col]:\n";
$display .= "Increment row $row:\n";
my $before = displayMatrix("Before", @matrix);
@matrix = incrementRow($row, @matrix);
my $after = displayMatrix("After", @matrix);
$display .= displayTwo($before, $after);
$display .= "Increment col $col:\n";
$before = displayMatrix("Before", @matrix);
@matrix = incrementCol($col, @matrix);
$after = displayMatrix("After", @matrix);
$display .= displayTwo($before, $after);
}
$display .= "\n";
return
countOdd(@matrix),
$display . displayMatrix('Final:', @matrix);
}
View the entire Perl script for this task on GitHub.
Python
Same deal with the Python version: it’s pretty much straight up translation of the Raku script.
- The Python
zip
function returns an iterator of tuples, so on line 6 we stuff the two parts of the tuple into variablesd1
andd2
, concatenate them together, and then append them to the display list
def display_two(display1, display2):
display = []
# split the two strings and join each row together
for d1,d2 in zip(display1.split("\n"), display2.split("\n")):
display.append(d1 + d2)
return "\n".join(display)
def display_matrix(label, matrix):
width = (len(matrix[0]) + 2) * 2 + 1
display = label.ljust(width) + "\n"
for r in range(len(matrix)):
display += "[ " + int_join(" ",matrix[r]) + " ] \n"
return display
def empty_matrix(row, col):
matrix = []
for r in range(row):
matrix.append([])
for c in range(col):
matrix[r].append(0)
return matrix
def increment_row(row, matrix):
for col in range(len(matrix[0])):
matrix[row][col] += 1
return matrix
def increment_col(col, matrix):
for row in range(len(matrix)):
matrix[row][col] += 1
return matrix
def count_odd(matrix):
count = 0
for row in range(len(matrix)):
for col in range(len(matrix[0])):
if matrix[row][col] % 2: count += 1
return count
def odd_matrix(row, col, locations):
matrix = empty_matrix(row, col)
display = display_matrix("Initial:", matrix)
for row,col in locations:
display += f"\nApply [{row},{col}]:\n"
display += f"Increment row {row}:\n"
before = display_matrix("Before", matrix)
matrix = increment_row(row, matrix)
after = display_matrix("After", matrix)
display += display_two(before, after)
display += f"Increment col {col}:\n"
before = display_matrix("Before", matrix)
matrix = increment_col(col, matrix)
after = display_matrix("After", matrix)
display += display_two(before, after)
display += "\n"
return(
count_odd(matrix),
display + display_matrix('Final:', matrix)
)
def int_join(joiner, arr):
return joiner.join(map(lambda i: str(i), arr))
View the entire Python script for this task on GitHub.
Elixir
For the Elixir solution, I rediscovered how to use ELixir’s List Comprehensions to do a bunch of the looping over lists.
require Integer # because is_odd is a guard
def display_two(display1, display2) do
Enum.zip_with(
String.split(display1, "\n"),
String.split(display2, "\n"),
fn x, y -> x <> y end
)
|> Enum.join("\n")
end
def display_matrix(label, matrix) do
rows = for row <- matrix, do:
"[ " <> Enum.join(row, " ") <> " ] "
width = (length(Enum.at(matrix, 0)) + 2) * 2 + 1
Enum.join(
[ String.pad_trailing(label, width) ] ++ rows, "\n"
) <> "\n"
end
def empty_matrix(row, col) do
for _r <- 1..row do
for _c <- 1..col, do: 0
end
end
def increment_row(row, matrix) do
List.replace_at(matrix, row,
(for elem <- Enum.at(matrix, row), do: elem + 1)
)
end
def increment_col(col, matrix) do
for row <- matrix, do:
List.replace_at(row, col, Enum.at(row, col) + 1)
end
def count_odd(matrix) do
{_, count} = Enum.map_reduce(matrix, 0, fn row, count ->
{_, count} = Enum.map_reduce(row, count, fn elem, count ->
{
elem,
cond do
Integer.is_odd(elem) -> count + 1
true -> count
end
}
end)
{ row, count }
end)
count
end
def odd_matrix([], matrix, display) do
{
count_odd(matrix),
display <> "\n" <> display_matrix("Final:", matrix)
}
end
def odd_matrix([loc | locs], matrix, display) do
{row, col} = {List.first(loc), List.last(loc)}
display = display <> "\nApply [#{row},#{col}]:\n"
display = display <> "Increment row #{row}:\n"
before_var = display_matrix("Before", matrix)
matrix = increment_row(row, matrix)
after_var = display_matrix("After", matrix)
display = display <> display_two(before_var, after_var)
display = display <> "Increment col #{col}:\n"
before_var = display_matrix("Before", matrix)
matrix = increment_col(col, matrix)
after_var = display_matrix("After", matrix)
display = display <> display_two(before_var, after_var)
odd_matrix(locs, matrix, display)
end
def odd_matrix(row, col, locations) do
matrix = empty_matrix(row, col)
odd_matrix(
locations, matrix,
display_matrix("Initial:", matrix)
)
end
View the entire Elixir script for this task on GitHub.
Here’s all my solutions in GItHub: https://github.com/packy/perlweeklychallenge-club/tree/master/challenge-337/packy-anderson