I recently got an Apple M1 Mac Mini, half out of curiosity, half because it was exactly what I would need: I have a low end Mac just to try out things like new Xcode betas etc, like a "canary" machine. My old 2012 Mac Mini stopped getting official Apple updates, so it could no longer do what I needed and the 8GB RAM, 256GB SSD M1 mini at $699 is easily the cheapest Mac you can buy.
Overall, unlike the typical Mac Minis of old which seemed to be on the slow side, it did feel quite fast from the start, so I thought I'd run some benchmarks on it for fun to see how Apple's ARM M1 fares against some x86 competition. And, as in my day job I use mostly Perl, I thought some perl-related benchmarks would be of interest to me.

For those not aware, the M1 is an ARM-based CPU (well, includes GPU, so SoC really), with 8 cores total (4x performance @ 3.2GHz/12MB L3, 4x efficiency @ 2GHz/4MB L3) built at 5nm and consuming up to 15W. Basically the "laptop" class CPU of what Apple has been building for iPhones/iPads. Apart from native ARM code, it can run x86 code through Rosetta 2, but I still can't use it for work - our dev environment currently relies on VirtualBox which needs actual x86/VT-x silicon. I ran benchmarks against my work laptop, a Mid 2015 15" MacBook Pro with a 2.5GHz i7 Crystalwell. Even though it was Apple's top of the line at the time, it's a bit old now, I keep it for the non-butterfly keyboard and the full complement of ports, and until recently the newer Macs weren't much faster anyway. Although an older i7 will make it easier for the M1 to compete, I still find the comparison quite interesting, especially since the Mac Mini has always been the "slow/cheap" Mac - and it's now even cheaper. Plus I'll throw some tests with different hardware just for comparison.

The Benchmarks

I will definitely not claim the benchmarks I ran are truly representative of real world performance, especially when I am of the opinion you should benchmark your own code - what you personally would run. But, I also added some typical things most perl users might do and some things that came up when looking for Perl "benchmarks", so that anybody can try them and get an idea of the relative performance of their own machine.

  • Building perl 5.32.1
It's nice that Apple finally updated the MacOS system Perl. BigSur now comes with v5.28.2 (threaded) by default, after being stuck at v5.18 for many years. However, I rarely rely on system Perl, so the first thing to do which is sort of a benchmark in itself, would be to get perlbrew and run:
perlbrew install perl-5.32.1
  • Moose 2.2015
The Moose test suite is, like the object system itself, a relatively slow affair. I'll time the default cpan installation which builds and runs the test-suite single-threaded:

cpan Moose
Then, I can try the test suite after preloading Moose with yath at 1, 4, 6 threads. In the last case, the M1 will have to use its efficiency cores, while the i7 will use HT:
yath -PMoose
yath -PMoose -j4
yath -PMoose -j6
  • prime.pl
I modified a bit the primes.pl script from here to:

use strict;
use warnings;

use Time::HiRes 'time';
my $time = time();

my $n = $ARGV[0] || 100000000;
my @s = ();
for (my $i = 3; $i < $n + 1; $i += 2) {
    push(@s, $i);
}
my $mroot = $n**0.5;
my $half  = scalar @s;
my $i     = 0;
my $m     = 3;
while ($m <= $mroot) {
    if ($s[$i]) {
        for (my $j = int(($m * $m - 3) / 2); $j < $half; $j += $m) {
            $s[$j] = 0;
        }
    }
    $i++;
    $m = 2 * $i + 3;
}

my @res = (2, grep($_, @s));
warn "Found ".scalar(@res)." primes in ".(time()-$time)." sec.\n";

I ran it as it is, and also on 4 threads with the argument 20000000 (to avoid hitting the 8GB M1 RAM limits).

  • phoronix-test-suite-10.2.2
The only test suite that advertises perl tests, although it turns out in just has 2 small subtests for perl (interpreter, pod2html), with the command:
phoronix-test-suite run pts/perl-benchmark
  • BioPerl
I downloaded some bacteria from genbank and benchmarked loading the sequences to count codons or monomers.

use strict; 
use Bio::SeqIO; 
use Bio::Tools::SeqStats; 
use Benchmark qw(:all);

my $in = Bio::SeqIO->new(-file => "gbbct10.seq", -format => "genbank");

timethis(1, sub {
    my $seq = $in->next_seq;
    my $seq_stats = Bio::Tools::SeqStats->new($seq); 
    my $codon_ref = $seq_stats->count_codons(); 
});

timethis(1, sub {
    my $builder = $in->sequence_builder();
    $builder->want_none();
    $builder->add_wanted_slot('display_id','seq');
    for (1..10000) {
        my $seq = $in->next_seq;
        my $seq_stats = Bio::Tools::SeqStats->new($seq); 
        my $weight = $seq_stats->get_mol_wt(); 
        my $monomer_ref = $seq_stats->count_monomers();
    }
});
  • Precession
Let's precess 1 million random celestial coordinates between random epochs using my Astro::Coord::Precession.

use Astro::Coord::Precession 'precess';

my $precessed = precess([rand(24), rand(180)-90], rand(200)+1900, rand(200)+1900)
    for (1..1000000);
  • Text processing
I threw in a script (called DSOgenerate) that reads various astronomical catalogues and compiles the database for my Polar Scope Align iOS app, and another that parses webpages to get articles (its slowest component is HTML::FormatText) that was used for a university project I worked on.

The Good (aka: The Results)

BenchmarkUnitsi7M1M1 Diff
Build perl 5.32.1min21.5213.7856.1%
cpanm Moosesec116.6144.33163.0%
yath -PMoosesec47.6318.43158.4%
yath -PMoose -j4sec18.055.88207.0%
yath -PMoose -j6sec16.945.77193.6%
prime,plsec23.2215.5649.2%
prime.pl 4xsec4.903.0660.0%
Phoronix pod2htmlmsec213.0091.17133.6%
Phoronix Interpretermsec4.571.25266.8%
BioPerl codonssec149.89127.5817.5%
BioPerl monomerssec16.647.82112.8%
Precessionsec6.853.27109.5%
DSOgeneratesec13.125.71129.8%
HTML::FormatTextsec8.524.7081.3%
Average:124.2%



Or, if we want a nice comparison graph where the i7 is "1x" speed and plot the M1 in relation to it:

chart (2).png

I found the results quite remarkable. I mean, the main reason I went through all these is that I could see the M1 going through the installation of cpan modules at a ridiculous pace compared to my i7 when I was setting it up, it was very obviously faster.
t turns out it is over 2x faster as a crude "average" of the above tests. You can see from the two multithreaded tests that it actually gains even more an advantage when using all its (performance) cores compared to the i7.
There is at least one test (the codons) where the M1 does not really "shine", so, as I said, benchmarking your own specific workload is important - the M1 does seem very fast at many common Perl tasks, but not *all*.
Could my old i7 be just too slow? Just to make sure, I had my colleagues with the 16" Mac with the fastest CPU available, the 8-core 2.4GHz i9, run a couple of the single core benchmarks to make a comparison, one that did really well on the M1, one that did below average:

BenchmarkUnitsi9M1M1 Diff
cpanm Moosesec86.1444.3394.3%
prime,plsec19.3515.5624.4%

So while the i9 is generally 20-30% faster than the i7, it's still nowhere near the M1 being more than twice as fast. Note that the i9 has 8 full speed cores, so things might get tighter for workloads using more than 4 cores at a time.

The Bad

Simply put, not everything works yet. Sometimes it's something simple, like a patch I submitted to Sys::Info::Driver::OSX due to the different reporting of the asymmetric processor cores. But I have been unable to install some other CPAN modules or see test failures that are not easy to explain.

At least Perl developers will have a native-running environment, even though there are some glitches that should get sorted out in time. I am saying this because while some things run fast even under Rosetta, I have encountered cases where non-native software runs slowly. For example, an Android project I tried, takes almost twice the time to compile on the M1. Android studio is not yet native and it shows, I would not recommend the platform to android devs. It is the opposite for iOS devs of course, the M1 is the ideal platform for obvious reasons.

Additionally, the comparison shows the M1 can be much faster than the i7/i9, however that comparison is important only if you are limited to the Apple world. If you don't need a Mac specifically and will just run Linux (to not mention Windows), then you are not limited to what Apple has to offer. I am referring to AMD of course, for most workloads, a Zen 2 based CPU is quite a bit faster than intel per thread, and on top will offer more cores at a similar price. I don't have a Zen 2 CPU to try out right now, however I do have a ThinkPad X395 which has a Zen-1 based 2.1GHz Ryzen 5 3500U. While it's not an old CPU, the newer Zen-2 based 4000-series and 5000-series CPUs seem to be almost twice as fast per core in various benchmarks, which would probably make those faster than an M1, given that the "slow" 3500U is already a bit faster (around 15% on average it seems) than the i7:

BenchmarkUnits3500UM1M1 Diff
cpanm Moosesec101.2244.33128.3%
prime.plsec20.0115.5628.6%

That M1 advantage over 3500U is probably not enough to hold off Zen 2 cpus, which also come with many more full-power cores than an M1.
Then again, the M1 is Apple's first "laptop/desktop" silicon and they were possibly targeting efficiency more than raw performance - as the latter was an easy win vs Intel, so I would keep an eye on what their next CPU will bring.

The Ugly?

The #1 criticism of the M1 Macs is not related to their CPU, but the fact that the SSD is soldered on. This means that when the SSD dies (will take several years, but depending on the usage SSDs will eventually fail), you can't just replace it (unless there's complete disassembly, desoldering etc). This "obsolescence by design" might not be that bad given the price of a Mac Mini compared to what Apple users are used to paying, but it is made worse by the fact that an M1 Mac has a signed system volume on the SSD, which is required for the Mac to boot even when booting from an external device. So when the SSD goes, you might not be able to boot your Mac at all - permanently. As I said, not a criticism of the M1 CPU directly, but of the devices that feature it.

Lastly, while my benchmarks were reproducible in general, there was one benchmark - the prime.pl script - that gave me some trouble, exposing a strange and disconcerting issue. So, I run the benchmark for n=20000000 multiple times and I get consistent results. Also, if I run it by launching 4 instances in parallel background processes with a batch file, I also get consistent results. it goes a bit like this:

test % perl prime.pl
Found 1270607 prime numbers in 2.7765851020813 sec.
test % perl prime.pl
Found 1270607 prime numbers in 2.78401112556458 sec.
test % perl prime.pl
Found 1270607 prime numbers in 2.77585196495056 sec.
test % sh batch.prime.sh
test % Found 1270607 prime numbers in 3.00496196746826 sec.
Found 1270607 prime numbers in 3.01989006996155 sec.
Found 1270607 prime numbers in 3.02487397193909 sec.
Found 1270607 prime numbers in 3.02904796600342 sec.
test % sh batch.prime.sh
test % Found 1270607 prime numbers in 3.01903891563416 sec.
Found 1270607 prime numbers in 3.02826595306396 sec.
Found 1270607 prime numbers in 3.02855086326599 sec.
Found 1270607 prime numbers in 3.03278708457947 sec.
If I try again in a couple of hours or so, I will still see the same thing. But, if I try after a sufficiently long time (I am not clear on "sufficiently" seems like several hours, but definitely by the next day) - without using the Mac Mini in the interim, just left powered on - I start seeing this:

test % perl prime.pl
Found 1270607 prime numbers in 4.18084216117859 sec.
test % perl prime.pl
Found 1270607 prime numbers in 3.94040703773499 sec.
test % perl prime.pl
Found 1270607 prime numbers in 5.06315612792969 sec.
test % perl prime.pl
Found 1270607 prime numbers in 5.53617215156555 sec.
test % perl prime.pl
Found 1270607 prime numbers in 5.34210705757141 sec.
test % sh batch.prime.sh
test % Found 1270607 prime numbers in 3.04679107666016 sec.
Found 1270607 prime numbers in 3.07015514373779 sec.
Found 1270607 prime numbers in 3.07026290893555 sec.
Found 1270607 prime numbers in 3.07335591316223 sec.
test % sudo nice -20 perl prime.pl
Found 1270607 prime numbers in 5.50178408622742 sec.
test % perl prime.pl
Found 1270607 prime numbers in 5.03745698928833 sec.
test % sh batch.prime.sh
test % Found 1270607 prime numbers in 3.04621696472168 sec.
Found 1270607 prime numbers in 3.0637059211731 sec.
Found 1270607 prime numbers in 3.07231998443604 sec.
Found 1270607 prime numbers in 3.07551097869873 sec.
Running a single process is suddenly unpredictably slow. The i7 takes 4.3s at this benchmark on a single thread, so the M1 can be much slower. However, once I batch run 4 parallel processes I get the same great performance I was seeing before. It is reproducible, it's not a matter of if, but a matter of when I will eventually will get into this problematic state which, it seems, I can only solve via a reboot. After a reboot everything is fine once more. I tried to see what's going on in several ways. Checking to see whether something like the efficiency cluster taking over, or the scheduler switching cores etc, I tried monitoring with
powermetrics -s cpu_power
The result is not interesting enough to post, because both when the system is in the "good" and the "bad" state, only the performance cores are used (but not just one of them as I expected - a random mix, different each time, but similarly random for both "states"). It's the same story using the CPU history window:
cores2 copy.png

