首页 > 解决方案 > Perl 哈希数组正在添加来自其他数组的元素

问题描述

我在 perl 5.34.0 中创建了一个哈希数组:

#!/usr/bin/env perl

use strict;
use warnings FATAL => 'all';
use autodie ':default';
use DDP;

my (@a1, @a2);
my %h = ('a' => 1);
push @a1, \%h; # make @a1 a hash of array with 'a' defined
$h{b} = 2; # define b within the hash, but don't write to @a1
push @a2, \%h; # push `a` and `b` onto @a2, but *not* @a1
p @a1; # DDP gives "p" which pretty-prints
p @a2;

这输出:

[
    [0] {
            a   1,
            b   2
        }
]
[
    [0] {
            a   1,
            b   2
        }
]

问题是b密钥显示在 中@a1,但$h{b}在写入数据时不存在@a1

我不想b出现@a1,也不应该出现。

如何修改%h,使其不会神奇地出现在不同的数组中?

标签: perl

解决方案


该代码添加了对现有(命名)哈希的引用,

push @a1, \%h;

因此,当稍后查询时,您会看到当时在哈希中看到的任何内容。该数组仅携带一个带有数据地址的指针,也称为哈希;它是某种别名。因此,如果哈希同时被写入,那么这就是您将通过@a1. †</sup>

再次添加它,即使哈希改变了,您也只需添加相同的旧引用。

您想要的是制作数据副本并添加对其的引用——匿名哈希

push @a1, { %h };    # but may need deep copy instead

现在,哈希数据被复制以填充由构造的匿名哈希,{}并且我们拥有独立的数据,只能通过其在中的引用写入来更改这些数据@a1

但请注意,如果该散列中的值本身是引用,那么这些引用会被复制,我们也会遇到同样的问题!在这种情况下,您需要一个深拷贝,最好使用库来完成;Storable::dclone是个好人

use Storable qw(dclone);

push @a1, dclone \%h;

现在所有的实际数据都被复制了,用于(参考)一个独立的数据副本@a1


一个重要的例外,经常使用

foreach my $elem (@ary) { 
    my %h;
    # ... code that does its work and populates the hash ...
    push @res, \%h;
}

现在没关系,因为哈希%h在每次迭代时都会重新创建,并在循环内声明。那么在上一次迭代中创建的数据会发生什么?

由于它的引用被添加到数组中,因此数据本身被保留,由数组中的该引用引用,并且仅由该引用引用。正是您想要的,只能通过数组访问的单独数据。

在这种情况下,这优于push @res, { %h },这也有效,因为在保存数据的同时避免了数据复制,也就是说,仅更改其所有权。


†</sup> 并且 ff 数据通过@a1, like更改$a1[0]->{key} = 'val';,然后也可以看到新值$h{key}


推荐阅读