Re: Dynamically Calling a Subroutine
by Tanktalus (Canon) on Jul 13, 2011 at 00:00 UTC
|
#!/usr/bin/perl
use strict;
use warnings;
my $name = shift;
my $func = "update_$name";
main->$func(); # "main" should be the namespace the functions are in.
sub update_test { print "!test\n" }
sub update_hello { print "hello!\n" }
The downside is that the first parameter to the function is its namespace. In the above example, it's "main". Note the output:
$ perl x.pl test
!test
$ perl x.pl hello
hello!
$ perl x.pl heheh
Can't locate object method "update_heheh" via package "main" at x.pl l
+ine 10.
The (more) normal way to do this is:
#!/usr/bin/perl
use strict;
use warnings;
my $name = shift;
my %updates = (
test => \&update_test,
hello => \&update_hello,
);
$updates{$name}->();
sub update_test { print "!test\n" }
sub update_hello { print "hello!\n" }
Either way, you should check if the function exists before trying to run it, e.g., main->can($func) or exists $updates{$name}. | [reply] [d/l] [select] |
|
Wow. I have forgotten this already.
| [reply] |
Re: Dynamically Calling a Subroutine
by BrowserUk (Patriarch) on Jul 12, 2011 at 23:19 UTC
|
sub update_test{ print 'hi' };;
$n = 'test';;
*{'update_' . $n }->();;
hi
Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
"Science is about questioning the status quo. Questioning authority".
In the absence of evidence, opinion is indistinguishable from prejudice.
| [reply] [d/l] |
|
Thanks. It works! But I want to understand why it works.
Can you explain what is happening in this line?
*{'update_' . $n }->();
I thought glob is only used for directories. | [reply] [d/l] |
|
See Globject sigils.
Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
"Science is about questioning the status quo. Questioning authority".
In the absence of evidence, opinion is indistinguishable from prejudice.
| [reply] |
|
Remember that perl allows you to have the same name for different things: $foo, @foo, %foo, and foo() (the subroutine 'foo') are all stored in different slots of the symbol table entry for the key 'foo': $foo is in SCALAR, @foo in ARRAY, %foo in HASH, and foo() in the CODE slot. We reference all of these things at once via a "glob".
The string inside the curlies is the key we'll use to access the symbol table.
The *{...} (the glob reference syntax) is essentially saying "find the symbol table entry that contains references to all the things with this name".
The ->() following the dereference says "call what the CODE slot references in this entry." It the sub had arguments, you could put them in the parens.
| [reply] |
Re: Dynamically Calling a Subroutine
by 7stud (Deacon) on Jul 13, 2011 at 07:17 UTC
|
Thanks. It works! But I want to understand why it works.
Can you explain what is happening in this line?
*{'update_' . $n }->();
download
I thought glob is only used for directories.
The glob() function is used to search for files.
On the other hand, perl has many different types: scalars, arrays, hashes, etc. And a perl program can happily have variables named $x, @x, and %x, and it will run without error. A typeglob is one of the types in perl. The typeglob *x represents all variables named 'x' in your program, e.g. $x, @x, %x, sub x {}, etc. In order to understand typeglobs, you need to read perlreftut first, so that you know what references are.
The code fragment:
*{'update_' . $n }
resolves to the typeglob: *update_test. The arrow following the brackets:
*{'update_' . $n }->
...dereferences the reference on the left of the arrow. The type of dereference is determined by what's on the right of the arrow:
*{'update_' . $n }->()
In this case, the left side of the arrow, i.e. the typeglob, is treated as a reference to a subroutine, and the subroutine is called. Compare to this:
#####use strict;
use warnings;
use 5.010;
sub greet {
my $greeting = shift;
say $greeting;
}
%greet = (
a => 'hello',
b => 'goodbye',
);
my $code_ref = \&greet;
$code_ref->('hello');
my $hash_ref = \%greet;
say $hash_ref->{b};
*greet->('goodbye');
say *greet->{a};
--output:--
hello
goodbye
goodbye
hello
| [reply] [d/l] [select] |
Re: Dynamically Calling a Subroutine
by Somni (Friar) on Jul 13, 2011 at 17:49 UTC
|
Typically, I'll revert to a dispatch table for this sort of task:
my %dispatch = (
test => sub { print "test\n" },
hello => sub { print "hello\n" },
);
my $name = 'test';
$dispatch{$name}->();
$name = 'hello';
$dispatch{$name}->();
This has the benefit of confining your subroutines to a very small namespace, as opposed to the entire symbol table. You can validate your input (lookup the key before calling it), and thereby provide better diagnostics.
In the event I actually want the symbol table (I can't actually recall wanting this, outside of calling methods...) then I tend to prefer can(). It does have caveats: if you're dealing with a class, it will search up the inheritance tree. However, this is usually something I want.
my $name = 'test';
__PACKAGE__->can("update_$name")->();
$name = 'hello';
__PACKAGE__->can("update_$name")->();
sub update_test { print "test\n" }
sub update_hello { print "hello\n" }
You can similarly check for the function before simply calling it, by checking the return value of can(), which is a subref. | [reply] [d/l] [select] |
|
I can't find any documentation for the can() function. Link?
| [reply] |
|
| [reply] |
Re: Dynamically Calling a Subroutine
by Anonymous Monk on Jul 12, 2011 at 23:42 UTC
|
use strict;
It gave an error.
Can't use string ("update_test") as a symbol ref while "strict refs"
| [reply] [d/l] [select] |
|
my $result = do{
no strict 'refs';
*{ 'update_' . $n }->()
};;
hi
Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
"Science is about questioning the status quo. Questioning authority".
In the absence of evidence, opinion is indistinguishable from prejudice.
| [reply] [d/l] |
Re: Dynamically Calling a Subroutine
by jim_jr (Initiate) on Mar 13, 2015 at 19:06 UTC
|
I just wanted to thank the poser and respondents for this question. It was exactly the information I needed for a project I was working on. I could not find this specific issue on any other support site.
Jim in Cleveland | [reply] |