调用定义为变量的子程序

Call a subroutine defined as a variable

我正在开发一个在不同文件中使用不同子例程的程序。

一共有三个部分

子例程从文本文件中获取数据。

我需要用户选择文本文件,然后程序提取子程序的名称。

文本文件包含

cycle.name=cycle01

这是主要程序:

# !/usr/bin/perl -w

use strict;
use warnings;

use cycle01;

my $nb_cycle = 10;

# The user chooses a text file

print STDERR "\nfilename: ";

chomp($filename = <STDIN>);

# Extract the name of the cycle 

open (my $fh, "<", "$filename.txt") or die "cannot open $filename";

while ( <$fh> ) {

    if ( /cycle\.name/ ) {

        (undef, $cycleToUse) = split /\s*=\s*/;
    }
}

# I then try to launch the subroutine by passing variables.
# This fails because the subroutine is defined as a variable.

$cycleToUse($filename, $nb_cycle);

这是另一个文件中的子例程

# !/usr/bin/perl

package cycle01;

use strict;
use warnings;

sub cycle01 {

    # Get the total number of arguments passed
    my ($filename, $nb_cycle) = @_;
    print "$filename, $nb_cycle";

您的代码无法编译,因为在最后的调用中,您输错了 $nb_cycle 的名称。如果您 post 实际运行的代码会很有帮助:-)

传统上,Perl 模块名称以大写字母开头,因此您可能希望将包重命名为 Cycle01

快速而肮脏的方法是使用 eval 的字符串版本。但是评估包含代码的任意字符串是危险的,所以我不会向您展示这一点。最好的方法是使用调度 table - 基本上是一个散列,其中键是有效的子例程名称,值是对子例程本身的引用。添加它的最佳位置是在 Cycle01.pm 文件中:

our %subs = (
  cycle01 => \&cycle01,
);

那么,你程序的结尾变成:

if (exists $Cycle01::subs{$cycleToUse}) {
  $Cycle01::subs{$cycleToUse}->($filename, $nb_cycle);
} else {
  die "$cycleToUse is not a valid subroutine name";
}

(请注意,您还需要 chomp()while 循环中阅读这些行。)

为了基于 Dave Cross 的回答,我通常避免使用散列 table,部分原因是,在 perl 中,无论如何,一切都是散列 table。相反,我所有的入口点子都以特定前缀开头,该前缀取决于我在做什么,但在这里我们只使用 ep_ 作为入口点。然后我做这样的事情:

my $subname = 'ep_' . $cycleToUse;
if (my $func = Cycle01->can($subname))
{
  $func->($filename, $nb_cycle);
}
else
{
  die "$cycleToUse is not a valid subroutine name";
}

UNIVERSAL 中的 can 方法从 perl 的散列 tables 中为我提取 CODE 参考,而不是我自己维护(并忘记更新它)。前缀允许我在同一个命名空间中拥有用户代码不能直接调用的其他函数和方法,允许我仍然将代码重构为通用函数等。

如果您还想拥有其他命名空间,我建议将它们都放在一个父命名空间中,并且可能所有的前缀都相同,理想情况下,不允许 ::'(单引号)在这些名字中,这样你就可以将用户可能调用的范围最小化到你愿意测试的范围。

例如,

die "Invalid namespace $cycleNameSpaceToUse"
  if $cycleNameSpaceToUse =~ /::|'/;
my $ns = 'UserCallable::' . $cycleNameSpaceToUse;
my $subname = 'ep_' . $cycleToUse;
if (my $func = $ns->can($subname))
# ... as before

换一种方式肯定有好处,例如明确说明要公开的内容。这里的优点是不必维护单独的列表。我做那件事总是很糟糕。