«Переменная $ foo не будет оставаться общей» Предупреждение / Ошибка в Perl при вызове подпрограммы - PullRequest
20 голосов
/ 29 октября 2010

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: Я обновляю код, который работает как есть, с сообщением об ошибке actall

Обновление2: Я попытался изменить с помощью 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 ]

53 голосов
/ 29 октября 2010

В соответствии с perldiag perldoc для этой ошибки ваша проблема заключается в том, что внутренняя подпрограмма ссылается на лексическую переменную (помеченную%), определенную во внешней подпрограмме.

Исправление находится в третьем абзаце (используйте анонимную подпункт):

(Предупреждение; закрытие) Внутренняя (вложенная) подпрограмма с именем ссылается на лексическую переменную, определенную во внешнем имениподпрограмма.

Когда вызывается внутренняя подпрограмма, она будет видеть значение переменной внешней подпрограммы, как это было до и во время первого вызова внешней подпрограммы;в этом случае после завершения первого вызова внешней подпрограммы внутренняя и внешняя подпрограммы больше не будут иметь общего значения для переменной.Другими словами, переменная больше не будет общей.

Эту проблему обычно можно решить, сделав внутреннюю подпрограмму анонимной, используя синтаксис 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];
}
# ....
6 голосов
/ 31 октября 2013

Другой (возможно, более простой) выход - объявить переменные как «наши» вместо «мои»

Итак,

our %marked;

вместо

my %marked;

и т.д.

6 голосов
/ 29 октября 2010

При получении диагностического сообщения от perl обычно хорошей идеей является проверка perldiag, чтобы выяснить, что это означает.Эта man-страница также покрывает предупреждение, которое вы получаете.

По сути, именованные подпрограммы не вкладываются так, как вы ожидали.Решения включают использование анонимных внутренних подпрограмм, не вложение именованных подпрограмм и просто передачу состояния между ними в явном виде, или использование чего-то вроде mysubs из CPAN.

0 голосов
/ 10 октября 2012

Эта ошибка также может произойти, если вы случайно повторно объявили общие переменные в основном потоке скрипта,

`

use vars qw(%types  %colors); 

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

`

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...