The graph above shows a couple of single-process runs of prime.pl while in a "good" state, it causes all 4 performance cores (numbers 8 on CPU monitor) to be used at random proportions and it's the same for "bad" status runs - just the bars are twice as wide, the calculation takes longer.

To add another clue that makes things weirder rather than explain the issue, I checked to see if it's my compiled perl at fault, so ran with the built-in perl which I assume Apple made sure to compile correctly. System 5.28 is a bit slower in "good" status runs:

test % perl prime.pl
Found 1270607 prime numbers in 2.77031397819519 sec.
test % /usr/bin/perl prime.pl
Found 1270607 prime numbers in 2.95687794685364 sec.
test % perl prime.pl
Found 1270607 prime numbers in 2.77954316139221 sec.
test % /usr/bin/perl prime.pl
Found 1270607 prime numbers in 2.95602297782898 sec.
test % perl prime.pl
Found 1270607 prime numbers in 2.77461099624634 sec.
test % /usr/bin/perl prime.pl
Found 1270607 prime numbers in 2.94599509239197 sec.
But on "bad" status it is faster than my compiled perl (quite consistently, I've done this a few times) - although still much slower than after a reboot:

test % perl prime.pl
Found 1270607 prime numbers in 5.44245409965515 sec.
test % /usr/bin/perl prime.pl
Found 1270607 prime numbers in 4.92102980613708 sec.
test % perl prime.pl
Found 1270607 prime numbers in 5.34624910354614 sec.
test % /usr/bin/perl prime.pl
Found 1270607 prime numbers in 3.51168012619019 sec.
test % perl prime.pl
Found 1270607 prime numbers in 5.66441202163696 sec.
test % /usr/bin/perl prime.pl
Found 1270607 prime numbers in 3.62216806411743 sec.
test % perl prime.pl
Found 1270607 prime numbers in 5.46292304992676 sec.
I did eventually find a good clue: I can trigger this weird behaviour if I force the Mini to sleep and then wake up - it wakes up in the bad state. However, as there are no battery settings (not a laptop), I can't find any "go to sleep" timer in the settings - and, as I said, just leaving it for an hour or two (the screen does go to sleep, there's a setting for that) does not get it in the weird state. In any case it's probably got something to do with the CPU sleep states that Apple has missed. Since I couldn't reproduce it with the other workloads I would assume it's not gonna be easy to track down. Reminds me a bit of the problems I had waking an older Macbook (the white ones) from sleep while connected with multiple monitors - they never actually fixed that, so it had put me off Macbooks for a few years. I seem to hit Apple sleep state bugs.

Overall

I'd say, despite some caveats, the M1 is showing some impressive potential, especially for people who use MacOS and would not get much choice other than Intel's not-that-impressive-lately offerings. If I was looking for my main work machine, I'd probably wait a bit longer for some teething troubles to be solved (unless I wanted to help solve potential perl-specific issues) - and perhaps wait for the rumoured release later this year of a faster chip ("M1X" or whatever).

Immutable Objects

I’ve been spending time designing Corinna, a new object system to be shipped with the Perl language. Amongst its many features, it’s designed to make it easier to create immutable objects, but not everyone is happy with that. For example, consider the following class:

class Box {
    has ($height, $width, $depth) :reader :new;
    has $volume :reader = $width * $height * $depth;
}

my $original_box = Box->new(height=>1, width=>2, depth=>3);
my $updated_box  = $original_box->clone(depth=>9);  # h=1, w=2, d=9

Because none of the slots have a :writer attribute, there is no way to mutate this object. Instead you call a clone method, supplying an overriding value for the constructor argument you need to change. The $volume argument doesn’t get copied over because it’s derived from the constructor arguments.

But not everyone is happy with this approach. Aside from arguments about utility of the clone method, the notion that objects should be immutable by default has frustrated some developers reading the Corinna proposal. Even when I point out just adding a :writer attribute is all you need to do to get your mutability, people still object. So let’s have a brief discussion about immutability and why it’s useful.

But first, here’s my last 2020 Perl Conference presentation on Corinna (at the time, called "Cor").

The Problem

Imagine, for example, that you have a very simple Customer object:

my $customer = Customer->new(
    name      => "Ovid", 
    birthdate => DateTime->new( ... ),
);

In the code above, we’ll assume the $customer can give us useful information about the state of that object. For example, we have a section of code guarded by a check to see if they are old enough to drink alcohol:

if ( $ovid->old_enough_to_drink_alcohol ) {
    ...
}

The above looks innocent enough and it’s the sort of thing we regularly see in code. But then this happens:

if ( $ovid->old_enough_to_drink_alcohol ) {
    my $date = $ovid->birthdate;
    ...
    # deep in the bowels of your code
    my $cutoff_date = $date->set( year => $last_year ); # oops!
    ...
}

We had a guard to ensure that this code would not be executed if the customer wasn’t old enough to drink, but now in the middle of that code, due to how DateTime is designed, someone’s set the customer birth date to last year! The code, at this point, is probably in an invalid state and its behavior can no longer be considered correct.

But clearly no one would do something so silly, would they?

Global State

We’ve known about the dangers of global state for a long time. For example, if I call the following subroutine, will the program halt or not?

sub next ($number) {
    if ( $ENV{BLESS_ME_LARRY_FOR_I_HAVE_SINNED} ) {
        die "This was a bad idea.";
    }
    return $number++;
}

You literally cannot inspect the above code and tell me if it will die when called because you cannot know, by inspection, what the BLESS_ME_LARRY_FOR_I_HAVE_SINNED environment variable is set to. This is one of the reasons why global environment variables are discouraged.

But here we’re talking about mutable state. You don’t want the above code to die, so you do this:

$ENV{BLESS_ME_LARRY_FOR_I_HAVE_SINNED} = 0;
say next(4);

Except that now you’ve altered that mutable state and anything else which relies on that environment variable being set is unpredicatable. So we need to use local to safely change that in the local scope:

{
    local $ENV{BLESS_ME_LARRY_FOR_I_HAVE_SINNED} = 0;
    say next(4);
}

Even that is not good because there’s no indication of why we’re doing this but at least you can see how we can safely change that global variable in our local scope.

ORMs

And I can hear your objection now:

“But Ovid, the DateTime object in your first example isn’t global!”

That’s true. What we had was this:

if ( $ovid->old_enough_to_drink_alcohol ) {
    my $date = $ovid->birthdate;
    ...
    # deep in the bowels of your code
    my $cutoff_date = $date->set( year => $last_year ); # oops!
    ...
}

But the offending line should have been this:

    # note the clone().
    my $cutoff_date = $date->clone->set( year => $last_year );

This is because the set method mutates the object in place, causing everything holding a reference to that object to silently change. It’s not global in the normal sense, but this action at a distance is a source of very real bugs.

It’s a serious enough problem that DateTime::Moonpig and DateTimeX::Immutable have both been written to provide immutable DateTime objects, and that brings me to DBIx::Class, an excellent ORM for Perl.

As of this writing, it’s been around for about 15 years and provides a component called DBIx::Class::InflateColumn::DateTime. This allows you to do things like this:

package Event;
use base 'DBIx::Class::Core';

__PACKAGE__->load_components(qw/InflateColumn::DateTime/);
__PACKAGE__->add_columns(
  starts_when => { data_type => 'datetime' }
  create_date => { data_type => 'date' }
);

Now, whenever you call starts_when or create_date on an Event instance, you’ll get a DateTime object instead of just the raw string from the database. Further, you can set a DateTime object and not worry about your particular database’s date syntax. It just works.

Except that the object is mutable and we don’t want that. You can fix this by writing your own DBIx::Class component to use immutable DateTime objects.

package My::Schema::Component::ImmutableDateTime;

use DateTimeX::Immutable;
use parent 'DBIx::Class::InflateColumn::DateTime';

sub _post_inflate_datetime {
    my ( $self, @args ) = @_;
    my $dt = $self->next::method(@args);
    return DateTimeX::Immutable->from_object( object => $dt );
}

1;

And then load this component:

__PACKAGE__->load_components(
    qw/+My::Schema::Component::ImmutableDateTime/
);

And now, when you fetch your objects from the database, you get nice, immutable DateTimes. And it will be interesting to see where your codebase fails!

Does all of this mean we should never use mutable objects? Of course not. Imagine creating an immutable cache where, if you wanted to add or delete an entry, you had to clone the entire cache to set the new state. That would likely defeat the main purpose of a cache: speeding things up. But in general, immutability is a good thing and is something to strive for. Trying to debug why code far, far away from your code has reset your data is not fun.

Jonathan writes:


March didn't see me doing that many grant hours; I was keeping my keyboard time down for the first half of the month, and then had a lot to catch up on in the second half (and still needed to avoid putting in more than a usual working day). On the upside, the time that was spent was productive: I got some way into implementing more of Raku's multiple dispatch semantics in terms of the new dispatcher. I've written a blog post about that work (although some of the work it covers was done during April).

Total time worked:: 7 hours 29 minutes

Following issues raised within the community, the CAT opened an investigation into community conduct. A second investigation was opened based on information discovered during the first investigation.

What follows constitutes our second transparency report as well as an additional conclusion statement by the CAT. If you have not read it already, we highly recommend you read our latest Community Affairs Team Update.

Investigation #1

Based on a recent public post viewed by the CAT, the CAT investigated two individuals for potentially unacceptable behavior over IRC and Twitter. The investigation found that the first individual continued communication on multiple platforms despite being asked repeatedly to stop. Past behavior by the first individual was discovered during the course of the investigation. The CAT opened a separate investigation into this past behavior, and that investigation’s results are explained as part of “Investigation #2”.

Regarding the second individual investigated as part of “Investigation #1”, the CAT has decided to issue them a warning. The CAT is in contact with them and has scheduled a meeting with them to discuss this.

Investigation #2

Based on the previous investigation, the CAT investigated past behavior by the first individual. The CAT found that as an attendee of a 2019 Perl event, they had made repeated discriminatory statements and arguments in a conversation with the target, which was also overheard by other attendees. The individual continued arguing for this position despite being asked repeatedly to stop, the next day there were asked to leave the event.

They were instructed not to repeat the discriminatory statements and arguments. They were told an apology should not be made to the target of the harassment. Later, the individual repeated the discriminatory statements publicly on Twitter in discussions with other members of the community which were also viewed by the target. The target told the individual not to contact them ever again. Despite being asked explicitly not to contact them, the individual later contacted the target by email, which was an additional instance of unacceptable behavior.

Consequences

The CAT has informed the individual investigated as part investigation #1 and #2 that they are banned from attending any Perl Foundation conferences or events in perpetuity.

The CAT has instructed the individual to leave and cease participation on any public IRC channels on irc.perl.org, leave and cease participation on the Perl and Raku public channels on Freenode, unsubscribe and cease participation on any Perl and Raku community mailing lists including but not limited to the perl5-porters mailing list. The CAT has informed them to not contact the targets of their behavior from “Investigation #1” or “Investigation #2”. The CAT has informed them to not repeat the offending statements or arguments from “Investigation #2”. If the CAT decides the letter or the spirit of this decision has been violated, it may: explicitly extend the scope of the ban, take further technical measures to ensure compliance, or take other actions as it deems necessary.

Other findings

In addition to the findings of both investigations, the CAT wants to make it clear that separate and inclusive of the two reported investigations, it found many instances of communication which alone may not have constituted unacceptable behavior, but when taken together did constitute unacceptable behavior. The CAT asks all members of the Perl community to be respectful of others and that discriminatory or harassing conduct will not be tolerated.

Conclusion

The Community Affairs Team is committed to a safe environment, at conferences and physical events as well as online between members of the Perl and Raku community. If you have comments regarding this public statement you can send them to cat@perlfoundation.org with the word “comment” in the subject. If you want to report: discriminatory, harassing, or otherwise unacceptable behavior, including evasion of CAT decisions, please contact cat@perlfoundation.org. For examples of what may constitute unacceptable behavior, please see the Standards of Conduct for this year’s Conference in the Cloud.

The CAT’s purpose is to foster a positive and safe environment for anyone who shares an interest in Perl and Raku.

It has been one year since the Community Affairs Team was first announced in March of 2020. In June, the Perl & Raku Conference in the Cloud took place. This was the first conference where Community Affairs Moderators were present to respond to and handle Standards of Conduct violations. Prior to the conference, Community Affairs Moderators and the CAT committee for the event received training on handling and responding to SoC complaints within the community. A month later in July, the Chair of the CAT was appointed by the TPF board. In October, the first Standards of Conduct transparency report was released.

