2016-06-28 61 views
1

当我遇到这种意外的情况时,我试图给另一个散列分配一个散列。来自Perl 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' 
    } 
}; 
+4

哪一部分是意外? Data :: Dumper正确地标识了对同一散列引用有多个引用,并且有助于显示它。 – tripleee

+0

@tripleee:据我所知,Dumper显示了对同一个引用的多个引用。我期望看到的是散列散列的全部细节而不是引用。也许我对输出的理解略有错位。感谢您的澄清。 – Soumya

+0

我正在写一个长期的解释... – simbabque

回答

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使用JSON module,这是一种可以与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的语言都支持重用部分数据结构。

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

1)我没有该证明,但它得到翻过

+5

非常好的解释。但是如果你设置了'$ Data :: Dumper :: Deepcopy = 1',那么Data :: Dumper也会避免引用并且打印真实的数据两次。 – PerlDuck

+0

非常详尽和精彩的解释。感谢您的澄清和努力。 – Soumya

1

点使用

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

docs

  • $ 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 
       } 
    } 
}; 
+0

大约两年前我放弃使用Data :: Dumper。现在我只使用Data :: Printer进行调试,并且避免使用Data :: Dumper来共享数据。这就是JSON和CO的目的。或者Sereal,或者Freeze/Thaw。无论如何,很好找。 :) – simbabque

+0

哦。好。我认为在克卢日,你更有可能让你的血被德库拉吸走。那么明年的德语Perl Workshop就是。可能在汉堡。 – simbabque