【问题标题】:Why does a push of a hash onto an array seem to be overwriting all of the array elements?为什么将哈希推送到数组似乎会覆盖所有数组元素?
【发布时间】:2018-06-07 20:16:08
【问题描述】:

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

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

【问题讨论】:

    标签: perl


    【解决方案1】:

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

    【讨论】:

    • 只是一个旁注。但是只有当哈希是字符串值的简单键时,您的“复制”才有效。如果 %val 还包含 arrayrefs 或 hashrefs 你需要一个“深拷贝”。最好是使用像“克隆”这样的模块。 search.cpan.org/perldoc?Clone
    • 那又怎样?数组不应该包含一堆对哈希的引用,而不是一遍又一遍地覆盖相同的元素吗? push 不只是在数组上添加标量引用吗?
    • @felwithe 是的,原始代码将两个对同一哈希的引用推送到数组中。然后作者在修改引用的哈希,想知道为什么在修改哈希之前它不包含哈希值。数组中没有任何内容被覆盖,只是哈希值发生了变化。
    【解决方案2】:

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

    【讨论】:

      【解决方案3】:

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

      【讨论】:

        【解决方案4】:

        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 正在使用的内存,即使您将新的空哈希分配给@ 987654334@,引用现在将指向那个空哈希。

        基本上可以说:在 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
        );
        

        但这只是语法糖,事实上你也可以使用,而不是=&gt;

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

        将散列转换为列表时,每个第一个列表项都是一个键,每个第二个列表项都是它的值,所以当从一个列表创建一个散列时,列表值就是这样解释的。现在如果你想直接创建一个哈希的引用,你可以这样写:

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

        这和写法基本一样:

        my $hashRef = { %myHash };
        

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

        push @people, { %person };
        

        除了新创建的hash的引用直接push到数组上。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2016-07-16
          • 1970-01-01
          • 1970-01-01
          • 2021-03-20
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多