Following that, the CAT has been working on a charter document which outlines in more concrete terms its role and responsibilities as it relates to the community, as well as an updated Standards of Conduct which would apply not just to conferences, but to other TPF online spaces. Recent events in the community have expedited the CAT’s work and the board’s approval of these documents, which are planned to be publicly released within the next week.

The CAT is also preparing for the upcoming Perl & Raku Conference in the Cloud, and training similar that received in 2020 is planned for the moderators and CAT committee for this event. The CAT thanks the many people who provided their input to the formation of the CAT, and Samantha McVey, Stuart Mackintosh, and Ricardo Signes for continuing their work on the CAT committee as well as their work on the CAT committee during last year’s conference.

The Community Affairs Team is committed to a safe environment, at conferences and physical events as well as online between members of the Perl and Raku community. If you have comments regarding this update you can send them to cat@perlfoundation.org with the word “comment” in the subject.

Our client is a financial company whose extraordinary growth over the last twenty years has seen them build a worldwide presence. They’ve recently opened a new location in Belarus and are looking for Perl developers with a strong background in Modern Perl – you should be comfortable with Moose and PSGI/Plack, and a solid grounding in using Perl’s testing tools.
Everyone loves a great deal, which is why our client’s team is thriving. As one of the largest and most trusted product and price comparison platforms in German-speaking countries, our client has made it their mission to save people from overpaying. Consider all candidates with strong Perl, but they make heavy use of PostgreSQL, Elasticsearch, and Modern Perl.
The client is interested in anyone with experience building web apps in Perl, using one of the major Perl frameworks. If you’re a crack-hand with Catalyst, a Mojolicious master, or a distinguished Dance, they want you. You’ll be deploying apps your work to AWS, so experience would be handy, and the company’s big on testing, so they’d like you to know your way around Test::More.
If you are a senior Perl/JS programmer with a passion for finance and business, this could be the role you’ve been waiting for. Our client is looking for an individual who understands Go programming languages, is experienced with Python and Django, and knows the ins and outs of databases like MySQL, and NoSQL databases like Google BigTable.
Our client is looking for a Perl programmer who wants to tame the job jungle for employers and employees through their candidate sourcing and data management products. The role is 100% remote within the United States.
Online 9-11 June 2021.
Thursday, May 6, 2021
Wednesday, April 28, 2021
Thursday, April 22, 2021
What a coincidence, I also got some help from the same animated GIF. Nice to know that both of us follow the same path.
I loved the hack shared by Roger. It a nice reminder to all of us, RTFM.
I loved the use of Raku's 'multi' feature. It makes code so clean and easy to read. Thanks for sharing.
I enjoyed the refactoring process in the solution to the Bell Numbers task.
Laurent is on a mission to show his skill. Plenty of choices of languages can be found in this blog post.
A very clean demonstration of Bell Numbers in Raku. Take this opportunity to learn Raku.
Flavio shares the power of state variables. Please check it out yourself.
Flavio makes good use of the core Perl library. Nice interpretaion.
A thorough discussion on the Bell Numbers task is the highlight of this blog post. You don't want to miss it.
For me Colin's blog is very useful to improve my vocabulary. I love how he presents his thought process. I wish I could do that one day.
Arne is unbeatable when it comes to the creativity of his blog titles. You will also find the content to be high quality.
Adam took the opportunity to share solutions in Perl and Prolog. You don't want to miss the opportunity to dig deep.
Abigail once again gives us a glimpse of various languages while dealing with the Bell Numbers task. Thank you Abigail.
Cool hack to get a memory location in Perl. Thanks Abigail for sharing with us.
You are going to enjoy the discussion in Aaron's blog post. I simply love it.
Perl Solutions Review by Colin Crain.
Enjoy a quick recap of last week's contributions the Team PWC dealing with the "Locate Memory" and "Bell Numbers" tasks in Perl and Raku. You will find plenty of solutions to keep you busy.
Welcome to a new week with a couple of fun tasks "Chowla Numbers" and "Four Squares Puzzle". If you are new to the weekly challenge then why not join us and have fun every week. For more information, please read FAQ page.
The Register published a news article about the recent news.
Leon shares his truth in this blog post. Worth reading.
Dean shared a proposal for TPF.
Neil shared the behind-the-scenes story with the rest of the world. I am happy Neil is part of the team that is leading us forward.
Thibault shared the process of adopting a CPAN module.
Flavio shared a really useful trick dealing with PERL5LIB.
Thibault presents his blog contributions from the past six months.
Mark suggests how to deal with CPAN conflicts.
The Perl Foundation has proposed a project to help improve Perl documentation.
Neil shared the latest update with regard to the recent activities.
Please check out the detailed changes that the latest release will bring.
Jason shared the changes that the latest release of Dancer2 brought.

Hi there

The start of last week was terrible for all Perl fans. We found out that Sawyer had decided to step down from the Perl Steering Council and all other affiliates. At this time, when Perl is making a nice comeback, this came as quite a shocker. We need him now, more than ever, to be actively involved and take us to a new heights. Having said that, I do respect his decision and wish him all the best. I hope to meet him once again personally at some Perl conference.

We shouldn't lose hope, though. Neil Bowers came up with detailed plan that gave me confidence that we will bounce back.

I have seen Sawyer regularly at London Perl Workshops but never got to speak to him. Then came a big moment of my life, I was giving my talk Create tube map in 20 minutes using Map::Tube at the London Perl Workshops in 2017. Sawyer was the man behind the camera recording the talk. At the end of the talk, Sawyer came up to me and suggested how I could automate some of the tasks I mentioned in the talk. It was a big fan moment for me. Everything is still so fresh in my mind.

Apologies for the delay as this is first edition for me during the month of Ramadan. I wish all the readers a very happy Ramadan. May ALLAH s.w.t. protect us all from the evils. Amen.

This is a monthly report by Tony Cook on his Maintaining Perl 5 grant. We thank the TPF sponsors to make this possible. ``` Approximately 29 tickets were reviewed, and 3 patches were applied

[Hours] [Activity] 1.63 #18519 manually rebase and some clean up, make PR #18647 0.17 #18523 recheck and apply to blead 2.33 #18557 review, find several similar tickets, work on adding a warning, create #18643 2.52 #18587 review discussion and changes, and review perlguts, comment #18587 review and comment 0.22 #18589 review and apply to blead 0.08 #18629 review and approve 0.70 #18632 review makemeta, work on a fix #18632 more work on a fix, testing, make PR #18670 1.12 #18634 review changes, review list discussion 0.38 #18639 review and research, comment 1.74 #18642 research and comment #18642 documentation fix, and look over magic handling for setsockopt and make PR 18660 #18642 fix non-portable new test 3.06 #18643 add each @{ anonarray } too, fixes, debugging #18643 add more tests, testing, force push #18643 update each() documentation too 1.97 more autovivification, debugging, work out what’s wrong, tests and make PR #18650 #18650 review, research and comment 6.64 #18651 review ticket, work on a reproducer, debugging, research and comment #18651 debugging, try a fix and bounce off my_snprintf(), try a simpler solution, testing, add new test, debug test to ensure code works as expected, polish (need a bit more test debugging) #18651 more testing, code checks (some code handling q size flag or not) #18651 post PR with comment, comment on original ticket 0.35 #18652 review and briefly comment 0.35 #18656 comment 0.68 #18658 recheck and apply to blead, perldelta update 0.33 #18662 review and comment (request changes) 0.45 #18663 request more information #18663 ask for more information again 0.53 #18664 research and comment 4.96 #18667 testing, reproduce, try bisect #18667 try bisect again on different hardware #18667 review commit found by bisect - it’s not the cause (fa353c3d2), debug some, the error feels like a stack-not- refcounted #18667 more debugging, trying to find code that might trigger stack-not-refcounted 0.72 cage cleaning - review tickets, closing some, seeing which need work 0.23 comment on Fix string leaks thread 0.82 feature.[ch] on perl5-porters 1.87 list catch up, also some cygwin testing for khw 0.72 look at autovivication feature 0.92 more list catch up 0.58 review github notifications 1.67 review list discussion 1.02 review The current state of perl email and the following

discussion

38.76 hours total 39.```

The third part of the live development course management application using Mojolicous together with Mark Gardner.

I've been using Digital Ocean for many years for some of my hosting needs. Besides the nice GUI they also have an API and there is a Perl module called DigitalOcean that can be used to access it.

This morning (or yesterday evening, depending on your time zone), Gabór Szabó and I embarked on another episode of Mojolicious-powered development on his course management application. Video evidence and links are here.

On behalf of the Dancer Core Team, I'd like to announce the availability of Dancer2 0.301002. This release includes a number of enhancements and documentation changes along with several bugfixes. The most notable enhancement is a brand new command line interface, and I highly encourage you to check it out.

This is feature-for-feature compatible with the previous CLI, but has a few minor cosmetic differences from the prior version. Under the hood, however, it is entirely different. We previously used App::Cmd as the basis for our CLI, but recent changes to it bumped the minimum Perl version to 5.20. As we strive to maintain compatibility back to at least Perl 5.10, this caused some problems for users stuck on older Perl versions. We monkey-patched a fix in a previous version, but our new-and-improved CLI based on CLI::Osprey is the path forward. Even better, CLI::Osprey has no dependencies outside of those we already had in the Dancer2 toolchain, so your favorite lightweight web framework just got even lighter.

