Re: Canon concerning coderef calls?
by tmoertel (Chaplain) on Nov 15, 2004 at 19:43 UTC
|
Try replacing your loop with this:
$::{$_}->($_) foreach @list;
Seems to do the trick:
Sub one reporting for duty.
Sub two reporting for duty.
Sub three reporting for duty.
Cheers, Tom
| [reply] [Watch: Dir/Any] [d/l] [select] |
|
foreach my $MySubName (@list) {
## &{\&$MySubName}($MySubName); # This works!
## $::{$MySubName}($MySubName); # This (tmoertel) is better.
no strict qw(refs);
&{*$MySubName}($MySubName); # Use GLOB - Also cool!
#Chromatic's OO way --
#can(METHOD)
# can checks to see if its object has a method called METHOD,
# if it does then a reference to the sub is returned, i
# if it does not then undef is returned.
# main->can($MySubName)->($MySubName);
}
Update:Added Chromatic's OO-Style + Doc
Earth first! (We'll rob the other planets later)
| [reply] [Watch: Dir/Any] [d/l] |
|
Chromatic's OO way
I wouldn't really call that an OO way. Sure, it uses can as a class method on main, but that's where the OOishness ends. It returns a plain coderef and executes it with a different dereferencing syntax than you used. I believe it is also the only alternative that does not use a symbolic reference.
To reinforce the point about being a plain coderef, this works:
foreach (@list) {
my $coderef = main->can($_);
&$coderef($_);
}
And this does too, if you really want to get rid of all the pointy arrows:
foreach (@list) {
my $coderef = UNIVERSAL::can('main', $_);
&$coderef($_);
}
Update: and if you want to see what I would consider the "OO way":
foreach (@list) {
main->$_($_);
}
But, frankly, I think that's very ugly. It uses a symbolic reference too. I would use can as chromatic originally did. | [reply] [Watch: Dir/Any] [d/l] [select] |
|
|
|
This works beautifully, but this novice is at a loss to understand why.
Is not $:: an indicator that the sub is in the main package? So that $::{$_} is the subroutine $_ in the main package? And then that is called with the arguments $_?
Why does this allow us to do the referencing/dereferencing in one step, is this novice missing something here?
| [reply] [Watch: Dir/Any] [d/l] [select] |
|
Yes - you have understood the sequence correctly.
The relevant document is the "Symbol Table" section of perlmod.
The symbol table for a package happens to be stored in the hash of that name with two colons appended. The main symbol table's name is thus %main::, or %:: for short. ...
Hence "$::" is de-referencing the "%main::" hash, using "$_" as the key. The following parens prvide the "sub" context, and allow the sub to be passed the $_ param.
Earth first! (We'll rob the other planets later)
| [reply] [Watch: Dir/Any] |
|
Re: Canon concerning coderef calls?
by chromatic (Archbishop) on Nov 15, 2004 at 19:54 UTC
|
main->can( $_ )->( $_ ) for (qw( one two three )); | [reply] [Watch: Dir/Any] [d/l] |
Re: Canon concerning coderef calls?
by pg (Canon) on Nov 15, 2004 at 19:23 UTC
|
Store the code ref itself, not the names in your list.
use warnings;
use strict;
my @list = (\&one, \&two, \&three);
for (0 .. $#list) {
&{$list[$_]}($_);
}
sub one {
my $number = shift;
print "Sub $number reporting for duty.\n"
}
sub two {
my $number = shift;
print "Sub $number reporting for duty.\n"
}
sub three {
my $number = shift;
print "Sub $number reporting for duty.\n"
}
| [reply] [Watch: Dir/Any] [d/l] |
|
Alas, in the specific project I am working on (not the example code) the list is not for me to create, just to use.
Still, thank you for your wisdom.
jobi
| [reply] [Watch: Dir/Any] |
|
my @list = (\&one, \&two, \&three);
my @list = \(&one, &two, &three);
:-)
ihb
See perltoc if you don't know which perldoc to read!
Read argumentation in its context!
| [reply] [Watch: Dir/Any] [d/l] |
Re: Canon concerning coderef calls?
by calin (Deacon) on Nov 16, 2004 at 02:06 UTC
|
Do yourself a favour and implement a dispatch table. This additional
level of indirection may seem a maintenance burden, but it will sure
pay off in the long run. You will know exactly when and why it's failing,
and you will also minimize the risk of malicious code injection.
Here's a sketch:
#!/usr/bin/perl
use strict;
use warnings;
use Carp;
{
my %actions =
(
one => \&one,
two => \&two,
three => \&three
);
sub dispatch {
# detect early all invalid arguments
my @invalid = grep {!exists($actions{$_})} @_;
if (@invalid) {
local $" = ', ';
croak("invalid action(s): (@invalid)");
# or the death spell of your choice
}
# else {
for (@_) {
# call $actions{$_}->(...), for example:
$actions{$_}->("<<$_>>");
}
# }
}
}
# stub subs generator for exemplification only
BEGIN {
eval 'sub ' . $_ . q{ {
local $" = ', ';
print "sub [@{[(caller 0)[3]]}] with args: (@_)\n";
}} for qw/one two three/;
}
dispatch(qw/one two three/);
dispatch(qw/three two one kaboom silence/);
Don't just cut and paste my code. I don't have the slightest idea
about what you're trying to do with your program. The code above is
not substitute for your own judgement.
P.S.
If you decide to use symbol tables instead of plain hashes for lookup, as other
people have suggested in this thread, try to use a separate namespace
for this purpose and not main::. "Whitelisting" is
important from a security perspective.
| [reply] [Watch: Dir/Any] [d/l] [select] |
|
Thank you for your sage advice, this novice did have in mind to implement some form of dispatch table in the actual project code, but your sketch makes a few points this novice most certainly wouldn't have thought about on his own.
jobi
| [reply] [Watch: Dir/Any] |
Re: Canon concerning coderef calls?
by hv (Prior) on Nov 15, 2004 at 23:34 UTC
|
You want to call a subroutine by the name in a variable, which is disallowed by strict 'refs'. One of the values of strictness is that if strictness is turned off in one part of some code that is otherwise compliant, it is a useful warning to the reader that dangerous stuff is happening here - rather than hide it, highlight it:
for my $subname (@list) {
# we have to turn off strict 'refs' here because ...
# but it is safe to do so because ...
no strict 'refs';
$subname->($subname);
}
Hugo | [reply] [Watch: Dir/Any] [d/l] |
Re: Canon concerning coderef calls?
by NetWallah (Canon) on Nov 15, 2004 at 19:32 UTC
|
Clunky, but this works ...
foreach my $MySubName (@list) {
&{\&$MySubName}($MySubName);
}
Earth first! (We'll rob the other planets later)
| [reply] [Watch: Dir/Any] [d/l] |