dispatch functions in perl, how to include our list - perl

I found that the fastest way to dispatch many functions in perl is to use function references.
The remaining problem is, that I have to include the function names in an our ($func1, $func2, ...) list both in the dispatcher and in the function module. I could not fin d any way to include them, like C include would do. Here my code:
Main module:
use strict;
our ($base);
$base = '/home/en/dtest/perl/forditas/utf8/forditas/test1';
require("$base/disph1.pl");
require("$base/fut1h1.pl");
for (my $j = 0; $j < 5; $j++){
dispatch($j);
}
Dispatcher module:
use strict;
our ($base);
require("$base/fut1h1.pl");
our ($sref1, $sref2, $sref3, $sref4, $sref5); # This is what I'd like to include
my %shash = (
'0' => $sref1,
'1' => $sref2,
'2' => $sref3,
'3' => $sref4,
'4' => $sref5,
);
sub dispatch($){
my ($ix) = #_;
my ($a, $b, $c);
$a = 1; $b = 2; $c = 3;
my $ref = $shash{$ix};
&$ref($a,$b, $c);
}
1;
Function module:
use strict;
our ($sref1, $sref2, $sref3, $sref4, $sref5); # This is what I'd like to include
$sref1 = sub($$$) {
my ($a,$b,$c) = #_;
print "sub1 $a,$b,$c\n";
};
$sref2 = sub($$$) { my ($a,$b,$c) = #_; print "sub2 $a, $b, $c\n"; };
$sref3 = sub { print "sub3\n"; };
$sref4 = sub { print "sub4\n"; };
$sref5 = sub { print "sub5\n"; };
1;
This is the result of a run:
$ perl enhufh1.pl
sub1 1,2,3
sub2 1, 2, 3
sub3
sub4
sub5
Thanks in advance for tips.

You really should be using Perl modules - *.pm files - and including them where they are needed with use. Making these modules subclasses of Exporter allows them to export variable and aubroutine names into the calling package.
Take a look at this set of three sources, which also add several improvements on your original code.
Note that you can use the #EXPORT array instead of #EXPORT_OK, in which case the corresponding use statement doesn't have to list the symbols to be imported. However it is better to have the symbols listed at the point of use, otherwise the code for the module has to be inspected to discover exactly what is being imported.
main.pl
use strict;
use warnings;
use lib '/home/en/dtest/perl/forditas/utf8/forditas/test1';
use Dispatcher qw/ dispatch /;
dispatch($_) for 0 .. 4;
/home/en/dtest/perl/forditas/utf8/forditas/test1/Dispatcher.pm
package Dispatcher;
use strict;
use warnings;
require Exporter;
our #ISA = qw/ Exporter /;
our #EXPORT_OK = qw/ dispatch /;
use Utils qw/ sub1 sub2 sub3 sub4 sub5 /;
my #dtable = ( \&sub1, \&sub2, \&sub3, \&sub4, \&sub5 );
sub dispatch {
my ($i) = #_;
my ($a, $b, $c) = (1, 2, 3);
$dtable[$i]($a, $b, $c);
}
1;
/home/en/dtest/perl/forditas/utf8/forditas/test1/Utils.pm
package Utils;
use strict;
use warnings;
require Exporter;
our #ISA = qw/ Exporter /;
our #EXPORT_OK = qw/ sub1 sub2 sub3 sub4 sub5 /;
sub sub1 {
my ($a, $b, $c) = #_;
print "sub1 $a,$b,$c\n";
}
sub sub2 {
my ($a, $b, $c) = #_;
print "sub2 $a, $b, $c\n";
}
sub sub3 {
print "sub3\n";
}
sub sub4 {
print "sub4\n";
}
sub sub5 {
print "sub5\n";
}
1;
output
sub1 1,2,3
sub2 1, 2, 3
sub3
sub4
sub5

First of all mapping integers to elements is a misuse of hash. You might as well use arrays.
Then second, you seem to want to isolate algorithm from implementation, joining them in a main script. While this is admirable, it's clear that the functions module knows something of what it is being used for. Thus while deriving a sort of knowledge graph, the simplest case is that your function module knows the diapatch module.
You can just create a helper function for this purpose:
use strict;
use warnings;
our #EXPORT_OK = qw<setup_dispatch dispatch>;
use parent 'Exporter';
my #dispatch_subs;
sub setup_dispatch { #dispatch_subs = #_; }
sub dispatch {
my ($a, $b, $c) = ( 1, 2, 3 );
return $dispatch_subs[shift()]->( $a, $b, $c );
}
Now your function module can call the setup funciton:
use strict;
use warnings;
use Dispatch ();
Dispatch::setup_dispatch(
# I echo the caution about using prototypes
sub ($$$) {
my ($a,$b,$c) = #_;
print "sub1 $a,$b,$c\n";
}
, sub ($$$) { my ($a,$b,$c) = #_; print "sub2 $a, $b, $c\n"; }
, sub { print "sub3\n"; }
, sub { print "sub4\n"; }
, sub { print "sub5\n"; }
);
And you would just use both of them in the main module like this:
use strict;
use warnings;
require 'plugin_functions.pl';
use Dispatch qw<dispatch>;
...
You really don't need "names" if you just want to use "indexed" generic names. Just put them in a list.

What you need is Exporter.
Within your module:
require Exporter;
#EXPORT = qw($sref1 $sref2 $sref3);
However, it might be worth considering a different design:
Script:
set_dispatch(0,sub{ .... });
Dispatcher Module:
my #dispatch; #If just indexing to numbers, use an array instead of a hash.
sub set_dispatch {
$dispatch[$_[0]] = $_[1];
}
Main module:
for (0..4) #equivalent to before, but more Perlish.
{
dispatch($_);
}
Using a function call to set up the dispatch functions is better than exporting a bunch of variables, to my mind.

Related

How can I combine Data::Dumper and Statistics::Diversity::Shannon into a whole loop?

I want to combine this two functions together to get Shannon Diversity Index.
How can do ?
The first function is using Data::Dumper to get the unique numbers.
#!perl
use warnings;
use strict;
use Data::Dumper;
$Data::Dumper::Sortkeys=1;
my #names = qw(A A A A B B B C D);
my %counts;
$counts{$_}++ for #names;
printf "\$VAR1 = { %s};\n",
join ' ',
map "$_ ",
sort { $b <=> $a }
values(%counts);
exit;
This is the output
$VAR1 = { 4 3 1 1 };
Then I can input it into the second function.
The second function is using Statistics::Diversity::Shannon to get Shannon Diversity Index.
#!perl
use warnings;
use strict;
use Statistics::Diversity::Shannon;
my #data = qw( 4 3 1 1 );
my $d = Statistics::Diversity::Shannon->new( data => \#data );
my $H = $d->index();
my $E = $d->evenness();
print "$d/$H/$E";
exit;
How can I combine this two functions into a whole loop by using the original data set (A A A A B B B C D) to get the Shannon Diversity Index.
Data::Dumper is a debugging tool, not a serializing too. Not a good one, at least.
But you aren't even using Data::Dumper. You're using something far worse.
Let's start by using something acceptable like JSON.
#!/usr/bin/perl
use strict;
use warnings;
use Cpanel::JSON::XS qw( encode_json );
{
my #names = qw( A A A A B B B C D );
my %counts; ++$counts{$_} for #names;
my #data = sort { $b <=> $a } values(%counts);
print encode_json(\#data);
}
(Note that the sort { $b <=> $a } doesn't appear required.)
And this is one way to read it back in:
#!/usr/bin/perl
use strict;
use warnings;
use Cpanel::JSON::XS qw( decode_json );
use Statistics::Diversity::Shannon qw( );
{
my $json = do { local $/; <> };
my $data = decode_json($json);
my $d = Statistics::Diversity::Shannon->new( data => $data );
my $H = $d->index();
my $E = $d->evenness();
print "$H/$E\n";
}
Above, I assumed you meant "work together" when you said "combine into whole loop".
On the other hand, maybe you meant "combine into a single file". If that's the case, then you can use the following:
#!/usr/bin/perl
use strict;
use warnings;
use Statistics::Diversity::Shannon qw( );
{
my #names = qw( A A A A B B B C D );
my %counts; ++$counts{$_} for #names;
my #data = values(%counts);
my $d = Statistics::Diversity::Shannon->new( data => \#data );
my $H = $d->index();
my $E = $d->evenness();
print "$H/$E\n";
}
Your first code snippet does not use Data::Dumper correctly. Data::Dumper mainly provides one function, Dumper, which outputs any data in a format that can be interpreted as Perl code.
# instead of printf "\$VAR1 = ...
print Dumper([values %counts]);
Since the output of Data::Dumper::Dumper is Perl code, you can read it by evaluating it as Perl code (with eval).
So if your first script writes output to a file called some.data, your second script can call
my $VAR1;
open my $fh, "<", "some.data";
eval do { local $/; <$fh> }; # read data from $fh and call eval on it
# now the data from the first script is in $VAR1
my $d = Statistics::Diversity::Shannon->new( data => $VAR1 );
...

How can I join two lists using map?

I have such code in Perl:
#!/usr/bin/perl -w
my #a = ('one', 'two', 'three');
my #b = (1, 2, 3);
I want to see in result this: #c = ('one1', 'two2', 'three3');
Is there way I can merge these lists into one?
Assuming that you can guarantee the two arrays will always be the same length.
my #c = map { "$a[$_]$b[$_]" } 0 .. $#a;
As an alternative, you can use pairwise from List::MoreUtils:
#!/usr/bin/env perl
use strict;
use warnings;
use List::MoreUtils qw( pairwise );
my #a = ( 'one', 'two', 'three' );
my #b = ( 1, 2, 3 );
my #c = do {
no warnings 'once';
pairwise { "$a$b" } #a, #b;
};
For completeness, and to make Tom happy, here is a pure perl implementation of pairwise that you can use:
use B ();
use List::Util 'min';
sub pairwise (&\#\#) {
my ($code, $xs, $ys) = #_;
my ($a, $b) = do {
my $caller = B::svref_2object($code)->STASH->NAME;
no strict 'refs';
map \*{$caller.'::'.$_} => qw(a b);
};
map {
local *$a = \$$xs[$_];
local *$b = \$$ys[$_];
$code->()
} 0 .. min $#$xs, $#$ys
}
Since that is a bit involved, it is probably easier to just use map as davorg shows.

Can I dynamically get a list of functions or function names from any Perl module?

I would like to dynamically get a list of either function names (as strings) or function references from any arbitrary Perl module available on my system. This would include modules that may or may not have, e.g., a global #EXPORT_OK array in its namespace. Is such a feat possible? How does one pull it off if so?
Edit: From reading perlmod, I see that %Some::Module:: serves as a symbol table for Some::Module. Is this the correct place to be looking? If so, how can I whittle the table down to just the function names in Some::Module?
You're on the right track. To wittle down the full symbol table to just the subs, something like this can be done (Hat tip "Mastering Perl", ch 8, for main package version of this):
use strict; # need to turn off refs when needed
package X;
sub x {1;};
sub y {1;};
our $y = 1;
our $z = 2;
package main;
foreach my $entry ( keys %X:: ) {
no strict 'refs';
if (defined &{"X::$entry"}) {
print "sub $entry is defined\n" ;
}
}
# OUTPUT
sub y is defined
sub x is defined
You may find this simple script handy:
#!/usr/bin/env perl
use strict;
use warnings;
use Data::Dumper;
# dump of object's symbol table:
foreach my $className (#ARGV)
{
print "symbols in $className:";
eval "require $className";
die "Can't load $className: $#" if $#;
no strict 'refs';
print Dumper(\%{"main::${className}::"});
}
But, if you're doing this in production code, I'd use Package::Stash instead:
my #subs_in_foo = Package::Stash->new('Foo')->list_all_symbols('CODE');
I'm using Perl 5.20. This works on my machine:
use strict;
package foo;
our $some_var;
sub func1 { return 'func1'}
sub func2 { return 'func2'}
package main;
sub callable {
my ($x) = #_;
return defined(&$x);
}
while (my ($k, $v) = each(%foo::)) {
if (callable($v)) {
print("$k\n");
}
}
# output:
# func1
# func2

Set a variable in another package

I'd like to set a variable with a chosen name in another package. How can I do this easily?
Something like:
$variable_name = 'x';
$package::$variable_name = '0';
# now $package::x should be == '0'
You can do that, but you would have to disable strictures like so:
package Test;
package main;
use strict;
my $var_name = 'test';
my $package = 'Test';
no strict 'refs';
${"${package}::$var_name"} = 1;
print $Test::test;
So I'd not recommend that. Better to use a hash.
use 5.010;
use strict;
use warnings;
{
no warnings 'once';
$A::B::C::D = 5; # a test subject
}
my $pkg = 'A::B::C';
my $var = 'D';
# tearing down the walls (no warranty for you):
say eval '$'.$pkg."::$var"; # 5
# tearing down the walls but at least feeling bad about it:
say ${eval '\$'.$pkg."::$var" or die $#}; # 5
# entering your house with a key (but still carrying a bomb):
say ${eval "package $pkg; *$var" or die $#}; # 5
# using `Symbol`:
use Symbol 'qualify_to_ref';
say $${ qualify_to_ref $pkg.'::'.$var }; # 5
# letting us know you plan mild shenanigans
# of all of the methods here, this one is best
{
no strict 'refs';
say ${$pkg.'::'.$var}; # 5
}
and if the following make sense to you, party on:
# with a recursive function:
sub lookup {
#_ == 2 or unshift #_, \%::;
my ($head, $tail) = $_[1] =~ /^([^:]+:*)(.*)$/;
length $tail
? lookup($_[0]{$head}, $tail)
: $_[0]{$head}
}
say ${ lookup $pkg.'::'.$var }; # 5
# as a reduction of the symbol table:
use List::Util 'reduce';
our ($a, $b);
say ${+ reduce {$$a{$b}} \%::, split /(?<=::)/ => $pkg.'::'.$var }; # 5
And of course you can assign to any of these methods instead of saying them.
Given that $variable_name was validated, you could do:
eval "\$package::$variable_name = '0'";

perl: iterate over a typeglob

Given a typeglob, how can I find which types are actually defined?
In my application, we user PERL as a simple configuration format.
I'd like to require() the user config file, then be able to see which variables are defined, as well as what types they are.
Code: (questionable quality advisory)
#!/usr/bin/env perl
use strict;
use warnings;
my %before = %main::;
require "/path/to/my.config";
my %after = %main::;
foreach my $key (sort keys %after) {
next if exists $before{$symbol};
local *myglob = $after{$symbol};
#the SCALAR glob is always defined, so we check the value instead
if ( defined ${ *myglob{SCALAR} } ) {
my $val = ${ *myglob{SCALAR} };
print "\$$symbol = '".$val."'\n" ;
}
if ( defined *myglob{ARRAY} ) {
my #val = #{ *myglob{ARRAY} };
print "\#$symbol = ( '". join("', '", #val) . "' )\n" ;
}
if ( defined *myglob{HASH} ) {
my %val = %{ *myglob{HASH} };
print "\%$symbol = ( ";
while( my ($key, $val) = each %val ) {
print "$key=>'$val', ";
}
print ")\n" ;
}
}
my.config:
#A = ( a, b, c );
%B = ( b=>'bee' );
$C = 'see';
output:
#A = ( 'a', 'b', 'c' )
%B = ( b=>'bee', )
$C = 'see'
$_<my.config = 'my.config'
In the fully general case, you can't do what you want thanks to the following excerpt from perlref:
*foo{THING} returns undef if that particular THING hasn't been used yet, except in the case of scalars. *foo{SCALAR} returns a reference to an anonymous scalar if $foo hasn't been used yet. This might change in a future release.
But if you're willing to accept the restriction that any scalar must have a defined value to be detected, then you might use code such as
#! /usr/bin/perl
use strict;
use warnings;
open my $fh, "<", \$_; # get DynaLoader out of the way
my %before = %main::;
require "my.config";
my %after = %main::;
foreach my $name (sort keys %after) {
unless (exists $before{$name}) {
no strict 'refs';
my $glob = $after{$name};
print "\$$name\n" if defined ${ *{$glob}{SCALAR} };
print "\#$name\n" if defined *{$glob}{ARRAY};
print "%$name\n" if defined *{$glob}{HASH};
print "&$name\n" if defined *{$glob}{CODE};
print "$name (format)\n" if defined *{$glob}{FORMAT};
print "$name (filehandle)\n" if defined *{$glob}{IO};
}
}
will get you there.
With my.config of
$JACKPOT = 3_756_788;
$YOU_CANT_SEE_ME = undef;
#OPTIONS = qw/ apple cherries bar orange lemon /;
%CREDITS = (1 => 1, 5 => 6, 10 => 15);
sub is_jackpot {
local $" = ""; # " fix Stack Overflow highlighting
"#_[0,1,2]" eq "barbarbar";
}
open FH, "<", \$JACKPOT;
format WinMessage =
You win!
.
the output is
%CREDITS
FH (filehandle)
$JACKPOT
#OPTIONS
WinMessage (format)
&is_jackpot
Printing the names takes a little work, but we can use the Data::Dumper module to take part of the burden. The front matter is similar:
#! /usr/bin/perl
use warnings;
use strict;
use Data::Dumper;
sub _dump {
my($ref) = #_;
local $Data::Dumper::Indent = 0;
local $Data::Dumper::Terse = 1;
scalar Dumper $ref;
}
open my $fh, "<", \$_; # get DynaLoader out of the way
my %before = %main::;
require "my.config";
my %after = %main::;
We need to dump the various slots slightly differently and in each case remove the trappings of references:
my %dump = (
SCALAR => sub {
my($ref,$name) = #_;
return unless defined $$ref;
"\$$name = " . substr _dump($ref), 1;
},
ARRAY => sub {
my($ref,$name) = #_;
return unless defined $ref;
for ("\#$name = " . _dump $ref) {
s/= \[/= (/;
s/\]$/)/;
return $_;
}
},
HASH => sub {
my($ref,$name) = #_;
return unless defined $ref;
for ("%$name = " . _dump $ref) {
s/= \{/= (/;
s/\}$/)/;
return $_;
}
},
);
Finally, we loop over the set-difference between %before and %after:
foreach my $name (sort keys %after) {
unless (exists $before{$name}) {
no strict 'refs';
my $glob = $after{$name};
foreach my $slot (keys %dump) {
my $var = $dump{$slot}(*{$glob}{$slot},$name);
print $var, "\n" if defined $var;
}
}
}
Using the my.config from your question, the output is
$ ./prog.pl
#A = ('a','b','c')
%B = ('b' => 'bee')
$C = 'see'
Working code using a CPAN module that gets some of the hair out of the way, Package::Stash. As noted in my comment to gbacon's answer, this is blind to the config file doing $someval = undef but that seems to be unavoidable, and at least the other cases are caught. It also limits itself to the SCALAR, ARRAY, HASH, CODE, and IO types -- getting GLOB and FORMAT is possible but it makes the code less pretty and also creates noise in the output :)
#!perl
use strict;
use warnings;
use Package::Stash;
sub all_vars_in {
my ($package) = #_;
my #ret;
my $stash = Package::Stash->new($package);
for my $sym ($stash->list_all_package_symbols) {
for my $sigil (qw($ # % &), '') {
my $fullsym = "$sigil$sym";
push #ret, $fullsym if $stash->has_package_symbol($fullsym);
}
}
#ret;
}
my %before;
$before{$_} ++ for all_vars_in('main');
require "my.config";
for my $var (all_vars_in('main')) {
print "$var\n" unless exists $before{$var};
}
Beginning in 5.010, you can distinguish whether a SCALAR exists using the B introspection module; see Detecting declared package variables in perl
Update: example copied from that answer:
# package main;
our $f;
sub f {}
sub g {}
use B;
use 5.010;
if ( ${ B::svref_2object(\*f)->SV } ) {
say "f: Thar be a scalar tharrr!";
}
if ( ${ B::svref_2object(\*g)->SV } ) {
say "g: Thar be a scalar tharrr!";
}
1;
UPDATE:
gbacon is right. *glob{SCALAR} is defined.
Here is the output I get using your code:
Name "main::glob" used only once:
possible typo at
test_glob_foo_thing.pl line 13.
'FOO1' (SCALAR)
'FOO1' (GLOB)
'FOO2' (SCALAR)
'FOO2' (GLOB)
'_<my.config' (SCALAR)
'_<my.config' (GLOB)
This is despite FOO2 being defined as a hash, but not as a scalar.
ORIGINAL ANSWER:
If I understand you correctly, you simply need to use the defined built-in.
#!/usr/bin/env perl
use strict;
use warnings;
my %before = %main::;
require "/path/to/my.config";
my %after = %main::;
foreach my $key (sort keys %after) {
if (not exists $before{$key}) {
if(defined($after{$key}){
my $val = $after{$key};
my $what = ref($val);
print "'$key' ($what)\n";
}
}
}
I hate to ask, but instead of messing around with typeglobs, why not switch to a real configuration format? e.g. check out Config::Simple and YAML.
I wouldn't recommend messing around with typeglobs and symbol tables in normal cases (some CPAN modules do that, but only at the bottom levels of large systems - e.g. Moose in the lowest levels of Class::MOP). Perl gives you a lot of rope to work with, but that rope is also quite happy to self-noosify and self-tie-around-your-neck if you're not careful :)
See also: How do you manage configuration files in Perl?
no strict 'refs';
my $func_name = 'myfunc';
*{$func_name}{CODE}()
use strict 'refs';
If you don't mind parsing Data::Dump output, you could use it to tease out the differences.
use strict;
use warnings;
use Data::Dump qw{ dump };
my %before = %main::;
require "my.config";
my %after = %main::;
foreach my $key ( sort keys %after ) {
if ( not exists $before{$key} ) {
my $glob = $after{$key};
print "'$key' " . dump( $glob) . "\n";
}
}
Using this code with the following config file:
$FOO1 = 3;
$FOO2 = 'my_scalar';
%FOO2 = ( a=>'b', c=>'d' );
#FOO3 = ( 1 .. 5);
$FOO4 = [ 1 .. 5 ];
I believe that this output provides enough information to be able to figure out which parts of each type glob are defined:
'FOO1' do {
my $a = *main::FOO1;
$a = \3;
$a;
}
'FOO2' do {
my $a = *main::FOO2;
$a = \"my_scalar";
$a = { a => "b", c => "d" };
$a;
}
'FOO3' do {
my $a = *main::FOO3;
$a = [1 .. 5];
$a;
}
'FOO4' do {
my $a = *main::FOO4;
$a = \[1 .. 5];
$a;
}
'_<my.config' do {
my $a = *main::_<my.config;
$a = \"my.config";
$a;
}