perl 为什么将哈希值压入数组似乎会覆盖所有数组元素?

r55awzrz  于 2023-02-13  发布在  Perl
关注(0)|答案(5)|浏览(193)

只是想知道我是否做错了什么或者这是一个perl bug...我想创建一个散列值的数组。我正在使用“push”将值放入数组。第一次将散列写入数组时工作正常,但当我将第二个不同的散列推入数组时,数组的第一个元素似乎被我刚刚推到数组上的内容覆盖了。2为什么会发生这种情况?3请看下面的代码:

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";
fwzugrvs

fwzugrvs1#

“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";
j2datikz

j2datikz2#

注意到你是如何将一个 reference 推送到散列%瓦尔的吗?如果你修改了那个散列,那么这个reference自然会指向一个不同的值。

kiz8lqtg

kiz8lqtg3#

这是很自然的:两次使用同一个容器%val。显式覆盖其内容。

6kkfgxo0

6kkfgxo04#

Perl中的引用行为与许多程序员在其他编程语言中使用的引用行为稍有不同。例如,考虑以下Perl代码:

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

然后你得到了一个对它的引用

my $personRef = \%person;

在大多数编程语言中,该语句读作“* 获取%person使用的后备存储器的内存地址并将其写入$personRef*"。在这些语言中,我可以更改%person本身(而不是它所指向的内存),这不会对$personRef产生任何影响,因为$personRef仍然指向与以前相同的内存,仍然包含与以前相同的值。
但是在Perl中,这条语句读起来更像是“Let $personRefalwayspoint to memory currently in use by %person"这里重要的关键字是always,因为这意味着如果你对%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 };

除了新创建的哈希的引用被直接推到数组上。

nfs0ujit

nfs0ujit5#

有一个简单的方法可以形象化发生的事情:
use Data::Dumper;之后添加此行:

$Data::Dumper::Deepcopy = 1;

将您的照片替换为:

print 'After push 1 size of @myArr is ' . @myArr . " : @myArr \n" . Dumper(\@myArr);

以及

print 'After push 2 size of @myArr is ' . @myArr . " : @myArr \n" . Dumper(\@myArr);
print 'Address of %val is: ' . \%val . "\n";
  • 注意Dumper(\@myArr)的用法,而不是问题中的Dumper(@myArr)。*

您将获得以下输出:

After push 1 size of @myArr is 1 : HASH(0xa0003bfb0)
$VAR1 = [
          {
            'key1' => 'Val1'
          }
        ];
After push 2 size of @myArr is 2 : HASH(0xa0003bfb0) HASH(0xa0003bfb0)
$VAR1 = [
          {
            'key2' => 'Val2'
          },
          {
            'key2' => 'Val2'
          }
        ];
Address of %val is: HASH(0xa0003bfc0)

现在很明显,$myArr [0]和$myArr [1]包含相同的值,即% val的地址:0xa0003bfb0.
因此,% val、% {$myArr [0 ]}和% {$myArr [1 ]}是相同的散列。更改其中一个会影响其他散列。

  • 注意:$Data::Dumper::Deepcopy = 1;在存在交叉引用时禁用可视化优化。请在不使用此行的情况下进行测试,并查看Data::Dumper文档。*

相关问题