如何在循环中调用动态子例程

时间:2017-03-23 07:41:34

标签: perl

任何人都可以帮助找到在循环中调用子程序的简单方法吗? 现在,我手动调用它们如下。我该如何以编程方式执行此操作?

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

2 个答案:

答案 0 :(得分:9)

strict 'refs'抱怨间接引用(“使用变量作为变量名”)是有原因的。事实上,有几个原因,其中大部分都与间接引用有关,降低了调试和维护代码的能力。

更好的方法是创建一个代码引用数组并迭代它,依次调用它们:

#!/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');
}

另一种技术,在你可能不希望每次调用每个子节点并且总是以相同的顺序非常有用的方法是使用散列作为调度表

#!/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"};
}

答案 1 :(得分:0)

您需要先将函数名称构建到变量中,然后关闭use strict 'refs'。之后,您可以使用&号&调用它。

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 );
}

此程序将输出

1
2
3

确保在尽可能小的范围内禁用strict 'refs'。如果您不关闭它,您将收到错误

  

不能使用字符串(" case1Validate")作为子程序ref,而严格参考"" strict refs"在使用中

请注意,这只是几种解决方案中的一种,在大多数情况下,它不应该是首选解决方案。最好创建一个dispatch table(或代码引用列表,like Dave explains in his excellent answer),因为这样您可以更好地控制程序内部发生的事情。

由于您现在正在迭代一系列函数,因此您可以在调用它们之前使用exists检查它们是否存在。

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

对于您在运行中创建密钥的调度表,也应该这样做。