Perl Weekly Challenge 182: Max Index and Common Path
These are some answers to the Week 182 of the Perl Weekly Challenge organized by Mohammad S. Anwar.
Spoiler Alert: This weekly challenge deadline is due in a few of days from now (on Sept. 18, 2022 at 23:59). This blog post offers some solutions to this challenge, please don’t read on if you intend to complete the challenge on your own.
Task 1: Max Index
You are given a list of integers.
Write a script to find the index of the first biggest number in the list.
Example:
Input: @n = (5, 2, 9, 1, 7, 6)
Output: 2 (as 3rd element in the list is the biggest number)
Input: @n = (4, 2, 3, 1, 5, 0)
Output: 4 (as 5th element in the list is the biggest number)
Max Index in Raku
The initial idea here was to to store the input data into a hash and to use the max routine to find the maximum value. The max
documentation says:
Coerces the invocant to Iterable and returns the numerically largest element; in the case of Hashes, the Pair with the highest value.
But that did not work properly in my tests: max
consistently appeared to return the pair with the highest key, not the highest value. I tried to use the maxpairs routine, which, according to the documentation,
returns a Seq with all of the Pairs with maximum value.
The maxpairs
method works as expected. This leads to a very short program:
for (5, 2, 9, 1, 7, 6), (4, 2, 3, 1, 5, 0) -> @test {
my %nums = @test.kv;
say "@test[] : ", %nums.maxpairs;
}
This program displays the following output:
$ raku ./max-index.raku
5 2 9 1 7 6 : (2 => 9)
4 2 3 1 5 0 : (4 => 5)
Max Index in Perl
In Perl, we use a standard for
loop to traverse the input list and find the index of the largest value:
use strict;
use warnings;
use feature qw/say/;
for my $test ([5, 2, 9, 1, 7, 6], [4, 2, 3, 1, 5, 0]) {
my @nums = @{$test};
my ($max_i, $max_n) = (0, 0);
for my $i (0..$#nums) {
if ($nums[$i] > $max_n) {
$max_n = $nums[$i];
$max_i = $i;
}
}
say "@nums : $max_i => $max_n";
}
This program displays the following output:
$ perl ./max-index.pl
5 2 9 1 7 6 : 2 => 9
4 2 3 1 5 0 : 4 => 5
Max Index in 7 Other Languages
We have now implementations of the max index task in 7 languages as follows:
- Awk
- JavaScript
- Julia
- Python
- Ring
- Ruby
- Scala
Max Index in Awk
In awk, the data is normally passed as standard input. So we use a Unix pipe to pass the data to the program.
# run for example as:
# echo '5 2 9 1 7 6
# 4 2 3 1 5 0' | awk -f ./max-index.awk
function find_max() {
max_i = 0
max_n = $1
for (i = 2; i < NF; i++) {
if ($i > max_n) {
max_i = i
max_n = $i
}
}
printf("Max index for %s: %d => %d\n", $0, max_i - 1, max_n)
}
{
find_max()
}
Output:
$ echo '5 2 9 1 7 6
4 2 3 1 5 0' | awk -f ./max-index.awk
Max index for 5 2 9 1 7 6: 2 => 9
Max index for 4 2 3 1 5 0: 4 => 5
Max Index in JavaScript
let tests = [ [5, 2, 9, 1, 7, 6], [4, 2, 3, 1, 5, 0] ]
for (let j = 0; j < tests.length; j++) {
let test = tests[j]
let max_i = 0
let max_n = test[0]
for (let i = 1; i <= test.length; i++) {
if (test[i] > max_n) {
max_n = test[i]
max_i = i
}
}
console.log("Max index for " + test + ": " + max_i + " => " + max_n)
}
Output:
Max index for 5,2,9,1,7,6: 2 => 9
Max index for 4,2,3,1,5,0: 4 => 5
Max Index in Julia
Note that Julia indexes start at 1. So we need to subtract 1 from the max_i
variable to get a result consistent with other (0-based index) languages.
for test in [[5, 2, 9, 1, 7, 6], [4, 2, 3, 1, 5, 0]]
max_i = 1
max_n = test[1]
for i in 2:length(test)
if (test[i] > max_n)
max_n = test[i]
max_i = i
end
end
println("Max index for $test: $(max_i - 1) => $max_n)")
end
Output:
$ julia ./max-index.jl
Max index for [5, 2, 9, 1, 7, 6]: 2 => 9)
Max index for [4, 2, 3, 1, 5, 0]: 4 => 5)
Max Index in Python
for test in [5, 2, 9, 1, 7, 6], [4, 2, 3, 1, 5, 0]:
max_i = 0
max_n = test[0]
for i in range(1, len(test)):
if test[i] > max_n:
max_n = test[i]
max_i = i
print("Max index for ", test, ": ", max_i, " => ", max_n)
Output:
$ python3 ./max-index.py
Max index for [5, 2, 9, 1, 7, 6] : 2 => 9
Max index for [4, 2, 3, 1, 5, 0] : 4 => 5
Max Index in Ring
Note that Ring indexes start at 1 (like Julia indexes). So we need to subtract 1 from the max_i variable to get a result consistent with other (0-based index) languages.
for test in [[5, 2, 9, 1, 7, 6], [4, 2, 3, 1, 5, 0]]
max_i = 1
max_n = test[1]
str = ""
for i = 1 to len(test)
str = str + test[i] + " "
if test[i] > max_n
max_n = test[i]
max_i = i
ok
next
see "Max index for " + str + ": " +
(max_i - 1) + " => " + max_n + nl
next
Output:
$ ring ./max-index.ring
Max index for 5 2 9 1 7 6 : 2 => 9
Max index for 4 2 3 1 5 0 : 4 => 5
Max Index in Ruby
for test in [[5, 2, 9, 1, 7, 6], [4, 2, 3, 1, 5, 0]]
max_i = 0
max_n = test[0]
for i in 1..(test.length - 1)
if test[i] > max_n
max_n = test[i]
max_i = i
end
end
printf("Max index for %s: %d => %d\n", test.to_s, max_i, max_n)
end
Output:
Max index for [5, 2, 9, 1, 7, 6]: 2 => 9
Max index for [4, 2, 3, 1, 5, 0]: 4 => 5
Max Index in Scala
object fraction_tree extends App {
val tests: List[List[Int]] =
List(List(5, 2, 9, 1, 7, 6), List(4, 2, 3, 1, 5, 0))
for (test <- tests) {
var max_i = 0
var max_n = test(max_i)
for (i <- 1 to test.length - 1) {
if (test(i) > max_n) {
max_n = test(i)
max_i = i
}
}
println("Max index for " + test.mkString(" ") + s": $max_i => $max_n")
}
}
Output:
Max index for 5 2 9 1 7 6: 2 => 9
Max index for 4 2 3 1 5 0: 4 => 5
Task 2: Common Path
Given a list of absolute Linux file paths, determine the deepest path to the directory that contains all of them.
Example:
Input:
/a/b/c/1/x.pl
/a/b/c/d/e/2/x.pl
/a/b/c/d/3/x.pl
/a/b/c/4/x.pl
/a/b/c/d/5/x.pl
Ouput:
/a/b/c
Common Path in Raku
This program converts the input into an array of arrays and then compares each item of the first line with the corresponding item of the other lines. The program stops as soon as a difference is found.
my @input = qw <
/a/b/c/1/x.pl
/a/b/c/d/e/2/x.pl
/a/b/c/d/3/x.pl
/a/b/c/4/x.pl
/a/b/c/d/5/x.pl
>;
my @paths = gather {
for @input <-> $line {
$line ~~ s/^'/'//;
my @subpaths = split /'/'/, $line;
take @subpaths[0..*-2];
}
}
my $end = @paths.end;
my $k = 0;
OUTLOOP: for 0..(@paths[0].end) -> $i {
for 0..$end -> $j {
if @paths[$j][$i]:!exists or @paths[$j][$i] ne @paths[0][$i] {
$k = $i - 1;
last OUTLOOP;
}
}
}
say '/', join '/', @paths[0][0..$k];
This program displays the following output:
$ raku ./common-path.raku
/a/b/c
Common Path in Perl
use strict;
use warnings;
use feature qw/say/;
my @input = qw <
/a/b/c/1/x.pl
/a/b/c/d/e/2/x.pl
/a/b/c/d/3/x.pl
/a/b/c/4/x.pl
/a/b/c/d/5/x.pl
>;
my @paths;
for my $line (@input) {
$line =~ s|^/||;
my @subpaths = split /\//, $line;
push @paths, [@subpaths[0..$#subpaths-1]];
}
my @first = @{$paths[0]};
my $end = $#paths;
my $k = 0;
OUTLOOP: for my $i (0..$#first) {
for my $j (0..$end) {
if ((not exists $paths[$j][$i]) or $paths[$j][$i] ne $first[$i]) {
$k = $i - 1;
last OUTLOOP;
}
}
}
say '/', join '/', @first[0..$k];
This program displays the following output:
$ perl ./common-path.pl
/a/b/c
Wrapping up
The next week Perl Weekly Challenge will start soon. If you want to participate in this challenge, please check https://perlweeklychallenge.org/ and make sure you answer the challenge before 23:59 BST (British summer time) on September 25, 2022. And, please, also spread the word about the Perl Weekly Challenge if you can.
Leave a comment