Perl Dumper的意外输出

时间:2016-06-28 07:36:49

标签: perl data-dumper

当我遇到这种意想不到的情况时,我试图将一个哈希值分配给另一个哈希。

我正在打印转储器以验证散列是否正确形成。

当我遍历哈希时,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'
  }
};

2 个答案:

答案 0 :(得分:3)

作为tripleee says in their comment,这没有错。我同意这可能是出乎意料的。之所以会发生这种情况,是因为您在数据结构中多次使用相同的引用。这是由于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 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
                }
     }
};