我对Perl知之甚少,甚至不知道我要求的是什么,但我正在编写一系列子程序,可用于处理不同传入平面文件的许多单独脚本。这个过程远非完美,但这是我必须处理的事情,我正在尝试建立一个小型的子库,这使我更容易管理它。每个脚本处理一个不同的传入平面文件,它具有自己的格式,排序,分组和输出要求。一个常见的方面是我们有一些小文本文件,其中包含用于命名输出文件的计数器,因此我们没有重复的文件名。
因为每个文件的数据处理都不同,我需要打开文件来获取我的计数器值,因为这是一个常见的操作,我想把它放在一个子中来检索计数器。但随后需要编写特定的代码来处理数据。并希望第二个子允许我在处理完数据后用计数器更新计数器。
如果调用第一个子调用,是否有办法使第二个子调用成为一个要求?理想情况下,如果它甚至可能是一个错误,会阻止脚本运行,就像语法错误一样。
编辑:这是一个有点[丑陋和简化]的伪代码,可以更好地了解当前的流程:
require "importLibrary.plx";
#open data source file
open DataIn, $filename;
# call getCounterInfo from importLibrary.plx to get
# the counter value from counter file
$counter = &getCounterInfo($counterFileName);
while (<DataIn>) {
# Process data based on unique formatting and requirements
# output to task files based on requirements and name files
# using the $counter increment $counter
}
#update counter file with new value of $counter
&updateCounterInfo($counter);
答案 0 :(得分:4)
我不太了解你正在尝试的东西,但你总是可以让你的潜艇可插拔:
我们有一个子process_file
。它需要一个子程序作为参数来进行主要处理:
our $counter;
sub process_file {
my ($subroutine, @args) = @_;
local $counter = get_counter();
my @return_value = $subroutine->(@args);
set_counter($counter);
return @return_value;
}
# Here are other sub definitions for the main processing
# They can see $counter and always magically have the right value.
# If they assign to it, the counter file will be updated afterwards.
假设我们有一个子process_type_A
,我们可以做
my @return_values = process_file(\&process_type_A, $arg1, $arg2, $arg3);
除了额外的调用堆栈帧和process_type_A($arg1, $arg2, $arg3)
设置外,其行为与$counter
类似。
如果您更喜欢传递名称而不是代码参数,我们也可以安排。
package MitchelWB::FileParsingLib;
our $counter;
our %file_type_processing_hash = (
"typeA" => \&process_type_A,
"typeB" => \&process_type_B,
"countLines" => sub { # anonymous sub
open my $fh, '<', "./dir/$counter.txt" or die "cant open $counter file";
my $lines = 0;
$lines++ while <$fh>;
return $lines;
},
);
sub process_file {
my ($filetype, @args) = @_;
local $counter = get_counter();
# fetch appropriate subroutine:
my $subroutine = $file_type_processing_hash{$filetype};
die "$filetype is not registered" if not defined $subroutine; # check for existence
die "$filetype is not assigned to a sub" if ref $subroutine ne 'CODE'; # check that we have a sub
# execute
my @return_value = $subroutine->(@args);
set_counter($counter);
return @return_value;
}
...;
my $num_of_lines = process_file('countLines');
为什么愚蠢的回调?为何要额外代码?为何调用约定?为什么派遣表?虽然它们都非常有趣和灵活,但有一个更优雅的解决方案。我刚刚忘记了一小部分信息,但现在它已全部落实到位。 Perl有“Attributes”,在其他语言中称为“Annotations”,它允许我们注释代码或变量。
定义新的Perl属性很简单。我们use Attribute::Handlers
并定义一个与您要使用的属性同名的子:
sub file_processor :ATTR(CODE) {
my (undef, $glob, $subroutine) = @_;
no strict 'refs';
${$glob} = sub {
local $counter = get_counter();
my @return_value = $subroutine->(@_);
set_counter($counter);
return @return_value;
}
我们使用属性:ATTR(CODE)
来表示这是适用于子例程的属性。我们只需要两个参数,我们想要注释的子程序的全名,以及一个subref的代码。
然后我们关闭严格性的一部分,用${$glob}
重新定义sub。这有点高级,但它基本上只是访问内部符号表。
我们使用上面给出的process_file
的简化版本替换带注释的子版本。我们可以直接传递所有参数(@_
)而无需进一步处理。
毕竟,我们在您之前使用的潜艇中添加了一小部分信息:
sub process_type_A :file_processor {
print "I can haz $counter\n";
}
......它只是在没有进一步修改的情况下进行更换。使用库时,更改是不可见的。我知道这种方法的限制,但在编写普通代码时你不太可能遇到它们。
答案 1 :(得分:0)
好吧,你可以设置一个全局标志并使用END block。
或许更整洁就像是@ amon的提议,或者甚至只是将你的文件处理放在标准的命名sub中并从你的计数器代码中调用它。
my ($fh, counter) = get_counter(...);
my $ok = process_file($fh, $counter);
update_counter($counter) if $ok;
您的process_file将从模块导出,如果您想保持简单,请使用perl的-M来加载带有process_file sub的模块。