"Переменная $foo не останется открытой" Предупреждение/ошибка в Perl при вызове подпрограммы
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;
}
Ответы
Ответ 1
В соответствии с perldoc perldiag для этой ошибки, ваша проблема заключается в том, что внутренний 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];
}
# ....
Ответ 2
При получении диагностического сообщения от perl обычно рекомендуется проверить perldiag
, чтобы узнать, что это значит. Эта справочная страница также покрывает предупреждение, которое вы получаете.
В принципе, названные подпрограммы не вложены так, как вы ожидали. Решения включают использование анонимных внутренних подпрограмм, а не вложенные подпрограммы и просто передачу состояния между ними явно или использование чего-то вроде mysubs
из CPAN.
Ответ 3
Другой (возможно, более простой) выход - объявлять переменные как "наш", а не "мой"
Итак,
our %marked;
вместо
my %marked;
и др.
Ответ 4
Эта ошибка также может произойти, если вы случайно повторно объявили общие переменные в основном потоке script,
`
use vars qw(%types %colors);
my %types = (...); # bad
%colors = (...); # good
`