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

nev*_*int 20 unix perl 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;
}
Run Code Online (Sandbox Code Playgroud)

但为什么它会给出这样的信息:

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.
Run Code Online (Sandbox Code Playgroud)

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

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

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;
}
Run Code Online (Sandbox Code Playgroud)

DVK*_*DVK 53

根据perldoc对该错误的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];
}
# ....
Run Code Online (Sandbox Code Playgroud)


Mat*_*lis 7

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

所以,

our %marked;
Run Code Online (Sandbox Code Playgroud)

代替

my %marked;
Run Code Online (Sandbox Code Playgroud)

等等


raf*_*afl 6

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

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