They returned an empty package
I don’t like to solve maths-puzzles. I do like to read other folks solutions thought. You never know where to spot a new idiom. A good way to find them is to look for code that feels unusual.
method normalize (Numeric:D $sum = 1) {
my $total = self.total or return;
my $factor = $sum / $total;
%!pmf.values »*=» $factor;
self;
}
I have never seen the construct in the first line, at least not in a method. The return
is triggered when self.total
returns something falsesy, like 0. It protects the 2nd line from a division by zero by returning Nil
. Let’s see if that actually works.
$cookie.multiply('Bowl 1', 0);
$cookie.multiply('Bowl 2', 0);
say 'probability it came from Bowl 1: ', $cookie.P('Bowl 1');
# OUTPUT: Attempt to divide by zero when coercing Rational to Str
in sub MAIN at tbr-pmf.rakumod line 73
in block <unit> at tbr-pmf.rakumod line 3
Well, it does blow up some place else. This is not an unreasonable scenario either. When I’m around, the likelihood of a cookie to come from bowl1 or bowl2 is indeed 0. Returning to normalize
we can check what happens when a method that should return self
returns Nil
.
class C {
method foo { Nil }
}
dd C.new.foo.bar;
# OUTPUT: Nil
The cause of this mis-dispatch can be found in src/core.c/Nil.pm6:16:
method FALLBACK(| --> Nil) { }
Nil
is the baseclass of Failure
and as such will only throw when assigned to or used to gain values from a list. It’s purpose is to revert containers to their default value. In a numeric context it will warn and turn into 0. Eventually it might end up in a division and cause much grief. Dealing with a depressing lack of cookies could be done with a multi-method.
multi method P ($key where { self.total == 0 } ) {
0
}
multi method P ($key) {
die "no key '$key' in PMF" unless %!pmf{$key}:exists;
return %!pmf{$key} / self.total;
}
This works because self
is an implicit part of the signature of a method (sans an explicit invocant).
Another thing I never seen before is the following gist
method.
method gist () {
return gather {
take '---';
for %!pmf.keys.sort -> $key {
take " «$key» {%!pmf{$key}}";
}
}.join("\n");
}
Using take
to prefix the resulting list is quite neat. Using a gather
-block avoids any nesting that might need flattening afterwards. In a gist
-method it’s a bit wasteful though. We like to truncate lists after 100 elements in say
. This could be done with a .head(100)
after .keys
. Or we skip the rather slow gather
-block altogether (each take
fires a control exception and Rakudo can not optimise those away yet).
method gist () {
( |'---', |%!pmf.keys.sort.map: { " «$_» %!pmf{$_}" } ).head(100).join($?NL)
}
I like to avoid explicit flattening by using Slip
s. If the map
-block gets complicated, this allows to sneak a .hyper
in to gain some speed on large lists.
Please keep in mind that cookies are nice and Nil
is shifty.
-
August 9, 2021 at 21:192021.32 FirstCon Done – Rakudo Weekly News