在Perl中深拷贝哈希表的最佳方式是什么?该哈希表包含嵌套的哈希表。

27

可能的重复问题:
如何最佳地在Perl中创建数据结构的深层拷贝?

在我开始自己编写代码并重复造轮子之前,如何复制一个哈希表的哈希表而不复制哈希引用?

我正在通过Config::General读取哈希表的哈希表的哈希表,即数据结构为:

my %config = ( group => { item1 => { foo => 'value',
                                     bar => 'value',
                                   },
                          item2 => { foo => 'value',
                                     bar => 'value',
                                   },
                          item3 => { foo => 'value',
                                     bar => 'value',
                                   },
                        },
             );

我通过取消引用(config)得到我的组,然后在重写配置文件之前在运行时更改其内容:

my %group = %{$config{'group'}};

问题在于我需要检查是否进行了更改,并对系统文件结构进行相关更改。我无法通过检查来实现这一点:

if ($group{'item1'}{'foo'} ne $config{'group'}{'item1'}{'foo'}) {
    ### Stuff!
}

由于$group{'item1'}$config{'group'}{'item1'}都是完全相同的哈希引用,因此应该很容易地重新解析配置文件,并将从磁盘中解析的副本与在保存到磁盘之前编辑的版本进行比较。不过,我相信有一种方法可以对复杂数据结构进行嵌套解引用,复制哈希引用的内容而不仅仅是复制引用本身。CPAN的初步检查并没有发现任何有用的信息。我错过了什么吗?

基准测试

得到了答案:

#!/usr/bin/perl

use Benchmark qw(:all) ;
use Storable qw(dclone);
use Clone qw(clone);

my %config = ( group => { item1 => { foo => 'value',
                                     bar => 'value',
                                   },
                          item2 => { foo => 'value',
                                     bar => 'value',
                                   },
                          item3 => { foo => 'value',
                                     bar => 'value',
                                   },
                        },
             );

my $ref = $config{'group'};

timethese(100000, {
  'Clone' => sub { my %group = %{ clone $ref }},
  'Storable' => sub {  my %group = %{ dclone $ref }},
});

结果如下:

基准测试:Clone,Storable的100000次迭代计时...
   Clone:  2秒墙钟时间 ( 2.26用户 +  0.01系统 =  2.27 CPU) @ 44052.86/s (n=100000)
Storable:  5秒墙钟时间 ( 4.71用户 +  0.02系统 =  4.73 CPU) @ 21141.65/s (n=100000)

3
如果你所说的是正确答案,那么你应该将回答你问题的帖子标记为正确答案。 - Sinan Ünür
4个回答

37
use Storable qw(dclone);
$group2 = dclone(\%group);

30

我从Storable::dclone文档中发现了Clone

my $copy = clone (\@array);

# or

my %copy = %{ clone (\%hash) };

不需要灵活性,并声称比Storable::dclone更快。


2
基准测试显示,这比 dclone 快大约两倍。 - Oesor
看起来这个程序没有克隆threads::shared数据结构?无法通过包“threads::shared::tie”定位对象方法“FETCH”。 - ealeon

7

深度数据结构 101:

  • 使用Storabledclone来创建一个数据结构的深拷贝,并使用freezethaw将其序列化/反序列化以进行存储(比如在数据库中或者http cookie中(但是您应该加密发送给用户的任何内容,以使其更难以被篡改))。
  • 使用Data::Compare(或者在单元测试中使用Test::DeepTest::Differences)来比较两个深度数据结构。
  • 在调试中使用Data::Dumper或者Data::Dump查看您的对象长什么样子。但不要利用此作为更改另一个对象内部的许可;请使用API。:)

1
Data::Compare对我来说也是新的,我只是在展开哈希表以检查它们。我一定会尝试使用它来比较真正复杂的东西;谢谢。 - Oesor
1
Test::Deep和Test::Differences因为普遍存在的Test::More的新is_deeply功能而逐渐失宠 - 试试看吧。非常简单易用,而且你会得到一个漂亮的错误提示。 - Evan Carroll

-3

可以通过使用Storable或Data::Dumper存储哈希值,并将存储的值重新分配到新的哈希中。这样可以获得完整的副本,而不必维护引用链接。

use Storable;
my $serialized = freeze \%config;
my %newconfig = %{ thaw($serialized) };

3
有一个特殊的函数dclone用于这种情况。 - Ivan Nevostruev

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接