You can read the complete changelog here. If you need help, please reach out on IRC (irc.perl.org#dancer) or our mailing list.

Thank you to our users for being one of the best parts of the Perl community. We appreciate your continued feedback and support. Please reach out with any suggestions or needs you have.

Happy Dancing!
CromeDome

The examples used here are from the weekly challenge problem statement and demonstrate the working solution.

Part 1

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

Solution


use strict;
use warnings;
use Devel::Peek;
use Capture::Tiny q/capture_stderr/;
use constant A => "test";
my $a = 1;    
my $address;  
my $stderr = capture_stderr {
    Dump(A)
};
$stderr =~ m/at\s(0x.*\n).*/;
$address = $1;  
chomp($address);
print "Address of constant A: $address\n"; 
$stderr = capture_stderr {
    Dump($a)
};
$stderr =~ m/at\s(0x.*\n).*/;
$address = $1;  
chomp($address);
print "Address of \$a: $address\n";

Sample Run


$ perl perl/ch-1.pl
Address of constant A: 0xfd31ae90
Address of $a: 0xfdb2f770

Notes

This is a somewhat unusual challenge for Perl. Sometimes these challenges allow for a certain amount of interpretation. For example, under the hood, the representation of Perl data in memory involves more complicated data structures. I think it is in the spirit of this challenge to demonstrate access to this, without necessarily implementing complete and fully generalized solution.

Here I use Devel::Peek in order to get a report on the underlying memory usage of the given variables. The Dump function only prints a memory report to STDERR, so in order to obtain the information we seek Capture::Tiny is used to encapsulate the STDERR output and save it to a variable. A regex is then used to pull out the memory address which is then printed.

The memory address printed here is the reference address. For additional details on Perl’s core see the perlguts documentation.

Part 2

Write a script to display the first 10 Bell Numbers.

Solution


use strict;
use warnings;

sub bell_triangle{
    my($n) = @_; 
    my @bell_numbers = ([]);
    $bell_numbers[0]->[0] = 1;
    for (my $i=1; $i<=$n; $i++) {
      $bell_numbers[$i]->[0] = $bell_numbers[$i-1]->[$i-1];
      for (my $j=1; $j<=$i; $j++){  
          $bell_numbers[$i]->[$j] = $bell_numbers[$i-1]->[$j-1] + $bell_numbers[$i]->[$j-1];
       }
   }
   return $bell_numbers[$n]->[0];
}

MINA:{
    for my $b (0 .. 9){  
        print "B_$b: " . bell_triangle($b) . "\n";  
    } 
}

Sample Run


$ perl perl/ch-2.pl
B_0: 1
B_1: 1
B_2: 2
B_3: 5
B_4: 15
B_5: 52
B_6: 203
B_7: 877
B_8: 4140
B_9: 21147

Notes

This is an interesting problem. At first glance one might be tempted to proceed and compute the partitions and then take the total number of them all. Instead, it turns out that there is a simpler closed form solution whereby we can compute the Bell Triangle and then take the values on the leftmost diagonal to be the Bell Numbers as required.

For fun the Prolog solution does indeed compute the partitions instead of simply using the Bell Triangle!

References

Challenge 108

perlguts

Bell Numbers

Bell Triangle

These are some answers to the Week 108 of the Perl Weekly Challenge organized by Mohammad S. Anwar.

Spoiler Alert: This weekly challenge deadline is due in a few days (April 18, 2021). This blog post offers some solutions to this challenge, please don’t read on if you intend to complete the challenge on your own.

Task 1: Locate Memory

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

Locate Memory in Raku

In languages such as Perl and C, it is a fairly common task to take a reference or a pointer to a variable, and a reference or a pointer are essentially the memory addresses of such a variable (for some definition of memory address). In Raku, using the memory address of a variable is almost never necessary (except possibly for low-level debugging purpose). Actually, I originally wasn’t even completely sure I was going to find a way of doing that in Raku. However, the Metaobject Protocol (MOP) offers some metamethods, which are introspective macros that provide information about objects (including variables). One such metamethod is WHERE, which returns an Int representing the memory address of the object. Once we know that, the task is very easy:

my $i = 42;
say $i.WHERE;

This small script displays the following output:

$ raku memory.raku
41688736

Locate Memory in Perl

As mentioned before, taking a reference to a variable is very common in Perl. And a reference is in effect a memory address. So the task is very easy in Perl:

use strict;
use warnings;
use feature "say";

my $i = 42;
say \$i;

This displays the following output:

$ perl memory.pl
SCALAR(0x600079020)

If we want to get rid of irrelevant information and print out only the memory address, we can just extract the address, for example with a regular expression:

use strict;
use warnings;
use feature "say";

my $i = 42;
my $ref = \$i;
my $addr = $1 if $ref =~ /\((0x\w+)/;
say $addr;

which prints only the memory address:

$ perl memory.pl
0x600079020

Locate Memory in Other Languages

Memory Address in the C Programming Language

In C, & is the “address-of” operator:

#include <stdio.h>

int main () {
    int val = 42;
    printf("Memory location of val is: %p", &val);
    return 0;
}

Output:

$ ./a.out
Memory location of val is: 0xffffcc1c

Memory Address in C++

Rather than copying almost verbatim the C program above, we’ll use the fact that C and C++ array names are actually pointers to memory locations:

#include <iostream>
using namespace std;

int main() {
  int array[4] = {42, 43, 44, 45};
  cout << "Memory address of the array is: " << array;
  return 0;
}

Output:

Memory address of the array is: 0x7ffc3775ad50

Memory Address in the D Programming Language

D pointers are similar to C’s. So we can do basically the same as in C:

import std.stdio;

void main () { 
   int val = 42; 
   writeln("Address of val is: ", &val);
}

Output:

Address of val is: 7FFD967574F8

Memory Address in Python

Python doesn’t really have pointers, but we can still retrieve the integer representation of the address of a Python object with the id built-in:

i = 42
print("Address of variable i is: ", id(i))

Output:

Address of variable i is:  9786208

Memory Address in Go

In Go, & is the “address-of” operator:

package main

import "fmt"

func main() {
    i := 42
    fmt.Println("Address of vaiable i is: ", &i)
}

Output:

Address of vaiable i is:  0xc000018050

Memory Address in Julia

In Julia, we’ll use an array. The pointer built-in function returns a pointer to the array:

arr = [1, 2, 3, 7]
p_arr = pointer(arr)
println("Memory address of arr is: ", p_arr)

Output:

Memory address of arr is: Ptr{Int64} @0x00007f70a29334d0

Memory Address in Rust

In Rust, & is the “address-of” operator as in C:

fn main() {
    let val: i32 = 42;
    println!("Memory locacion of variable val is: {:p}", &val);
}

Output:

Memory location of variable val is: 0x7fff4b32e2fc

Memory Address in Pascal

Last time I used Pascal was in the first year of my CS curriculum, back in 1990. As you might imagine, I don’t remember much of the syntax. It did not even remember that Pascal had pointers, except that, thinking about it, I remembered that we had to implement linked lists or trees, something that probably required some form of pointer. The big difference between now and then, of course, is that it wasn’t possible at the time to look up on the Internet. So you needed to buy books, as well as the software (a Turbo-Pascal compiler from Borland at the time). Pascal might not be the most modern language, but it has a clean and clear syntax, so that it turned out to be quite simple to write this little program. The only thing that seems to be missing from Niklaus Wirth’s brainchild programming language is a good harmless goto statement. ;-). But it’s OK, I did not need it.

program memAddress;
var
   value: integer;
   intPtr: ^integer;
   result: ^word;

begin
   value := 42;
   intPtr := @value;
   result := addr(intPtr);
   writeln('Memory address of value is: ', result^);
end.

Output:

Memory address of value is: 23008

Pascal lets you easily use pointers to access the data itself. But, if, for some reason, you want to work with the memory address itself, you need to store it in a word type variable (the result variable in the program above).

Task 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: 2

{a}{b} {a,b}

B3: 5

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

B4: 15

{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}

The Bell numbers count the possible partitions of a set. There are various ways to compute the Bell numbers, but one of the most common ones is to construct the Bell triangle, which may be displayed as follows:

                    1
                 1     2
              2     3     5
           5     7    10    15
       15    20    27    37    52
    52    67    87   114   151   203
203   255   322   409   523   674   877

The Bell triangle may be constructed by placing the number 1 in its first position. After that placement, the leftmost value in each row of the triangle is filled by copying the rightmost value in the previous row. The remaining positions in each row are filled by a rule very similar to that for Pascal’s triangle: they are the sum of the two values to the left and upper left of the position.

Thus, after the initial placement of the number 1 in the top row, it is the last position in its row and is copied to the leftmost position in the next row. The third value in the triangle, 2, is the sum of the two previous values above-left and left of it. As the last value in its row, the 2 is copied into the third row, and the process continues in the same way.

The first (and last) item on each row provides the individual Bell numbers:

1 1 2 5 15 52 203 877 ...

There may be faster ways to compute Bell numbers, but since we are requested to compute only the first 10 Bell numbers, this will be amply sufficient.

Bell Numbers in Raku

We just build the Bell triangle (the tr array of arrays) and extract the Bell numbers from it:

constant \MAX = 9;
my @tr;
@tr[0][0] = 1;
for 1..MAX -> $row {
    @tr[$row][0] = @tr[$row - 1][*-1];
    for 1..$row -> $i {
        @tr[$row][$i] = @tr[$row][$i-1] + @tr[$row - 1][$i-1];
    }
}
say join " ", map { @tr[$_][0] }, 0..@tr.end;

This script displays the first 10 Bell numbers:

$ raku bell.raku
1 1 2 5 15 52 203 877 4140 21147

Bell Numbers in Perl

As in Raku, we build the Bell triangle and extract the Bell numbers:

use strict;
use warnings;
use feature "say";
use constant MAX => 9;

my @tr;
$tr[0][0] = 1;
for my $row (1..MAX) {
    $tr[$row][0] = $tr[$row - 1][-1];
    for my $i (1..$row) {
        $tr[$row][$i] = $tr[$row][$i-1] + $tr[$row - 1][$i-1];
    }
}
say join " ", map { $tr[$_][0] } 0..$#tr;

And we obtain the same output as in Raku:

$ perl bell.pl
1 1 2 5 15 52 203 877 4140 21147

Bell Numbers in Other Languages

Bell numbers in Scala

This is essentially the same algorithm using the Bell triangle in Scala. The slight difference is that the Bell triangle is mapped on a square matrix, with only the items of the triangular area actually defined.

object bellNum extends App {
  val max = 10
  var tr = Array.ofDim[Int](max, max)
  tr(0)(0) = 1
  for (row <- 1 until max) {
    tr(row)(0) = tr(row - 1)(row - 1)
    for (i <- 1 until row + 1) {
      tr(row)(i) = tr(row)(i - 1) + tr(row - 1)(i - 1)
    }
  }
  val result = for (i <- 0 until max) yield tr(i)(0)
  println (s"Bell numbers: ${result.mkString(" ")}")
}

Output:

Bell numbers = 1 1 2 5 15 52 203 877 4140 21147

Bell numbers in Python

Same algorithm again. As in Scala, the Bell triangle is mapped on a square matrix, with only the items of the triangular area actually relevant (other positions are set to 0).

max = 10
tr = [[0] * max for i in range(max)]
tr[0][0] = 1
for row in range(1, max):
    tr[row][0] = tr[row - 1][row - 1]
    for i in range(1, row+1):
        tr[row][i] = tr[row][i-1] + tr[row - 1][i-1];

print( [row[0] for row in tr] )

Output:

[1, 1, 2, 5, 15, 52, 203, 877, 4140, 21147]

Bell numbers in Julia

We also allocate a square matrix and populate it with 0’s. Only the items of the triangular area are relevant. A slight difference is that we populate a result array when we compute the first item of each row.

max = 10
tr = zeros(Int32, max, max)
tr[1, 1] = 1
results = ones(Int32, max)
for row = 2:max
    res = tr[row - 1, row - 1]
    tr[row, 1] = res
    results[row] = res
    for i = 2:row
        tr[row, i] = tr[row, i-1] + tr[row - 1, i-1]
    end
end
for n in results print("$n ") end

Output:

$ julia bell.jl
1 1 2 5 15 52 203 877 4140 21147

Bell Numbers in C

We also allocate a square matrix. Only the items of the triangular area are populated and relevant. We also populate a result array when we compute the first item of each row.

#include <stdio.h>
#include <stdlib.h>
#define MAX 10

int main() {
    int tr[MAX][MAX];
    tr[0][0] = 1;
    int results[MAX] = {1};
    for (int row = 1; row < MAX; row++) {
        int res = tr[row - 1][row - 1];
        tr[row][0] = res;
        results[row] = res;
        for (int i = 1; i <= row; i++) {
            tr[row][i] = tr[row][i-1] + tr[row - 1][i-1];
        }
    }
    printf("The ten first Bell numbers are: %i ", (results[0]));
    for (int i = 1; i < MAX; i++) {
        printf("%d ", results[i]);
    }
    printf("\n");
    return 0;
}

Output:

$ ./a.out
The ten first Bell numbers are: 1 1 2 5 15 52 203 877 4140 21147

Bell Numbers in Awk

Just as in Julia and in C, we also allocate a square matrix. Only the items of the triangular area are populated and relevant. We also populate a result array when we compute the first item of each row.

BEGIN {
    max = 10
    tr[0, 0] = 1
    results[0] = 1
    for (row = 1; row < max; row++) {
        res = tr[row -1, row -1]
        tr[row, 0] = res
        results[row] = res
        for (i = 1; i <= row; i++) {
            tr[row, i] = tr[row, i-1] + tr[row - 1, i-1]
        }
    }
    printf("First Bell numbers are: %d ", results[0])
    for (i = 1; i < max; i++) printf ("%d ", results[i])
    printf("\n");
}

Output:

$ awk -f bell.awk
First Bell numbers are: 1 1 2 5 15 52 203 877 4140 21147

Bell Numbers in Ruby

I had some trouble getting the syntax right for declaring bi-dimensional arrays (and the error messages are less than awesome). But it eventually worked. Note that Ruby has this nice feature that you can use the #{ ... } to interpolate some code (e.g. the result of an expression) within a string.

max = 9
tr = Array.new(max+1){Array.new(max+1)}
tr[0][0] = 1
results = [1]
for row in 1..max
    tr[row][0] = tr[row - 1][row -1]
    results << tr[row][0]
    for i in 1..row
        tr[row][i] = tr[row][i-1] + tr[row - 1][i-1]
    end
end
puts "The #{max+1} first Bell numbers are: #{results.join(" ")}"

Output:

The 10 first Bell numbers are: 1 1 2 5 15 52 203 877 4140 21147

Note: I tried to initialize max to 15 in order to generate the first 16 Bell numbers and check the program’s correctness, and the output shows that Bell numbers are growing very rapidly (faster than a geometric progression):

The 16 first Bell numbers are: 1 1 2 5 15 52 203 877 4140 21147 115975 678570 4213597 27644437 190899322 1382958545

Bell Numbers in Pascal

As mentioned above, I took up Pascal for the first time since 1990 for task # 1. So I thought I could implement tack # 2 also in Pascal and it turned out to be quite easy:

program bell;
const
    max = 9;

var
    tr: array [0..max, 0..max] of integer;
    row, i : integer;

begin
    tr[0, 0] := 1;
    for row := 1 to max do
        begin
            tr[row, 0] := tr[row - 1, row -1];
            for i := 1 to row do  
                tr[row, i] := tr[row, i-1] + tr[row - 1, i-1]; 
        end;
    write('The first Bell numbers are: ');
    for row :=0 to max do
        write(tr[row, 0], ' ');
    writeln;    
end.

Output:

The first Bell numbers are: 1 1 2 5 15 52 203 877 4140 21147

Bell Numbers in D

This program is quite similar to the C program, with only a few syntax variations:

import std.stdio;

void main() {
    enum MAX = 10;
    int tr[MAX][MAX];
    tr[0][0] = 1;
    int results[MAX] = 1;
    for (int row = 1; row < MAX; row++) {
        tr[row][0] = tr[row - 1][row - 1];
        results[row] = tr[row][0];
        for (int i = 1; i <= row; i++) {
            tr[row][i] = tr[row][i-1] + tr[row - 1][i-1];
        }
    }    
    writeln("The first 10 Bell numbers are: ", results);
}

Output:

The first 10 Bell numbers are: [1, 1, 2, 5, 15, 52, 203, 877, 4140, 21147]

Bell Numbers in Go

Except for small syntactic differences, the program is again quite similar to the C or D implementations.

package main

import "fmt"

func main() {
    const MAX int = 10
    var tr [MAX][MAX]int
    tr[0][0] = 1
    var results [MAX]int
    for row := 1; row < MAX; row++ {
        tr[row][0] = tr[row-1][row-1]
        results[row] = tr[row][0]
        for i := 1; i <= row; i++ {
            tr[row][i] = tr[row][i-1] + tr[row-1][i-1]
        }
    }
    fmt.Println("The first Bell numbers are: ", results)
}

Output:

The first Bell numbers are:  [0 1 2 5 15 52 203 877 4140 21147]

Wrapping up

The next week Perl Weekly Challenge will start soon. If you want to participate in this challenge, please check https://perlweeklychallenge.org/ and make sure you answer the challenge before 23:59 BST (British summer time) on Sunday, April 25, 2021. And, please, also spread the word about the Perl Weekly Challenge if you can.

Tasks, My solutions

TASK #1 › Locate Memory

Task

Write a script to declare a variable or constant and print it’s[sic] location in the memory.

My solution

One good thing about Perl (and most modern languages) is that you don't really need to worry about memory management. Perl will automatically reclaim memory when a variable is no longer used.

For this task, I take the reference to the variable (for example SCALAR(0x559184ba5890) and use regular expression to display the location (the bit between the parenthesis).

Minified

In 22 characters, this can be minified to the below example.

» perl -E 'say\$a=~/(0x[0-9a-f]+)/'
0x555668d2a3f8

Example

» ./ch-1.pl 
0x555668d2a3f8

TASK #2 › Bell Numbers

Task

Write a script to display top 10 Bell Numbers.

My solution

In tackling this task, the animated gif is very helpful. I use the @bell array to reproduce this. I start by seeding the table with a single value, and then use the copy and add functions to generate the subsequent values. Finally I use a foreach loop to display the last number in each row to display the list of bell numbers.

Example

» ./ch-2.pl
1
2
5
15
52
203
877
4140
21147
115975

In this part of the counter examples series we have Perl Dancer based application using Redis as the in-memory cache/database to store the counter.

The code runs in a Docker container and we have another container running the Redis server.

Updates for great CPAN modules released last week. A module is considered great if its favorites count is greater or equal than 12.

  1. App::opan - A CPAN overlay for darkpan and pinning purposes
    • Version: 0.003005 on 2021-04-13
    • Votes: 13
    • Previous version: 0.003003 was 23 days before
  2. App::perlbrew - Manage perl installations in your $HOME
    • Version: 0.92 on 2021-04-15
    • Votes: 166
    • Previous version: 0.91 was 2 months, 15 days before
  3. Cpanel::JSON::XS - cPanel fork of JSON::XS, fast and correct serializing
    • Version: 4.26 on 2021-04-12
    • Votes: 39
    • Previous version: 4.25 was 5 months, 15 days before
  4. ExtUtils::MakeMaker - Create a module Makefile
    • Version: 7.62 on 2021-04-13
    • Votes: 49
    • Previous version: 7.60 was 1 month, 24 days before
  5. Minion::Backend::mysql - MySQL backend
    • Version: 0.24 on 2021-04-12
    • Votes: 12
    • Previous version: 0.23 was 3 months, 24 days before
  6. Mojolicious - Real-time web framework
    • Version: 9.17 on 2021-04-13
    • Votes: 451
    • Previous version: 9.16 was 4 days before
  7. PDF::API2 - Facilitates the creation and modification of PDF files
    • Version: 2.040 on 2021-04-13
    • Votes: 27
    • Previous version: 2.039 was 1 month, 9 days before
  8. PDL - Perl Data Language
    • Version: 2.037 on 2021-04-16
    • Votes: 41
    • Previous version: 2.034 was 16 days before
  9. SPVM - Static Perl Virtual Machine. Fast Calculation, Fast Array Operation, and Easy C/C++ Binding.
    • Version: 0.0942 on 2021-04-15
    • Votes: 21
    • Previous version: 0.0937 was 9 days before
  10. Statocles - A static site generator
    • Version: 0.098 on 2021-04-11
    • Votes: 29
    • Previous version: 0.097 was 1 year, 10 days before
  11. version - Structured version objects
    • Version: 0.9929 on 2021-04-16
    • Votes: 18
    • Previous version: 0.9928 was 6 months, 24 days before
  12. XML::LibXML - Interface to Gnome libxml2 xml parsing and DOM library
    • Version: 2.0207 on 2021-04-17
    • Votes: 91
    • Previous version: 2.0206 was 7 months, 2 days before

This is the weekly favourites list of CPAN distributions. Votes count: 63

Week's winner: XS::Parse::Keyword (+3)

Build date: 2021/04/17 19:17:49 GMT


Clicked for first time:


Increasing its reputation:

In my previous article, I talked about using amCharts library with Perl Mojolicious. Today we will looking at creating the similar chart with React.js instead of plain JavaScript. I will keep it short since we already talked about it previously and will be reusing most of the code.

There are 2 ways we can use the react.js -

  1. Without JSX (using <script> tag)
  2. With JSX

JSX stand for JavaScript XML. It allow you to easily write HTML in react.
For now we will take the baby step and start without JSX.

Creating the data config

We will use the exact same example as in previous article and try to create a multi line chart.

{
    "title": "Number of automobiles sold per day by manufacturer",
    "label": {
        "domainAxis": "Date",
        "rangeAxis": "Numbers of automobiles sold"
    },
    "data": [
        {
            "Date": "2020-04-15",
            "Honda": 10,
            "Toyota": 20,
            "Ford": 6,
            "Renault": 16
        },
        {
            "Date": "2020-04-16",
            "Honda": 3,
            "Toyota": 15,
            "Ford": 19,
            "Renault": 10
        },
        {
            "Date": "2020-04-17",
            "Honda": 5,
            "Toyota": 8,
            "Ford": 12,
            "Renault": 6
        },
        {
            "Date": "2020-04-18",
            "Honda": 9,
            "Toyota": 10,
            "Ford": 4,
            "Renault": 12
        }
    ]
}

Creating the mojo app

The version I am using for this article is 9.14.

$  mojo generate app MojoReactApp

This command will generate a example application with proper directory structure for a MVC application and mentioned previously.

Now go inside the dir and try to run this app.

$ morbo ./script/mojo_app
Web application available at http://127.0.0.1:3000

Open the browser and hit http://localhost:3000/ and you can see the welcome page.

The rest of step is exact similar to mention in 'Creating the mojo app' section in previous article. So I will not board you by repeating it again. We will directly see the react part.

Adding React.js to app

We will update the default.html.ep to include the react.js

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <title><%= title %></title>

        %= content 'head'
    </head>
    <body>
        <div>
            %= content
        </div>
        <script src="https://unpkg.com/react@17/umd/react.production.min.js" crossorigin></script>
        <script src="https://unpkg.com/react-dom@17/umd/react-dom.production.min.js" crossorigin></script>

        %= content 'end'
    </body>
</html>

We are using the production minified version. You can also use the development version also for debugging purpose.
I have added react.js on layout template as we will be using it all our web pages.

In multi_line_chart.html.ep

% layout 'default';
% title 'Charts';

% content_for 'head' => begin
    <link rel="stylesheet" type="text/css" href="css/charts.css">
% end

<div id="root"></div>

% content_for 'end' => begin
    %= javascript "https://cdn.amcharts.com/lib/4/core.js"
    %= javascript "https://cdn.amcharts.com/lib/4/charts.js"
    %= javascript "https://cdn.amcharts.com/lib/4/themes/animated.js"

    %= javascript "js/multi_line_chart.js"

    %= javascript begin 
        var domContainer = document.getElementById("root");
        createMultiLineChart(domContainer, <%== $chart_data %>);
    % end
% end

We are geeting the $chart_data form create_multi_line_chart in lib\MojoReactApp\Controller\Charts.pm when the template get rendered.
Lets update the public/js/multi_line_chart.js to make it a React Component.

"use strict";

// React without JSX

const e = React.createElement;

class MultiLineChart extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            chartId: this.props.chartId,
            chartData: this.props.data,
        };
    }

    createSeries = (chart, axis, field, name) => {
        // Create series
        var series = chart.series.push(new am4charts.LineSeries());
        series.dataFields.dateX = "Date";
        series.dataFields.valueY = field;
        //series.dataFields.categoryX = "Date";
        series.strokeWidth = 2;
        series.xAxis = axis;
        series.name = name;
        series.tooltipText = "{name}: [bold]{valueY}[/]";
        //series.fillOpacity = 0.8;

        // For curvey lines
        series.tensionX = 0.8;
        series.tensionY = 1;

        // Multiple bullet options - circle, triangle, rectangle etc.
        var bullet = series.bullets.push(new am4charts.CircleBullet());
        bullet.fill = new am4core.InterfaceColorSet().getFor("background");
        bullet.fillOpacity = 1;
        bullet.strokeWidth = 2;
        bullet.circle.radius = 4;

        return series;
    };

    createChart = (chart) => {
        // Increase contrast by taking evey fouth color
        chart.colors.step = 4;
        //chart.hiddenState.properties.opacity = 0;             // this creates initial fade-in

        // Add title to chart
        var title = chart.titles.create();
        title.text = this.state.chartData["title"];
        title.fontSize = 25;
        title.marginBottom = 15;

        chart.data = this.state.chartData["data"];

        // Create axes - for normal Axis
        // var categoryAxis = chart.xAxes.push(new am4charts.CategoryAxis());
        // categoryAxis.dataFields.category = "Date";
        // categoryAxis.renderer.grid.template.location = 0;

        // Create axes - for Date Axis
        var dateAxis = chart.xAxes.push(new am4charts.DateAxis());
        //dateAxis.dataFields.category = "Date";
        dateAxis.renderer.grid.template.location = 0;
        dateAxis.renderer.minGridDistance = 50;
        dateAxis.title.text = this.state.chartData["label"]["domainAxis"];

        var valueAxis = chart.yAxes.push(new am4charts.ValueAxis());
        //valueAxis.renderer.line.strokeOpacity = 1;
        //valueAxis.renderer.line.strokeWidth = 2;
        valueAxis.title.text = this.state.chartData["label"]["rangeAxis"];

        //var single_data_item = this.state.chartData["data"][0];
        var series1 = this.createSeries(chart, dateAxis, "Toyota", "Toyota");
        var series2 = this.createSeries(chart, dateAxis, "Ford", "Ford");
        var series3 = this.createSeries(chart, dateAxis, "Honda", "Honda");
        var series4 = this.createSeries(chart, dateAxis, "Renault", "Renault");

        // Add legend
        chart.legend = new am4charts.Legend();

        // Add cursor
        chart.cursor = new am4charts.XYCursor();
        chart.cursor.xAxis = dateAxis;

        // Add scrollbar
        chart.scrollbarX = new am4core.Scrollbar();

        // Add export menu
        chart.exporting.menu = new am4core.ExportMenu();
    };

    componentDidMount() {
        am4core.useTheme(am4themes_animated);
        const chart = am4core.create(this.state.chartId, am4charts.XYChart);
        this.createChart(chart);
        this.chart = chart;
    }

    componentWillUnmount() {
        if (this.chart) {
            this.chart.dispose();
        }
    }

    render() {
        return e("div", { id: this.state.chartId }, null);
    }
}

