使用相同的哈希,为什么在散列引用后不会覆盖数据?

时间:2013-07-31 18:01:12

标签: perl

我编写了一个使用哈希构建复杂数据结构的例程。

use strict;
my %th1 = ();
my %th2 = ();
my $idx = 0;

$th2{"suffix"} = "A";
$th2{"status"} = 0;
$th2{"consumption"} = 42;

$th1{$idx} = \%th2;

$idx++;

$th2{"suffix"} = "B";
$th2{"status"} = 0;
$th2{"consumption"} = 105;

$th1{$idx} = \%th2;

for my $key1 (keys %th1)
{
    for my $key2 (keys %{$th1{$key1}})
    {
        print "Key1=$key1, Key2=$key2, value=" . $th1{$key1}->{$key2} . "\n\n";
    }
}

我的问题是,当分配哈希引用时,为什么$idx == 0的第一组数据不会被破坏?

分配哈希引用时是否创建了副本$th1{$idx} = \%th2;

执行此行时

$th2{"suffix"} = "B";

为什么$th1{0}的哈希值没有损坏?

值没有被破坏,但我很好奇保留这些值的机制。代码未明确创建%th2的新副本。那么,幕后发生了什么?

3 个答案:

答案 0 :(得分:2)

该程序的输出是:

Key1=1, Key2=status, value=0

Key1=1, Key2=suffix, value=B

Key1=1, Key2=consumption, value=105

Key1=0, Key2=status, value=0

Key1=0, Key2=suffix, value=B

Key1=0, Key2=consumption, value=105

如果您在运行时看到不同的内容,请说明您所看到的内容。

没有腐败,也没有复制。 %th1包含指向其他哈希的指针。

一个内存位置有一个哈希值,另一个哈希值另一个哈希值。在您修改%th2时,它会发生变化。

稍微修改一下,以便我可以拥有更紧凑的输出,并能够将显示调用为函数。

#!/usr/bin/perl

my %th1 = ();
my %th2 = ();

$th2{"suffix"} = "A";
$th2{"status"} = 0;
$th2{"consumption"} = 42;
$th1{0} = \%th2;

print "--- After first block:\n";
display(\%th1);


$th2{"suffix"} = "B";

print "--- Modified th2 suffix to B:\n";
display(\%th1);

$th2{"status"} = 0;
$th2{"consumption"} = 105;

print "--- finished modification of th2:\n";
display(\%th1);

$th1{1} = \%th2;

print "--- after assignment to th1{1} :\n";
display(\%th1);

exit;

sub display {
    my $hr = shift;
    for my $key1 (keys %$hr) {
        print "$key1:\n";
        for my $key2 (keys %{$hr->{$key1}}) {
            print "\t$key2 = $hr->{$key1}{$key2}\n";
        }
    }
}

这个输出是:

--- After first block:
0:
        status = 0
        suffix = A
        consumption = 42
--- Modified th2 suffix to B:
0:
        status = 0
        suffix = B
        consumption = 42
--- finished modification of th2:
0:
        status = 0
        suffix = B
        consumption = 105
--- after assignment to th1{1} :
1:
        status = 0
        suffix = B
        consumption = 105
0:
        status = 0
        suffix = B
        consumption = 105

您可以看到%th2的修改在%th1中的解除引用值中生效。

让我们以不同的方式看待它...而不是打印出值,让我们打印出%th1包含的内容?两个变化......添加了显示内存附近的行:

my %th1 = ();
my %th2 = ();

print \%th1, "\t", \%th2,"\n";  # this line added

display已更改:

sub display {
    my $hr = shift;
    for my $key1 (keys %$hr) {
        print "$key1 --> $hr->{$key1}\n";
    }
}

现在输出是:

HASH(0x239edb0) HASH(0x239edf8)
--- After first block:
0 --> HASH(0x239edf8)
--- Modified th2 suffix to B:
0 --> HASH(0x239edf8)
--- finished modification of th2:
0 --> HASH(0x239edf8)
--- after assignment to th1{1} :
1 --> HASH(0x239edf8)
0 --> HASH(0x239edf8)

%th1的值一直指向单个哈希值。没有副本,只有一个哈希在%th1的后面更改。


有可能,您希望每个点都有单独的值。通过创建匿名哈希并分配:

,可以轻松完成此操作
#!/usr/bin/perl

my %th1 = ();
my %th2 = ();

$th1{0} = {"suffix" => "A", "status" => 0, "consumption" => 42 };

print "--- After first block:\n";
display(\%th1);

$th1{1} = {"suffix" => "B", "status" => 0, "consumption" => 105 };

print "--- after assignment to th1{1} :\n";
display(\%th1);

