我正在开发一个程序,该程序在单独的文件中使用不同的子例程。
共有三个部分
带有子例程名称的文本文件
带有子例程的Perl程序
提取子程序名称并启动它的主程序
该子例程从文本文件获取数据。
我需要用户选择文本文件,然后程序提取子例程的名称。
文本文件包含
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";
答案 0 :(得分:4)
您的代码无法编译,因为在最后一次调用中,您键入了$nb_cycle
的名称。如果您发布实际运行的代码会很有帮助:-)
传统上,Perl模块名称以大写字母开头,因此您可能希望将软件包重命名为Cycle01
。
快速而肮脏的方法是使用字符串eval
。但是评估包含代码的任意字符串是危险的,因此我不会向您展示。最好的方法是使用调度表-基本上是一个哈希,其中的键是有效的子例程名称,而值是对子例程本身的引用。最好的添加位置是在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
行。)
答案 1 :(得分:1)
要建立在Dave Cross的答案上,我通常避免使用哈希表,部分原因是在perl中,无论如何一切都是哈希表。取而代之的是,我所有的入口点子都以特定的前缀开头,该前缀取决于我在做什么,但是在这里,我们仅将ep_
用作入口点。然后我做这样的事情:
my $subname = 'ep_' . $cycleToUse;
if (my $func = Cycle01->can($subname))
{
$func->($filename, $nb_cycle);
}
else
{
die "$cycleToUse is not a valid subroutine name";
}
can
中的UNIVERSAL
方法是从perl的哈希表中为我提取CODE引用的,而不是我自己维护(忘记更新它)。前缀使我可以在同一名称空间中拥有其他功能和方法,而这些功能和方法不能由用户代码直接调用,从而使我仍可以将代码重构为常用功能,等等。
如果您还想拥有其他名称空间,我建议将它们全部放在一个父名称空间中,并可能以相同的方式添加所有前缀,并且在理想情况下,禁止使用::
或{{ 1}}(单引号)中,以便将用户可能调用的内容的范围最小化为仅愿意测试的范围。
例如
'
相反,这样做绝对有好处,例如明确要公开的内容。此处的优点是不必维护单独的列表。我总是很恐怖。