function createMultiLineChart(domContainer, chartData) {
    ReactDOM.render(
        e(MultiLineChart, { chartId: "chartdiv", data: chartData }, null),
        domContainer
    );
}

We are calling the createMultiLineChart function from our template with parameters. The main point to know here is the state and lifecycle functions - componentDidMount and componentWillUnmount.
Since there are already plenty of documentation available, I encourage you to look into it. One of the place to learn the concept is react official docs- State and Lifecycle

If you look closely the rest of function definition is not much changes from the previous used javascript. We just wrapped it in react.

The final directory structure is -

📦mojo_react_app
┣ 📂etc
┃ ┗ 📜input_data.json
┣ 📂lib
┃ ┣ 📂MojoReactApp
┃ ┃ ┣ 📂Controller
┃ ┃ ┃ ┗ 📜Charts.pm
┃ ┃ ┗ 📂Model
┃ ┃ ┃ ┗ 📜Data.pm
┃ ┗ 📜MojoReactApp.pm
┣ 📂public
┃ ┣ 📂css
┃ ┃ ┗ 📜charts.css
┃ ┗ 📂js
┃ ┃ ┗ 📜multi_line_chart.js
┣ 📂script
┃ ┗ 📜mojo_react_app
┣ 📂t
┃ ┗ 📜basic.t
┣ 📂templates
┃ ┣ 📂charts
┃ ┃ ┗ 📜multi_line_chart.html.ep
┃ ┗ 📂layouts
┃ ┃ ┗ 📜default.html.ep
┣ 📜mojo_react_app.yml
┗ 📜README.md

