http://qs321.pair.com?node_id=705876

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

Lets say I have a list of subroutine names in an array, and I want to loop throught that array and call the subroutines named. (putting references as the values isnt an option because I dynamically generating the list from a db, but I am simplifying).
@listofsubs = qw(sub1 sub2 sub3); foreach $sub (@listofsubs) { #something to call $sub; } sub sub1 { #does sub1 stuff } sub sub2 { #does sub2 stuff } sub sub3 { #does sub3 stuff }
The list of subs will be known, I just dont want to write a bunch of if statements to process each one. I was thinking about creating a hash that points to each of the sub routines, but I was thinking there was something easier. Thanks, B

Replies are listed 'Best First'.
Re: calling subroutines from variables
by Tanktalus (Canon) on Aug 21, 2008 at 17:16 UTC

    You could use sub references instead:

    @listofsubs = (\&sub1, \&sub2, \&sub3); foreach $sub (@listofsubs) { $sub->(); }
    Or you might be doing this on objects:
    foreach $sub (@listofsubs) { $self->$sub() }
    Or you could just try to find them... if they're in the current package, this might work (untested):
    foreach $sub (@listofsubs) { if (not ref $sub) { $sub = __PACKAGE__->can($sub); } $sub->(); }
    (by checking if it's a ref, you could comingle names and code-refs: @listofsubs = ('sub1', \&sub2, sub { do_stuff() }); - the object example can handle this, too, only better.)

Re: calling subroutines from variables
by Fletch (Bishop) on Aug 21, 2008 at 17:15 UTC

    No, creating a hash of name => code ref is the normal technique. Do a search for "dispatch table".

    The cake is a lie.
    The cake is a lie.
    The cake is a lie.

      This doesn't look like a dispatch table to me. If you're reading in a bunch of sub names from an external source, and calling them, order may matter. Hashes don't preserve that. "But I'm just suggesting using the table to convert from string to code ref! You can use the order of strings to control the order!" True, but why produce another hash when the symbol table (another hash) already exists, with ways to query it?

      $ perl -le 'sub foo { }; print \&foo;print __PACKAGE__->can("foo")' CODE(0xf81930) CODE(0xf81930)
      No non-strict code. And when I add new functions, no need to remember to update the symbol table - perl does that automatically.

      Don't get me wrong. I like dispatch tables. I use them. A lot. This just doesn't seem like a use for them to me, mostly because it seems like the OP is trying to loop through them.

        I'm not sure I want an external source telling me which subs in my package to run without some control over which ones it's allowed to run. A hash of the allowed subrefs with a default error sub makes sense in that context.
        use strict; use warnings; sub one { print "one\n"; } sub two { print "two\n"; } sub tres { # some private sub they don't need to access } my %dispatch = ( 'one' => \&one, 'uno' => \&one, 'two' => \&two, 'dos' => \&two, 'default' => sub { print "sorry, $_[0] not found.\n"; }, ); my @do_these = qw( one two uno dos tres ); foreach my $do_this ( @do_these ) { if ( exists $dispatch{ $do_this } ) { $dispatch{ $do_this }->(); } else { $dispatch{ 'default' }->( $do_this ); } }
      Thanks, I will try the building the hash. I was looking at that and wanted to see if there were other options. For this particular problem that is the way to go.
      Thanks, B

        Of course, if you have a lot of subroutines, you may still want symbolic refs to help construct the hash:

        my %dispatch = map { ; no strict 'refs' ; ($_, \&$_) } qw(sub1 sub2 +sub3 ...) ;
Re: calling subroutines from variables
by GrandFather (Saint) on Aug 21, 2008 at 20:47 UTC

    Update: Doh! Didn't read through the thread far enough to see Tanktalus had already given a better version of the same trick (twice).

    Really light weight OO stuff can ease the path:

    use strict; use warnings; my @listofsubs = qw(sub1 wibble sub2 sub3); my $obj = bless {}; for my $sub (@listofsubs) { if (my $callSub = $obj->can ($sub)) { $callSub->(); next; } print "Can't $sub\n"; } sub sub1 { print "does sub1 stuff\n"; } sub sub2 { print "does sub2 stuff\n"; } sub sub3 { print "does sub3 stuff\n"; }

    Prints:

    does sub1 stuff Can't wibble does sub2 stuff does sub3 stuff

    and all you need to do to add a new sub is add the sub.


    Perl reduces RSI - it saves typing
Re: calling subroutines from variables
by Bloodnok (Vicar) on Aug 21, 2008 at 17:11 UTC
    Hi ,

    What's wrong with generating your list of subs as is ... and then turning off strict qw/refs/; before calling the subs in the list by name ??

    A user level that continues to overstate my experience :-))
Re: calling subroutines from variables
by injunjoel (Priest) on Aug 21, 2008 at 18:44 UTC
    Just a thought, though probably not the most secure...
    #!/usr/bin/perl -w use strict; my @list_o_subs = ('one','two','three','one','two','one','three','two' +); { no strict 'refs'; for(@list_o_subs){ &{$_}; } } sub one { print "called sub 'one'\n"; } sub two { print "called sub 'two'\n"; } sub three { print "called sub 'three'\n"; }
    Outputs
    called sub 'one' called sub 'two' called sub 'three' called sub 'one' called sub 'two' called sub 'one' called sub 'three' called sub 'two'

    Update!
    I really should read more thoroughly before I post... This is basically what Bloodnok suggested.

    -InjunJoel
    "I do not feel obliged to believe that the same God who endowed us with sense, reason and intellect has intended us to forego their use." -Galileo