在Perl中复制哈希引用

34

可能是重复问题:
在Perl中制作数据结构的深拷贝的最佳方法是什么?

我好像没有理解Perl中哈希引用的工作原理,现在想要纠正这一点。

我需要获取一个哈希引用的副本,以便在不修改原始哈希引用的情况下进行操作。根据我的研究,复制哈希引用只需使用等号运算符即可:

my $hashref_copy = $hashref;

但据我所知,这只是将$hashref_copy作为原始对象的一个指针。考虑下面这个玩具代码:


my $hashref = {data => "fish"};

my $hashref_copy = $hashref;
$hashref_copy->{data} = "chips";

print "$hashref->{data}\n";
如果$hashref_copy是一个真正的独立副本,我期望这段代码会打印“fish”,但实际上它打印出来的是“chips”。 所以,要么1)我有什么误解,要么2)Perl存在问题。尽管我的自尊心让我相信是第二种情况,但我相当确定不是这样。
我哪里错了?我需要做什么才能使对$hashref_copy的修改不会影响到原始的$hashref

请参见:https://dev59.com/1HI_5IYBdhLWcg3wDOnW#1546334 - Joel
实际上,将一个标量赋值给另一个标量并不会导致任何哈希被复制。它只是复制了标量的值。您正在询问如何复制哈希,创建对其的引用,并将该引用放入标量中。 - ikegami
2
我不确定将其标记为重复项是否有帮助 - 所涉及的问题是制作深拷贝的最有效/清洁方式。这是一个更基本的问题。 - Ross Attrill
3个回答

77

当您将哈希引用复制到另一个标量中时,相当于复制对同一哈希的引用。这类似于将一个指针复制到另一个指针中,但不更改所指向的内存。

您可以轻松创建哈希的浅层副本:

my $copy = { %$source };
%$source在列表上下文中会扩展为键值对列表。然后,花括号(匿名哈希构造器)使用该列表创建一个新的哈希引用。如果您的结构是一维的,或者您不需要克隆任何包含的数据结构,则浅层复制就可以了。
要进行完全深度拷贝,您可以使用核心模块Storable
use Storable 'dclone';

my $deep_copy = dclone $source;

2
请参见Clone - Zaid
在64位Linux中,使用Storable qw(clone)对我来说很好用。使用Clone qw(clone)会导致完全相同的代码由于Perl内部的segfault而崩溃。建议使用Storable - code_dredd

10

是的, 赋值只是复制引用(类似在其他语言中复制指针值). 你需要的是所谓的"深度复制".

Storable::dclone 似乎是一种解决方案(perldoc Storable获取更多信息).

Clone 是另一种解决方案 (perldoc clone); 感谢Zaid在评论中提到这个.


Storable::dclone 对于我的目的来说似乎有点过头了,但还是谢谢你;这是值得拥有的信息。 - BlairHippo
1
请参见Clone - Zaid

4
如果只是一个哈希值,而你只是想要它的副本:
my $hashref = {data => "a"};

my %hash_copy = %{$hashref}; # Create a copy of the hash that $hashref points to
my $hashref_copy = \%hash_copy; # Ref to %hash_copy
$hashref_copy->{data} = "b";

print "$hashref->{data}\n"; # Outputs 'a'
print "$hashref_copy->{data}\n"; # Outputs 'b'

谢谢,这确实会起作用。 - BlairHippo
6
@BlairHippo:你可能已经知道了,但我还是要提醒一下,如果哈希值中有一些是引用,那么复制的只是这些引用,而不是它们所指向的内容。听起来对你并不是问题,但请记住这一点。 - Keith Thompson
1
@Keith Thompson:非常感谢,你说的都对。 :-) 我已经记录了那行代码,以表明如果有人要求散列引用存储比标量更复杂的信息,则需要像Storable::dclone或Clone::clone这样的深拷贝机制。 - BlairHippo
@BlairHippo:用Storable::dclone或者Clone::clone来替换注释可能更容易。这只是一个想法。 - Keith Thompson

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