Save it and try to hit 'http://localhost:3000' again. From the user perspective side nothing has changed, you will see the same output as before.
fi7og88vjcasg4ishx45

As I mentioned before we will starting with baby step.
The usage of the above mentioned way is very limited. You can use the react.js without jsx when your scope is small - where you have to make a website of few pages because here we are not using the full power of react.
To use the react.js with full potential and unleash its power you have to use jsx. We will be looking in to it our next article.

The above example is available at github.

Perl onion logo taken from here
Mojolicious logo taken from here
React logo taken from here
amCharts logo taken form here

It has been around 6 months since I started to blog a lot.

I followed the calls from Gabor Szabo, Joel Berger and Dave Cross then on top I added mine

My intention was clearly to setup a "Perl blogging scene" on devto and I feel like it works, see #Perl and numbers from perlweekly:
Blogs

The number of blog posts does not decreased on BPO (it was not the goal, I want more posts, not less!) but the number of posts on DevTo increased lot yeah ! 💃
It reached a minimum steady number that make it a viable place to monitor for posts :) (and to make people join and contribute)

It's a teamwork and I do not deserve much credits, but I did a pretty decent effort (more than 50 blog posts in 6 months):
A lot of posts

Will I blog as much for the coming 6 months? Probably not, but I have to say that blogging is very rewarding since I have the feeling to show Perl in a nice way and I have a lot of interactions with great people (comments ❤️).

It also helps me practice the "art of writing", help me to dig deeper in some areas and suddenly there are so much great blog posts from Perl figures all around!
@mjgardner @leontimmermans @kraih @davorg @ovid @haarg @tobyink
(I wanted to put more names but there is a limit on how many people I can mention)

These people are often more successful than me (I have to admit that they produce better blog posts 😃), Ovid had an immediate huge success with Perl-but-generalist posts, that's crazy!

Hits

Back to me, my biggest hit is not Perl related but about how a user triggered a crypto miner in my github action through a malicious pull request, if you're interested in github actions and security, check it out.

My other hits (could we really talk about hits? 😀) are around the topic of Perl and blogging: A call to Perl bloggers to come on DevTo or To help Perl, just blog

Or around Perl reputation like A Perl love letter or The hate of Perl in memes

I also made books reviews that I wrote also as a kind of "personal notes". The list of reviews is here

Mojolicious related posts are always well received! Like for instance this Simple live chart using mojolicious

In between I wrote a lot of posts about toolchain/(Meta)CPAN:

And do you read my first post?

Failures

To be honest there are also some failures that I consider even more interesting to link 😃

My parallel between Perl devs and Mandalorians was cool but a bit weird for readers 😃 (but I get some very kind likes)

I am proud of my serie about JAPHs (what a "risky" topic) but the 2 first episodes JAPHs autopsies 1 and JAPHs autopsies 2 were almost totally ignored.

My posts in French never worked but I continued to post them anyway 😀 like Book review "Perl pour l'impatient" or this terrible absolutely-no-like Critique de livre : Gimp 2.10 - Special debutants

Aside from Perl, I published some posts about docker (1, 2) or security (Billions laughs attack)

(I'm kidding with "failures" but it is not, I'm not writing for likes)

Conclusion

As I said in my call, I'm more a blog reader than a writer, so I'm very eager to read your posts!

So please dear reader, unleash the writer living in you! 😃

As you might know Perl version 5.10 has introduce a keyword called state that allows the creation of so-called state variables. These are very similar to the static variables of C. You declare them insied a function. They are scoped to the function so you cannot reach them from the outside, but they never get destroyed and they are initialized only once.

Dave Horsfall suggested to use this feature to detect unwanted recursion. Or calling the same function twice.

