Perl Weekly Challenge 107
I wrote this week’s answers several days ago, but forgot to commit them. I ended up getting a new laptop and had to rewrite them. I think they ended up the same, but I guess no one knows for sure!
Task 1: Self-descriptive Numbers
Write a script to display the first three self-descriptive numbers. As per wikipedia, the definition of “self-descriptive number” is:
In mathematics, a self-descriptive number is an integer m that in a given base b is b digits long in which each digit d at position n (the most significant digit being at position 0, and the least significant at position b−1) counts how many instances of digit n are in m.
For example:
1210 is a four-digit self-descriptive number:
Position 0 has value 1 i.e. there is only one 0 in the number
Position 1 has value 2 i.e. there are two 1 in the number
Position 2 has value 1 i.e. there is only one 2 in the number
Position 3 has value 0 i.e. there is no 3 in the number
Expected output:
1210, 2020, 21200
Solution
See below for explanation and any implementation-specific comments.
sub challenge(Int $n) returns Str { # [1]
my @output;
for (^∞) -> $i { # [2][3]
my @digits = $i.comb;
my $valid = True;
for @digits.kv -> $index, $value {
$valid = @digits.grep($index).elems == $value;
last unless $valid; # [4]
}
@output.push($i) if $valid; # [5]
last if @output.elems == $n;
}
@output.join(', ');
}
sub MAIN(Int $n = 3) {
say challenge($n);
}
This program runs as such:
$ raku ch-1.raku
1210, 2020, 21200
Explanation
We begin by defining a list to hold our output, then kick of an infinite loop starting at 0. For each number, we convert it to a list of digits (.comb
). For each index, value pair, we check if the input number has $value
number of $index
digits. So for 1210 we would check if it had 1 zero, 2 ones, 1 two, and 0 threes. If it meets the conditions, we add it to @output
. Finally, if we have found all 3 that we are looking for, we break out of the infinite loop and return.
Specific comments
- We make this generic by accepting the argument
$n
, but the fourth self-describing number is3,211,000
, and the fifth is42,101,000
, so this method would get slow very quickly. - We could have said
loop
to start an infinite loop, but then we wouldn’t have access to$i
. - When using this method, we always have to use the carrot (
^
) to say we are not including infinity. This is because it is impossible to be inclusive of infinity. unless
is just the opposite ofif
. I feel this reads better thanlast if !$valid
.- I find the post-fix way of using conditionals to read better a lot of the time in Raku, as seen here.
Task 2: List Methods
Write a script to list methods of a package/class.
Example
Class definition:
package Calc;
use strict;
use warnings;
sub new { bless {}, shift; }
sub add { }
sub mul { }
sub div { }
1;
Expected output:
BEGIN
mul
div
new
add
Solution
See below for explanation and any implementation-specific comments.
# Used for testing
class Calc { # [1]
method add {}
method mul {}
method div {}
}
sub challenge(Any $class) returns Str { # [2]
$class.^methods.map(*.gist).join("\n"); # [3][4]
}
sub MAIN {
say challenge(Calc.new);
}
This program runs as such:
$ raku ch-2.raku
add
mul
div
BUILDALL
Explanation
The example shows a class definition in Perl, but we are using Raku, so we can just define our class using the class
keyword as shown. Additionally, we have a BUILDALL
method instead of BEGIN
.
Raku gives us the ^methods
meta method to introspect an object’s methods, so we just have to utilize that on the input object!
Specific Comments
- In Perl, we have to define classes in their own file, but in Raku we can just use the
class
keyword. - All objects inherit from
Any
, so we are just saying we accept any class here. - Raku provides us a nice introspection method,
^methods
, which returns a list of defined methods andBUILDALL
. We can also pass in the flag:local
meaning “only show us methods defined inCalc
and not super classes,” or we could pass in:all
meaning “show us all methods that can act on this class.” - The returned type is
List[Method]
, so to cast everything to a string, we need to call each method’sgist
method.
Final Thoughts
Once again, Raku makes these challenges super easy. Looking forward to something tougher in the future!