ETOOBUSY 🚀 minimal blogging for the impatient
PWC120 - Clock Angle
TL;DR
On with TASK #2 from the Perl Weekly Challenge #120. Enjoy!
The challenge
You are given time
$T
in the formathh:mm
.Write a script to find the smaller angle formed by the hands of an analog clock at a given time.
HINT: A analog clock is divided up into 12 sectors. One sector represents 30 degree (360/12 = 30).
Example
Input: $T = '03:10' Output: 35 degree The distance between the 2 and the 3 on the clock is 30 degree. For the 10 minutes i.e. 1/6 of an hour that have passed. The hour hand has also moved 1/6 of the distance between the 3 and the 4, which adds 5 degree (1/6 of 30). The total measure of the angle is 35 degree. Input: $T = '04:00' Output: 120 degree
The questions
The input format is a bit foggy as to what are the possible values. As
there is no indication of AM
or PM
, it can be that:
- this bit of information has been left out because it has little to do
with the required angle. In this case,
hh
would range from01
to12
; - a 24-hours format is adopted, i.e.
hh
would range from00
to23
; - there is some other possibility that I can’t think of now.
We will also assume a regular analog clock where there are 12 sectors. This seems to be supported by the HINT in the challenge text.
We will assume that the requested angle should be the smaller non-negative one - otherwise we would have fun.
Last, we will assume that the movement of the hands is continuous, with particular reference to the hours hand. This seems consistent with the first example.
The solution
First of all, we can get rid of the ambiguity in the hh
part by taking
the remainder to the division by 12. We will always get an integer
between 0 and 11 included, and this is what we need.
Assuming that we count an absolute angle from the vertical and going in the clockwise direction, we can calculate the angles at which the two hands are at:
- for the minutes, it’s just the number of minutes times 6, because there are 60 minutes to be mapped onto 360 degrees;
- for the hours, we have to take into account the value we get (filtered as explained above) multiplying it by 30 (according to the HINT), plus the additional rotation due to the minutes, which is equal to $\frac{m}{60} \frac{360}{12} = \frac{m}{2}$ degrees.
Last, we can take the absolute value of their difference and compare with 180 degrees: if greater, we take the complement of that angle to 360 degrees, so that we find the smallest non-negative angle.
On to the implementation, then!
Raku goes first, in an effort to learn more:
#!/usr/bin/env raku
use v6;
sub clock-angle ($t) {
my ($hrs, $mins) = $t.split(/\:/);
my $angle = ($mins * 6) - (($hrs % 12) * 30 + $mins / 2);
$angle = -$angle if $angle < 0;
return $angle <= 180 ?? $angle !! 360 - $angle;
}
put "{clock-angle($_)} degree" for @*ARGS ?? @*ARGS !! qw< 03:10 04:00 >;
Yes, yes… I know… there’s an abs routine that’s perfect for taking the absolute value! Silly me 😊
The printout allows showing off the use of {...}
inside double quotes,
which allows calling code and expanding the result in the string.
Something similar in Perl would be one of the secret operators
(not real operators, but compositions of basic ones!) like the baby
cart operator @{[...]}
or so.
Perl now:
#!/usr/bin/env perl
use v5.24;
sub clock_angle {
my ($hrs, $mins) = split m{:}mxs, $_[0];
my $angle = ($mins * 6) - (($hrs % 12) * 30 + $mins / 2);
$angle = -$angle if $angle < 0;
return $angle <= 180 ? $angle : 360 - $angle;
}
say clock_angle($_) . ' degree' for @ARGV ? @ARGV : qw< 03:10 04:00 >;
This time I opted with an implementation that provides a clear view of
the extreme similarity to the Raku counterpart. I’m not including
warnings
(which I usually do) and signatures, but the input is only
used once so it’s OKish 🙄 And yes, I’m forgetting that Perl too
has its own abs function 😊
The two lines with formulas are copied verbatim from the Raku
solution. The return line only changes due to the new ternary operator
(?? !!
vs Perl’s ? :
).
Stay safe everyone, and have fun!