The current Yet Another Society (The Perl Foundation) bylaws exclude the possibility of membership (see Article II https://www.perlfoundation.org/bylaws.html). As reference, the Python Foundation has 5 membership classes with various rights and privileges associated (https://www.python.org/psf/bylaws/).

I believe this to be a wasted opportunity to increase engagement with stake holders in the Perl community, be they individuals, business or other organizations. And also to secure funding for vital Perl related activities arranged by the Perl Foundation.

Rather than a complicated class system,a good starting point would be a single membership option with an annual renewal and no specific benefits than a warm feeling. (Although, throwing in some exclusive swag and a membership card might not be a bad little bonus)

This could then evolve based on feedback via a membership committee.

I have sent a brief proposal to the YAS (TPF) board to this effect. I invite your support in comments below and/or by contacting board members voicing your support (see https://www.perlfoundation.org/committees.html)

Jonathan Worthington has submitted his report for the RakuAST grant, which you can read below.

In addition, you should check out his post about his work on the dispatcher.

During March I implemented RakuAST nodes and updated the RakuAST-based compiler frontend to cover the following features:

  • The for statement modifier, both in sink and non-sink form
  • Specifying the return type or value in signatures using -->
  • The rx// construct, so far only without adverbs
  • The m// construct (immediate match against the topic)
  • Regexes in sink context matching against the topic

I also made some further corrections to sink handling.

This work increased the number of fully passing spectests files using the RakuAST-based compiler by 21, meaning it now entirely passes 246 spectest files.

Let's examine if in 2021 an email redistribution list, i.e. perl5-porters@ (p5p) is still the best model for collaborating on the perl language. This is a discussion so comment below!

Advantages of an email list:

  • Familiar interface, people can use their client of choice
  • Low resources to run and maintain
  • Easy to derive automation from as email is all well known protocols
  • Everything is email

Disadvantages of an email list:

  • Email addresses disclosed to all participants (can be changed)
  • UI experience for participants inconsistent, may require client side configuration to "get right"
  • Email "reply" text can lower the signal to noise ratio
  • No topic categorization of posts, its all dumped in to your inbox
  • Tricky to respond to missed emails
  • Moderation is crude, every email is reviewed and approved, or everything is approved
  • Once an email is relayed it can't be moderated further
  • Encourages side channel correspondence
  • Everything is email

Given the long list of disadvantages we can guess why email lists (and newsgroups) have largely fallen by the wayside.

Self-hosted web forums largely replaced them, which themselves are now largely replaced by "social media" - be that Facebook pages/groups, reddit topics, stackoverflow etc. Although we can learn much from how web forums are still used by android developers etc

My own personal experience is that my current employer has all but eliminated email, seriously. I receive 2-3 emails each day which are always automated notifications and often from external sources (health insurance notifications, stockholder notices etc)
For so many of us, our professional lives are built on top of email - so imagining work without email is like imagining a new colour.

Because imagining new things is hard, let's start a discussion around requirements first and see where that leads us.

In terms of prior art- there is no shortage of "collaboration software" in 2021. From issue trackers that have bolted on kanban boards, to chat software on steroids - there are different approaches we can learn from. Perhaps we should use one of those instead? There are good arguments to be made around this.

For convenience, I will split my proposed requirements in to functional and non-functional, as well as aspirations which can help guide design choices.

Functional requirements:

  • People and automation can post content
  • People and automation can post content that has various relationships with other content
    • Reply being the most obvious relationship
  • Content can have relationships with external things (CVE notices, GH bugs, RT issues, etc)
  • Content can be tagged with arbitrary tags, allowing it to be classified
  • Content has various metadata
  • A system for voting
  • UI makes responding quick and easy
  • Personal contact details controlled by each participant
  • Moderation features
    • Moderators able remove posts from view, still visible to moderators
    • Features to correspond with moderation actions privately
    • Content is never deleted, only hidden
  • API for integration with other systems, github, irc bots, etc
  • Generalized notification system with user controls
    • Send's emails
    • Calls other API's as mentioned
  • Uses CPAN users
    • 2fa
  • Rate limiting & throttling
  • Anti-spam
  • Web based and cli tools, perhaps email driven interaction
  • Well Indexed and searchable
Non-Functional requirement:
  • FOSS code
  • Self hosted?
  • Written in Perl
  • Encrypted backups
  • Modular, so to make contributing and administering simple

Aspirations:

  • Help prioritize urgent matters like CVE's
  • Reduce busy work for people
    • Automate and integrate with RT, GH etc.
    • Reduce unnecessary reading
    • High signal to noise ratio
  • Respect peoples privacy
  • Promote respectful collaboration
  • Promote data driven decision making
  • Modern looking interface

In my minds-eye I have something of a hybrid of Hacker News/Reddit and Asana/Monday dot com. It's hard to resist the temptation to make our own. I suspect that in just on hack-o-thon a minimum product that is already more effective than an email list could be built using on Mojolicious and GraphQL.

How would we get this thing?

I mentioned a hack-o-thon, this type of thing worked nicely enough for MetaCPAN.

TPF might reasonably call for some well known Perl consultancies for priced proposals then do fundraising activities to cover the costs. Such proposals could be milestone based or components assigned to different firms working in parallel.

Now what?

What are your thoughts? What could we add or clarify? Hopefully you have a radically different concept you can describe?


Footnotes

According to Neil Bowers, the purpose of p5p is "the development and maintenance of Perl" (https://www.nntp.perl.org/group/perl.perl5.porters/2021/04/msg259782.html) which is also covered in the "perlpolicy" document (https://perldoc.perl.org/perlpolicy)


In my training course I give exercises to my students. They can (and in some of these course they must) submit them. I can then review them and give comments. I would like to have a web application to keep track of all the exercises and the submission.

In this experimental project Mark Gardner and myself will develop this application using live pair programming.

The examples used here are from the weekly challenge problem statement and demonstrate the working solution.

Part 1

Write a script to generate self-descriptive numbers.

Solution


use strict;
use warnings;
use Thread; 
use boolean;
use constant SDN_COUNT => 3;
use constant THREAD_COUNT => 4;
use constant RANGE_SIZE => 10_000;

sub self_describing{
    my($i) = @_;
    my @digits = split(//, $i);
    for my $x (0 .. @digits - 1){
        my $count = 0;
        for my $j (0 .. @digits - 1){
            $count++ if($digits[$j] == $x);
            return false if($count > $digits[$x]);
        }
        return false if($count != $digits[$x]);
    }
    return true;
}

sub self_describing_number{
    my($start, $end) = @_;  
    my @r = (); 
    for(my $i = $start; $i < $end; $i++){
        push @r, [length($i), $i] if(self_describing($i));  
    }   
    return \@r;  
}

MAIN:{
    my @threads; 
    my $count = 0; 
    my $lower = 1; 
    my $upper = RANGE_SIZE; 
    do{
        for(0..(THREAD_COUNT - 1)){  
            my $t = Thread->new(\&self_describing_number, ($lower, $upper));
            push @threads, $t;  
            $lower = $upper + 1;  
            $upper = $lower +  RANGE_SIZE;  
        }  
        foreach my $t (@threads){  
            my $sdns = $t->join();                 
            foreach my $sdn (@{$sdns}){ 
                print "Base " . $sdn->[0] . ":" .  $sdn->[1] . "\n" if $count < SDN_COUNT; 
                $count++;  
            }  
        }   
        @threads = ();   
    } while($count < SDN_COUNT);
}

Sample Run


$ perl perl/ch-1.pl
Base 4:1210
Base 4:2020 
Base 5:21200

Notes

Part 1 this week is repeated from Challenge 043. In order to provide something fresh for the same problem I modified the previous code to be multi-threaded.

Part 2

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

Solution


use strict;
use warnings;

sub analyze{
    my($file) = @_;
    my @subs;
    my @uses; 
    my @subroutines;
    my $subs = `perlanalyst $file --analysis Sub`;
    $subs =~ s/$file://;
    @subs = split(/\n/, $subs);   
    my $uses = `perlanalyst $file --analysis Use`;
    $uses =~ s/$file://;
    @uses = split(/\n/, $uses);   
    for my $s (@subs){
        $s =~ s/\s+//;
        my @fields = split(/:/, $s); 
        push @subroutines, $fields[1] if(length($s) > 0); 
    }
    push @subroutines, "BEGIN" if(@uses); 
    return @subroutines; 
}

MAIN:{
    my $FILE = $ARGV[0];
    my @subroutines = analyze($FILE);
    print join("\n", sort {$a cmp $b} @subroutines) . "\n"; 
}

Sample Run


$ perl perl/ch-2.pl perl/Calc.pm 
BEGIN
DESTROY
add
div
mul
new

Notes

Getting a list of methods can mostly be done via just some plain analysis of the code. Rather than re-invent the wheel I am using a module, Perl::Analysis::Static, to do that for me. This is a pretty neat tool but has been left in an alpha state. The most stable way to use it is via the command line instead of its incomplete API. In this code I call the perlanalyst command and then parse the output.

If given a BEGIN block or if use-ing a module Perl will execute a BEGIN at compile time. I would argue that this is out of scope for this challenge. However, as given in the problem statement we are expected to catch this it seems. I do this by inspecting the perlanalyst output for use lines. I could have done a few other things as well but decided not to do more with this since it seems like a funny requirement anyway!

References

Challenge 107

Challenge 043

Perl::Analysis::Static

Updates for great CPAN modules released last week. A module is considered great if its favorites count is greater or equal than 12.

  1. Devel::IPerl - Perl language kernel for Jupyter
    • Version: 0.010 on 2021-04-05
    • Votes: 19
    • Previous version: 0.009 was 3 years, 3 months, 19 days before
  2. Devel::NYTProf - Powerful fast feature-rich Perl source code profiler
    • Version: 6.07 on 2021-04-06
    • Votes: 167
    • Previous version: 6.06 was 2 years, 10 months, 2 days before
  3. GraphQL - Perl implementation of GraphQL
    • Version: 0.50 on 2021-04-08
    • Votes: 18
    • Previous version: 0.49 was 1 month, 21 days before
  4. Mojolicious - Real-time web framework
    • Version: 9.16 on 2021-04-09
    • Votes: 450
    • Previous version: 9.14 was 17 days before
  5. SPVM - Static Perl Virtual Machine. Fast Calculation, Fast Array Operation, and Easy C/C++ Binding.
    • Version: 0.0937 on 2021-04-06
    • Votes: 21
    • Previous version: 0.0936 was 3 days before
  6. XML::Compile::SOAP - SOAP version 1.1
    • Version: 3.27 on 2021-04-07
    • Votes: 13
    • Previous version: 3.26 was 1 year, 4 months, 17 days before

This is the weekly favourites list of CPAN distributions. Votes count: 40

Week's winner: Mojolicious (+2)

Build date: 2021/04/10 19:01:29 GMT


Clicked for first time:


Increasing its reputation:

The examples used here are from the weekly challenge problem statement and demonstrate the working solution.

Part 1

You are given an array of integers @N. Write a script to display the maximum difference between two successive elements once the array is sorted.

Solution


use strict;
use warnings;
sub max_difference_sorted{
    my(@sorted) = @_;
    return 0 if(@sorted == 1);
    my $x = $sorted[1] - $sorted[0];  
    my $y = max_difference_sorted(@sorted[1 .. @sorted - 1]);   
    return ($x > $y)? $x: $y; 
}

sub max_difference{
    my (@numbers) = @_;
    return max_difference_sorted(
        sort { $a <=> $b } @numbers
    ); 
}

MAIN:{
    my (@N);
    @N = (2, 9, 3, 5);
    print max_difference(@N) . "\n"; 
    @N = (1, 3, 8, 2, 0); 
    print max_difference(@N) . "\n"; 
    @N = (5);
    print max_difference(@N) . "\n"; 
}

Sample Run


$ perl perl/ch-1.pl
4
5
0

Notes

I believe this code is straightforward enough! max_difference performs the sort and max_difference_sorted recursively finds the largest difference as required.

Part 2

You are given numerator and denominator i.e. $N and $D. Write a script to convert the fraction into decimal string. If the fractional part is recurring then put it in parenthesis.

Solution


use strict;
use warnings;
use boolean;

sub divide{
    my($n, $d) = @_; 
    my @remainders;
    my $q = (int($n / $d)) . ".";
    my $r = $n % $d; 
    push @remainders, $r; 
    my @a;
    for (0 .. $d){
        $q .= int($r*10 / $d);  
        $r = $r*10 % $d;
        @a = grep { $remainders[$_] == $r } (0 .. @remainders - 1);
        last if(@a); 
        push @remainders, $r; 
    }
    my $r_i = $a[0];
    my $i = index($q, ".");
    my $decimal_part = substr($q, $i+1); 
    return substr($q, 0, $i + 1) . substr($decimal_part, 0, $r_i) . "(" . substr($q, $i + $r_i + 1) . ")";  
}   

sub prime_factor{
    my $x = shift(@_); 
    my @factors;    
    for (my $y = 2; $y <= $x; $y++){
        next if $x % $y;
        $x /= $y;
        push @factors, $y;
        redo;
    }
    return @factors;  
}

sub nd2decimal{
    my($n, $d) = @_;
    my $max_repetend = $d - 1; 
    my $repeats = false; 
    my @factors = prime_factor($d);
    for my $factor (@factors){
        $repeats = true if($factor != 2 && $factor != 5); 
    } 
    unless($repeats){ 
        return sprintf("%0.${max_repetend}g", $n / $d); 
    }
    else{
        my $x = divide($n, $d, [], []); 
        return $x; 
    }  
}

MAIN:{
    my($N, $D);
    ($N, $D) = (1, 3);
    print nd2decimal($N, $D) . "\n";  
    ($N, $D) = (1, 2);
    print nd2decimal($N, $D) . "\n";  
    ($N, $D) = (5, 66);
    print nd2decimal($N, $D) . "\n";  
    ($N, $D) = (1, 6);
    print nd2decimal($N, $D) . "\n";  
    ($N, $D) = (1, 8);
    print nd2decimal($N, $D) . "\n";  
}

Sample Run


$ perl perl/ch-2.pl
0.(3)
0.5
0.0(75)
0.1(6)
0.125

Notes

Part 2 is a bit trickier than the first part. The approach here is as follows:

  • determine if it is a repeated decimal by checking if $d has prime factors other than 2 or 5
  • if it is not a repeated decimal then this is quick work, divide and display the solution
  • in the case of repeated decimals we essentially implement grade school long division in the divide function and keep track of remainders. When a remainder is repeated we know that we have found the cycle.

There are some interesting theoretical properties to repeat decimals but none are particularly helpful in actually computing them. One observation is that the length of the cycle must be smaller than the value of the denominator, whence the use of $d in the main loop in the divide function.

I’m re-using the same prime_factors function that I used in Challenge 041.

References

Challenge 106

Repeating Decimal

During the great RT will be shutdown craze earlier this year, I updated my Dist::Zilla setup to use a custom author bundle (Dist::Zilla::PluginBundle::Author::DOMM) so I can unify my Dist::Zilla config in one place (instead of copying dist.ini from project to project, adding & modifying it from time to time, but never backporting improvements to older projects).

Two of my decisions might have been not too smart (as indicated by various pull requests), so I'm now looking for feedback...

META.yml vs META.json?

I stopped generating META.yml files and now only generate META.json. As far as I can tell, the contents of those two files is identical, so I think it should be enought to include the data once per dist (and I prefer JSON to YAML...)

Of course, CPANTS now complains about the missing META.yml. (Which is of course ironic, because it was me who first implemented this CPANTS metric...)

Maybe some other (old?) tools still need META.yml? So should I again produce both META.yml and META.json?

Dist::Zilla user config?

During my deep dive into Dist::Zilla plungins and author bundles, I learned that Dist::Zilla looks for per-user configuration in ~/.dzil/config.ini. So I moved some general information (my name & email, default license) from all my dist.ini files into this central config file.

But it seems that this makes contributing to my dists even harder, as the dist.ini file included in the dist now is invalid. Contributors will get a (helpful) error message when running dzil build, but this is yet another hurdle for potential contributors. I've already got two pull requests to "fix" this.

On the one hand I would prefer to keep the global config file, so I can easily change data. On the other hand I could just as well run a small command to change whatever value I might want to change on multiple files (using this hot new tool called "Perl", which makes mangling text in multiple files very easy) and thus make contributions easier.

How are other CPAN authors handling this?

Or is it all Dist::Zilla's fault?

Some people (like brian d foy in this thread about Distar, a simpler authoring tool (but it works with ExtUtils::MakeMaker, which is a module I don't want to touch..)) argue that Dist::Zilla is too complex and make contributing very hard (because potential contributors now need to install a lot of Dist::Zilla plugins).

But I actually like Dist::Zilla a lot (esp after finally coming up with a PluginBundle that fits my workflow). So I will keep using Dist::Zilla, but would really appreciate any feedback on my two questions (META.yml and user config).

You can either enter a comment below (if my homegrown comments system works..), or via reddit

Update: user config vs github

After applying another patch supplied by Paul Cochrane that adds some github testing hooks, it seems to be clear that depending on the user config is a bad idea. Surprisingly, github will NOT set up a ~/.dzil/config.ini so it can "contribute" to my dist (by running it's test suite), see eg here

So I guess I will stop using the global config file...

dist author version abstract
Acme-Automatix CONTRA 0.01 The great new Acme::Automatix!
Acme-CPANModules-Frameworks PERLANCAR 0.001 List of frameworks on CPAN
Acme-MetaSyntactic-legodcsupervillains BINGOS 0.02 LEGO DC Super Villains theme
Acme-MetaSyntactic-legotheincredibles BINGOS 0.02 LEGO The Incredibles theme
Alien-uPB-Core MBARBON 0.16 build and find uPB (a.k.a. Unleaded)
App-PerlNitpick GUGOD 0.01 change-suggester about insignificant details.
App-ThisDist-OnMetaCPAN PERLANCAR 0.001 this-dist-on-metacpan and some other CLIs
App-dateseq-id PERLANCAR 0.001 Generate a sequence of dates
App-knradio WOLDRICH 0.044 interface for knradio, 92,2 FM
App-lcpan-CmdBundle-depsort PERLANCAR 0.001 More lcpan subcommands related to sorting by dependencies
Asm-C PRBRENAN 20210328 Extract macro values and structure details from C programs.
Boundary KFLY 0.01 declare interface package
Complete-Finance-SE-IDX PERLANCAR 0.001 Completion routines related to Indonesian Stock Exchange
Crypt-Passphrase LEONT 0.001 A module for managing passwords in a cryptographically agile manner
Crypt-Passphrase-Argon2 LEONT 0.001 An Argon2 encoder for Crypt::Passphrase
Crypt-Passphrase-Bcrypt LEONT 0.001 A bcrypt encoder for Crypt::Passphrase
Crypt-Passphrase-PBKDF2 LEONT 0.001 A PBKDF2 encoder for Crypt::Passphrase
Crypt-Passphrase-Scrypt LEONT 0.001 A scrypt encoder for Crypt::Passphrase
Crypt-xxHash CDN 0.01 xxHash implementation for Perl
Decl-Tok MICHAEL 0.01 Given a line iterator, returns a token stream that tokenizes the lines as first-pass Decl
Devel-PatchPerl-Plugin-Darwin GUCCHISK v0.1.0 patchperl plugin for darwin
Devel-Util TDRUGEON 0.80 a collection of general-utility development subroutines
Game-HeroesVsAliens LNATION 0.01 A tower defense game.
Geo-LibProj-FFI AJNN 0.01 Foreign function interface to PROJ coordinate transformation software
Go-Tokenize BKB 0.01 Tokenize Go
GuacLite JBERGER 0.01 Toolkit for implementing a frontend server/client the Apache Guacamole system
INI-Reader-Regexp RAJ 0.01 INI Parser
IO-AIO-Promiser FELIPE 0.01_01 Promise interface around IO::AIO
IO-FDSaver FELIPE 0.01 Save file descriptors from Perl’s garbage collection.
Image-Magick JCRISTY v6.9.12 ImageMagick PERL Extension
Iterator-Records-Lines MICHAEL 0.01 Provides simple record iterators for reading text line by line
JSON-JQ DONGXU 0.01 jq (https://stedolan.github.io/jq/) library binding
JSON-Schema-Tiny ETHER 0.001 Validate data against a schema, minimally
LINQ TOBYINK 0.000_001 an interpretation of Microsoft's Language Integrated Query
Linux-Sys-CPU-Affinity CDN 0.01 Perl XS extension for setupping CPU affinity
List-GroupingPriorityQueue JMATES 0.01 priority queue with grouping
List-Helpers-XS CDN 0.01 Perl extension to provide some usefull functions with arrays
Log-Any-Adapter-JSON TONKIN 1.00 One-line JSON logging of arbitrary structured data
Lyrics-Fetcher-LyricsOVH BIGPRESH 0.01 Get song lyrics from api.lyrics.ovh.
MIME-DB TDRUGEON v1.46.0 Media Type Database, a port of the mime-db JavaScript package
Math-LinearApprox ZHMYLOVE 0.01 fast linear approximation of 2D sequential points
Module-Features-PerlTrove PERLANCAR 0.001 Put Perl trove classifiers in your module
Module-Features-PythonTrove PERLANCAR 0.001 Put Python trove classifiers in your module
Module-FeaturesUtil-Check PERLANCAR 0.001 Check feature set specification and feature declaration
Mojolicious-Plugin-Export PREACTION 0.008 Export a Mojolicious website to static files
Mojolicious-Plugin-Export-Git PREACTION 0.001 Export a Mojolicious site to a Git repository
Mojolicious-Plugin-GSSAPI OETIKER v0.1.0 Provide Kerberos authentication for incomming https requests
MooX-Params-CompiledValidators ABELTJE 0.01 A Moo::Role for using Params::ValidationCompiler.
Music-RhythmSet JMATES 0.01 sets of rhythms and various generation functions
Nasm-X86 PRBRENAN 20210330 Generate Nasm X86 code from Perl.
OPM-Maker PERLSRVDE 1.00 Module/App to build and test OPM packages for Znuny, OTOBO, ((OTRS)) Community edition.
OPM-Validate PERLSRVDE 1.00 Validate .opm files
POE-Filter-EPPTCP MAT 0.001 EPP Frame parsing for POE
POE-Filter-SimpleXML MAT 1.000 Simple XML parsing for POE
Path-Tiny-Archive-Tar DIONYS 0.001 Tar/untar add-on for file path utility
Paws-Credential-AssumeRoleWebIdentity PRAJITH 0.0.2 The AssumeRoleWebIdentity provider is used to obtain temporary credentials with an OIDC web identity token file.
Perinci-Sub-XCompletionBundle-Finance-SE-IDX PERLANCAR 0.001 Completion routines related to the Indonesian Stock Exchange
Pinto-Remote-SelfContained ARC 0.900 interact with a remote Pinto repository
Pod-Thread RRA 2.00 Convert POD data to the HTML macro language thread
RT-Extension-HelpDesk BPS 0.01 RT-Extension-HelpDesk Extension
RT-Extension-TerminalTheme BPS 0.01 RT-Extension-TerminalTheme Extension
Rex-Interface-Shell-Idrac4 ALIP v0.1.0 Rex module to support iDRAC 4.00.00.00
Rex-Interface-Shell-Ilo ALIP v0.1.0 Rex module to support iLO
Rex-Shell-Interface-Idrac4 ALIP v0.1.0 Rex module to support iDRAC 4.00.00.00
Rex-Shell-Interface-Ilo ALIP v0.1.0 Rex module to support iLO
Search-Typesense OVID 0.01 Perl interface to Typesense search engine.
SmallRegistry-EPP MAT 0.001 SmallRegistry EPP Server
Spreadsheet-Compare TOMK 0.10 Module for comparing spreadsheet-like datasets
Sub-WrapInType-Attribute KFLY 0.01 attribute for Sub::WrapInType
Syntax-Keyword-Defer PEVANS 0.04 add defer block syntax to perl
Sys-Binmode FELIPE 0.01 Fix Perl’s system call character encoding.
Task-Lyrics-Fetcher BIGPRESH 0.01 install all known-to-work Lyrics::Fetcher fetchers
Test2-Tools-Process PLICEASE 0.05 Unit tests for code that calls exit, exec, system or qx()
Text-Sparkline PETDANCE v0.1.0 Creates text-based sparklines
Text-Table-Tiny-_ModuleFeatures PERLANCAR 0.001 Features declaration for Text::Table::Tiny
Text-TokenStream ARC 0.01 lexer to break text up into user-defined tokens
WWW-LinkRot BKB 0.01 check web page link rot
WordList-ID-AnimalName-PERLANCAR PERLANCAR 0.004 List of animals in Indonesian
p5-Paws-Credential-Webidentity PRAJITH 0.0.1 The AssumeRoleWebIdentity provider is used to obtain temporary credentials with an OIDC web identity token file.

Stats

Number of new CPAN distributions this period: 79

Number of authors releasing new CPAN distributions this period: 43

Authors by number of new CPAN distributions this period:

No Author Distributions
1 PERLANCAR 11
2 LEONT 5
3 ALIP 4
4 FELIPE 3
5 CDN 3
6 MAT 3
7 KFLY 2
8 PERLSRVDE 2
9 JMATES 2
10 BINGOS 2
11 MICHAEL 2
12 ARC 2
13 BPS 2
14 BKB 2
15 TDRUGEON 2
16 BIGPRESH 2
17 PRBRENAN 2
18 PREACTION 2
19 PRAJITH 2
20 DONGXU 1
21 TONKIN 1
22 TOBYINK 1
23 ZHMYLOVE 1
24 ETHER 1
25 PLICEASE 1
26 AJNN 1
27 ABELTJE 1
28 OVID 1
29 GUGOD 1
30 JBERGER 1
31 OETIKER 1
32 GUCCHISK 1
33 PETDANCE 1
34 WOLDRICH 1
35 LNATION 1
36 JCRISTY 1
37 PEVANS 1
38 DIONYS 1
39 TOMK 1
40 RAJ 1
41 CONTRA 1
42 MBARBON 1
43 RRA 1

Paul “LeoNerd” Evans is a CPAN author, blogger, and core Perl contributor. He introduced the experimental isa operator in Perl 5.32 and the try/catch syntax in an upcoming version.

Tell me a little about yourself and your background; whatever you feel comfortable sharing.

Lets see—I’m going to do this in reverse chronological order.

Currently I’m a self-employed contractor, splitting my time between Perl and other computery things, and electronics. Most of the jobs I’ve had before I did that were based on Perl, with the minor exception of a little Internet startup company called “Google”— maybe you’ve heard of them? Sadly they don’t do much Perl there.


How did you first get into programming Perl, and then later hacking on Perl’s core?

I dabbled in a little amount of it at university, during my final year. When I ought to have been studying type systems and other academic stuff I found it much more interesting to be hacking on bits of C and Perl instead, much to the dismay of my supervisors. My first post-study job happened to be in Perl and I’ve just stuck with it ever since.

The core hacking all came as a slow progression from writing Perl code, to writing modules, to the inevitable having to write bits of XS code for some of those modules. The deeper you dive into that area the more you find you have to understand how the internals of the interpreter work. The largest amount of time I spent on that was probably while making the Future::AsyncAwait module—that has to have quite a tight in-depth integration with the interpreter core, in order to successfully suspend and resume running functions, which is the basis of how the async/await syntax all works.


You first uploaded the Syntax::Keyword::Try module to CPAN in 2016, and at the time there were (and are still) a number of other modules with similar functionality. You compared their differences in the Syntax::Keyword::Try documentation, but were there any particular issues that inspired you to contribute another module?

Two reasons. The first reason I wrote it just for myself, was a learning exercise to see if I could understand and use this new-fangled “custom keyword” mechanism that was recently added to Perl. Once I had a proof-of-concept working, it didn’t take me long to work out how to write it “correctly”—in the sense that the body of the try and catch blocks were true blocks, and not closures-in-disguise like all of the pure Perl and even all of the custom syntax modules at the time were all doing. This meant it had a much ligher calling overhead, doesn’t interact with @_, plays nicer with return and next/last/redo, and all sorts of other advantages. From there it didn’t take me too long before I had something that I felt had real technical advantages than anything else that came before, so I tried to encourage its use. Freenode’s #perl channel in particular were very instrumental in helping that effort, adopting it in their recommendations to new users.


Recently you’ve spearheaded adding native try/catch syntax to native Perl, and released the Feature::Compat::Try module to offer the same syntax for earlier versions. Currently the former is enabled by a feature guard; do you anticipate a time when this will no longer be the case? Would that cause issues with code that uses other try/catch syntax modules?

I think it will be quite a while yet before we can see a Perl that would enable it by default, but I hope very soon it will make its way into the numbered version bundles. That is, I hope that simply

use v5.36;

would be enough to enable the try syntax, and if and when such a time comes that we decide to bump the major version to 7, that will continue to hold—merely saying

use v7;

would be sufficient to get that—along with all the other fancy fun things I hope to see by that time.


How do you envision Syntax::Keyword::Try’s role going forward? Will it be a testbed for future native Perl exception features?

It already is just that. There are more features in Syntax::Keyword::Try than the “minimal viable product” part that I ported to core in 5.33. Two main things come to mind—the typed exception dispatch, and the finally blocks. I’ve lately been looking at some defer syntax for a more general-purpose version of finally.

The question of how to handle typed dispatch is a more general one, which needs addressing in a wider language context—perhaps including considerations of signatures, match/case syntax, variable or object slot type assertions, and so on…


What’s next for you aside from exceptions in Perl? I’ve been reading about the work you’ve been doing with Curtis “Ovid” Poe on and your Object::Pad module—would you like to speak on that?

Yes, object systems seem to be of interest currently—so part of my thoughts are about Corinna and Object::Pad. But I’m also working on a number of other things. defer I already mentioned above. Additionally I have some thoughts in the direction of match/case, and a few other bits and pieces. These would mostly be done as CPAN modules at first to experiment with the ideas. I mentioned a lot of them in my recent “Perl in 2025” talk at FOSDEM.

The examples used here are from the weekly challenge problem statement and demonstrate the working solution.

Part 1

You are given positive numbers $N and $k. Write a script to find out the $Nth root of $k

Solution


use strict;
use warnings;
sub nth_root{
    my($n, $k) = @_;
    my $x_i = int(rand(10) + 1); 
    my $r;
    for my $i (0 .. 100){
        $x_i = (1 / $n) * (($n - 1) * $x_i + ($k / $x_i ** ($n - 1)));  
    } 
    return $x_i;  
}

MAIN:{
    my($N, $k);
    $N = 5;
    $k = 248832;
    print nth_root($N, $k) . "\n";
    $N = 5;
    $k = 34;
    print sprintf("%0.2f", nth_root($N, $k)) . "\n";
}

Sample Run


$ perl perl/ch-1.pl
12
2.02

Notes

One of my neatest things one can learn in calculus class, I would argue, is Newton’s method for computing square roots. One can read more on this elsewhere but this works by using a recurrence relationship, defined using directives, to compute the zero of a function. If the function is x^n - a, the zero we are computing is the nth root of a.

To start the process any x_0 may be chosen. Here we pick an integer from 1 to 10 at random.

You can compute this for as many iterations as you’d like, of course, but here even 100 iterations is much more than enough to properly converge.

Part 2

You are given a $name. Write a script to display the lyrics to the Shirley Ellis song The Name Game.

Solution


use strict;
use warnings;

sub name_game{
    my($name) = @_;
    my($b, $f, $m); 
    my $first_letter = lc(substr($name, 0, 1));
    my $irregular_v = $first_letter =~ tr/aeiou//d;
    my $irregular_bfm = $first_letter =~ tr/bfm//d;
    unless($irregular_v || $irregular_bfm){
        $b = "b" . lc(substr($name, 1)); 
        $f = "f" . lc(substr($name, 1)); 
        $m = "m" . lc(substr($name, 1)); 
    }   
    elsif($irregular_v){
        $b = "b" . lc($name);
        $f = "f" . lc($name); 
        $m = "m" . lc($name); 
    }
    elsif($irregular_bfm){
        $b = "b" . lc(substr($name, 1)); 
        $f = "f" . lc(substr($name, 1)); 
        $m = "m" . lc(substr($name, 1)); 
        $b = lc(substr($name, 1)) if lc(substr($name, 0, 1)) eq "b"; 
        $f = lc(substr($name, 1)) if lc(substr($name, 0, 1)) eq "f"; 
        $m = lc(substr($name, 1)) if lc(substr($name, 0, 1)) eq "m"; 
    }  
    format NAME_GAME = 
        @*, @*, bo-@* 
        $name, $name, $b 
        Banana-fana fo-@* 
        $f 
        Fee-fi-mo-@*
        $m
        @*!
        $name
.
    
    select(STDOUT);
    $~ = "NAME_GAME";
    write();  
}


MAIN:{
    my($name);
    $name = "Katie";  
    name_game($name); 
    print "\n"; 
    $name = "Adam";  
    name_game($name); 
    print "\n"; 
    $name = "Mary";  
    name_game($name); 
}

Sample Run


$ perl perl/ch-2.pl
        Katie, Katie, bo-batie
        Banana-fana fo-fatie
        Fee-fi-mo-matie
        Katie!

        Adam, Adam, bo-badam
        Banana-fana fo-fadam
        Fee-fi-mo-madam
        Adam!

        Mary, Mary, bo-bary
        Banana-fana fo-fary
        Fee-fi-mo-ary
        Mary!

Notes

My first comment is that I am a terrible singer and have never been able to reliably remember songs with rules like this at all, at any time of my life! Practically speaking that means I may have had to do more research on this part than one might expect. I did find an excellent reference (listed below) which detailed the rules for each case very clearly.

Perhaps the trickiest case in the one in which the name starts with a b, f, or m. For these you need to adjust the one specific rewrite rule which uses that letter. In the example above we see that Mary requires special handling and becomes Fee-fi-mo-ary.

To print the verses out I use a Perl Format. Formats are not the most commonly used feature of Perl these days but still have some nice uses such as here where we want to define a simple template for plain text output. Formats can even be used to write to files, but here we just print to the console.

One Format trick which I have not used before is the use of a variable width field. Much of the documentation for Formats has to do with fixed width fields which can be centered, padded left, padded right, involve multiple lines, and so forth. A common case which is not typically well explained is one we need here. Names are of different lengths and may be followed by a comma or an exclamation point. Padding right adds unwanted space before the “,” or “!”. Padding left adds unwanted space before the name. Centering does both! The trick is to use @* for the field specifier in the Format definition. This will allow the value to be substituted in without any padding.

References

Challenge 105

nth root by Newton’s method

Name Game Rules

Perl Formats

@davorg / Tuesday 20 April 2021 10:47 UTC