Короткая версия: Если у вас есть именованное вложенное объявление (sub foo { ... }
) внутри другого элемента или внутри цикла, вы, вероятно, делаете что-то не так, возможно, вам нужен анонимный элемент. Это тот случай, здесь.
sub foo { ... }
в основном совпадает с
BEGIN { *foo = sub { ... }; }
Если sub ссылается на какие-либо лексические (my
) переменные вне себя, они будут захвачены при выполнении sub
, что происходит во время компиляции с именованными подпрограммами. Распространенная ошибка - сделать
sub outer {
my ($x) = @_;
sub inner {
... $x ...
}
outer();
}
Это не работает, потому что my $x
вызывается несколько раз для создания нескольких переменных, но inner
захватывает только ту, которая существовала во время компиляции. (К счастью, это предупреждает.)
На первый взгляд, в вашем коде есть только одна переменная с именем $key
, поэтому не должно возникнуть проблем с ее захватом во время компиляции. Но они выглядят обманчиво. Из-за возможности псевдонимов foreach переменная итератора не является той переменной, которая вообще была вне цикла.
$ perl -E'
my $x; say "x: ", 0+\$x; # Show address of variable.
my $y; say "y: ", 0+\$y;
my $z; say "z: ", 0+\$z;
for $x ($y, $z) { say "i: ", 0+\$x; }
'
x: 155771632
y: 155771584
z: 155771744
i: 155771584
i: 155771744
В вашем случае у вас было
$ perl -E'
my $x; say "x: ", 0+\$x;
my $y; say "y: ", 0+\$y;
my $z; say "z: ", 0+\$z;
for $x ($y, $z) {
say "i: ", 0+\$x;
sub f { say "f: ", 0+\$x; }
f();
}
'
x: 142992144
y: 142992096
z: 142992256
i: 142992096
f: 142992144
i: 142992256
f: 142992144
Вы хотите, чтобы саб захватывал $x
в во время выполнения , и это делается с помощью анонимного саб.
$ perl -E'
my $x; say "x: ", 0+\$x;
my $y; say "y: ", 0+\$y;
my $z; say "z: ", 0+\$z;
for $x ($y, $z) {
say "i: ", 0+\$x;
my $f = sub { say "f: ", 0+\$x; };
$f->();
}
'
x: 159675200
y: 159675152
z: 159675312
i: 159675152
f: 159675152
i: 159675312
f: 159675312
Fix:
for my $key (keys %hash) {
if (@{ $hash{$key} } > 1) {
for my $path (@{ $hash{$key} }) {
open(my $fh, "+<", $path) # +< ???
or die("Can't open \"$path\": $!\n");
my $tweak_server = sub {
my ($twig, $root) = @_;
...
};
my $twig = new XML::Twig->new(
TwigRoots => { TAG => 1 },
TwigHandlers => { 'ROOT/TAG' => $tweak_server },
twig_print_outside_roots => $fh, # No need for \*$fh
);
$twig->parsefile($path);
}
}
}
Вы также можете использовать анонимную оболочку, которая передает переменные в качестве аргументов именованной подпрограмме.
sub tweak_server {
my ($fh, $key, $twig, $root) = @_;
...
}
for my $key (keys %hash) {
if (@{ $hash{$key} } > 1) {
for my $path (@{ $hash{$key} }) {
open(my $fh, "+<", $path) # +< ???
or die("Can't open \"$path\": $!\n");
my $twig = new XML::Twig->new(
TwigRoots => { TAG => 1 },
TwigHandlers => {
'ROOT/TAG' => sub { tweak_server($fh, $key, @_) },
},
twig_print_outside_roots => $fh, # No need for \*$fh
);
$twig->parsefile($path);
}
}
}