Perl Weekly Challenge 100: Fun Time and Triangle Sum
These are some answers to the Week 100 of the Perl Weekly Challenge organized by Mohammad S. Anwar.
Task 1: Fun Time
You are given a time (12 hour / 24 hour).
Write a script to convert the given time from 12 hour format to 24 hour format and vice versa.
Ideally we expect a oneliner.
Example 1:
Input: 05:15 pm or 05:15pm
Output: 17:15
Example 2:
Input: 19:15
Output: 07:15 pm or 07:15pm
Well, I like concise code, but I don’t think it makes real sense to try to pack this task into a oneliner. I have no doubt that it can be done, but it will either be not a real oneliner (like a pipeline chaining multiple statements into one expression), or a difficult to understand golfing attempt. So, I will not try to do a oneliner.
Fun Time in Raku
We use a regex to detect whether the input string matches “am” or “pm”; if so, we remove that “am” or “pm” string and add 12 hours if the matched string was “pm”; otherwise, we subtract 12 from the hour part it if is larger than 12 and add “pm” or “am” depending on the case.
use v6;
my $time = @*ARGS[0];
if $time ~~ /(<[ap]>)m/ {
if $0 eq 'a' {
$time ~~ s/(\d\d':'\d\d).*/$0/;
} else {
$time ~~ s/(\d\d)':'(\d\d).*/{$0 + 12}:$1/;
}
} else {
$time ~~ /^(\d\d)':'(\d\d)/;
my ($suffix, $hour) = $0 > 12 ?? ('pm', $0  12) !! ('am', $0);
$time = "$hour:$1 $suffix";
}
say $time;
These are some execution examples with various input strings:
$ raku funtime.raku '10:54'
10:54 am

$ raku funtime.raku '17:54'
5:54 pm

$ raku funtime.raku '10:54 pm'
22:54

$ raku funtime.raku '10:54 am'
10:54
Fun Time in Perl
This is a port to Perl of the Raku program just above:
use strict;
use warnings;
use feature "say";
my $time = shift;
if ($time =~ /([ap])m/) {
if ($1 eq 'a') {
$time =~ s/(\d\d:\d\d).*/$1/;
} else {
$time =~ /(\d\d):(\d\d.).*/;
my $hour = $1 + 12;
$time = "$hour:$2";
}
} else {
$time =~ /^(\d\d):(\d\d)/;
my ($suffix, $hour) = $1 > 12 ? ('pm', $1  12) : ('am', $1);
$time = "$hour:$2 $suffix";
}
say $time;
And some execution examples:
$ perl funtime.pl '10:54 pm'
22:54
$ perl funtime.pl '10:54 am'
10:54
$ perl funtime.pl '10:54'
10:54 am
$ perl funtime.pl '15:54'
3:54 pm
Task 2: Triangle Sum
You are given triangle array.
Write a script to find the minimum path sum from top to bottom.
When you are on index i on the current row then you may move to either index i or index i + 1 on the next row.
Example 1:
Input: Triangle = [ [1], [2,4], [6,4,9], [5,1,7,2] ]
Output: 8
Explanation: The given triangle
1
2 4
6 4 9
5 1 7 2
The minimum path sum from top to bottom: 1 + 2 + 4 + 1 = 8
[1]
[2] 4
6 [4] 9
5 [1] 7 2
Example 2:
Input: Triangle = [ [3], [3,1], [5,2,3], [4,3,1,3] ]
Output: 7
Explanation: The given triangle
3
3 1
5 2 3
4 3 1 3
The minimum path sum from top to bottom: 3 + 1 + 2 + 1 = 7
[3]
3 [1]
5 [2] 3
4 3 [1] 3
Triangle Sum in Raku
We use the traverse
recursive subroutine to find the smallest path through the triangular array:
use v6;
my @triangle = (1), (2,4), (6,4,9), (5,1,7,2);
my @minpath = map { $_[0] }, @triangle; # pick any path
my $minpathval = @minpath.sum;
my $index = 0;
traverse @triangle, (), $index;
say @minpath;
sub traverse (@triangle, @path, $index) {
my @firstline = @triangle[0];
my @newtriangle = @triangle[1 .. @triangle.end];
say "Firstline: ", @firstline;
my @newpath =  (@path, @firstline[$index]);
say @newpath, " ", "\n";
if @newtriangle.elems > 0 {
traverse(@newtriangle, @newpath, $index);
traverse(@newtriangle, @newpath, $index + 1);
} else {
my $newpathval = @newpath.sum;
if $newpathval < $minpathval {
@minpath = @newpath;
$minpathval = $newpathval
}
}
}
Output:
$ raku trianglesum.raku 1 2 4 1
Triangle Sum in Perl
Except for the fact that we define also a sum
subroutine, this essentially the same Raku algorithm ported to Perl:
use strict;
use warnings;
use feature "say";
my $triangle = [ [1], [2,4], [6,4,9], [5,1,7,2] ];
# $triangle = [ [1], [2,4], [6,30,9], [30,30,30,2] ];
my $min_path = [ map { $_>[0] } @$triangle];
my $min_path_val = sum($min_path);
my $index = 0;
find_min_path($triangle, [], $index);
say "@$min_path";
sub sum {
my $aref = shift;
my $sum = 0;
$sum += $_ for @$aref;
return $sum;
}
sub find_min_path {
my ($tri_ref, $path, $index) = @_;
my @triangle = @$tri_ref;
my @first_row = @{$triangle[0]};
my @new_triangle = @triangle[1 .. $#triangle];
my $new_path = [ @$path, $first_row[$index] ];
if (@new_triangle) {
find_min_path([@new_triangle], $new_path , $index);
find_min_path([@new_triangle], $new_path, $index + 1);
} else {
my $new_path_val = sum($new_path);
if ($new_path_val < $min_path_val) {
$min_path = $new_path;
$min_path_val = $new_path_val;
}
}
}
Output with the above input data:
$ perl trianglesum.pl
1 2 4 1
If you uncomment the second triangle definition, you get the following output:
$ perl trianglesum.pl
1 4 9 2
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 Sunday, February 28, 2021. And, please, also spread the word about the Perl Weekly Challenge if you can.