我使用从0开始的递增数字键将数据添加到哈希。键/值很好。当我添加第二个时,第一个键/值对指向第二个。之后的每次添加都会替换第二个键的值,然后再指向它。 Dumper
输出将是这样的。
$VAR1 = { '0' => { ... } };
添加第一个键/值后。在添加第二个之后我得到了
$VAR1= { '1' => { ... }, '0' => $VAR1->{'1} };
添加第三个键/值后,它看起来像这样。
$VAR1 = { '1' => { ... }, '0' => $VAR1->{'1'}, '2' => $VAR1->{'1'} };
我的问题是为什么这样做?我希望每个键/值都显示在哈希中。当我遍历哈希时,我得到每个键/值的相同数据。如何摆脱第二个添加键的引用指针?
答案 0 :(得分:4)
您正在将每个元素的值设置为对同一哈希的引用。 Data :: Dumper只是反映了这一点。
如果您使用Data :: Dumper作为序列化工具(哎呀!),那么您应该将$Data::Dumper::Purity
设置为1
以获得eval
可以处理的内容。
use Data::Dumper qw( Dumper );
my %h2 = (a=>5,b=>6,c=>7);
my %h;
$h{0} = \%h2;
$h{1} = \%h2;
$h{2} = \%h2;
print("$h{0}{c} $h{2}{c}\n");
$h{0}{c} = 9;
print("$h{0}{c} $h{2}{c}\n");
{
local $Data::Dumper::Purity = 1;
print(Dumper(\%h));
}
输出:
7 7
9 9
$VAR1 = {
'0' => {
'c' => 9,
'a' => 5,
'b' => 6
},
'1' => {},
'2' => {}
};
$VAR1->{'0'} = $VAR1->{'1'};
$VAR1->{'2'} = $VAR1->{'1'};
另一方面,如果您不想将商店引用用于不同的哈希值,则可以使用
# Shallow copies
$h{0} = { %h2 }; # { ... } means do { my %anon = ( ... ); \%anon }
$h{1} = { %h2 };
$h{2} = { %h2 };
或
# Deep copies
use Storable qw( dclone );
$h{0} = dclone(\%h2);
$h{1} = dclone(\%h2);
$h{2} = dclone(\%h2);
输出:
7 7
9 7
$VAR1 = {
'0' => {
'a' => 5,
'b' => 6,
'c' => 9
},
'1' => {
'a' => 5,
'b' => 6,
'c' => 7
},
'2' => {
'a' => 5,
'b' => 6,
'c' => 7
}
};
答案 1 :(得分:2)
您尚未发布用于构建哈希的实际代码,但我认为它看起来像这样:
foreach my $i (1 .. 3) {
%hash2 = (number => $i, foo => "bar", baz => "whatever");
$hash1{$i} = \%hash2;
}
(实际上,我猜你在实际的代码中,你可能正在从while (<>)
循环中的文件中读取数据,并根据它将值分配给%hash2
,但是{ {1}}循环将用于演示目的。)
如果您运行上面的代码并使用Data :: Dumper转储生成的foreach
,您将获得输出:
%hash1
为什么会这样?好吧,这是因为$VAR1 = {
'1' => {
'baz' => 'whatever',
'number' => 3,
'foo' => 'bar'
},
'3' => $VAR1->{'1'},
'2' => $VAR1->{'1'}
};
中的值都是指向相同哈希的引用,即%hash1
。在循环中为%hash2
分配新值时,这些值将覆盖%hash2
中的旧值,但它仍将是相同的散列值。 Data :: Dumper只是强调了这一事实。
那么,你怎么解决它?嗯,有(至少)两种方式。一种方法是将%hash2
替换为\%hash2
,将%hash2
替换为{ %hash2 }
,将%hash2
的内容替换为新的匿名hash并返回对它的引用:
foreach my $i (1 .. 3) {
%hash2 = (number => $i, foo => "bar", baz => "whatever");
$hash1{$i} = { %hash2 };
}
另一种(IMO最好的)方法是使用%hash2
在循环中将my
声明为(词法范围的)局部变量:
foreach my $i (1 .. 3) {
my %hash2 = (number => $i, foo => "bar", baz => "whatever");
$hash1{$i} = \%hash2;
}
这样,循环的每次迭代都会创建一个 new ,不同的散列名为%hash2
,而在之前的迭代中创建的散列将继续存在(因为它们是从%hash1
)独立。
顺便说一下,如果您遵循标准的Perl最佳实践,首先就不会遇到这个问题,具体而言:
始终use strict;
(和use warnings;
)。这会强迫您使用%hash2
声明my
(虽然它不会强迫您在循环中
始终在尽可能小的范围内声明局部变量。在这种情况下,由于%hash2
仅在循环中使用,因此您应该在循环中声明它,如上所述。
遵循这些最佳实践,上面的示例代码如下所示:
use strict;
use warnings;
use Data::Dumper qw(Dumper);
my %hash1;
foreach my $i (1 .. 3) {
my %hash2 = (number => $i, foo => "bar", baz => "whatever");
$hash1{$i} = \%hash2;
}
print Dumper(\%hash1);
正如预期的那样,将打印出来:
$VAR1 = {
'1' => {
'baz' => 'whatever',
'number' => 1,
'foo' => 'bar'
},
'3' => {
'baz' => 'whatever',
'number' => 3,
'foo' => 'bar'
},
'2' => {
'baz' => 'whatever',
'number' => 2,
'foo' => 'bar'
}
};
答案 2 :(得分:0)
当您不发布Data :: Dumper的代码或实际结果时,很难看出问题是什么。
关于Data :: Dumper,您应该知道一件事:当您转储数组或(特别是)散列时,您应该转储对它的引用。否则,Data :: Dumper会将其视为一系列变量。另请注意,哈希 不 保留在您创建它们的顺序中。我在下面附上一个例子。确保您的问题与令人困惑的Data :: Dumper输出无关。
另一个问题:如果您通过顺序键键入哈希值,那么使用数组会更好吗?
如果可以,请编辑您的问题以发布您的代码和实际结果。
use strict;
use warnings;
use autodie;
use feature qw(say);
use Data::Dumper;
my @array = qw(one two three four five);
my %hash = (one => 1, two => 2, three => 3, four => 4);
say "Dumped Array: " . Dumper @array;
say "Dumped Hash: " . Dumper %hash;
say "Dumped Array Reference: " . Dumper \@array;
say "Dumped Hash Reference: " . Dumper \%hash;
输出:
Dumped Array: $VAR1 = 'one';
$VAR2 = 'two';
$VAR3 = 'three';
$VAR4 = 'four';
$VAR5 = 'five';
Dumped Hash: $VAR1 = 'three';
$VAR2 = 3;
$VAR3 = 'one';
$VAR4 = 1;
$VAR5 = 'two';
$VAR6 = 2;
$VAR7 = 'four';
$VAR8 = 4;
Dumped Array Reference: $VAR1 = [
'one',
'two',
'three',
'four',
'five'
];
Dumped Hash Reference: $VAR1 = {
'three' => 3,
'one' => 1,
'two' => 2,
'four' => 4
};
答案 3 :(得分:0)
这样做的原因是你给同一个哈希提供了相同的引用 大概是在一个循环结构中。
这是一个有这种行为的简单程序。
use strict;
use warnings;
# always use the above two lines until you
# understand completely why they are recommended
use Data::Printer;
my %hash;
my %inner; # <-- wrong place to put it
for my $index (0..5){
$inner{int rand} = $index; # <- doesn't matter
$hash{$index} = \%inner;
}
p %hash;
要修复它,只需确保每次循环都要创建一个新的哈希引用。
use strict;
use warnings;
use Data::Printer;
my %hash;
for my $index (0..5){
my %inner; # <-- place the declaration here instead
$inner{int rand} = $index; # <- doesn't matter
$hash{$index} = \%inner;
}
p %hash;
如果您只是为索引使用数字,并且它们从0开始单调增加,那么我建议使用数组。
数组会更快,内存效率更高。
use strict;
use warnings;
use Data::Printer;
my @array; # <--
for my $index (0..5){
my %inner;
$inner{int rand} = $index;
$array[$index] = \%inner; # <--
}
p @array;