Perl: Как сделать дубликат анонимных переменных
Всякий, кто использует в программе анонимные хэши и массивы, не раз сталкивался с необходимостью создания автономного и независимого дубликата переменной. Вариантов решения много, в том числе и с помощью сериализации.
Если кому-то первый абзац показался непонятным, то сейчас разжую.
Допустим у вас есть переменная $a с такой структурой:
Ага!, скажете вы и сделаете присвоение так:
Вариант первый, используя класс Data::Dumper
Как вы заметили, вся структура хранится в промежуточной переменной $r (в первом варианте в виде текста, во втором в виде двоичных данных), поэтому, в случае надобности, можно легко сохранить её в базе данных или в обычном файле, а потом, по необходимости, прочитать и обратно преобразовать в нормальную переменную (десериализировать).
Если кому-то первый абзац показался непонятным, то сейчас разжую.
Допустим у вас есть переменная $a с такой структурой:
my $a = { name => 'Vasya', date => '2009-10-15', };И теперь вам надо создать дубликат этой переменной. Казалось бы что тут сложного:
my $b = $a;А вот тут-то и заковыка: у нас $a это не просто переменная, а анонимный хэш массив, ссылку на который мы продублировали в переменную $b и теперь, если изменить значение в $b , то оно изменится и в $a
$b->{name} = 'Petya';
print $a->{name}; # Будет выведено Petya
Ага!, скажете вы и сделаете присвоение так:
my $b = {}; $b->{name} = $a->{name}; $b->{date} = $a->{date};Или так:
my $b = {}; map { $b->{$_} = $a->{$_}; }keys %{$a};Решение правильное, но оно годится для простых структур. А что если в $a у вас куча вложенных хэшей и массивов? Тогда вам придётся написать метод, который рекурсивно вызывая сам себя, пройдется по всем закоулкам нашей переменной. Выглядеть он будет примерно так:
sub copy_var { my $var = shift; my $out = undef; if (ref($var) eq 'HASH') { foreach my $k (keys %{$var}) { my $res = copy_var($var->{$k}); $out->{$k} = $res; } } elsif (ref($var) eq 'ARRAY') { foreach my $a (@{$var}){ my $res = copy_var($a); push(@{$out}, $res); } } elsif (ref(\$var) eq 'SCALAR') { return $var; } return $out; }и копирование $a в $b будет происходить так:
$b = copy_var($a);Но есть еще, как минимум два быстрых способа сделать копию переменной,- используя сериализацию.
Сериализация (в программировании) — процесс перевода какой-либо структуры данных в последовательность битов. Обратной к операции сериализации является операция десериализации — восстановление начального состояния структуры данных из битовой последовательности.
Сериализация используется для передачи объектов по сети и для сохранения их в файлы. Например, нужно создать распределённое приложение, разные части которого должны обмениваться данными со сложной структурой. В таком случае для типов данных, которые предполагается передавать, пишется код, который осуществляет сериализацию и десериализацию. Объект заполняется нужными данными, затем вызывается код сериализации, в результате получается, например, XML-документ. Результат сериализации передаётся принимающей стороне, например, по электронной почте или HTTP. Приложение-получатель создаёт объект того же типа и вызывает код десериализации, в результате получая объект с теми же данными, что были в объекте приложения-отправителя.
Сериализация используется для передачи объектов по сети и для сохранения их в файлы. Например, нужно создать распределённое приложение, разные части которого должны обмениваться данными со сложной структурой. В таком случае для типов данных, которые предполагается передавать, пишется код, который осуществляет сериализацию и десериализацию. Объект заполняется нужными данными, затем вызывается код сериализации, в результате получается, например, XML-документ. Результат сериализации передаётся принимающей стороне, например, по электронной почте или HTTP. Приложение-получатель создаёт объект того же типа и вызывает код десериализации, в результате получая объект с теми же данными, что были в объекте приложения-отправителя.
Вариант первый, используя класс Data::Dumper
use Data::Dumper; my $r = Dumper($a); eval "\$b = $r";Вариант второй, используя класс Storable
use Storable qw(nfreeze thaw); my $r = nfreeze($a); my $b = thaw($r);
Как вы заметили, вся структура хранится в промежуточной переменной $r (в первом варианте в виде текста, во втором в виде двоичных данных), поэтому, в случае надобности, можно легко сохранить её в базе данных или в обычном файле, а потом, по необходимости, прочитать и обратно преобразовать в нормальную переменную (десериализировать).
%bh = %$a;
$b = \%bh;