Going down chimneys is a dangerous business.
Chimneys can be narrow, high, and sometimes not well constructed to begin with.
This year, Santa wants to be prepared. Therefore, he is combining a chimney inspection with the delivery of presents.
A chimney inspection involves ensuring that every layer of bricks is at the correct height; i.e. that the layers of mortar are consistent, and that the bricks are also a consistent height.
For instance, for bricks that are 2ΒΌ” high, and mortar that is β ” thick, the sequence of measurements should look like this:
π ββββ || layer total ββββββββββ βββββββββββββββ ββββββββββ 2ΒΌ ββββββββββ βββββββββββββββ ββββββββββ ββββββββββ βββββββββββββββ ββββββββββ β βΎβΎ??? βββββ βββββββββββββ ββββββββββββ ββββ 2ΒΌ βββββ βββββββββββββ ββββββββββββ ββββ βββββ βββββββββββββ ββββββββββββ ββββ β βΎβΎ5β ββββββββββ βββββββββββββ ββββββββββββ 2ΒΌ ββββββββββ βββββββββββββ ββββββββββββ ββββββββββ βββββββββββββ ββββββββββββ β βΎβΎ3 ββββββ βββββββββββββ ββββββββββββ βββ 2ΒΌ ββββββ βββββββββββββ ββββββββββββ βββ ββββββ βββββββββββββ ββββββββββββ βββ β _____________________________________βΎβΎβ
The plan is for the Elves to do the dangerous descent to the bottom, tape measure in hand, and then come back up, ensuring that the top of each brick layer is at precisely the correct place on the tape measure.
One particular Elf, named Elvis, has taken it upon himself to write a program to help out with the task of computing this sequence of heights.
Being lazy, Elvis did not even want to add any of the fractions above, and wanted the program to do all the work. He also did not want to exert the mental effort required to figure out the formula for the height of each layer. Luckily, he was using Perl 6, which properly turns unicode fractions into rational numbers (type Rat
), and also has a sequence operator (...
) which figures out arithmetic sequences based on the first few terms.
So, Elvis’ first cut at the program looked like this:
my @heights = 0, β , 3, 5+β ... *; say @heights[^10].join(', ')
This gave him the first 10 heights that he needed:
0, 0.375, 3, 5.625, 8.25, 10.875, 13.5, 16.125, 18.75, 21.375
While this was correct, it was hard to use. The tape measure had fractions of an inch, not decimals. The output Elvis really wanted was fractions.
Fortunately, he knew that using join
turned the Rat
s into strings, Turning a Rat
into a Str
is done by calling the Str
method of the Rat
class. So, by modifying the behavior of Rat.Str
, he figured he could make the output prettier.
The way he decided to do this was to wrap
the Str
method (aka using the decorator pattern), like so:
Rat.^find_method('Str').wrap:
Β sub ($r) {
my $whole = $r.Int || "";
Β Β my $frac = $r - $whole;
Β Β return "$whole" unless $frac > 0;
Β Β return "$whole" ~ <β
ΒΌ β
Β½ β
ΒΎ β
>[$frac * 8 - 1];
Β }
In other words, when stringifying a Rat, return the whole portion unless there is a fractional portion. Then treat the fractional portion as the number of eighths, and use that as an index into an array to look up the right unicode fraction.
He combined that with his first program to get this sequence of heights:
0, β , 3, 5β , 8ΒΌ, 10β , 13Β½, 16β , 18ΒΎ, 21β
“Hooray!” he thought. “Exactly what I need.”
Santa took a look at the program and said “Elvis, this is clever, but not quite enough. While most brick dimensions are multiples of β , that might not be true of mortar levels. Can you make your program handle those cases, too?”
“Sure” said Elvis with a wry smile. And he added this line into his wrapper function:
return "$whole {$frac.numerator}β{$frac.denominator}"
unless $frac %% β
;
using the “is divisible by” operator (%%
), to ensure that the fraction was evenly divisible into eighths, and if not to just print the numerator and denominator explicitly. Then for mortar that was β
” thick, the sequence:
my @heights = 0, β , β + 2+ΒΌ + β , β + 2+ΒΌ + β + 2+ΒΌ + β ... *; say @heights[^10].join(', ');
0, 1β5, 2 13β20, 5 1β10, 7 11β20, 10, 12 9β20, 14 9β10, 17 7β20, 19 4β5
“Actually”, Santa said, “now that I look at it, maybe this isn’t useful — the tape measure only has sixteenths of an inch, so it would be better to round to the nearest sixteenth of an inch.”
Elvis added a call to round
to end up with:
Rat.^find_method('Str').wrap:
Β sub ($r) {
my $whole = $r.Int || '';
my $frac = $r - $whole;
return "$whole" unless $frac > 0;
my $rounded = ($frac * 16).round/16;
return "$whole" ~ <β
ΒΌ β
Β½ β
ΒΎ β
>[$frac * 8 - 1] if $rounded %% β
;
return "$whole {$rounded.numerator}β{$rounded.denominator}";
Β }
which gave him
0, 3β16, 2β , 5β , 7 9β16, 10, 12 7β16, 14β , 17ΒΌ, 19 13β16
He showed his program to Elivra the Elf who said, “What a coincidence, I wrote a program that is almost exactly the same! Except, I also wanted to know where the bottoms of the layers of bricks are. I couldn’t use a sequence operator for this, since it isn’t an arithmetic progression, but I could use a lazy gather and an anonymous stateful variable! Like this:
my \brick = 2 + ΒΌ;
my \mortar = β
;
my @heights = lazy gather {
take 0;
loop { take $ += $_ for mortar, brick }
}
Elvira’s program produced:
0, β , 2β , 3, 5ΒΌ, 5β , 7β , 8ΒΌ, 10Β½, 10β
i.e. both the tops and the bottoms of the layers of bricks:
\ π / ββ || layer total ββββββββββ βββββββββββββββ ββββββββββ 2ΒΌ ββββββββββ βββββββββββββββ ββββββββββ ββββββββββ βββββββββββββββ ββββββββββ β βΎβΎ8ΒΌ βββββ βββββββββββββ ββββββββββββ βββββΎβΎ7β 2ΒΌ βββββ βββββββββββββ ββββββββββββ ββββ βββββ βββββββββββββ ββββββββββββ ββββ β βΎβΎ5β ββββββββββ βββββββββββββ βββββββββββββΎβΎ5ΒΌ 2ΒΌ ββββββββββ βββββββββββββ ββββββββββββ ββββββββββ βββββββββββββ ββββββββββββ β βΎβΎ3 ββββββ βββββββββββββ ββββββββββββ ββββΎβΎ2β 2ΒΌ ββββββ βββββββββββββ ββββββββββββ βββ ββββββ βββββββββββββ ββββββββββββ βββ β _____________________________________βΎβΎβ βΎβΎ0
With their programs in hand, the Elves checked out the chimneys and Santa made it through another holiday season without any injuries.