biznez
biznez

Reputation: 4021

How can I loop through a list of functions in Perl?

I have a list of functions in Perl. Example:

my @funcs = qw (a b c)

Now they all belong to this module Foo::Bar::Stix. I would like to call them iteratively in a loop:

foreach $func (@funcs) {
    Foo::Bar::Stix::$func->(%args)
}

where args is a hash of arguments. However I keep getting this error: "Bad name after :: ..." at the line which contains Foo::Bar::Stix::$func->(%args) How do I fix this error?

a b and c are not function objects but strings

Upvotes: 4

Views: 865

Answers (5)

Hynek -Pichi- Vychodil
Hynek -Pichi- Vychodil

Reputation: 26121

You can use can

my @funcs = qw (a b c)
foreach $func (@funcs) {
    Foo::Bar::Stix->can($func)->(%args)
}

Upvotes: 1

James Thompson
James Thompson

Reputation: 48162

Rather than storing the names of the functions in your array, store references to them in a hash so that you can refer to them by name. Here's a simple code example:

#!/usr/bin/perl

use strict;
use warnings;

my %func_refs = (
    'a' => \&Foo::Bar::Stix::a,
    'b' => \&Foo::Bar::Stix::b,
    'c' => \&Foo::Bar::Stix::c
);

foreach my $func_ref ( values %func_refs ) {
    print $func_ref->( "woohoo: " ), "\n";
}

{
  package Foo::Bar::Stix;

  sub a {
    my $arg = shift;
    return $arg . "a";
  }

  sub b {
    my $arg = shift;
    return $arg . "b";
  }

  sub c {
    my $arg = shift;
    return $arg . "c";
  }
}

If you're stuck with storing the names for some reason, try this:

my $package    = "Foo::Bar::Stix";
my @func_names = qw/ a b c /;
foreach my $func_name (@func_names) {
    my $str = &{ "$package\::$func_name" }( "woohoo: " );
    print $str, "\n";
}

However, this doesn't work under use strict, and because of this I prefer the first solution. Whatever you do, try to avoid using eval. It's unnecessary, and will likely only cause you problems.

Also, most people who work with Perl capitalize it as Perl rather than PERL. Here's a Stackoverflow question on the subject:

How should I capitalize Perl?

Upvotes: 9

Brad Gilbert
Brad Gilbert

Reputation: 34110

You could access it through the special %Foo::Bar::Stix:: variable. This gives full access directly to the symbol table. You'll also notice that it works under strict mode.

#! /usr/bin/env perl
use strict;
use warnings;

{
  package Foo::Bar::Stix;
  sub a{ print "sub a\n" }
  sub b{ print "sub b\n" }
  sub c{ print "sub c\n" }
}

my @funcs = qw' a b c ';
my %args;

for my $func (@funcs) {
  $Foo::Bar::Stix::{$func}->(%args); # <====
}

Another option:

my $symbol_table = $::{'Foo::'}{'Bar::'}{'Stix::'};

my %funcs = (
  # we only want the CODE references
  'a' => *{ $symbol_table->{'a'} }{'CODE'},
  'b' => *{ $symbol_table->{'b'} }{'CODE'},
  'c' => *{ $symbol_table->{'c'} }{'CODE'},
);

for my $func (@funcs) {
  $funcs{$func}->(%args); # <====
}

If you are going to be doing that for a large number of subroutines, this is how I would load up the %funcs variable.

my %funcs;
BEGIN{
  my $symbol_table = $::{'Foo::'}{'Bar::'}{'Stix::'};

  for my $name (qw' a b c '){
    $funcs{$name} = *{ $symbol_table->{$name} }{'CODE'};
  }
}

I wouldn't do this unless you need the subroutines to have both a fully qualified name, and access to it through a hash variable.

If you only need access to the subroutines through a hash variable this is a better way to set it up.

my %funcs = (
  'a' => sub{ print "sub a\n" },
  'b' => sub{ print "sub b\n" },
  'c' => sub{ print "sub c\n" },
);

Note: you could replace "my %funcs" with "our %funcs"

Upvotes: 0

ysth
ysth

Reputation: 98378

Bad answer: use a symbolic reference:

for $func (@funcs) {
    &{"Foo::Bar::Stix::$func"}(\%args);
}

Good answer: use a dispatch table:

my %call_func = (
    'a' => \&Foo::Bar::Stix::a,
    'b' => \&Foo::Bar::Stix::b,
    'c' => \&Foo::Bar::Stix::c,
);
...
for $func (@funcs) {
    $call_func{$func}->(\%args);
}

Upvotes: 2

Beano
Beano

Reputation: 7831

Slight change of syntax will give you what you want

Foo::Bar::Stix->$func(%args)

Though this will pass the package name as the first parameter.

Upvotes: 1

Related Questions