“变量$ foo将不会保持共享”Perl在调用子例程时出现警告/错误

时间:2010-10-29 00:51:39

标签: perl unix scope local-functions

Update3: 如果你喜欢这个帖子,请不要向我投票,而是通过下面的DVK赞助天才答案。

我有以下子程序:

 use warnings;
#Input
 my @pairs = (
    "fred bill",
    "hello bye",
    "hello fred",
    "foo bar",
    "fred foo");

#calling the subroutine
my @ccomp = connected_component(@pairs);

use Data::Dumper;
print Dumper \@ccomp;

sub connected_component {

    my  @arr    = @_;
    my %links;

    foreach my $arrm (  @arr ) {
        my ($x,$y) = split(/\s+/,$arrm);;
        $links{$x}{$y} = $links{$y}{$x} = 1;

    }

    my %marked;  # nodes we have already visited
    my @stack;

    my @all_ccomp;

    for my $node (sort keys %links) {
        next if exists $marked{$node};
        @stack = ();
        connected($node);
        print "@stack\n";
        push @all_ccomp, [@stack];
    }

    sub connected {
        no warnings 'recursion';
        my $node = shift;
        return if exists $marked{$node};  # Line 43
        $marked{$node} = 1;
        push @stack, $node;   # Line 45
        my $children = $links{$node};  # Line 46
        connected($_) for keys %$children;
    }


    return @all_ccomp;
}

但为什么它会传达这样的信息:

Variable "%marked" will not stay shared at mycode.pl line 43.
Variable "@stack" will not stay shared at mycode.pl line 45.
Variable "%links" will not stay shared at mycode.pl line 46.

有害吗?错误?如何修复我的代码,以便它摆脱那条消息?

Update1:​​我使用确实错误消息更新运行的代码

Update2:我尝试使用sub修改为DVK建议。它工作了!

use warnings;
#Input
 my @pairs = (
    "fred bill",
    "hello bye",
    "hello fred",
    "foo bar",
    "fred foo");

#calling the subroutine
my @ccomp = connected_component(@pairs);

use Data::Dumper;
print Dumper \@ccomp;

sub connected_component {

    my  @arr    = @_;
    my %links;

    foreach my $arrm (  @arr ) {
        my ($x,$y) = split(/\s+/,$arrm);;
        $links{$x}{$y} = $links{$y}{$x} = 1;

    }

    my %marked;  # nodes we have already visited
    my @stack;

    my @all_ccomp;

    my $connected_sub;
     $connected_sub = sub {
        no warnings 'recursion';
        my $node = shift;
        return if exists $marked{$node};  
        $marked{$node} = 1;
        push @stack, $node;  
        my $children = $links{$node};  
        &$connected_sub($_) for keys %$children;
    };

    for my $node (sort keys %links) { # Line 43
        next if exists $marked{$node};
        @stack = ();
        &$connected_sub($node);
        #print "@stack\n";
        push @all_ccomp, [@stack]; # Line 49
    }

    return @all_ccomp;
}

4 个答案:

答案 0 :(得分:53)

根据perldoc's perldiag对于该错误,您的问题是内部子引用了外部子中定义的词法变量(标​​记为%)。

修复程序位于第三段(使用匿名子):

  

(警告;闭包)一个内部(嵌套)命名   子程序引用词法   在外部命名中定义的变量   子程序。

     

调用内部子程序时   它会看到外部的价值   子程序的变量和以前一样   并在第一次调用期间   外子程序;在这种情况下,之后   第一次调用外部子程序   完整,内在和外在   子程序将不再共享一个   变量的公共值。在   换句话说,变量将不会   更长时间的分享。

     

这个问题通常可以解决   使内部子程序匿名,   使用sub {}语法。内心的时候   匿名潜水员参考   外部子程序中的变量是   创建,它们是自动的   反弹到这样的当前价值   变量

使用匿名子修正代码:

# ....
my $connected_sub;
$connected_sub = sub {
    no warnings 'recursion';
    my $node = shift;
    return if exists $marked{$node};  # Line 280
    $marked{$node} = 1;
    push @stack, $node;   # Line 282
    my $children = $links{$node};  # Line 283
    &$connected_sub($_) for keys %$children;
};

for my $node (sort keys %links) {
    next if exists $marked{$node};
    @stack = ();
    &$connected_sub($node);
    #print "@stack\n";
    push @all_ccomp, [@stack];
}
# ....

答案 1 :(得分:6)

从perl获取诊断消息时,通常最好查看perldiag以了解它的含义。该联机帮助页也恰好涵盖了您收到的警告。

基本上,命名子例程不会按照您期望的方式嵌套。解决方案包括使用匿名内部子例程,而不是嵌套命名子例程,只是明确地在它们之间传递状态,或者使用CPAN中的mysubs之类的东西。

答案 2 :(得分:6)

另一种(可能更简单)的出路是将变量声明为“我们的”而不是“我的”

所以,

our %marked;

而不是

my %marked;

答案 3 :(得分:0)

如果您不小心在脚本的主线程中重新声明了共享变量,也会发生此错误,

`

use vars qw(%types  %colors); 

my %types = (...);    # bad
%colors = (...);   # good

`