无法读取子内的公共标量

发布于 2024-12-10 22:01:07 字数 2093 浏览 1 评论 0原文

Perl代码如下:问题是我无法在子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,正如我所说,$key可以在子外部读取,但不能在内部读取..出现错误:使用未初始化的值$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可以被读取...... 我花了一天的时间,但仍然无法修复它......现在疯了 预先感谢!

the perl code is like followed : the problem is that I can not read $key inside 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');
        }
    }
}

}

as i state, the $key can be read outside the sub, but not inside..the error appeared:Use of uninitialized value $key

and then i tried a simple situation, just like

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');

}

in this code, the $a can be read....
i spent one day on this, but still can't fix it...crazy now
thank in advance!!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

飘逸的'云 2024-12-17 22:01:07

您在 for 循环之外声明 $key。然后,在 for 循环内,定义一个在 $key 上结束的子例程。

作为一般规则,在最小的适用范围内声明变量。例如:

for my $key (keys ...) {

或者

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

为什么要在 for 循环体中定义 subtweak_server?在我看来,你想要做的是为每次迭代定义一个新的匿名子。

首先,一个简短的示例来复制您所观察到的内容:

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->();
}

这样,我们在每次迭代时定义一个新的匿名函数,并且每个新的子函数都会关闭循环变量的每个值。

就您的代码而言,您应该将命名的 sub 替换为

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, @_) },
};

You are declaring $key outside of the for loop. Then, inside the for loop, you define a subroutine that closes on $key.

As a general rule, declare variables in the smallest applicable scope. For example:

for my $key (keys ...) {

or

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

Why are you defining sub tweak_server in the body of the for loop? It seems to me, what you want to do is to define a new anonymous sub for each iterationn.

First, a short example that replicates what you are observing:

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";
    }
}

Now, the fix:

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->();
}

This way, we define a new anonymous function at each iteration and each new sub closes over each value of the loop variable.

In terms of your code, you should then replace the named sub with

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,
};

Or, even better, as @mirod observes, pass $key to tweak_server:

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

And, in the body of the loop,

my $handlers = { 
    'ROOT/TAG' => sub { tweak_server($key, @_) },
};
故事灯 2024-12-17 22:01:07

简短版本:如果您在另一个子程序内部或循环内部有一个命名子程序声明(sub foo { ... }),那么您可能做错了什么,您可能需要一个匿名子程序。这里就是这种情况。


sub foo { ... }

基本上与如果 sub 引用其自身之外的任何词法 (my) 变量相同

BEGIN { *foo = sub { ... }; }

,则在执行 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

修复:

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);
      }
   }
}

Short version: If you have a named sub declariation (sub foo { ... }) inside of another sub or inside of a loop, you're probably doing something wrong, you probably want an anonymous sub. This is the case here.


sub foo { ... }

is basically the same as

BEGIN { *foo = sub { ... }; }

If a sub refers to any lexical (my) variables outside of itself, they will be captured when the sub is executed, which is at compile-time with named subs. A common mistake is to do

sub outer {
    my ($x) = @_;

    sub inner {
       ... $x ...
    }

   outer();
}

That doesn't work because my $x is called multiple times to create multiple variables, but inner only captures the one that existed at compile time. (Fortunately, it warns.)

At first glance, your code only has one variable named $key, so there should be no problem capturing it at compile time. But they looks are deceiving. Due to foreach's aliasing feature, the iterator variable is not the variable it was outside the loop at all.

$ 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

In your case, you had

$ 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

You want the sub to capture $x at run-time, and that's done by using an anonymous sub.

$ 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);
      }
   }
}

You could also use an anonymous wrapper that passes the variables as arguments to a named sub.

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);
      }
   }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文