exit;

sub display {
    my $hr = shift;
    for my $key1 (keys %$hr) {
        print "$key1: $hr->{$key1}\n";
        for my $key2 (keys %{$hr->{$key1}}) {
            print "\t$key2 = $hr->{$key1}{$key2}\n";
        }
    }
}

打印哪些:

--- After first block:
0: HASH(0xcf6998)
        status = 0
        suffix = A
        consumption = 42
--- after assignment to th1{1} :
1: HASH(0xd143c0)
        status = 0
        suffix = B
        consumption = 105
0: HASH(0xcf6998)
        status = 0
        suffix = A
        consumption = 42

您可以看到两个独立的内存地址和两组不同的值。

答案 1 :(得分:0)

为什么你认为价值被复制?如果我运行你的代码,我得到了 以下输出(删除空行):

Key1=1, Key2=status, value=0
Key1=1, Key2=suffix, value=B
Key1=1, Key2=consumption, value=105
Key1=0, Key2=status, value=0
Key1=0, Key2=suffix, value=B
Key1=0, Key2=consumption, value=105

这正是我所期待的,并表明没有正在进行复制 通过任一哈希引用产生相同的结果。

答案 2 :(得分:0)

Mea Culpa。我很感激每个人的耐心,因为我第一次重新学习Perl。我在OP中提出了错误的问题。

我应该问一下如何创建动态变量,因此可以将其引用(对新哈希变量的唯一引用)分配给另一个哈希变量。我在OP中写的直线代码将无效,正如人们所评论的那样。

以下 - 我已经测试和工作 - 是我最后做的事情。不知道Perl内部,我假设每次执行此语句时都会创建一个新的动态变量:(请注意下面的代码示例是OP的样本所进入的。)

my %read_data = ();

每次执行$tiered_cns{$idx} = \%read_data;时,引用都是另一个变量。

sub ws_get_cons_from_latest_read

# $PkNam Package name. We disregard it.
# $DBHdl --  ICS database handle.
# $acct_no -- water acct number (integer)
# $time_stamp -- usually this value is today's date as a year to second
{
# Grab input parameters.
my ($PkNam, $DBHdl, $acct_no, $time_stamp) = @_;

# Declare local variables.
my $ptSelHdl = undef;
my $ptMtrRecRef = undef;
my $ptWsMtrReadRecRef = undef;

my $consumption = 0;
my $statement = "";
my %tiered_cns = ();
my $idx = undef;

die("wgbl_get_cons_from_latest_read passed undef handles.")
  if(!defined($DBHdl) || !defined($acct_no));

$statement = "select    m.* ".
             "from      meter m ".
             "where     m.acct_no = ".$acct_no;

$ptSelHdl = $DBHdl->prepare($statement);

if(!$ptSelHdl || !$ptSelHdl->execute)
{
  die("Could not prepare select suffix numdigits from meter statement.");
} 

$ptMtrRecRef = $ptSelHdl->fetchrow_hashref;

$idx = 0;

if(!defined($ptMtrRecRef))
{
  my %read_data = ();
  $read_data{"status"} = MISSING_METER_REC;
  $read_data{"suffix"} = " ";
  $read_data{"consumption"} = 0;
  $tiered_cns{$idx} = \%read_data;
}
else
{
  do
  {
    my %read_data = ();

    $ptWsMtrReadRecRef = MtrGblFunc->mgbl_get_latest_ws_mtr_rec($DBHdl,
                        $ptMtrRecRef->{"acct_no"},
                        $ptMtrRecRef->{"suffix"});


    if(!$ptWsMtrReadRecRef)
    {
      $read_data{"status"} = MISSING_LATEST_READ;
      $read_data{"suffix"} = $ptMtrRecRef->{"suffix"};
      $read_data{"consumption"} = 0;
    }
    else
    {
      $consumption = WsGblFunc->wgbl_calc_mtr_cons( $DBHdl,
                     $acct_no,
                     $ptMtrRecRef->{"suffix"},
                     $ptWsMtrReadRecRef->{"counter"},
                     $ptWsMtrReadRecRef->{"reading"},
                     $ptWsMtrReadRecRef->{"date_read"},
                     $time_stamp); 

      $read_data{"status"} = SUCCESS;
      $read_data{"suffix"} = $ptMtrRecRef->{"suffix"};
      $read_data{"consumption"} = $consumption;

      $tiered_cns{$idx} = \%read_data;
      $idx++;

      $ptMtrRecRef = $ptSelHdl->fetchrow_hashref;

     }
    } until !defined($ptMtrRecRef) || !defined($ptWsMtrReadRecRef);
  }

   return \%tiered_cns;
}