Подтвердить что ты не робот

"Переменная $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;
}
4b9b3361

Ответ 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

`