rvosa has asked for the wisdom of the Perl Monks concerning the following question:

Oh Monks! Teach me The Right Incantation! I think this is a simple question but somehow I can't figure it out. Here's what I am trying to do:
my $var = "foo"; &$var(); # now sub foo is called; $var = "bar"; &$var(); # now sub bar is called; sub foo { print "foo answering"; } sub bar { print "bar answering"; }
... but all I'm getting is: Can't use string ("foo") as a subroutine ref while "strict refs" in use ... What should I be doing? It works if I don't use strict, so something like this is possible.

Replies are listed 'Best First'.
Re: calling a sub using a variable's value
by Zaxo (Archbishop) on Apr 23, 2004 at 04:37 UTC

    Your code will work if you switch off the stricture,

    { no strict 'refs'; # the code }
    but that is usually not the best practice. Use hard references, as NetWallah suggests.

    If you must select a sub with a string, wrap up the hard references in a hash,

    my %code = ( foo => \&foo, bar => \&bar, baz => sub { print 'baz here', $/ } ); my $pick = 'foo'; $code{$pick)();
    Props to you for using strict. It's a good habit and it paid off for you here.

    After Compline,
    Zaxo

Re: calling a sub using a variable's value
by thor (Priest) on Apr 23, 2004 at 04:29 UTC
    What you want is a dispatch table. Here's a quick example:
    use warnings; use strict; my %dispatch = ( foo => \&foo, bar => \&bar ); $dispatch{foo}->("here we go!"); my $key = "bar"; $dispatch{$key}->("another message"); sub foo { my $arg = shift; print "foo: $arg\n"; } sub bar { my $arg = shift; print "bar: $arg\n"; } __END__ foo: here we go! bar: another message

    thor

Re: calling a sub using a variable's value
by NetWallah (Canon) on Apr 23, 2004 at 04:25 UTC
    You need to make a Subroutine REFERENCE, then call it thus:
    use strict; my $var = \&foo; # Sub ref &$var(); #Call it - no errors!

    Offense, like beauty, is in the eye of the beholder, and a fantasy.
    By guaranteeing freedom of expression, the First Amendment also guarntees offense.
      Here's more of a general question which I would like your comments on. I asked the original question for the following reason: I am writing a script that parses #NEXUS files, which are text files that contain data and processing instructions for phylogenetic inference. The data is marked up using various tokens. For example, there might be one or more sections that define species names. These would be of the form:
      begin taxa; [...stuff goes here..] end;
      There might also be one or more sections that deal with DNA sequence data, which are like this:
      begin characters; [...stuff here...] end;
      There's a bunch of other possible blocks, but you get the idea.

      The reason why I asked the question is that I thought it would be clever to have it such that, while the script is doing its while(<>) thing, it would call the appropriate sub to process that section simply by virtue of stumbling across the begin (this would be the signal to start calling the sub); token. For instance, it finds begin taxa;, there would be a &$var() where $var now contains the value "taxa" so the sub taxa { [...] } starts doing its thing.

      I realize now that the script would fail if something that looks like a token that should reference a sub is found, but there is no sub for it, i.e. begin (something I haven't thought of);. I could then of course work around this by including a bunch of ifs, but the dispatch table looks more elegant so I'll go with that. Thank you those that suggested that (well, and everyone else for replying also - this place is wonderful).

      Now comes my question: can something else bad happen, along the lines of $$var = 'something'; when, inadvertently, $var takes on a value such as *, or ! but then in the case of subs? Let's say the #NEXUS file contains the string begin die;. I guess that would be problematic, right?
Re: calling a sub using a variable's value
by broquaint (Abbot) on Apr 23, 2004 at 19:19 UTC
    As others have said, a dispatch table is almost certainly the way to go. But, of course, there's more than one way to symbolically dereference a subroutine in compliance with strictures
    use strict; sub foo { print "this is foo(@_)\n" } sub bar { print "this is bar(@_)\n" } __PACKAGE__->can($_)->('invoked via can()') for qw/ foo bar /; ( \&$_ )->('invoked without a temp var') for qw/ foo bar /; __output__ this is foo(invoked via can()) this is bar(invoked via can()) this is foo(invoked without a temp var) this is bar(invoked without a temp var)
    In the first instance we're executing the subroutine returned by UNIVERSAL, and in the second instance we're creating a sub reference and then executing (the parens are necessary for syntactical disambiguation).
    HTH

    _________
    broquaint

      I really like the __PACKAGE__->can() construct. Obfu'ed, but clear at the same time. :-)

      ------
      We are the carpenters and bricklayers of the Information Age.

      Then there are Damian modules.... *sigh* ... that's not about being less-lazy -- that's about being on some really good drugs -- you know, there is no spoon. - flyingmoose