This is my response to the Perl Weekly Challenge #120.
$N
less than or equal to 255.
Input: $N = 101
Output: 154
Binary representation of the given number is 01 10 01 01.
The new binary representation after the odd/even swap is 10 01 10 10.
The decimal equivalent of 10011010 is 154.
Input: $N = 18
Output: 33
Binary representation of the given number is 00 01 00 10.
The new binary representation after the odd/even swap is 00 10 00 01.
The decimal equivalent of 100001 is 33.
This resembles lask week's 1st challenge: Swap Nibbles, so the code is mostly identical (except the line marked [3]).
We are swapping the individual bits of neighbouring pairs. As an illustration, with the general idea to the left and the first example given in the challenge to the right:
File: swap-bits
#! /usr/bin/env raku
unit sub MAIN (Int $N where $N > 0 && $N <= 255, :v(:$verbose)); # [1]
my $binary = $N.fmt('%08b'); # [2]
my $swapped = $binary.comb(2)>>.flip.join; # [3]
################### # 3a ## # 3b # # 3c
if $verbose
{
say ": Binary: $binary";
say ": Swapped: $swapped (binary)";
}
say $swapped.parse-base(2); # [4]
[1] Ensure an integer in the range 1..255.
[2] Convert it to binary, zero padded (i.e. «00110011» instead of «110011»).
[3] Swap two and two bits at a time. We start by splitting the number
(a string with length 8) into (4) strings of length 2; each containing the two bits
that we are tasked to switch (with comb(2)
) [3a]. Then we flip all the
strings, thus reversing the order of the two bits (with >>.flip
, which
does it on each and every element in the list) [3b], and finally glue them back into
a single string (with join
) [3c].
[4] Convert back to decimal, and print it.
See
docs.raku.org/routine/comb
for more information about comb
.
Running it:
$ ./swap-bits 101
154
$ ./swap-bits 18
33
$ ./swap-bits -v 101
: Binary: 01100101
: Swapped: 10011010 (binary)
154
$ ./swap-bits -v 18
: Binary: 00010010
: Swapped: 00100001 (binary)
33
#! /usr/bin/env perl
use strict;
use warnings;
use feature 'say';
use Getopt::Long;
my $verbose = 0;
GetOptions("verbose" => \$verbose);
my $N = $ARGV[0] // "";
die "Please specify an integer in the range 1..255"
if $N !~ /^[1-9]\d*$/ || $N > 255;
my $binary = sprintf('%08b', $N);
my $swapped = substr($binary, 1, 1) . # [1]
substr($binary, 0, 1) . # [1a]
substr($binary, 3, 1) .
substr($binary, 2, 1) .
substr($binary, 5, 1) .
substr($binary, 4, 1) .
substr($binary, 7, 1) .
substr($binary, 6, 1);
if ($verbose)
{
say ": Binary: $binary";
say ": Swapped: $swapped (binary)";
}
say oct("0b" . $swapped);
[1] Start with the second bit (index 1), then add the first one (index 0) [1a]. And so on.
Running it gives the same result as the Raku version:
$ ./swap-bits-perl 101
154
$ ./swap-bits-perl 18
33
$ ./swap-bits-perl -v 101
: Binary: 01100101
: Swapped: 10011010 (binary)
154
$./swap-bits-perl -v 18
: Binary: 00010010
: Swapped: 00100001 (binary)
33
$T
in the format hh:mm
.
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
An illustration may help::
The starting position on an analog clock (straight up) is shown as the hour «12». I have decided to allow «12:xx» (as well as «00:xx). So «12:59» is the same as «00:59». «13:xx» is not allowed.
Using a custom type (with subset
) is difficult when we have a strange upper limit (as 12),
so I have used a Grammar:
#! /usr/bin/env raku
unit sub MAIN (Str $T, :v(:$verbose)); # [1]
grammar HHMM # [2]
{
token TOP { <HH> \: <MM> } # [3]
token HH { <[01]> <[0..9]> <?{ $/.Int <= 12 }> } # [4]
token MM { <[0..5]> <[0..9]> } # [5]
}
my $m = HHMM.parse($T) // die "Illegal HH:MM value"; # [6]
my $hour = $m<HH>; # [7]
my $min = $m<MM>; # [7]
$hour -= 12 if $hour >= 12; # [8]
my $degrees_h = 360 / 12 * $hour + $min / 2; # [9]
my $degrees_m = 360 / 60 * $min; # [10]
if $verbose
{
say ": Hours: $hour - Degrees: $degrees_h";
say ": minutes: $min - Degrees: $degrees_m";
}
my $diff = abs($degrees_h - $degrees_m); # [11]
say "$diff degree"; # [12]
[1] Set it up as a string, which does not say much.
[2] The Grammar, with grammar
as the keyword.
[3] The «TOP» token is the entry point. The token
keyword is one of three we can use in Grammars (the others are regex
and rule
). The top rule is an hour value, followed by a literal colon,
and a minute value.
[4] The hour value. The Regex (by itself) would allow hours from «00» to «19», so
I have added a constraining code block (the <?{ $/.Int <= 12 }>
part). If this block returns False
, the Regex fails (as in does not
match). The block simply check that the hour value (the match object, in
$/
coerced to an integer) is lower or equal to 12.
[5] The minute part. We do not need custom code here.
[6] Parse the input value against the Grammar, and exit if it fails.
[7] Collect the hour and minute values, by name (as given in the Grammar, line [4] and [5]).
[8] If the hour is 12 (not higher, as the Grammar has prevented that), we set it to 0 - as the angle calculation assumes that we start at 0.
[9] Get the degree for the hour value. The full circle is 360 degrees, so we divide it by 12 to get the size of each hour. Then we multiply it with the hour value to get the angle. The minutes must be added to the hour hand (as it moves along), and we do this by dividing the minutes by 2. (One hour is 30 degrees, which is 60 minues - so 1 minute gives 0.5 degrees.)
[10] The degree for the minute value. There are 60 minutes in an hour, so «360/60» gives the degree for one minute.
[11] The difference in the angles for the two hands. Remove the sign
(if any) with abs
.
[12] Print the result.
See docs.raku.org/language/grammar_tutorial for an introduction to Grammars, and docs.raku.org/language/grammars for more details.
See
docs.raku.org/routine/abs
more information about abs
.
Running it:
$ ./clock-angle 03:10
35 degree
$ ./clock-angle 04:00
120 degree
$ ./clock-angle -v 03:10
: Hours: 03 - Degrees: 95
: minutes: 10 - Degrees: 60
35 degree
$ ./clock-angle -v 04:00
: Hours: 04 - Degrees: 120
: minutes: 00 - Degrees: 0
120 degree
Looking good.
#! /usr/bin/env perl
use strict;
use warnings;
use feature 'say';
use Getopt::Long;
my $verbose = 0;
GetOptions("verbose" => \$verbose);
my $T = $ARGV[0] // "";
die "Please specify a HH:MM value" unless $T =~ /^[01]\d\:[0-5]\d$/;
my ($hour, $min) = split(":", $T);
die "Hours 00-12 only" if $hour > 12; # [1]
$hour -= 12 if $hour >= 12;
my $degrees_h = 360 / 12 * $hour + $min / 2;
my $degrees_m = 360 / 60 * $min;
if ($verbose)
{
say ": Degree H: $degrees_h";
say ": Degree M: $degrees_m";
}
my $diff = abs($degrees_h - $degrees_m);
say "$diff degree";
[1] Get rid of hour values larger than 12.
Running it gives the same result as the Raku version:
$ ./clock-angle-perl -v 03:10
: Degree H: 95
: Degree M: 60
35 degree
$ ./clock-angle-perl -v 04:00
: Degree H: 120
: Degree M: 0
120 degree
And that's it.