2013-05-09 75 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};)?

我試圖避免使用駝鹿,克隆或其他非核心模塊/軟件包,因此請在回覆時記住這一點。因此它更加突出:)

+0

接受的解決方案可能會改變:請注意,它是更淺拷貝簡單的數據結構,但不會解決深複製,甚至更先進的階級結構;這是我原來的問題。 記住這一點,選定的答案可能會在未來發生變化。 – vol7ron 2013-05-09 15:43:53

回答

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

我認爲這可能是要走的路。這將是我的下一步,我希望Perl有內置的東西做同樣的事情,這樣我就不會臃腫我的對象,但謝謝! – vol7ron 2013-05-09 15:20:45

+0

另外'my $ dictionary = clone $ book;'讀得更好。雖然這是被接受的答案,但用戶應該注意到,這對於簡單數據結構的淺拷貝來說更多,並且不能解決深層複製或更高級的類結構。 – vol7ron 2013-05-09 15:39:18

+2

'bless {%$ self},ref $ self'技術只會做一個淺拷貝。不會克隆作爲參考的'$ self'的屬性。例如,'$ obj - > {ponies} = [qw(Dash Sparkle Jack)]; $ clone = $ obj-> clone; push @ {$ clone - > {ponies}},「Pinkie」會修改兩個對象。 – Schwern 2013-05-09 18:47:38

9

use Storable 'dclone';

$ corelist Storable 

Storable was first released with perl v5.7.3 

另外,您可以擺弄可保存鉤在複製你的對象斷言更好的控制(不,我已經做到了,但是這就是文檔索賠)。

3

調用程序沒有什麼好方法來知道「複製對象」需要什麼,所以對象應該知道如何複製自己。 Perl的OO並不在這裏爲你提供任何幫助,但做傳統的事情是這樣的:

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或Stored :: dclone的包裝。這會將你的對象的用戶從你的對象克隆的細節中屏蔽掉。

+0

我會說,有時候你會希望兩個克隆的對象保持指向同一個對象。例如,如果您的Person類具有指向代表AcmeCorp組織的對象的僱主屬性,則您可能不希望克隆Bob克隆AcmeCorp。這些決定只能在每個班級的基礎上進行,並且應該全面記錄下來。 – tobyink 2014-02-21 10:08:52

+0

@tobyink是的,正是爲什麼克隆是通過'$ obj-> clone'而不是'clone($ obj)'完成的。該對象可以最好地決定如何克隆它。克隆方法應該做的是沒有定義。它是一個淺層克隆嗎?深度克隆?一個「我認爲合適」的克隆?您的克隆方法的行爲/接口應該被考慮並定義。您可能需要幾種克隆方法。 – Schwern 2014-02-25 21:47:15

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

解釋它是如何解決問題的。 – 2014-01-28 09:22:55

+0

嗨,Rahil Wazir,有網站網址:http://search.cpan.org/~ams/Storable-2.45/Storable.pm,你可以找到:「Storable爲你提供了一個dclone接口,它不會創建中介標量,而是凍結一些內部存儲空間中的結構,然後立即將其解凍出來。「,」Storable的核心是用C編寫的,速度很快。在操作perl內部時,進行了額外的低級優化,犧牲了封裝爲了更快的速度。「;我希望事情可以幫助你。 – 2014-02-02 12:12:49

+1

你可以編輯這部分到你的答案,而不是評論。 – 2014-02-02 13:17:51