2013-05-09 3 views
8

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

Foo класса

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 = bless { %{$Foo::FOO} }, q{Foo};)?

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

+0

Решение принятое решение может измениться: обратите внимание, что это больше для мелкого копирования простых структур данных, но не решит глубокое копирование или даже более сложные структуры классов; который был моим первоначальным вопросом. Учитывая это, выбранный ответ может измениться в будущем. – vol7ron

ответ

10

Копирование должно быть частью 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; 
} 
+0

Я думаю, что это может быть путь. Это было бы моим следующим шагом, я надеялся, что у Перла было что-то встроенное, что сделало то же самое, так что мне не пришлось бы раздувать мой объект, но спасибо! – vol7ron

+0

Дополнительный 'my $ dictionary = clone $ book;' читает намного лучше. Хотя это и есть принятый ответ, пользователи должны заметить, что это больше для мелких копий простых структур данных и не будет решать глубокое копирование или даже более сложные структуры классов. – vol7ron

+2

Метод 'bless {% $ self}, ref $ self' будет делать только мелкую копию. Атрибуты '$ self', которые являются ссылками, не будут клонированы. Например, '$ obj -> {ponies} = [qw (Dash Sparkle Jack)]; $ clone = $ obj-> clone; push @ {$ clone -> {ponies}}, "Pinkie" будет изменять оба объекта. – Schwern

9

use Storable 'dclone';?

$ corelist Storable 

Storable was first released with perl v5.7.3 

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

3

Существует не лучший способ для вызывающей программы знать, что означает «копирование объекта», поэтому объект должен знать, как копировать себя. OO 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 
    ); 
} 
4

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

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

У вас пока нет ссылок, но вы, вероятно, позже. Или кто-то другой вложит его в ваш объект. Или они подклассы и добавят один.

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

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

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

+0

Я бы сказал, что иногда вы * хотели бы, чтобы два клонированных объекта оставались указателями на один и тот же референт. Например, если ваш класс 'Person' имел атрибут« работодатель », указывающий на объект, представляющий AcmeCorp' Organization', вы, вероятно, не захотели бы клонировать Bob, чтобы также клонировать AcmeCorp. Такие решения могут приниматься только на основе поэтапно и должны быть тщательно документированы. – tobyink

+0

@tobyink Да, именно поэтому клонирование выполняется с помощью '$ obj-> clone', а не' clone ($ obj) '. Объект может наилучшим образом решить, как его клонировать. То, что нужно сделать методу «клонировать», плохо определено. Это мелкий клон? Глубокий клон? «Как мне кажется, подходит» клон? Поведение/интерфейс вашего метода clone следует продумать и определить. Вам может понадобиться несколько методов клонирования. – Schwern

0

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

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

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

#!/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"; 
+0

Объясните, как он решает проблему. –

+0

Привет, Рахиль Вазир, есть URL-адрес веб-сайта: http: //search.cpan.org/~ams/Storable-2.45/Storable.pm, вы можете узнать: «Storable предоставляет вам интерфейс dclone, который не создает этого промежуточный скаляр, но вместо этого замораживает структуру в некотором внутреннем пространстве памяти, а затем сразу же оттаивает ее ».« Сердце Storable написано на C для достойной скорости. Были реализованы дополнительные низкоуровневые оптимизации при манипулировании внутренними элементами perl, пожертвованию инкапсуляцией для большей скорости. "; Надеюсь, что все может вам помочь. –

+1

Можете ли вы отредактировать эту часть в своем ответе, а не комментировать. –

Смежные вопросы