获取变量“@xml_files”不会在 ... 行保持共享
Getting Variable "@xml_files" will not stay shared at ... line
我有以下 Perl 代码:
sub merge_xml {
foreach my $repository ('repo1', 'repo2') {
my @xml_files;
sub match_xml {
my $filename = $File::Find::dir . '/' . $_;
if ($filename =~ m%/(main|test)\.xml$%) {
push(@xml_files, $filename);
}
}
find(\&match_xml, $repository);
print Dumper(\@xml_files);
}
}
我收到警告:
Variable "@xml_files" will not stay shared at ./script/src/repair.pl line 87.
如何解决这个问题?
PS 找到 File::Find
“嵌套”命名的子程序实际上不是——它们被编译为单独的子例程,因此将它们写成“嵌套”只会产生误导。
此外,这会产生一个问题,因为“内部”子例程应该关闭它使用的变量 @xml_files
,该变量在每次新调用时被重新定义,是词法的。但是 sub 是在编译时构建的,不是词法闭包,它只在第一次调用时保留对值的引用,因此它只在第一次工作。
我们确实收到了警告。随着 use diagnostics;
Variable "$x" will not stay shared at -e line 1 (#1)
(W closure) An inner (nested) named subroutine is referencing a
lexical variable defined in an outer named subroutine.
When the inner subroutine is called, it will see the value of
the outer subroutine's variable as it was before and during the first
call to the outer subroutine; in this case, after the first call to the
outer subroutine is complete, the inner and outer subroutines will no
longer share a common value for the variable. In other words, the
variable will no longer be shared.
This problem can usually be solved by making the inner subroutine
anonymous, using the sub {} syntax. When inner anonymous subs that
reference variables in outer subroutines are created, they
are automatically rebound to the current values of such variables.
所以拔出“内部”子 (match_xml
) 并从“外部”子 (merge_xml
) 正常使用它。通常,您会将对数组 (@xml_files
) 的引用传递给它;或者,因为在这种情况下不能传递给 File::Find
的 find
,所以可以在这样的范围内拥有数组,以便根据需要查看。
或者,由于 match_xml
的目的是 find's "wanted" function,可以为此目的使用匿名 sub,因此不需要单独的命名 sub
find( sub { ... }, @dirs );
或者将该coderef存储在一个变量中,如
所示
my $wanted_coderef = sub { ... };
find( $wanted_coderef, @dirs );
在 zdim 的帮助下,我想到了:
sub merge_xml {
foreach my $repository ('repo1', 'repo2') {
my @xml_files;
my match_xml = sub {
my $filename = $File::Find::dir . '/' . $_;
if ($filename =~ m%/(main|test)\.xml$%) {
push(@xml_files, $filename);
}
};
find($match_xml, $repository);
print Dumper(\@xml_files);
}
}
我可以建议另一种选择。通过使用工厂函数,您可以避免每次都手写 find
子例程。
工厂是生成另一个函数(在本例中为子例程)的函数。你给它一些参数,它会创建一个自定义的子例程,其中包含这些参数。我的例子使用了一个闭包,但你也可以用一个字符串 eval
来构建它,如果闭包由于某种原因成本很高的话。
sub match_factory {
my ($filespec, $array) = @_;
# Validate arguments
die qq{File spec must be a regex, not "$filespec"\n}
unless ref $filespec eq "Regexp";
die qq{Second argument must be an array reference, not "$array"\n}
unless ref $array eq "ARRAY";
# Generate an anonymous sub to perform the match check
# that creates a closure on $filespec and $array
my $matcher = sub {
# Automatically compare against $_
m/$filespec/ and
push(@$array, $File::Find::name);
};
return $matcher;
}
sub merge_xml {
my @repos = @_ # or qw/foo bar .../;
foreach my $repository (@repos) {
my @matched_files;
find(
match_factory( qr/\/(main|test)\.xml$/, \@matched_files ),
$repository
);
print Dumper(\@matched_files);
}
}
HTH
我有以下 Perl 代码:
sub merge_xml {
foreach my $repository ('repo1', 'repo2') {
my @xml_files;
sub match_xml {
my $filename = $File::Find::dir . '/' . $_;
if ($filename =~ m%/(main|test)\.xml$%) {
push(@xml_files, $filename);
}
}
find(\&match_xml, $repository);
print Dumper(\@xml_files);
}
}
我收到警告:
Variable "@xml_files" will not stay shared at ./script/src/repair.pl line 87.
如何解决这个问题?
PS 找到 File::Find
“嵌套”命名的子程序实际上不是——它们被编译为单独的子例程,因此将它们写成“嵌套”只会产生误导。
此外,这会产生一个问题,因为“内部”子例程应该关闭它使用的变量 @xml_files
,该变量在每次新调用时被重新定义,是词法的。但是 sub 是在编译时构建的,不是词法闭包,它只在第一次调用时保留对值的引用,因此它只在第一次工作。
我们确实收到了警告。随着 use diagnostics;
Variable "$x" will not stay shared at -e line 1 (#1)
(W closure) An inner (nested) named subroutine is referencing a lexical variable defined in an outer named subroutine.When the inner subroutine is called, it will see the value of the outer subroutine's variable as it was before and during the first call to the outer subroutine; in this case, after the first call to the outer subroutine is complete, the inner and outer subroutines will no longer share a common value for the variable. In other words, the variable will no longer be shared.
This problem can usually be solved by making the inner subroutine anonymous, using the sub {} syntax. When inner anonymous subs that reference variables in outer subroutines are created, they are automatically rebound to the current values of such variables.
所以拔出“内部”子 (match_xml
) 并从“外部”子 (merge_xml
) 正常使用它。通常,您会将对数组 (@xml_files
) 的引用传递给它;或者,因为在这种情况下不能传递给 File::Find
的 find
,所以可以在这样的范围内拥有数组,以便根据需要查看。
或者,由于 match_xml
的目的是 find's "wanted" function,可以为此目的使用匿名 sub,因此不需要单独的命名 sub
find( sub { ... }, @dirs );
或者将该coderef存储在一个变量中,如
my $wanted_coderef = sub { ... };
find( $wanted_coderef, @dirs );
在 zdim 的帮助下,我想到了:
sub merge_xml {
foreach my $repository ('repo1', 'repo2') {
my @xml_files;
my match_xml = sub {
my $filename = $File::Find::dir . '/' . $_;
if ($filename =~ m%/(main|test)\.xml$%) {
push(@xml_files, $filename);
}
};
find($match_xml, $repository);
print Dumper(\@xml_files);
}
}
我可以建议另一种选择。通过使用工厂函数,您可以避免每次都手写 find
子例程。
工厂是生成另一个函数(在本例中为子例程)的函数。你给它一些参数,它会创建一个自定义的子例程,其中包含这些参数。我的例子使用了一个闭包,但你也可以用一个字符串 eval
来构建它,如果闭包由于某种原因成本很高的话。
sub match_factory {
my ($filespec, $array) = @_;
# Validate arguments
die qq{File spec must be a regex, not "$filespec"\n}
unless ref $filespec eq "Regexp";
die qq{Second argument must be an array reference, not "$array"\n}
unless ref $array eq "ARRAY";
# Generate an anonymous sub to perform the match check
# that creates a closure on $filespec and $array
my $matcher = sub {
# Automatically compare against $_
m/$filespec/ and
push(@$array, $File::Find::name);
};
return $matcher;
}
sub merge_xml {
my @repos = @_ # or qw/foo bar .../;
foreach my $repository (@repos) {
my @matched_files;
find(
match_factory( qr/\/(main|test)\.xml$/, \@matched_files ),
$repository
);
print Dumper(\@matched_files);
}
}
HTH