0

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

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. 那么,幕后发生了什么?

4

3 回答 3

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时,它会改变。

稍微修改一下,这样我就可以有一个更紧凑的输出,并且能够调用 display 作为一个函数。

#!/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

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

于 2013-07-31T18:47:34.503 回答
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

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

于 2013-07-31T18:45:50.380 回答
0

过错。感谢大家的耐心,因为我是第一次重新学习 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;
}
于 2013-08-02T17:18:12.993 回答