为什么将哈希推送到数组似乎会覆盖所有数组元素?

时间:2012-02-06 18:55:26

标签: perl

只是想知道我做错了什么或者这是一个perl bug ...我想创建一个哈希值数组。我正在使用'push'将值放到数组中。第一次向数组写入哈希工作正常,但是当我将第二个不同的哈希值推送到数组时,第一个数组元素似乎被我刚刚推入数组的内容覆盖了。为什么会这样?请参阅以下代码:

use Data::Dumper;

my %val;

%val = (key1 => "Val1");

my @myArr;

my $cnt = push(@myArr,\%val);

print "After push (should contain 1 element): " . Dumper(@myArr) . "\n";

%val = (key2 => "Val2");

my $cnt = push(@myArr,\%val);

print "After push 2: (should contain 2 different elements):" . Dumper(@myArr) . "\n";
print " You can see above that element 1 and 2 of the array equal each other when they should be different\n";

4 个答案:

答案 0 :(得分:18)

“perl bug” - 是的,很有可能。 : - )

您正在将对哈希的引用推送到您的数组中,然后更改该哈希值,然后再次推送相同的引用。

您可能需要副本或完整的不同哈希:

不同的变量:

#!/usr/bin/perl

use strict; # always use strict
use warnings;
use Data::Dumper;

my ( %val, %other_val, @myArr );

%val       = ( key1 => "Val1" );
%other_val = ( key2 => "Val2" );

push(@myArr, \%val);
push(@myArr, \%other_val);

print Dumper(\@myArr) . "\n";

复制:

#!/usr/bin/perl

use strict; # always use strict
use warnings;
use Data::Dumper;

my ( %val, %other_val, @myArr );

%val = ( key1 => "Val1" );
push(@myArr, { %val } );

%val = ( key2 => "Val2" );
push(@myArr, { %val } );


print Dumper(\@myArr) . "\n";

答案 1 :(得分:2)

注意你是如何将引用推送到hash%val的?好吧,如果你修改那个哈希,引用自然会指向不同的值。

答案 2 :(得分:0)

这很自然:您使用相同的容器%val两次。您明确覆盖其内容。

答案 3 :(得分:0)

Perl中的引用与其他编程语言中使用的许多程序员的行为略有不同。例如。考虑一下这个Perl代码:

my %person = ();
$person{"name"} = "John Doe";
$person{"age"} = 34;

然后你获得了它的引用:

my $personRef = \%person;

在大多数编程语言中,该语句读作“获取%person使用的后备存储的内存地址并将其写入$personRef ”。在这些语言中,我可以更改%person本身(而不是它指向的内存),这对$personRef仍然没有任何影响仍然指向与之前相同的内存,它仍然包含与之前。

但是在Perl中,这句话反映为“$personRef 总是指向%person ”当前使用的内存。这里重要的关键字始终是,因为这意味着如果您对%person进行更改,这些更改也会立即反映在参考文件中。

因此,当您在Perl中执行此操作时:

my %person = ();
$person{"name"} = "John Doe";
$person{"age"} = 34;

my $personRef = \%person;

%person = (); # <-- Assigns new empty hash!
$person{"name"} = "Jane Doe";
$person{"age"} = 47;

print "$personRef->{'name'}\n";

它实际上会打印“Jane Doe”,因为无论你对%person做什么,$personRef总是会指向%person使用的内存,即使你指定了一个新的空哈希到%person,引用现在将指向空哈希。

基本上可以说:在Perl中,引用不是对值使用的内存的引用,而是引用这些值的变量。在任何时候使用引用来访问该值,引用变量的当前值将用于此,并且自创建引用以来,这可能已经发生了很多次更改。

接受的答案通过复制哈希来解决这个问题:

my @people = ();

my %person = ();
$person{"name"} = "John Doe";
$person{"age"} = 34;
push @people, { %person };

%person = ();
$person{"name"} = "Jane Doe";
$person{"age"} = 47;
push @people, { %person };

但是等等,这里的哈希在哪里被复制?嗯,这是Perl魔术。要创建新哈希,您可以使用该语法:

my %hash = (
    "name" => "John Doe",
    "age" => 34
);

但这只是语法糖,事实上你也可以使用,代替=>

my %hash = ("name", "John Doe", "age", 34);

将哈希值转换为列表时,每个第一个列表项都是一个键,每个第二个列表项都是其值,因此在从列表创建哈希值时,列表值的解释方式就是这样。现在,如果要直接创建对哈希的引用,可以编写:

my $hashRef = { "name", "John Doe", "age", 34 };

这与写作基本相同:

my $hashRef = { %myHash };

这读作“创建新哈希({}),用%myHash中的值填充它并将其引用写入$hashRef ” 。这正是这一行中发生的事情:

push @people, { %person };

除了将新创建的哈希引用直接推送到数组之外。