Locate the Bell
with Raku and Perl

by Arne Sommer

Locate the Bell with Raku and Perl

[124] Published 18. April 2021.

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

Challenge #108.1: Locate Memory

Write a script to declare a variable or constant and print it's location in the memory.

Everything in Raku is an object, if you want it to be, so we can use the MOP (Meta Object Protocol) WHERE method:

File: locate-memory
#! /usr/bin/env raku

my $str = 'A';
my $int = 1299;
my @array = ;

say $str.WHERE;
say $int.WHERE;
say @array.WHERE;

Running it:

$ ./locate-memory
140431262794192
140431262853352
140431263477168

See docs.raku.org/language/mop#WHERE for more information about WHERE (and MOP).

Note that the address is a single byte. Both values in the program require more than a byte, so the address is probably the location of an object (or container in Rakuesque) of constant size, which in turn contain the type (if any) and number of bytes to hold the value (or values, if a list or hash).

A Perl Version

Perl does not treat everything as objects, but we can get a reference to the variable - as printing a reference gives us the address.

File: locate-memory-perl
#! /usr/bin/env perl

use strict;
use warnings;

use feature 'say';

my $str = 'A';
my $int = 1299;
my @array = qw/A 1299/;

say \$str;
say \$int;
say \@array;

Running it gives this result:

$ ./locate-memory-perl
SCALAR(0x55e24109b390)
SCALAR(0x55e24109b270)
ARRAY(0x55e24109b348)

The first part is the type of object we got a reference to (in UPPER CASE LETTERS, for good measure). Then we get the actual address, in parens, as a hexadecimal value.

The challenge asked for the location in memory, and the «SCALAR» and «ARRAY» labels must go away:

File: locate-memory-perl-hex
#! /usr/bin/env perl

use strict;
use warnings;

use feature 'say';

my $str = 'A';
my $int = 1299;
my @array = qw/A 1299/;

say get_hex_value(\$str);
say get_hex_value(\$int);
say get_hex_value(\@array);

sub get_hex_value
{
    my $string = shift;
    $string =~ /(0x[a-f\d]+)/;  # [1]
    return $1;
}

[1] Note the regexp to get hold of the hexadecinal value.

Running it gives the addresses:

$ ./locate-memory-perl-hex
0x55ac8143d3a0
0x55ac8143d280
0x55ac8143d358

Still as hexadecimal values. Raku used decimal, so let us convert them to decimal.

To Hex or Not to Hex?

The built-in hex function gives a warning when used on large hexadecinal values, as we have here. Using the «Math::BigInt» module solves this problem:

> say hex("0x55ac8143d358");
Hexadecimal number > 0xffffffff non-portable at ...
94199391441752

> say Math::BigInt->new("0x55ac8143d358");
94199391441752

Note that hex gave us the correct result. On my pc at least...

File: locate-memory-perl-bigint
#! /usr/bin/env perl

use strict;
use warnings;

use feature 'say';
use Math::BigInt;

my $str = 'A';
my $int = 1299;
my @array = qw/A 1299/;


say get_int_value(\$str);
say get_int_value(\$int);
say get_int_value(\@array);

sub get_int_value
{
    my $string = shift;
    $string =~ /(0x[a-f\d]+)/;
    return Math::BigInt->new($1);
}

Running it:

$ ./locate-memory-perl-bigint
94303839351760
94303839351472
94303839351712

Challenge #108.2: Bell Numbers

Write a script to display top 10 Bell Numbers. Please refer to wikipedia page for more informations.

Example:

B0: 1 as you can only have one partition of zero element set

B1: 1 as you can only have one partition of one element set {a}.

B2:

   {a}{b}
   {a,b}

B5:

   {a}{b}{c}
   {a,b}{c}
   {a}{b,c}
   {a,c}{b}
   {a,b,c}

B15:

   {a}{b}{c}{d}
   {a,b,c,d}
   {a,b}{c,d}
   {a,c}{b,d}
   {a,d}{b,c}
   {a,b}{c}{d}
   {a,c}{b}{d}
   {a,d}{b}{c}
   {b,c}{a}{d}
   {b,d}{a}{c}
   {c,d}{a}{b}
   {a}{b,c,d}
   {b}{a,c,d}
   {c}{a,b,d}
   {d}{a,b,c}

I assume that «top 10» should mean the first ten values in the seqeunce. (The top 10 highest values is impossible, as the sequence never ends.)

There are several ways of doing this, but I have chosen to use gather/take to collect the values as I build up the values in the trangle described in the «Triangle scheme for calculations» section in the wikipedia article.

File: bell-triangle
#! /usr/bin/env raku

unit sub MAIN ($count = 10);

my $bell-triangle := gather
{
  take 1;                                    # [1]
  take 1;                                    # [2]
  my @triangle = ((1));                      # [3]
  my $row = 0;

  loop
  {
    $row++;                                  # [4]
    my @prev = @triangle[$row-1].flat;       # [5]
    my @new  = @prev[*-1];                   # [6]

    for ^@prev.elems -> $index               # [7]
    {
      @new.push: @new[*-1] + @prev[$index];  # [8]
    }
   
    @triangle.push: @new;                    # [9]

    take @new[*-1];                          # [10]
  }
}

say $bell-triangle[^$count];                 # [11]

[1] The first value.

[2] The second value.

[3] The triangle so far.

[4] The first iteration in this infinite loop gives is row number 1.

[5] Get the previous row.

[6] The first value on the new row is the lasy on on the previous one.

[7] We are going to add as many items as we had on the previous row (but with different values).

[8] Take the value to the left, add the value right above it, and save the result.

[9] Add the new row to the tree.

[10] The new value is returned here.

[11] Compute and print the requested number of values.

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

Running it gives the first ten values (or more, if you want):

$ ./bell-triangle
(1 1 2 5 15 52 203 877 4140 21147)

$ ./bell-triangle 11
(1 1 2 5 15 52 203 877 4140 21147 115975)

$ ./bell-triangle 12
(1 1 2 5 15 52 203 877 4140 21147 115975 678570)

$ ./bell-triangle 13
(1 1 2 5 15 52 203 877 4140 21147 115975 678570 4213597)

$ ./bell-triangle 20
(1 1 2 5 15 52 203 877 4140 21147 115975 678570 4213597 27644437 190899322 \
 1382958545 10480142147 82864869804 682076806159 5832742205057)

Looking good.

Perl

This is a straight forward(ish) translation of the Raku version. Perl does not have gather/take, but I have used say and and explit number of iterations in the loop instead:

File: bell-triangle-perl
#! /usr/bin/env perl

use strict;
use warnings;

use feature 'say';

my $count = 10;

my $count = $ARGV[0] // 10;

say 1; exit if $count == 1;
say 1; exit if $count == 2;

my @first = (1);
my @triangle = (\@first);            # [1]
my $row = 0;

while ($count-- > 0)
{
  $row++;
  my @prev = @{$triangle[$row-1]};  # [1a]
  my @new  = $prev[-1];

  for my $index (0 .. @prev -1)
  {
    push(@new, $new[-1] + $prev[$index]);
  }
   
  push(@triangle, \@new);

  say $new[-1];
}

[1] Perl does not support arrays of arrays, but we can store each row as a reference to a row (with \), and then dereference it again (with the @{…}

construct around the reference) in [1a].

Running it gives the same result as the Raku version, albeit with each value on a new line:

$ ./bell-triangle-perl
1
1
2
5
15
52
203
877
4140
21147
115975
678570

$ ./bell-triangle-perl 3
1
1
2
5
15

And that's it.