“variables$ foo不会保持共享”在调用子程序时在Perl中的警告/错误

Update3:如果你喜欢这个post,请不要upvote我,但upvote下面的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:我试图修改使用子DVKbuild议。 它工作!

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

根据perldoc的错误perldiag ,你的问题是内部子引用一个词汇变量(%标记)定义在外部子。

该修复程序在第三段(使用匿名子):

(Warning; closure)内部(嵌套)命名子例程引用外部命名子例程中定义的词法变量。

当内部子程序被调用时,它会看到外部子程序变量的值,就像在第一次调用外部子程序之前和之后一样。 在这种情况下,在完成对外部子程序的第一次调用之后,内部和外部子程序将不再共享该变量的公共值。 换句话说,变量将不再被共享。

这个问题通常可以通过使用sub {}语法使内部子程序匿名来解决。 当创建外部子例程中引用变量的内部匿名子代时,它们会自动回弹到这些变量的当前值。

使用匿名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]; } # .... 

从perl获取诊断消息时,通常检查perldiag以了解其含义通常是个好主意。 那个manpage也恰好覆盖了你所得到的警告。

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

另一种(也许更简单)的方式是将变量声明为“我们”而不是“我的”

所以,

 our %marked; 

代替

 my %marked; 

等等

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

`

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

`