2013-05-09 57 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