Subbaiya Arasu
Subbaiya Arasu

Reputation: 23

How to call dynamic subroutine in a loop

Can any one help to find easy way to call subroutines in a loop? Now, I called them as below manually. How do I do that programmatically?

&case1Validate($fTxt);
&case2Validate($fTxt);
&case3Validate($fTxt);
&case4Validate($fTxt);
&case5Validate($fTxt);
&case6Validate($fTxt);
&case7Validate($fTxt);
&case8Validate($fTxt);
&case9Validate($fTxt);
&case10Validate($fTxt);

Upvotes: 2

Views: 466

Answers (2)

Dave Sherohman
Dave Sherohman

Reputation: 46187

strict 'refs' complains about indirect references ("using a variable as a variable name") for a reason. Several reasons, in fact, most of them having to do with indirect references reducing your ability to debug and maintain your code.

The better way to do this is to create an array of code references and iterate over that, calling each in turn:

#!/usr/bin/env perl    

use strict;
use warnings;
use 5.010;

sub case1Validate { return 'Case 1: ' . $_[0] }
sub case2Validate { return 'Case 2: ' . $_[0] }
sub case3Validate { return 'Case 3: ' . $_[0] }

my @validators = (\&case1Validate, \&case2Validate, \&case3Validate);

for my $sub (@validators) {
  say $sub->('foo');
}

Another technique, which is very useful in cases where you might not want to call every sub every time, and always in the same order, is to use a hash as a dispatch table:

#!/usr/bin/env perl    

use strict;
use warnings;
use 5.010;

sub case1Validate { return 'Case 1: ' . $_[0] }
sub case2Validate { return 'Case 2: ' . $_[0] }

my %validators = (
  case1 => \&case1Validate, 
  case2 => \&case2Validate, 
  # If the sub is small and you're not using it separately, you can even define
  # it in-line!
  case5 => sub { return 'Case 5: ' . $_[0] }
);

for my $i (1 .. 5) {
  # Because of "exists", will only attempt to print for cases 1, 2, and 5,
  # since cases 3 and 4 don't exist
  say $validators{"case$i"}->('foo') if exists $validators{"case$i"};
}

Upvotes: 9

simbabque
simbabque

Reputation: 54323

You need to build the function name into a variable first, and then turn off use strict 'refs'. After that, you can call it using the ampersand &.

use strict;
use warnings;

sub case1Validate { return $_[0] }
sub case2Validate { return $_[0] }
sub case3Validate { return $_[0] }

for my $i ( 1 .. 3 ) {
    no strict 'refs';
    my $sub_name = 'case' . $i . 'Validate';
    CORE::say &{$sub_name}( $i );
}

This program will output

1
2
3

Make sure to disable strict 'refs' in the smallest possible scope. If you do not turn it off, you will get the error

Can't use string ("case1Validate") as a subroutine ref while "strict refs" in use

Note that this is just one of several solutions, and in most cases it should not be the preferred one. It's better to create a dispatch table (or a list of code refs, like Dave explains in his excellent answer) because that way you have better control over what happens inside your program.

Since you're iterating a list of functions now you can use exists to check if they exist before calling them.

&{ $sub_name }( $i) if exists &$sub_name; # both &{} and & are fine

The same should be done with a dispatch table where you create the keys on the fly.

Upvotes: 0

Related Questions