I’ve been doing the Perl Weekly
Challenges. The
latest
involved converting time formats and more tree traversal.
(Note that this is open until 21 February 2021.)
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 one-liner.
Roger don't do one-liners. Roger been maintenance programmer had to
fix bugs in somebody else's one-liners. Some one-liners got more bugs
than actual characters.
(I did once write a line of Perl with a complicated double-map
in
it, to transform a nested data structure into the PostScript fragment
the customer wanted. They swore blind that there would never be any
need to change this other than by changing the list of fields. So in a
comment under that double-map
I wrote out the full version of the
function, just in case.
I told that story at a London Perlmongers meet a few months later and
found beer in front of me, from the guy who'd been hired to change
it…)
Anyway, a full implementation of this should also take into account
that the 12-hour clock runs (am) 12, 1..11, then (pm) 12, 1..11, while
the 24-hour runs 0..23. I added several test cases to the few that
were given.
Of course if we had a time and date then we could just use
timelocal
/localtime
/strftime
, and equivalents in other
languages. But I'm a time purist.
Anyway, regex parsing into three components: hour, minute, optional
am/pm flag.
sub ft {
my $in=shift;
$in =~ /(\d+):(\d+)\s*([ap]m)?/;
my $h=$1;
my $t='';
If there is an am/pm flag, it's a 12-hour time; make a hour value of
12 into 0, then add 12 to whatever hour we have if the time is after
noon.
if ($3) { # 12 to 24
if ($h==12) {
$h=0;
}
if ($3 eq 'pm') {
$h+=12;
}
Otherwise, it's a 24-hour time so reverse the process: default am, set
pm (and subtract 12) if the hour value is after noon, then set an hour
value of 0 to 12.
} else { # 24 to 12
$t=' am';
if ($h > 11) {
$t=' pm';
$h-=12;
}
if ($h == 0) {
$h=12;
}
}
Finally, throw the whole thing back together (with the minute match
from the original regex).
return sprintf('%02d:%02d%s',$h,$2,$t);
}
The same basic algorithm applies to the other languages. Raku's
regexes are gratuitously incompatible with everyone else's:
$in ~~ /(\d+)\:(\d+)\s*(<[ap]>m)?/;
while Python, Ruby and Rust all default to letting me catch the
matches in a specific variable rather than globals. (I think Raku can
do this too with Match
objects, but the Perl approach mostly worked
there so I used it.)
Python and Rust have their own special formatting languages rather
than good old printf
that any Unix programmer already knows. Sigh.
return "{:02d}:{:02d}{:s}".format(h,int(m.group(2)),t)
return format!("{:02}:{:02}{}",h,caps.get(2).unwrap().as_str(),t);
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.
Well, if you're going to make it trivial with a hint like that…
My standard pattern for this is a FIFO buffer which makes a
breadth-first search, but there's no particular virtue in that for
this problem since I won't be terminating early (any valid solution
must traverse the entire depth of the structure); so here I decided to
use LIFO for a depth-first search, on the basis that the code then
appends and removes only at the end of the list rather than at the
start too, which should be a little faster if the problem is ever
large enough for execution speed to be significant.
Here's the Raku version (taking advantage of the new sigil policy to
throw lists and lists of lists around as though they were scalar
variables). The others look pretty similar.
sub ts($in) {
my @b;
my $n=0;
my $i=0;
my $s=$in[0][0];
my @r;
while (1) {
if (@b.elems > 0) {
my $t=@b.pop;
($n,$i,$s)=$t;
}
if ($n < $in.elems-1) {
$n++;
for ($i,$i+1) -> $ix {
push @b,[$n,$ix,$s+$in[$n][$ix]];
}
} else {
push @r,$s;
}
unless (@b) {
last;
}
}
return min(@r);
}
In Rust I built a custom data structure rather than using just another
Vec
. It felt like the Rust-y thing to do.
pub struct Node {
n: usize,
i: usize,
s: i32
}
and later
b.push(Node {n: n,
i: ix,
s: s+inp[n][ix]
});
Full code on
github.
Comments on this post are now closed. If you have particular grounds for adding a late comment, comment on a more recent post quoting the URL of this one.