How can I elegantly name a Perl routine whose name is stored in a variable?

I save the name of the routine that I want to call at runtime in a variable called $ action. Then I use this to call this sub at the right time:

&{\&{$action}}(); 

It works great. The only thing I don't like is that it is ugly, and every time I do this, I feel obligated to add a comment for the following developer:

 # call the sub by the name of $action 

Does anyone know a more beautiful way to do this?




UPDATE: The idea here was to avoid having to maintain a submit table every time I added a new sub called, since I am the only developer, I don’t worry about other programmers following or not following the “rules”. Donate a little security for my convenience. Instead, my dispatch module would check $ action to make sure that 1) it is the name of a particular routine and not malicious code to run with eval, and 2) that it will not run any sub marked with an underscore to be marked as internal subtitles in accordance with this naming convention.

Any thoughts on this approach? The whitelist list routines in the dispatch table are something that I will forget all the time, and my clients would prefer that I make a mistake on the side of “it works” than “it is unholy”. (very limited time for application development)




FINAL UPDATE: I still decided that I decided on the control desk. Although I would be curious if anyone who reads this question has ever tried to end this and how he did it, I should bow to collective wisdom here. Thanks everyone, many great answers.

+52
perl
Dec 16 '09 at 16:00
source share
12 answers

Instead of storing the names of the routines in a variable and calling them, the best way to do this is to use the hash of the routines references (otherwise known as sending a table .)

 my %actions = ( foo => \&foo, bar => \&bar, baz => sub { print 'baz!' } ... ); 

Then you can easily call on the right:

 $actions{$action}->(); 

You can also add some validation to make sure $action is a valid key in the hash, etc.

In general, you should avoid symbolic links (what are you doing now) as they cause all kinds of problems. In addition, when using real links, routines will work with strict .

+78
Dec 16 '09 at 16:10
source share

Just &$action() , but it's usually best to use coderefs from the start or use a dispatcher hash. For example:

 my $disp = {foo => \&some_sub, bar => \&some_other_sub }; $disp->{'foo'}->(); 
+21
Dec 16 '09 at 16:09
source share

BUT? You can just say

  $action->() 

Example:

  sub f { return 11 } $action = 'f'; print $action->(); $ perl subfromscalar.pl 11 

Designs like

  'f'->() # equivalent to &f() 

also work.

+16
Dec 16 '09 at 19:40
source share

I'm not sure I understand what you mean. (I think this is another of the recent group, “How can I use a variable as a variable name?”, But maybe not.)

In any case, you should be able to assign the entire routine to a variable (as a reference), and then call it directly:

 # create the $action variable - a reference to the subroutine my $action = \&preach_it; # later - perhaps much later - I call it $action->(); sub preach_it { print "Can I get an amen!\n" } 
+11
Dec 16 '09 at 16:08
source share

Most importantly: why do you want to use a variable as a function name. What happens if it is "eval"? Is there a list of features that can be used? Or can it be any function? If the list exists, how long does it take?

Typically, the best way to handle such cases is to use send tables:

 my %dispatch = ( 'addition' => \&some_addition_function, 'multiplication' => sub { $self->call_method( @_ ) }, ); 

And then just:

 $dispatch{ $your_variable }->( 'any', 'args' ); 
+10
Dec 16 '09 at 16:16
source share

I'm doing something like that. I split it into two lines to make it a little more recognizable, but it is not much prettier.

 my $sub = \&{$action}; $sub->(); 

I do not know a more correct or beautiful way to do this. For what it's worth, we have production code that does what you do, and it works without having to disable use strict .

+6
Dec 16 '09 at 16:09
source share
 __PACKAGE__->can($action)->(@args); 

For more information about can (): http://perldoc.perl.org/UNIVERSAL.html

+5
Mar 12 '16 at 14:22
source share

Each package in Perl is already a hash table. You can add items and refer to them with normal hash operations. In general, there is no need to duplicate functionality using an additional hash table.

 #! /usr/bin/perl -T use strict; use warnings; my $tag = 'HTML'; *::->{$tag} = sub { print '<html>', @_, '</html>', "\n" }; HTML("body1"); *::->{$tag}("body2"); 

The code prints:

 <html> body1 </html>
 <html> body2 </html>

If you need a separate namespace, you can define a dedicated package.

See perlmod for more information.

+3
Jan 05 '16 at 9:41
source share

Use

 &{\&{$action}}(); 

Or use eval to execute the function:

 eval("$action()"); 
+1
Oct 30 '13 at 13:46
source share

I did it as follows:

 @func = qw(cpu mem net disk); foreach my $item (@func){ $ret .= &$item(1); } 
+1
Mar 17 '14 at 18:09
source share

If it is in only one program, write a function that calls the subprogram using the variable name, and only need to document it / apologize once?

0
Dec 16 '09 at 16:03
source share

I used this: it works for me.

 (\$action)->(); 

Or you can use 'do', very similar to previous posts:

 $p = do { \&$conn;}; $p->(); 
0
Jul 23 '14 at 4:28
source share



All Articles