Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/perl/11.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Perl:如何深度复制受祝福的对象?_Perl_Oop_Deep Copy_Bless - Fatal编程技术网

Perl:如何深度复制受祝福的对象?

Perl:如何深度复制受祝福的对象?,perl,oop,deep-copy,bless,Perl,Oop,Deep Copy,Bless,我希望做一个深(在这一点上,浅可能足够)的祝福对象副本 福类 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, wa

我希望做一个深(在这一点上,浅可能足够)的祝福对象副本

福类

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或其他非核心模块/软件包,因此在回复时请记住这一点。加粗以使其更加突出:)

复制应该是API的一部分。模块的用户永远不会知道创建新对象时需要执行哪些特殊操作(请考虑在包中的
my
散列中注册每个对象)

因此,为您的对象提供一种克隆方法。在它里面,你可以使用任何你喜欢的肮脏把戏:

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;
}
?


此外,您还可以摆弄可存储钩子来对复制对象进行更精细的控制(虽然我没有这么做,但文档声称这是正确的)。

调用程序无法知道“复制对象”需要什么,因此对象应该知道如何复制自己。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
    );
}

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.pm复制到项目中

另一种选择是@Zaid提到的,它在核心中已经存在很长时间了


无论您使用什么,在类上提供clone方法都是正确的做法,即使它只是clone或Storable::dclone的包装器。这将使您的对象的用户不知道您的对象是如何克隆的。

对不起,我注意不到这句话:

*我试图避免使用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";

我想这可能是一条路。这将是我的下一步,我希望Perl有一些内置的东西可以做同样的事情,这样我就不必膨胀我的对象了,但是谢谢!另外
my$dictionary=clone$book读起来好多了。虽然这是公认的答案,但用户应该注意,这更多的是针对简单数据结构的浅层复制,而不会解决深度复制,甚至更高级的类结构。
bless{%$self},ref$self
技术只会进行浅层复制。作为引用的
$self
的属性将不会被克隆。例如,
$obj->{ponies}=[qw(Dash Sparkle Jack)]$克隆=$obj->clone;push@{$clone->{ponies},“Pinkie”
将修改这两个对象。@Schwern:是的。我认为深度克隆是你必须使用的“肮脏伎俩”之一。@Schwern:这是一个很好的例子,说明它是如何进行浅层复制的。不过,在我几小时前的评论中(这里和问题评论),我提到了这是肤浅副本的公认答案。我可能最终会听取关于使用
克隆
可存储
的建议,但我觉得如果我开始使用模块,我可能会将其情绪化。公认的解决方案可能会改变:注意,它更适合于浅层复制简单数据结构,但不能解决深层复制,甚至更高级的类结构;这是我最初的问题。请记住,所选答案将来可能会更改。嗨,Rahil Wazir,这里有网站URL:,您可以找到:“Storable为您提供了一个dclone接口,它不会创建中间标量,而是将结构冻结在一些内部内存空间中,然后立即将其解冻。”,“Storable的核心是用C语言编写的,以保证速度。在操作perl内部时进行了额外的低级优化,以牺牲封装来提高速度。"; 我希望事情能对你有所帮助。你能把这部分编辑到你的答案中,而不是评论。我想说,有时候你希望两个克隆对象保持指向同一个引用对象。例如,如果您的
Person
类具有一个
employer
属性,该属性指向表示AcmeCorp
组织的对象,则您可能不希望克隆Bob也克隆AcmeCorp。这些决定只能在一个类别一个类别的基础上做出,并且应该被彻底记录。@Tobynk是的,这就是为什么克隆是通过
$obj->clone
而不是
clone($obj)
完成的。对象可以最好地决定如何克隆它。
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";