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;