# RabbitFarm

### 2021-04-11

#### Static Analysis and Self Describing Numbers (now with Threads!): The Weekly Challenge 107

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 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 \$count = 0;
my \$lower = 1;
my \$upper = RANGE_SIZE;
do{
my \$t = Thread->new(\&self_describing_number, (\$lower, \$upper));
\$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++;
}
}
} 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
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

posted at: 17:51 by: Adam Russell | path: /perl | permanent link to this entry