我对 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);
最佳答案
我不太明白你在尝试什么,但你总是可以让你的潜艇可插入:
我们有一个子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');
编辑:最优雅的解决方案
或者:属性真的很简洁
为什么要进行愚蠢的回调?为什么要额外的代码?为什么要调用约定?为什么要分派(dispatch)表?虽然它们都非常有趣且灵活,但还有一个更优雅的解决方案。我刚刚忘记了一小部分信息,但现在一切都已就位了。 Perl 具有“属性”,在其他语言中称为“注释”,它允许我们对代码或变量进行注释。
定义新的 Perl 属性很容易。我们使用 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)
来表示这是一个适用于子例程的属性。我们只需要两个参数,我们想要注释的子例程的全名,以及子例程的代码引用。
然后我们关闭一部分严格性,以使用 ${$glob}
重新定义 sub。这有点高级,但它本质上只是访问内部符号表。
我们用上面给出的 process_file
的简化版本替换带注释的子文件。我们可以直接传递所有参数 (@_
),无需进一步处理。
毕竟,我们向您之前使用的潜艇添加了一小段信息:
sub process_type_A :file_processor {
print "I can haz $counter\n";
}
...它只是进行替换而无需进一步修改。使用库时这些更改是不可见的。我知道这种方法的限制,但在编写普通代码时不太可能遇到它们。
关于perl - Perl 中是否可以要求进行子例程调用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12358690/