Инициализация локального хеша в модуле perl приводит к пустому хэшу

Когда я инициализирую локальный хэш (используя «мой») в модуле perl, хэш кажется пустым из функций модуля.

Вот код Perl-модуля:

package Test;

use 5.014002;
use strict;
use warnings;
use Exporter qw(import);

our %EXPORT_TAGS = (
    'all' => [ qw(test) ]
);

our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );

our @EXPORT = qw(

);

our $VERSION = '0.01';

my %h = ( "1" => "one" );

BEGIN
{
}

sub test
{
    my $a = shift;
    print $Test::h{$a} . "\n";
}

1;
__END__

Здесь test видит пустой хэш.

Если вместо этого я сначала объявлю хэш, но инициализирую его в BEGIN, тогда все будет работать нормально. Вот измененный код:

package Test;

use 5.014002;
use strict;
use warnings;
use Exporter qw('import');

our %EXPORT_TAGS = (
    'all' => [ qw(test) ]
);

our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );

our @EXPORT = qw(

);

our $VERSION = '0.01';

my %h;

BEGIN
{
    %Test::h = ( "1" => "one" );
}

sub test
{
    my $a = shift;
    print $Test::h{$a} . "\n";
}

1;
__END__

Кроме того, если я объявлю хеш, используя вместо этого «наш», то он отлично работает в обоих случаях.

Что мне не хватает?


person kounoupis    schedule 06.01.2015    source источник
comment
Я попытался удалить объявление my %h; из второго примера, а код все равно скомпилировался и заработал! Это означает, что %h и %Test::h — две разные переменные. Я предполагаю, что %Test::h внутри блока BEGIN во втором примере автоматически объявляется Perl, хотя запрашивается use strict.   -  person kounoupis    schedule 07.01.2015
comment
Test::h и my %h совершенно разные переменные. Вы должны получить предупреждение, похожее на это Name "Test::h" used only once: possible typo at test.pl line 25. с вашим первым примером. Разве ты этого не видишь? Примечание: это прекрасно работает с our, потому что our %h = .... является псевдонимом для %Test::h = ....   -  person Hunter McMillen    schedule 07.01.2015
comment
В первом примере я получаю предупреждение: Использование неинициализированного значения в %Test::h в конкатенации (.) или строку в строке 30 Test.pm. Во втором примере я не получаю никаких предупреждений, хотя я не объявлял Test: :h используя мое, наше или состояние.   -  person kounoupis    schedule 08.01.2015


Ответы (2)


our создает лексический псевдоним для динамически пакетная переменная с областью действия, на которую может ссылаться $Fully::Qualified::name.

my создает переменную с лексической областью видимости, которая может ссылаться только по $name.

Опираясь на комментарий @Schwern, под "лексическим" мы подразумеваем ограниченный охватывающим блоком и любыми последующими вложенными блоками, но невидима внутри других блоков (например, определенная в другом месте подпрограмма, вызываемая из блока, объявляющего my $lexical_var, не может видеть эту переменную).

Итак, в вашем коде my %h — это переменная, лексически привязанная к неявному блоку окружающего ее файла (Test.pm?). our %h, с другой стороны, является лексическим псевдонимом для совершенно другой переменной, а именно для пакетной (глобальной) переменной с полным именем %Test::h. В обоих примерах ваш sub test запрашивает переменную пакета по ее полному имени. Но только во втором примере вы присваиваете значения этой переменной.

(Вы не спрашивали об этом конкретно, но local динамически определяет заданную переменную пакета к вложенному файлу, блоку или eval. Вы используете прилагательное «местный» несколько раз в своем сообщении таким образом, что это несовместимо с тем, что оно означает как ключевое слово Perl.)

person pilcrow    schedule 06.01.2015
comment
Возможно, вы захотите объяснить, что такое лексическая область видимости. Я обычно говорю, что это переменная, которая существует только внутри своего блока (и любых вложенных блоков) и больше нигде. Существует подразумеваемый блок вокруг всего файла. - person Schwern; 07.01.2015
comment
Итак, теперь у меня другая проблема. Я объявил свой %h = (); до этого блок BEGIN. Затем я добавил значение в хэш внутри блока BEGIN (BEGIN { $h{1} = one; }). Затем я печатаю $h{1} из тестовой функции и получаю сообщение об ошибке: Использование неинициализированного значения в %h в конкатенации (.) или строки в /tmp/Test.pm, строка 31. - person kounoupis; 08.01.2015
comment
Что касается моего предыдущего комментария, если вместо этого я объявлю свой %h перед блоком BEGIN, а затем сделаю BEGIN { %h = ( 1 =› one ); }, работает нормально. - person kounoupis; 08.01.2015
comment
И чтобы сделать это еще более запутанным, если я объявлю свой %h перед блоком BEGIN (вместо моего %h = ();), а затем сделаю $h{1} = one; внутри блока BEGIN это работает! Таким образом, инициализация хеша вне блока BEGIN имеет какой-то странный побочный эффект, которого я не понимаю. - person kounoupis; 08.01.2015
comment
@kounoupis, хорошо. Если эта запись из perlfaq7 не помогает, вам нужно найти хороший учебник по Perl. Может быть, спросите почтенных монахов - person pilcrow; 08.01.2015
comment
Ответ на мой комментарий заключается в том, что блок BEGIN выполняется раньше всего, и последующее объявление моего %h = (); стирает хэш, уже инициализированный в BEGIN ранее. Я не думаю, что это связано с масштабированием. Может быть, то, что я спрашиваю в комментарии, непонятно. - person kounoupis; 09.01.2015

Ой - что ты пытаешься сделать? Есть ли причина для этого «использовать 5.014002;»? Я имею в виду, что это круто, если вы пытаетесь использовать что-то из старой школы - меня только что раскритиковали на этом сайте за то, что я делаю старый добрый perl. Но если вы учитесь, может быть, вы хотите делать что-то новое?

ООП Perl, который я делаю с пакетами, и тому подобное приведены ниже. В любом случае.

package myModule;

sub new
{
   $class = shift;
   my $self = {};
   bless $self, $class;

   return $self;
}

sub someMethod
{
   my $self = shift;
   $self->{some} = "value";

   return $self;
}

1;
person terary    schedule 06.01.2015
comment
Я проголосовал против, потому что это не решает проблемы, с которыми сталкивается ОП. Они неправильно поняли лексические и глобальные переменные, очень важную и базовую вещь, которую нужно знать в Perl. Сказав им использовать объекты, это не исправит ситуацию и, скорее всего, вызовет еще большую путаницу. А 5.14.2 всего 3 года и вряд ли имеет отношение к вопросу. Все в вашем ответе было доступно с Perl 5.0. - person Schwern; 07.01.2015
comment
Я думаю, вы неправильно понимаете use 5.014002, который относится к Perl 5.14.2, а не Perl 5.0.1 или что-то в этом роде. См. документацию для use: в особой форме use VERSION VERSION может быть... положительная десятичная дробь, например 5,006, которая будет сравниваться с $] согласно perldoc perlvar, $] — это версия + patchlevel/1000 интерпретатора Perl. - person ThisSuitIsBlackNot; 07.01.2015