Perl: Как глубоко скопировать благословенный объект?

Я пытаюсь сделать глубокую (на данный момент, мелкую, может быть достаточной) копию благословенного объекта.

Фу класс

package Foo;
our $FOO = new Foo;       # initial run

sub new {
   my $class = shift;
   my $self  = {};
   bless $self, $class;
   return $self;
}

Основная программа

use Foo;
my $copy = $Foo::FOO;     # instead of creating a ref, want to deep copy here
$copy->{bar} = 'bar';

bar появляется в обоих $Foo::FOO а также $copy, Я понимаю, что могу создать копию объекта, настроив его как $copy = { %{$Foo::FOO} }но тогда это уже не будет благословением; Кроме того, это будет работать только для простых структур данных (сейчас это не проблема). Это единственный способ скопировать этот путь, а затем благословить после (например, $copy = bless { %{$Foo::FOO} }, q{Foo};)?

Я стараюсь не использовать Moose, Clone или другие неосновные модули / пакеты, поэтому, пожалуйста, помните об этом при ответе. Болтается так что больше выделяется:)

5 ответов

Решение

Копирование должно быть частью API. Пользователь вашего модуля никогда не узнает, какие специальные действия требуются при создании нового объекта (рассмотрите возможность регистрации каждого объекта в my хеш в вашей посылке).

Поэтому предоставьте clone метод для ваших объектов. Внутри вы можете использовать любые грязные трюки, которые вам нравятся:

sub clone {
    my $self = shift;
    my $copy = bless { %$self }, ref $self;
    $register{$copy} = localtime; # Or whatever else you need to do with a new object.
    # ...
    return $copy;
}

use Storable 'dclone';?

$ corelist Storable

Storable was first released with perl v5.7.3

Кроме того, вы можете возиться с хранимыми Storable, чтобы утвердить более точный контроль над копированием ваших объектов (не то, что я это сделал, но это утверждается в документации).

my $copy = bless { %$self }, ref $self; в @chorba ответ недостаточен. Это будет только клонировать первый слой. Любые ссылки хранятся в $self не будет клонирован. Последствия этого...

$obj->{ponies} = [qw(Dash Sparkle Jack)];
$clone = $obj->clone;
push @{$clone->{ponies}}, "Pinkie";
print join ", ", @{$obj->{ponies}};  # Dash Sparkle Jack Pinkie

Вы можете не иметь никаких ссылок сейчас, но вы, вероятно, будете позже. Или кто-то еще сунет один в ваш объект. Или они подкласс и добавят один.

Вы можете написать рутину глубокого клонирования, но это не просто. Я очень рекомендую использовать клон. Он не имеет зависимостей, поэтому вы можете просто скопировать Clone.pm в ваш проект.

Другой альтернативой является Storable:: dclone, упомянутый @Zaid, который был в ядре долгое время.

Независимо от того, что вы используете, предоставление метода клонирования в вашем классе является правильным решением, даже если это просто оболочка для Clone или Storable::dclone. Это защитит пользователя вашего объекта от деталей того, как ваш объект клонируется.

У вызывающей программы нет хорошего способа узнать, что влечет за собой "копирование объекта", поэтому объект должен знать, как копировать себя. ОО Perl здесь не оказывает никакой помощи, но обычно нужно сделать что-то вроде этого:

package Car;

sub clone {
    my ($self) = @_;

    return $self->new(
        ( map { $_ => $self->$_() } qw/make model/ ), # built-in types
        engine => $self->engine->clone(), # copying an object
    );
}

Извините, я не могу заметить это предложение:

* Я стараюсь не использовать Moose, Clone или другие неосновные модули / пакеты, поэтому помните об этом при ответе. Полужирный, так что больше выделяется:) *

так что этот ответ не может быть принят!

#!/usr/bin/env perl -w
use strict;
use warnings;
use Storable;
use Data::Dumper;

my $src = {
  foo => 0,
  bar => [0,1]
};

$src -> {baz} = $src;
my $dst = Storable::dclone($src);
print '$src is : '.Dumper($src)."\n";
print '$dst is : '.Dumper($dst)."\n";
Другие вопросы по тегам