1

当我遇到这种意外情况时,我正试图将一个哈希分配给另一个。

我正在打印转储程序以验证哈希是否正确形成。

当我迭代哈希时,Data::Dumper 确实提供了预期的输出,但是当我打印整个哈希时它显示了一些意外的结果。

请参阅下面的代码片段。任何见解都会有很大帮助。

my (@aBugs) = (111,222,333);
my $phBugsRec;
my $phProfiles;
$phProfiles->{profiles} = { 'profile1' => 'default1' };

形成最终的哈希:

foreach my $pBugNo(@aBugs){
    $phBugsRec->{bugAttributes}{$pBugNo}{totalEffort} = 0;
    $phBugsRec->{bugAttributes}{$pBugNo}{profiles}    = $phProfiles->{profiles};
}

如果我转储整个哈希,我不会得到预期的输出:

print '<pre>'.Dumper($phBugsRec).'</pre>';

$VAR1 = {
    'bugAttributes' => {
        '333' => {
            'totalEffort' => 0,
            'profiles' => {
                'profile1' => 'default1'
            }
        },
        '111' => {
            'totalEffort' => 0,
            'profiles' => $VAR1->{'bugAttributes'}{'333'}{'profiles'}
        },
        '222' => {
            'totalEffort' => 0,
            'profiles' => $VAR1->{'bugAttributes'}{'333'}{'profiles'}
        }
    }
};

但是当我遍历哈希时,我得到了预期的输出

foreach (sort keys $phBugsRec->{bugAttributes}){
    print '<pre>'.$_.':'.Dumper($phBugsRec->{bugAttributes}{$_}).'</pre>';
}

111:$VAR1 = {
  'totalEffort' => 0,
  'profiles' => {
    'profile1' => 'default1'
  }
};
222:$VAR1 = {
  'totalEffort' => 0,
  'profiles' => {
    'profile1' => 'default1'
  }
};
333:$VAR1 = {
  'totalEffort' => 0,
  'profiles' => {
    'profile1' => 'default1'
  }
};
4

2 回答 2

3

正如Tripleee 在他们的评论中所说,这没有错。我同意这可能出乎意料。发生这种情况是因为您在数据结构中多次使用相同的引用。这是由于 Perl 的引用是如何工作的。

Perl 中引用的简要概述

在perlrefperlreftutperldscperllol中解释了参考。

一旦你在 Perl 中有一个多层次的数据结构,第一层次之后的所有层次都被存储为引用。->运算符用于取消引用它们。Perls 将它们转换回散列或数组。如果您说$foo->{bar}->{baz}要获取内部值,则基本上是在遍历数据结构。

如果您$foo->{bar}->{baz} = 123直接设置,Perl 会自动为您创建所有这些引用。但你也可以自己做参考。

my @numbers = (42, 23, 1337);
my $ref = \@numbers;

print Dumper $ref;

__END__
$VAR1 = [ 42, 23, 1337 ]

这是对该数组的单一引用。如果你在同一个数据结构中多次使用它,它就会显示出来。

my $hash = { 
    foo => $ref, 
    bar => $ref,
};

__END__

$VAR1 = {
      'foo' => [
                 42,
                 23,
                 1337
               ],
      'bar' => $VAR1->{'foo'}
};

看起来和你的例子一样,对吧?让我们试试别的。如果你在标量上下文中打印一个引用,Perl 会告诉你它的地址。

print "$ref";

__END__
ARRAY(0x25df7b0)

我们都看到了这一点,当我们第一次看到它时,我们都认为某些事情是严重错误的。$hash让我们从上面回到我们的。

say $hash->{foo};
say $hash->{bar};

__END__
ARRAY(0x16257b0)
ARRAY(0x16257b0)

如您所见,它是相同的地址,因为它是相同的数据结构。

其他 Perl 序列化程序

这就是您的数据结构在Data::Dump中的样子。

do {
  my $a = {
    bugAttributes => {
      111 => { profiles => { profile1 => "default1" }, totalEffort => 0 },
      222 => { profiles => 'fix', totalEffort => 0 },
      333 => { profiles => 'fix', totalEffort => 0 },
    },
  };
  $a->{bugAttributes}{222}{profiles} = $a->{bugAttributes}{111}{profiles};
  $a->{bugAttributes}{333}{profiles} = $a->{bugAttributes}{111}{profiles};
  $a;
}
1

Data::Dump用于创建人类可读且可以放回 Perl 的输出。它比Data::Dumper更简洁一些。您可以看到它还显示了在数据结构中多次使用的值。

这就是Data::Printer对它所做的。

\ {
    bugAttributes   {
        111   {
            profiles      {
                profile1   "default1"
            },
            totalEffort   0
        },
        222   {
            profiles      var{bugAttributes}{111}{profiles},
            totalEffort   0
        },
        333   {
            profiles      var{bugAttributes}{111}{profiles},
            totalEffort   0
        }
    }
}

Data::Printer仅供人类使用。您不能将其作为代码运行,而是为了易于阅读。同样,它还表明在数据结构内部重用了东西。

所有这一切的结论是,那些序列化程序会这样做,因为要证明某些东西被重用并不容易。甚至当你用 Perl 说它的时候。

为什么看不到整个数据结构

如果 Perl 忽略了数据结构的某些部分已被重用的事实,那么序列化将是不可逆的。读回它的结果将是另一回事。这当然不是你会做的。

序列化而不重用

为了表明您的数据实际上没有丢失,这实际上只是一种显示(和端口)在数据结构内部重用的方法,我使用JSON 模块将其转换为 JSON ,这是一种可移植格式可以与 Perl 一起使用,但那不是 Perl

use JSON 'encode_json';
say JSON->new->pretty->encode( $phBugsRec);

这是结果。它看起来更像您的预期。

{
   "bugAttributes" : {
      "333" : {
         "profiles" : {
            "profile1" : "default1"
         },
         "totalEffort" : 0
      },
      "111" : {
         "totalEffort" : 0,
         "profiles" : {
            "profile1" : "default1"
         }
      },
      "222" : {
         "profiles" : {
            "profile1" : "default1"
         },
         "totalEffort" : 0
      }
   }
}

那是因为 JSON 是一种可移植的格式。它用于移动数据。关于它可以包含什么已经达成一致,并且重用数据不是其中的一部分。并非所有实现 JSON 读写的语言都支持重用部分数据结构1

如果我们转换为 YAML 或 XML,它也只会打印两次。

1)我没有证据,但它得到了重点

于 2016-06-28T08:59:41.220 回答
2

利用

$Data::Dumper::Deepcopy = 1;
print Dumper($phBugsRec);

文档

  • $Data::Dumper::Deepcopy 或 $OBJ->Deepcopy([NEWVAL])

    可以设置为布尔值以启用结构的深层副本。只有在绝对必要时才会进行交叉引用(即打破引用循环)。默认值为 0。

输出是:

$VAR1 = {
  'bugAttributes' => {
       '222' => {
          'totalEffort' => 0,
          'profiles' => {
                          'profile1' => 'default1'
                        }
                },
       '333' => {
          'profiles' => {
                          'profile1' => 'default1'
                        },
          'totalEffort' => 0
                },
       '111' => {
          'profiles' => {
                          'profile1' => 'default1'
                        },
          'totalEffort' => 0
                }
     }
};
于 2016-06-28T09:58:06.553 回答