Perl Weekly Challenge 381‘s tasks are “Same Row Column” and “Smaller Greater Element”.
This week I honed in on “great” for the music choice, and Freddie Mercury’s version of The Great Pretender immediately jumped to mind.
Task 1: Same Row Column
You are given a n x n matrix containing integers from 1 to n.
Write a script to find if every row and every column contains all the integers from 1 to n.
Input: @matrix = ([1, 2, 3, 4],
[2, 3, 4, 1],
[3, 4, 1, 2],
[4, 1, 2, 3],)
Output: true
Input: @matrix = ([1])
Output: true
Input: @matrix = ([1, 2, 5],
[5, 1, 2],
[2, 5, 1],)
Output: false
Elements are out of range 1..3.
Input: @matrix = ([1, 2, 3],
[1, 2, 3],
[1, 2, 3],)
Output: false
Input: @matrix = ([1, 2, 3],
[3, 1, 2],
[3, 2, 1],)
Output: false
Approach
Well, we’re looking at whether or not a given set of n integers has all the integers from 1..n. So we put the integers into a set-like structure (a hash or dict), and then test to see if the numbers from 1..n exist in the set.
Raku
This is the first solution I whipped up in Raku. First? Yeah, wait a moment—we’ll get back to that. Because I’m doing the check to see if the if the numbers from 1..n exist in the set in multiple places, I spun it out into its own function so I didn’t have to repeat the code. In the first loop, I check the columns to see if they have all the numbers, and I use an inner loop to build column arrays, so I can later, in a second loop, check those columns to see if they have all the numbers.
sub hasAllNum($n, @elems) {
my $set = set @elems;
return False unless $set.elems == $n;
for 1 .. $n -> $i {
return False unless $i ∈ $set;
}
return True;
}
sub sameRowColumn(@matrix) {
my $n = @matrix.elems;
my @col;
for 0 .. $n-1 -> $r {
# check the row
return False unless hasAllNum($n, @matrix[$r]);
# build columns
for 0 .. $n-1 -> $c {
@col[$c].push(@matrix[$r][$c]);
}
}
# check columns
for 0 .. $n-1 -> $c {
return False unless hasAllNum($n, @col[$c]);
}
return True;
}$ raku/ch-1.raku
Example 1:
Input: @matrix = [
[1, 2, 3, 4],
[2, 3, 4, 1],
[3, 4, 1, 2],
[4, 1, 2, 3]
]
Output: True
Example 2:
Input: @matrix = [
[1]
]
Output: True
Example 3:
Input: @matrix = [
[1, 2, 5],
[5, 1, 2],
[2, 5, 1]
]
Output: False
Example 4:
Input: @matrix = [
[1, 2, 3],
[1, 2, 3],
[1, 2, 3]
]
Output: False
Example 5:
Input: @matrix = [
[1, 2, 3],
[3, 1, 2],
[3, 2, 1]
]
Output: FalsePython
There is a trick in Python, however, for getting the columns from a 2D matrix: use the zip() function. The zip function is supposed to be used to zip together multiple lists into tuples with an item from each list, but if you feed it an n x n matrix, you wind up with n tuples of n items each… effectively the columns of the matrix!
def has_all_num(n, elems):
if len(elems) != n: return False # check num of elems
for i in range(1, n+1):
if i not in elems: return False
return True
def same_row_column(matrix):
n = len(matrix)
for row in matrix: # check the row
if not has_all_num(n, row): return False
for col in zip(*matrix): # check columns
if not has_all_num(n, col): return False
return TrueView the entire Python script for this task on GitHub.
Perl
And knowing that trick, I did the same thing in Perl, using List::AllUtils’ zip_by.
use List::AllUtils qw( zip_by );
sub hasAllNum($n, @elems) {
my %set = map { $_ => 1 } @elems;
return false unless (keys %set == $n); # check num of elems
for my $i ( 1 .. $n ) {
return false unless exists $set{$i};
}
return true;
}
sub sameRowColumn(@matrix) {
my $n = @matrix;
for my $r (0 .. $n-1) {
# check the row
return 'False' unless hasAllNum($n, @{$matrix[$r]});
}
my @col = zip_by { [ @_ ] } @matrix; # flip rows and col
# check columns
for my $c (0 .. $n-1) {
return 'False' unless hasAllNum($n, @{$col[$c]});
}
return 'True';
}View the entire Perl script for this task on GitHub.
Raku, take 2!
I kept coming back to the Raku solution, thinking there must be some way to transpose the matrix with zip and get the columns. The zip function didn’t work…
$ raku
Welcome to Rakudo™ v2026.01.
Implementing the Raku® Programming Language v6.d.
Built on MoarVM version 2026.01.
To exit type 'exit' or '^D'
[0] > my @matrix = [
[1, 2, 3],
[3, 1, 2],
[3, 2, 1]
];
[[1 2 3] [3 1 2] [3 2 1]]
[1] > zip @matrix;
(([1 2 3] [3 1 2] [3 2 1]))
But using the Zip metaoperator as a reduction metaoperator like [Z] does work!
[2] > [Z] @matrix;
((1 3 3) (2 1 2) (3 2 1))Hence, the improved Raku solution:
sub hasAllNum($n, @elems) {
my $set = set @elems;
return False unless $set.elems == $n; # check num of elems
for 1 .. $n -> $i {
return False unless $i ∈ $set;
}
return True;
}
sub sameRowColumn(@matrix) {
my $n = @matrix.elems;
for @matrix -> @row {
# check the row
return False unless hasAllNum($n, @row);
}
# check columns
for [Z] @matrix -> @col {
return False unless hasAllNum($n, @col);
}
return True;
}
View the entire Raku script for this task on GitHub.
Elixir
And we can do the same thing in Elixir, except that Elixir tuples have to be explicitly converted to lists (Tuple.to_list/1 on line 20) before we can pass it to has_all_num/2, which is expecting a list as its second parameter. I had been assigning the result of the Enum.reduce/3 on lines 14-16 to a variable result, and then passing that as the accumulator to the next Enum.reduce/3, but then I remembered Kernel.then/2 and decided I’m much rather pipe the result into the next reduce.
def has_all_num(n, elems) when length(elems) != n, do: false
def has_all_num(n, elems), do:
Enum.reduce(1..n, true, fn i,result ->
result and Enum.any?(elems, &(&1 == i))
end)
def same_row_column(matrix) do
n = length(matrix)
# check rows
Enum.reduce(matrix, true, fn row, result ->
result and has_all_num(n, row)
end)
|> then(&(
# check columns
Enum.reduce(Enum.zip(matrix), &1, fn col, result ->
result and has_all_num(n, Tuple.to_list(col))
end)
))
endView the entire Elixir script for this task on GitHub.
Task 2: Smaller Greater Element
You are given an array of integers.
Write a script to find the number of elements that have both a strictly smaller and greater element in the given array.
Input: @int = (2,4)
Output: 0
Not enough elements in the array.
Input: @int = (1, 1, 1, 1)
Output: 0
Input: @int = (1, 1, 4, 8, 12, 12)
Output: 2
The elements are 4 and 8.
Input: @int = (3, 6, 6, 9)
Output: 2
Both instances of 6.
Input: @int = (0, -5, 10, -2, 4)
Output: 3
The elements are 0, -2, and 4.
Approach
I looked at this, and immediately thought of my favorite data structure, the Bag.
Put the integers into a bag. If there are fewer than three keys, we don’t have enough unique elements to meet the condition. Then… ooh, it just occurred to me that we’re only asked to determine the number of elements with both a strictly smaller and greater element. If we needed to determine what those elements were, we could discard the lowest and highest from the bag’s keys, and the remaining keys would be the elements that have both a strictly smaller and greater element. But since we only need the number, we just take the count of keys, subtract 2, and return that value.
And I did that, and got the wrong answer for example 4, because we need to count each of the 6s. So instead of taking the count of the keys, we discard the lowest and highest from the bag’s keys, and then take a sum of the values (the count of times those keys appear in the array).
Raku
Because I want the Bag to be mutable so I can drop the lowest and highest keys, I need to use a BagHash.
And one more thing I discovered: when I asked for the maximum value from the list of keys, I didn’t get the result I expected, because the keys were converted to Strings.
$ raku
Welcome to Rakudo™ v2026.01.
Implementing the Raku® Programming Language v6.d.
Built on MoarVM version 2026.01.
To exit type 'exit' or '^D'
[0] > ("8", "1", "4", "12").Seq.max
8
[1] > ("12", "1", "8", "4").Seq.max(:by({ $_.Int }))
12
So I had to figure out how to pass a function to the min and max functions. It turns out, I needed to pass a function using :by({ }) and I could use $_ in that function to cast the keys as Ints.
sub smallerGreater(@int) {
my %bag = @int.BagHash;
return 0 if %bag.elems < 3; # not enough unique elements
%bag{ %bag.keys.min(:by({ $_.Int })) }:delete; # the smallest
%bag{ %bag.keys.max(:by({ $_.Int })) }:delete; # the greatest
%bag.values.sum;
}View the entire Raku script for this task on GitHub.
$ raku/ch-2.raku
Example 1:
Input: @int = (2, 4)
Output: 0
Example 2:
Input: @int = (1, 1, 1, 1)
Output: 0
Example 3:
Input: @int = (1, 1, 4, 8, 12, 12)
Output: 2
Example 4:
Input: @int = (3, 6, 6, 9)
Output: 2
Example 5:
Input: @int = (0, -5, 10, -2, 4)
Output: 3Perl
Fortunately, having worked all the wrinkles in my approach out in Raku, the Perl implementation went smoothly. Once again, I’m using List::MoreUtil’s frequency to make the bag (hash), and List::AllUtils’ minmax_by to get the minimum and maximum values from the keys, passing them through the Perl idiom of adding 0 to a value to cast it as a numeric. Then I sum those values and return that.
use List::AllUtils qw( minmax_by sum );
use List::MoreUtils qw( frequency );
sub smallerGreater(@int) {
my %bag = frequency @int;
return 0 if keys %bag < 3; # not enough unique elements
my ($min, $max) = minmax_by { $_ + 0 } keys %bag;
delete %bag{ $min }; # the smallest
delete %bag{ $max }; # the greatest
sum values %bag;
}View the entire Perl script for this task on GitHub.
Python
Python made this easy because it never re-cast the integers as strings, so I didn’t need to do anything special for the min/max functions, and the collections module’s Counter type has a .total() that returns the sum of all the values.
from collections import Counter
def smaller_greater(ints):
bag = Counter(ints)
keys = bag.keys()
if len(keys) < 3: return 0 # not enough unique elements
(min_k, max_k) = (min(keys), max(keys))
del bag[ min_k ] # the smallest
del bag[ max_k ] # the greatest
return bag.total()View the entire Python script for this task on GitHub.
Elixir
The fun think in the Elixir solution is that I got to use Map.drop/2 to drop the minimum and maximum keys from the bag map in a single statement. I broke out the logic into three functions smaller_greater, one of arity 1 to be called with just the list of integers, and two private functions of arity 2 to be called with the bag and the list of keys. Why pass the list of keys when I can just call Map.keys/1 to get the keys? Because passing the keys as a list allows me to use Kernel.length/1 as a guard to handle the case where we don’t have enough unique elements to have any elements matching the criteria.
# not enough unique elements
defp smaller_greater(_, keys) when length(keys) < 3, do: 0
defp smaller_greater(bag, keys) do
{{min, max} = {Enum.min(keys), Enum.max(keys)}}
# drop the smallest and the greatest
Map.drop(bag, [min, max])
|> Map.values |> Enum.sum
end
def smaller_greater(int) do
bag = Enum.frequencies(int)
smaller_greater(bag, Map.keys(bag))
endView the entire Elixir script for this task on GitHub.
Here’s all my solutions in GitHub: https://github.com/packy/perlweeklychallenge-club/tree/challenge-381-packy-anderson/challenge-381/packy-anderson