не могу прочитать общедоступный скаляр внутри подпрограммы - PullRequest
2 голосов
/ 20 октября 2011

код perl выглядит следующим образом: проблема в том, что я не могу прочитать $ key внутри sub tweak_server {} ....

my $key;
my %hash = ( flintstones => [ "C:/Users1/f1.xml", "C:/Users1/f2.xml" ], 
            jetsons => [ "C:/Users2/f1.xml" ], 
            simpsons =>    [ "C:/Users3/f1.xml", "C:/Users3/f1.xml", "C:/Users3/f1.xml" ], ); 

foreach $key (keys%hash){
if (scalar@{$hash{$key}}>1){
    foreach my $path (@{$hash{$key}}){
        my $filehandle;
        open($filehandle, "+<$path") or die "cannot open out file out_file:$!";
        my $roots = { TAG => 1 };
        my $handlers = { 'ROOT/TAG' => \&tweak_server,
                    };
        my $twig = new XML::Twig(TwigRoots => $roots,
                             TwigHandlers => $handlers,
                             twig_print_outside_roots => \*$filehandle);
        $twig->parsefile($path);
        say $key;#could read key
        sub tweak_server {
            my ($twig, $root) = @_;
            my $tag2=$root->first_child_text('TAG2'); 
            say $key;# could not read
            if ($tag2=~/$key/){
            #BLABLA
            }
            $twig->flush( $filehandle, pretty_print => 'indented');
        }
    }
}

}

как я утверждаю, ключ $ можно прочитать вне подпрограммы, но не внутри. Появилась ошибка: использование неинициализированного значения $ key

и затем я попробовал простую ситуацию, такую ​​же как

my $a="aaa";
open( $filehandle, "+<$path") or die "cannot open out file out_file:$!";
my $roots = { TAG => 1 };
my $handlers = { 'ROOT/TAG' => \&tweak_server,
            };
my $twig = new XML::Twig(TwigRoots => $roots,
                     TwigHandlers => $handlers,
                     twig_print_outside_roots => \*$filehandle
                     );
$twig->parsefile($path);
sub tweak_server {
    say $a;
    my ($twig, $root) = @_;
    my $tags=$root->first_child_text('TAG2');
    my $str="204B";
    if ($tag2=~m/$str/){
    foreach my $b(1...6){
                    say $a;             }
}
$twig->flush( $filehandle, pretty_print => 'indented');

}

в этом коде $ a можно прочитать .... я провел один день на этом, но все еще не могу это исправить ... сумасшедший сейчас заранее спасибо !!

Ответы [ 2 ]

4 голосов
/ 20 октября 2011

Короткая версия: Если у вас есть именованное вложенное объявление (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);
      }
   }
}
4 голосов
/ 20 октября 2011

Вы объявляете $key за пределами цикла for. Затем, внутри цикла for, вы определяете подпрограмму, которая закрывается на $key.

Как правило, объявляйте переменные в наименьшей применимой области видимости. Например:

for my $key (keys ...) {

или

open my $filehandle, '<', ...

Почему вы определяете sub tweak_server в теле цикла for? Мне кажется, что вы хотите сделать, это определить новую анонимную подпрограмму для каждой итерации.

Во-первых, короткий пример, который повторяет то, что вы наблюдаете:

use warnings; use strict;

my $key;
my %hash = qw(a b c d e f);

foreach $key (keys %hash) {
    somesub();
    sub somesub {
        print "$key\n";
    }
}

Теперь исправление:

use warnings; use strict;

my %hash = qw(a b c d e f);

foreach my $key (keys %hash) {
    my $somesub = sub { print "$key\n" };
    $somesub->();
}

Таким образом, мы определяем новую анонимную функцию на каждой итерации, и каждая новая подпрограмма закрывается для каждого значения переменной цикла.

С точки зрения вашего кода, вы должны заменить именованную подпрограмму на

my $tweak_server = sub {
    my ($twig, $root) = @_;
    my $tag2=$root->first_child_text('TAG2'); 
    say $key;# could not read
    if ($tag2=~/$key/){
        #BLABLA
    }
    $twig->flush( $filehandle, pretty_print => 'indented');
}

my $handlers = { 
    'ROOT/TAG' => $tweak_server,
};

Или, еще лучше, как заметил @mirod, передать $key в tweak_server:

sub tweak_server { 
    my( $key, $twig, $root)= @_;
    ...
}

И, в теле цикла,

my $handlers = { 
    'ROOT/TAG' => sub { tweak_server($key, @_) },
};
...