Сохранение пакетов Perl через local

от автора

В 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::? "одно и то же": "разное"; # -> одно и то же

Вот и всё. Остаётся только пожелать нам всем отличного программирования!


ссылка на оригинал статьи https://habr.com/ru/articles/585038/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *