让我们考虑下面的通缉代码。我有process
的递归调用,每次递归我都使用本地%context
。通过这种方式,当我从通话中返回时,我可以恢复上下文。
sub process {
my %context; # Local context
process() if rerun();
job1();
job2();
sub job1() {print $context{foo}}
sub job2() {print $context{bar}}
}
不幸的是perl没有像我预期的那样管理嵌套子程序。通过将子例程从进程子例程移到外部,我会遇到问题,因为我再也无法访问%context
了。所以我需要将其设置为全局并使用堆栈如下:
my %context; # Local context
my @context_stack;
sub process {
push @context_stack, %context;
%context = undef;
process() if rerun();
job1();
job2();
%context = pop @context_stack;
}
sub job1() {print $context{foo}}
sub job2() {print $context{bar}}
第三种解决方案是将上下文传递给所有子程序,这些子程序对于非常小的子程序来说可能很烦人。 %context
也成为我所有计划的全球。所以我放弃了这个变量的隐私。
my %context; # Local context
my @context_stack;
sub process {
push @context_stack, %context;
%context = undef;
process() if rerun(\%context);
job1(\%context);
job2(\%context);
%context = pop @context_stack;
}
sub job1() {$context = shift; print $context->{foo}}
sub job2() {$context = shift; print $context->{bar}}
最好的方法是什么?
修改
为了更好地理解我的具体情况,我提供了另一个例子:
process(@ARGV);
exit 0;
sub process {
my $infile = shift;
my $outfile = shift;
open my $fp_in, '<', $infile;
open my $fp_out, '>', $outfile;
LINE: while(<$fp_in>) {
remove_c_comment();
say STDERR "File is $infile";
process($1, "$1.processed") if /#include "(.*?)";
warning("Huh raisin, no!") if /\braisin/;
say STDERR "Fill is still $infile";
print $fp_out $_;
}
sub remove_c_comment { s|//.*$|| }
sub warning { say "[Warning] $infile:$. ".shift() }
}
答案 0 :(得分:2)
你正在寻找的东西 - 但你可能不知道 - 被称为closure
。 (另见:perlref
)
{
my %context;
sub job1 { print $context{foo} };
sub job2 { print $context{bar} };
sub init_context{ $context{foo} = 1 };
}
Context在此块中保持私有,但可供所有子例程访问。
作为替代方案 - 您可以从子例程返回代码引用 - 如下所示:
use strict;
use warnings;
sub make_sub_with_context {
my %context;
$context{"bar"} = 1;
return sub { print $context{"bar"}++ };
}
my $job1_with_context = make_sub_with_context();
my $another_job_with_context = make_sub_with_context();
$job1_with_context->();
$job1_with_context->();
$another_job_with_context->();
$another_job_with_context->();
$another_job_with_context->();
这可能是一个更好的例子。
编辑:
继续更新的示例后,您的问题规范似乎是迭代一组文件,并(递归地)遍历引用的文件。
类似于find
但遵循include
指令。我会指出,通过这样做,你正在做的事情可能会最终导致循环,这是不理想的。
我可以建议采用不同的方法吗?不要递言:
use strict;
use warnings;
my @files_to_process = @ARGV;
my %done;
while ( my $infile = pop @files_to_process ) {
next if $done{$infile}++;
open my $fp_in, '<', $infile or die $!;
open my $fp_out, '>', $infile . ".processed" or die $!;
while ( my $line = <$fp_in> ) {
$line =~ s|\/\/.*$||;
if ( my ($include) = ( $line =~ m/#include "(.*?)"/ ) ) {
push @files_to_process, $include;
}
print {$fp_out} $line;
}
close($fp_out);
close($fp_in);
}
有了更多的想法,以及这个任务需要按照声明顺序处理内容的扩展 - 我会提供 - 也许采取OO方法会有所帮助。类似的东西:
use strict;
use warnings;
package parser;
sub new {
my ($class) = @_;
my $self = {};
bless $self, $class;
return $self;
}
sub process {
my ( $self, $infile, $outfile ) = @_;
open my $fp_in, '<', $infile;
open my $fp_out, '>', $outfile;
LINE: while ( my $line = <$fp_in> ) {
$line =~ s|\/\/.*$||;
say STDERR "File is $infile";
if ( my ($includefile) = ( $line =~ m/#include "(.*?)"/ ) ) {
my $processor = parser->new();
$processor -> process( $includefile, "$includefile.processed" );
}
$self->warning("Huh raisin, no!") if /\braisin/;
say STDERR "Fill is still $infile";
print $fp_out $line;
}
}
package main;
my $processor = parser->new()->process(@ARGV);