2014-08-20 5 views
7

Я просмотрел несколько ответов, касающихся этого предупреждения, но они также не помогли мне, и я действительно не понимаю, что делает Perl здесь вообще. Вот что я хочу, чтобы это сделать:Perl: «Переменная не будет использоваться совместно»

sub outerSub { 
    my $dom = someBigDOM; 
    ... 
    my $otherVar = innerSub(); 
    return $otherVar; 

    sub innerSub { 
    my $resultVar = doStuffWith($dom); 
    return $resultVar; 
    } 
} 

Так в основном, у меня есть большой объект DOM, хранящийся в $ йоте, что я не хочу, чтобы пройти в стеке, если это возможно. В outerSub происходит что-то, что требует результатов от innerSub. innerSub нуждается в доступе к $ dom. Когда я это сделаю, я получаю это предупреждение: «Переменная $ dom не останется открытой».

То, что я не понимаю:

  1. ли это предупреждение касается меня здесь? Будет ли моя предполагаемая логика работать здесь или будут ли странные вещи?

  2. Если он не работает должным образом: возможно ли это сделать? Сделать локальный var видимым для вложенного суб? Или лучше просто передать это как параметр? Или лучше объявить «нашу» переменную?

  3. Если я вытолкнул его как параметр, будет ли весь объект со всеми его данными (может иметь несколько МБ) вставить в стек? Или я могу просто передать что-то вроде ссылки? Или Perl обрабатывает этот параметр как ссылку самостоятельно?

  4. В "Variable $foo will not stay shared" Warning/Error in Perl While Calling Subroutine, кто-то говорит об анонимном субблоке, который сделает это возможным. Я не понимал, как это работает, никогда не использовал ничего подобного.

  5. Я вообще не понимаю этого объяснения (может быть, английский не является моим первым языком): «Когда вызывается внутренняя подпрограмма, она увидит значение переменной внешней подпрограммы, как это было до и во время первого звонить внешнюю подпрограмму, в этом случае, после первого вызова внешней подпрограммы завершен, внутренние и внешние подпрограммы больше не будут делить общее значение для переменного «:

Что значит». первый вызов внешней подпрограммы завершен? означает «
Я имею в виду: сначала я называю внешний суб. Внешний sub вызывает внутренний sub. Внешняя часть, конечно же, все еще работает. Как только внешняя часть будет завершена, внутренняя часть будет закончена. Тогда как это все еще применяется, когда внутренняя часть уже завершена? А как насчет «первого» звонка? Когда происходит «второй» звонок ... извините, это объяснение смущает меня до конца.

Извините за много вопросов. Может быть, кто-то может хотя бы ответить на некоторые из них.

ответ

8

Короче говоря, вторые и более поздние времена outerSub называется будет иметь другой переменный $ РОМ, чем тот, которая используется innerSub. Вы можете исправить это, делая это:

{ 
    my $dom; 
    sub outerSub { 
     $dom = ... 
     ... innerSub() ... 
    } 
    sub innerSub { 
     ... 
    } 
} 

или делая это:

sub outerSub { 
    my $dom = ... 
    *innerSub = sub { 
     ... 
    }; 
    ... innerSub() ... 
} 

или это:

sub outerSub { 
    my $dom = ... 
    my $innerSub = sub { 
     ... 
    }; 
    ... $innerSub->() ... 
} 

Все переменные изначально задаются, позаботьтесь и innerSub и outerSub поделиться такой же $ dom. Когда вы покидаете область действия, perl проходит через лексические переменные, которые были объявлены в области и повторно инициализируют их. Итак, в тот момент, когда первый вызов outerSub завершен, он получает новый $ dom. Поскольку named subs - это глобальные вещи, хотя innerSub не влияет на это и продолжает ссылаться на старый $ dom. Поэтому, если внешний Sub вызывается во второй раз, его $ dom и domSub $ dom являются фактически отдельными переменными.

Так что либо перемещение декларации из externalSub, либо использование анонимного суб (которое получает свежую привязку к лексической среде во время выполнения) устраняет проблему.

+0

Странный язык действительно .... спасибо. – jackthehipster

+0

Thx для уточнения. Означает ли это, что, когда innerSub обращается к $ dom, он каким-то образом уничтожает его? Что такое innerSub в последующих вызовах, если не тот же $ dom, что и внешнийSub? – jackthehipster

+0

добавленный информация – ysth

3

Вы должны иметь анонимную подпрограмму для захвата переменных:

my $innerSub = sub { 
    my $resultVar = doStuffWith($dom); 
    return $resultVar; 
}; 

Пример:

sub test { 
    my $s = shift; 

    my $f = sub { 
     return $s x 2; 
    }; 

    print $f->(), "\n"; 

    $s = "543"; 

    print $f->(), "\n"; 
} 

test("a1b"); 

дает:

a1ba1b 
543543 
+1

Могу ли я спросить: почему? Хорошо, так оно и есть, но почему это работает не так, как ожидалось, и почему это работает с анонимными подсистемами? – jackthehipster

+1

Вот моя попытка/угадать, объясняя это :-) Компилятор имеет дело с анонимным sub по-другому. В вашем примере возвращаемое значение подпрограммы/функции присваивается скаляру. С ссылкой на код или анонимной подпрограммой код запускается, когда он вызывается и как он определяется. Сфера охвата затем более «зернистая». Несколько классических объяснений: http://www.perlmonks.org/?node_id=66677 (узел Perl Monks 66677) и http://www.plover.com/~mjd/perl/FAQs/Namespaces.html (устранение Scope) действительно помогают оценить эту функцию. –

3

Если вы хотите, чтобы свести к минимуму количества параметры просачивания размера к подсистемам, use Perl references. Недостаток/особенность заключается в том, что субмодуль может изменять ссылочное содержимое.

my $dom = someBigDOM; 
my $resultVar = doStuffWith(\$dom); 


sub doStuffWith { 
    my $dom_reference = shift; 
    my $dom_contents = $$dom_reference; 
    #... 
} 
+0

Коллеж сказал мне вчера, что Perl будет передавать ссылки по умолчанию. Поэтому вызов doStuffWith ($ dom) должен фактически помещать ссылку на стек. Это было бы согласуется с тем фактом, что если переданная переменная изменена внутри подкаталога, исходный var изменяется. Но я не знаю, что делает Perl на самом деле, это может просто сделать какой-то странный материал, который только делает его похожим на ref. – jackthehipster

+0

Нет, параметры передаются копией. Если вам нужна ссылка, вам нужно использовать явную ссылку Perl (с обратной косой чертой) –

+0

Thx Miguel. И знаете ли вы, почему изменение значения пройденного var в sub изменит исходный var? Я пытаюсь понять, как Perl тикает ... – jackthehipster