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

Недокументированная переменная Perl% _?

Недавно я обнаружил, что, как представляется, недокументированная переменная в Perl, %_. Я точно не помню, как я наткнулся на него (это было на прошлой неделе), но у меня была опечатка в моем коде, где я использовал map, а вместо $_->{key} использовал $_{key}. Когда я нашел ошибку, я был удивлен, что она не вызвала ошибку, и я подтвердил, что use strict и use warnings находятся на месте.

Итак, я сделал небольшой тест, и, конечно же, он работает без каких-либо предупреждений или ошибок:

$ perl
use strict;
use warnings;
print keys %_;
$

Итак, все, что я могу понять, это то, что %_ определяется где-то. Я не могу найти его в perlvar, так что же сделка? Он не содержит содержимого в script выше.

4b9b3361

Ответ 1

Пунктуационные переменные освобождаются от строгих. Вот почему вам не нужно использовать что-то вроде our $_; перед использованием $_. Из perlvar,

Идентификаторы Perl, начинающиеся с цифр, управляющих символов или знаков препинания [...], также освобождаются от ошибок strict 'vars'.


%_ не является недокументированным. Из perlvar,

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

У вас может быть хэш с именем _, потому что _ является допустимым именем для переменной. (Я уверен, что вы знакомы с $_ и @_.)

Нет встроенного Perl в настоящее время устанавливает или читает %_ неявно, но переменные препинания, такие как %_, зарезервированы.


Обратите внимание, что переменные пунктуации также являются особенными, поскольку они являются "суперглабильными". Это означает, что неквалифицированный %_ относится к %_ в корневом пакете, а не %_ в текущем пакете.

$ perl -E'
   %::x    = ( "%::x"    => 1 );
   %::_    = ( "%::_"    => 1 );
   %Foo::x = ( "%Foo::x" => 1 );
   %Foo::_ = ( "%Foo::_" => 1 );

   package Foo;
   say "%x      = ", keys(%x);
   say "%_      = ", keys(%_);
   say "%::x    = ", keys(%::x);
   say "%::_    = ", keys(%::_);
   say "%Foo::x = ", keys(%Foo::x);
   say "%Foo::_ = ", keys(%Foo::_);
'
%x      = %Foo::x
%_      = %::_      <-- surprise!
%::x    = %::x
%::_    = %::_
%Foo::x = %Foo::x
%Foo::_ = %Foo::_

Это означает, что забыть использовать local %_ (как и вы) может иметь очень далеко идущие последствия.

Ответ 2

Он не недокументирован, он просто не используется. Вы найдете его всегда пустым

perldoc perlvar говорит об этом

Имена переменных Perl также могут быть последовательностью цифр или одним символом пунктуации или управления... Эти имена зарезервированы для специального использования Perl; например, имена всех цифр используются для хранения данных, захваченных обратными ссылками после соответствия регулярному выражению.

Итак, %_ зарезервировано, но не используется.

Хэш-переменные являются наименее распространенными, поэтому вы можете использовать %1, %( и т.д. (код типа $({xx} = 99 в порядке), но вы не получите предупреждения из-за проблем с обратной совместимостью

Действительные имена переменных общего назначения должны начинаться с буквы (с помощью прагмы utf8 на месте, которая может быть любым символом с символьным символом Юникода) или символом подчеркивания ASCII, когда за ним должен следовать хотя бы один другой символ

Ответ 3

$_ - глобальная переменная. Глобальные переменные живут в таблицах символов, а встроенные переменные препинания все живут в таблице символов для пакета main.

Вы можете увидеть содержимое таблицы символов для main следующим образом:

$ perl -MData::Dumper -e'print Dumper \%main::' # or \%:: for short
$VAR1 = { 
          '/' => *{'::/'},
          ',' => *{'::,'},
          '"' => *{'::"'},
          '_' => *::_,
          # and so on
        };

Все вышеперечисленные записи являются типами символов, обозначаемыми сигаром *. Типglob похож на контейнер со слотами для всех разных типов Perl (например, SCALAR, ARRAY, HASH, CODE).

Типglob позволяет использовать разные переменные с тем же идентификатором (имя после сигилы):

${ *main::foo{SCALAR} } # long way of writing $main::foo
@{ *main::foo{ARRAY} }  # long way of writing @main::foo
%{ *main::foo{HASH} }   # long way of writing %main::foo

Значения $_, @_ и %_ хранятся в записи таблицы символов main с ключом _. Когда вы обращаетесь к %_, вы фактически получаете доступ к слоту HASH в *main::_ typeglob (*::_ для краткости).

strict 'vars' обычно будет жаловаться, если вы попытаетесь получить доступ к глобальной переменной без полного имени, но переменные пунктуации освобождены:

use strict;

*main::foo = \'bar'; # assign 'bar' to SCALAR slot
print $main::foo;    # prints 'bar'
print $foo;          # error: Variable "$foo" is not imported
                     #        Global symbol "$foo" requires explicit package name
print %_;            # no error