В perl есть выражение local. Оно подменяет указанное значение undef-ом до конца блока. В качестве значения могут выступать глобальные хеши, массивы и скаляры, а так же элементы или срезы хешей и скаляров.
Проблема в том, что хеши пакетов local не сохраняет.
Под хешами пакетов я понимаю хеш с двоеточием на конце (%Пакет::)
в котором хранятся символы пакета (GLOB).
package A {
sub fn {}
$x = 10;
@x = qw(1 2);
}
use DDP;
p %A:: # -> {
# fn *A::fn (layers: ),
# x *A::x (layers: )
# }
Тут @x
и $x
находятся в одном глобе - *A::x
.
Доступ к ним можно получить так:
package A {
sub fn {}
$x = 10;
@x = qw(1 2);
}
$\ = "\n"; $, = ", ";
print $A::x, ${ *A::x{SCALAR} }, ${ *{ $A::{x} }{SCALAR} };
# -> 10, 10, 10
print @A::x, @{ *A::x{ARRAY} }, @{ *{ $A::{x} }{ARRAY} };
# -> 1, 2, 1, 2, 1, 2
То есть вначале получаем GLOB из хеша пакета ($A::{x}
), разыменовываем его (*{ $A::{x} }
), получаем ссылку на скаляр (*{ $A::{x} }{SCALAR}
) и, наконец, разыменовываем скаляр (${ *{ $A::{x} }{SCALAR} }
).
GLOB же это такой специальный хеш с предустановленными ключами: SCALAR, ARRAY, HASH, CODE, REF, GLOB, LVALUE, FORMAT, IO, VSTRING.
Каждый из них соответствует отдельному типу значения который может хранится в символе (fn и x - символы в пакете A).
Но вернёмся к local.
use DDP;
our %x = qw/a b d c/;
{
local %x;
$x{a} = "R";
p %x; # -> {
# a "R"
# }
}
p %x; # -> {
# a "b"
# d "c"
# }
Как видим local подменила our %x
до конца блока.
С my %x
local отказывается работать (выбрасывает исключение):
@@файл x.pl:
my %x = qw/a b/;
{
local %x;
p %x;
}
@@консоль:
$ perl x.pl
Can't localize lexical variable %x at x.pl line 13.
А теперь самое интересное: на хеш пакета local не ругается, но и не сохраняет его:
package A {
sub fn { 10 }
$x = 20;
}
{
local %A::;
*A::fn = sub {6};
$A::x = 3;
print &A::fn; # -> 6
print $A::x; # -> 3
}
print &A::fn; # -> 6
print $A::x; # -> 3
То есть $A::x
и A::fn
не вернулись к первоначальному состоянию.
Тем не менее если сохранять не хеш %A::
, а срез всех его ключей (@A::{keys %A::}), то local сработает:
package A {
sub fn { 10 }
$x = 20;
}
{
local @A::{keys %A::};
*A::fn = sub {6};
$A::x = 3;
print &A::fn; # -> 6
print $A::x; # -> 3
}
print &A::fn; # 10
print $A::x; # 20
Вот и ещё одна тайна perl-а раскрыта!
Хотя не до конца. Если у кого-то есть объяснение такому странному поведению local c хешем пакета, то хотелось бы его увидеть в комментариях.
Ну и напоследок предлагаю полюбоваться содержимым пакета main:
Обратите внимание на наш пакет A, который на самом деле main::A.
Все остальные пакеты находятся в пакете main, а пакеты с четвероточиями укладываются в них иерархически образуя древовидную структуру:
package A {
sub fn { 10 }
$x = 20;
}
package A::A {
$x = 30;
}
# ->
# %main::
# A:: *main::A
# fn *A::fn
# x *A::x
# A:: *A::A::
# x *A::A::x
print \%main::A == \%A? "одно и то же": "разное";
# -> одно и то же
print $main::{A::}{A::} == $A::A::? "одно и то же": "разное";
# -> одно и то же
Вот и всё. Остаётся только пожелать нам всем отличного программирования!
parserpro
С одной стороны интересно. С другой - а как это использовать?
darviarush Автор
perl_mod (perl в apache) благодаря этой технологии умеет перезагружать один модуль (файл *.pm), причём весь остальной код остаётся в памяти. Остаются и все глобальные переменные с инициализированными объектами.
Это нужно для того, чтобы не перезагружать весь сайт, на что могут потребоваться минуты (в сайте десятки тысяч модулей и множество объектов нужно создать и инициализировать). Один же модуль заменяется мгновенно.