Self-Deceptive Methods with Raku

by Arne Sommer

Self-Deceptive Methods with Raku

[123] Published 11. April 2021.

This is my response to the Perl Weekly Challenge #107.

Challenge #107.1: Self-descriptive Numbers

I realised just before the launch this task was also part of the week 43 and contributed by Laurent Rosenfeld. It is too late to change now. Feel free to share your previous solutions if you took part in the week 43 already. I should have been more carefull, sorry.

The program from part 2 of my Olympic Numbers with Raku article looks like this:

File: selfdesc
#! /usr/bin/env raku

unit sub MAIN (UInt :$base = 10);

if $base == any(0,1,2,3,6) || $base > 39
{
  say "Error";
}
elsif $base == 5
{
  say "21200";   # Prevent "11100"
}
else
{
  my $number = "{ ($base -4).base(36) }21" ~ "0" x ($base - 3);
  $number.substr-rw(*-4,1) = 1;
  say $number;
}

Follow the link above for a discussion of the code, and the result of running the program.

Bonus

That was easy.

So let us have a go at something similarly named instead. Deceptive Numbers sound fun. (They are also called «Deceptive Non-Primes», but that is not as catchy a name.)

This is a sequence of values (integers), and we can use gather/take to collect the values:

File: deceptive-numbers
#! /usr/bin/env raku

unit sub MAIN ($count);

my $seq := gather
{
  my $index = 3;                                               # [1]

  loop
  {
    unless $index.is-prime                                     # [2]
    {
      take $index if (10 ** ($index -1)) % (9 * $index) == 1;  # [3]
    }
   
    $index++;
  }
} 

say $seq[^$count];

[1] The article uses the term «odd prime», which means that we must skip 2 (witch is the one and only even prime).

[2] Skip primes. Note that we could have started with 1 in [1] because of this, but it does not really matter.

[3] The formulae is taken from The On-Line Encyclopedia of Integer Sequences.

See my Raku Gather, I Take article or docs.raku.org/syntax/gather take for more information about gather/take.

Running it:

$ ./deceptive-numbers 3
(91 259 451)

$ ./deceptive-numbers 10
(91 259 451 481 703 1729 2821 2981 3367 4141)

$ ./deceptive-numbers 20
(91 259 451 481 703 1729 2821 2981 3367 4141 4187 5461 6533 6541 6601 7471 \
 7777 8149 8401 8911)

We got the correct numbers.

Challenge #107.2: List Methods

Write a script to list methods of a package/class.

Example
package Calc;

use strict;
use warnings;

sub new { bless {}, shift; }
sub add { }
sub mul { }
sub div { }

1;
Output
BEGIN
mul
div
new
add

We can get a list of methods by calling the built-in .^methods on an object or the class itself:

File: list-methods
#! /usr/bin/env raku

class Calc
{
  has $.value;           # [1]
  has $.next is rw;      # [2]

  method add { … }       # [3]
  method mul { … }
  method div { … }
}

.say for Calc.^methods;

[1] A read only attribute.

[2] A read write attribute.

[3] The three dots (...) or the Unicode variant gives a stubbed method. The code will compile, but will throw an error if we execute one of them. (I could have used an empty body instead ({ }), but stubbing is useful when developing a class. If you forget to remove the dots, the program will tell you (by dying) if you try to execute it.

Note that we do not have no declare a custom «new» method, as Raku provides one for us.

Raku has Metaobject protocol (MOP) with full introspection of the Raku object system. See docs.raku.org/language/mop for more information.

Running it:

$ ./list-methods
add    # [1]
mul    # [1]
div    # [1]
value  # [2]
next   # [2]
Submethod+{is-hidden-from-backtrace}.new  # [3]

[1] The first three are as expected.

[2] Oops. These are the attributes in the class. They show up here as Raku provides access methods for us automatically. We'll look into that later.

[3] This is the builtin «new» method.

The «new» method (in [3]) does not look very nice, but we can fix that by supplying our own version of the «new» method (so that it isn't inherited). We can get rid of the access methods (in [2]) by changing the attributes to private.

File: list-methods-new
#! /usr/bin/env raku

class Calc
{
  has $!value;      # [1]
  has $!next;

  method new { … }  # [2]
  method add { … }
  method mul { … }
  method div { … }
}

.say for Calc.^methods;

[1] A private attribute (with ! instead of . after the sigil).

[2] A custom «new» method, replacing the built-in one.

Running it:

$ ./list-methods-new
new
add
mul
div
BUILDALL

The «BUILDALL» method is probably ok (as the challenge listed a «BEGIN» method for Perl).

And that's it.