当我遇到这种意想不到的情况时,我试图将一个哈希值分配给另一个哈希。
我正在打印转储器以验证散列是否正确形成。
当我遍历哈希时,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'
}
};
答案 0 :(得分:3)
作为tripleee says in their comment,这没有错。我同意这可能是出乎意料的。之所以会发生这种情况,是因为您在数据结构中多次使用相同的引用。这是由于Perl的参考资料如何运作。
Perl中参考文献的简短概述
参考资料在perlref,perlreftut,perldsc和perllol中进行了说明。
只要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 module将其转换为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是一种便携式格式。它可以移动数据。有an agreement on what it can contain,重用数据不是其中的一部分。并非每种实现读写JSON的语言都支持重用部分数据结构 1 。
如果我们将转换为YAML或XML,它也会被打印两次。
1)我没有证据证明这一点,但它得到的结论
答案 1 :(得分:2)
使用
$Data::Dumper::Deepcopy = 1;
print Dumper($phBugsRec);
来自docs:
$ Data :: Dumper :: Deepcopy或$ OBJ-&gt; Deepcopy([NEWVAL])
可以设置为布尔值以启用结构的深层副本。然后仅在绝对必要时(即,打破参考周期)才进行交叉引用。默认值为0.
然后输出:
$VAR1 = {
'bugAttributes' => {
'222' => {
'totalEffort' => 0,
'profiles' => {
'profile1' => 'default1'
}
},
'333' => {
'profiles' => {
'profile1' => 'default1'
},
'totalEffort' => 0
},
'111' => {
'profiles' => {
'profile1' => 'default1'
},
'totalEffort' => 0
}
}
};