perl eval Использование неинициализированного значения, объявленного в пакете

Кто-нибудь может объяснить мне, почему функция eval не может получить доступ к переменной, объявленной в пакете, если только она не используется один раз внутри подпрограммы?
(perl v5.16.3 MSWin32-x64-multi-thread ActiveState)

Упаковка:

use strict;
use warnings;
package testPackage;
my $insertVar = "TEST";
sub testSub {
    my $class = shift;
    my $test = shift;
    eval '$test="'.$test.'";';
    return $test;
}
1;

Программа:

use strict ;
use warnings ;
use testPackage ;
my $testVar = q[insertVar = ${insertVar}] ;
$testVar = testPackage->testSub( $testVar ) ;
print "$testVar\n" ;

Результат при выполнении программы:

Использование неинициализированного значения $insertVar в конкатенации (.) или строки в (eval 1) строке 1. insertVar =

Теперь, если я использую переменную внутри testSub (например, распечатав ее):

use strict;
use warnings;
package testPackage;
my $insertVar = "TEST";
sub testSub {
    my $class = shift;
    my $test = shift;
    print $insertVar . "\n";
    eval '$test="'.$test.'";';
    return $test;
}
1;

Затем программа работает именно так, как я предполагал:

КОНТРОЛЬНАЯ РАБОТА

вставкаVar = ТЕСТ


person Veltro    schedule 16.06.2015    source источник
comment
С какой стати вам eval присваивать значение переменной?   -  person TLP    schedule 16.06.2015


Ответы (1)


my переменные, объявленные в файле (за пределами фигурных скобок), выходят из области видимости, когда файл завершает выполнение (до возврата require и use).

$insertVar будет продолжать существовать только после того, как файл завершит его выполнение, если он захвачен сабвуфером, и он будет виден только сабвуферам, которые его захватили.

Из соображений эффективности сабвуферы охватывают как можно меньше переменных. Если подпрограмма не ссылается на $insertVar, она не будет ее захватывать. Поскольку ваш первый testSub не ссылается на $insertVar, он его не фиксирует. Поскольку к моменту вызова testSub объект $insertVar вышел за пределы области действия, он недоступен для eval. Вы должны получить предупреждение Variable "$insertVar" is not available, но по неизвестным мне причинам оно выдается для конкретного кода, который вы использовали.

Ваша вторая ссылка testSub $insertVar, поэтому testSub захватывает $insertVar и сохраняет ее в живых. Несмотря на то, что $insertVar вышел из области действия к тому времени, когда вы вызываете testSub, он доступен для eval, так как он был захвачен сабвуфером.

Если вы объявите переменные с помощью our, они будут глобальными переменными пакета и, таким образом, не выйдут за рамки, и они будут доступны для eval.

>perl -wE"use strict; use warnings; { my $x = 'abc'; sub foo { $x } } say foo()"
abc

>perl -wE"use strict; use warnings; { my $x = 'abc'; sub foo { eval '$x' } } say foo()"
Variable "$x" is not available at (eval 1) line 2.
Use of uninitialized value in say at -e line 1.


>perl -wE"use strict; use warnings; { our $x = 'abc'; sub foo { eval '$x' } } say foo()"
abc
person ikegami    schedule 16.06.2015
comment
Правильным ответом, вероятно, было бы показать этому человеку, как получить доступ к переменной пакета. Это XY-проблема. - person TLP; 16.06.2015
comment
@TLP, да, хотя вместо этого следует использовать правильную систему шаблонов. - person ikegami; 16.06.2015
comment
ikegami, Принято и спасибо за ваш четкий ответ, часть о том, как переменная захватывается сабвуфером, - это ответ, который я искал. Я просто не мог понять, что здесь происходит. TLP, я знаю о «наших», а также о том, что это устраняет проблему. Я предпочитаю ответ от ikegami, потому что он объяснил мне, как работает жизненный цикл переменной в пакете, объявленной с помощью «my». - person Veltro; 16.06.2015