perl 为什么哈希键在打印时有不同的顺序?

bjg7j2ky  于 8个月前  发布在  Perl
关注(0)|答案(3)|浏览(74)

我想使用相同的键构建几个哈希,并且在打印它们时键的顺序相同。因此,在下面的示例中,$hash1$hash2的键应该总是具有相同的顺序,但是在创建哈希时不需要保持该顺序。

use Data::Dumper;

my $hash1 = {
  keyc => 2,
  key1 => 1,
  keya => 3,
  keyb => 4,
};

my $hash2 = {
  keyc => 2,
  key1 => 1,
  keya => 3,
  keyb => 4,
};

print Dumper $hash1, $hash2;

但输出如下:

$VAR1 = {
          'key1' => 1,
          'keyc' => 2,
          'keyb' => 4,
          'keya' => 3
        };
$VAR2 = {
          'keyb' => 4,
          'keya' => 3,
          'keyc' => 2,
          'key1' => 1
        };
  • 即 * 散列有一个不同的和意外的顺序。我的perl有什么问题?

我的Perl版本是:

This is perl 5, version 18, subversion 2 (v5.18.2) built for darwin-thread-multi-2level
(with 2 registered patches, see perl -V for more detail)

注意事项:我知道perl hash的键是无序的,我希望它们有相同的顺序,但不需要有排序的顺序。我希望如果我再次运行代码,我可以得到相同的打印输出。
根据answers的建议,我设置了两个环境变量:
PERL_HASH_SEED=0x00 PERL_PERTURB_KEYS=0
然后我可以得到相同的输出,当我重复运行代码。

fsi0uk1n

fsi0uk1n1#

当打印一个散列时,有几个不同的顺序概念是相关的:“插入顺序”,“排序顺序”和“随机”。请参阅perlrun文档的环境部分,了解如何控制这种行为,以及为什么默认使用散列随机化。
至少有十年的时间,perl中的散列不能保证键的顺序。最近,散列随机化已经成为一般安全性“强化”工作的一部分。散列随机化有很好的理由。有关详细信息,请参阅perlsec关于算法复杂性攻击的讨论。您将在Perl安全文档中注意到,perl-5.18中添加了进一步的增强功能-如果你看到一个不同的行为相比,以前的版本,这可能是由于这些最新的变化。
除了以确定性的方式显式地使用sorting your hash keys之外,还有其他方法可以用来对散列进行排序:Hash::Ordered就是一个例子。Hash::Ordered文档很好地讨论了许多其他模块的优缺点。
而哈希是一个“* 无序篮子 ”的标量安排在键-值对;数组 * 是标量的“ 有序序列 *”[1]。“切片”是访问“列表,数组,切片使用@符号,因为该操作返回多个值的列表-使用@,我们得到“其结果是,一种对散列施加某种“顺序”的方法是使用切片来访问它:

# We want alphabetical disorder ...
my %hashed = ( 1 => "z", 2 => "x", 3 => "y" );
for my $key ( keys %hashed ) { print $hashed{$key} } ;
__END__    
zyx

我们想要的是“zxy“而不是“zyx“。为了在这个哈希上强加我们任意版本的顺序,我们首先需要认识到罪魁祸首是keys %hashed,它以随机顺序返回密钥。解决方案是ccurse的sort密钥,在这个人为的例子中,我们将它们存储在@sort_order中,并用它从哈希中“切片”出我们想要的内容,我们想要的方式:

my @sort_order = sort keys %hashed ;
print @hashed{@sort_order} ;
__END__
zxy

Tada!!当你想在散列中存储键和值,但以有序的方式访问该数据时,切片可能很有用。当你想对散列进行切片时,请记住“@“;正如perldata所说:“你在散列切片上使用'@'......[因为]你得到的是......一个列表”。列表是有序的。
[1]散列的定义是“无序篮子”,数组的定义是“有序序列”,这些定义来自Mike Friedman(FRIEDO)关于Arrays vs. Lists in Perl的优秀文章。

更多参考资料

  • cf.perlfaq-q如何始终保持哈希排序?
  • 除了创建一些非常有用的CPAN模块之外,GARU(Breno de Oliveira)还发布了an excellent article on hash ordering,它全面涵盖了最近的Perl开发和散列随机化问题。
  • 有关可以使用散列切片做的更高级的事情的示例,请参阅Vince Veselosky的文章Hash slices can replace loops
vltsax25

vltsax252#

你的perl没什么问题,散列没有排序。
如果你想按键排序,你需要做这样的事情:

foreach my $key (sort keys %hash1) {
    print $key, $hash1{$key};
}

对于hash2也是一样。

klsxnrf1

klsxnrf13#

G. Cito的回答是正确的。但是,如果你想从Data::Dumper得到排序的输出,你可以这样做:

use Data::Dumper;

my $hash1 = {
  keyc => 2,
  key1 => 1,
  keya => 3,
  keyb => 4,
};

my $hash2 = {
  keyc => 2,
  key1 => 1,
  keya => 3,
  keyb => 4,
};

my $dumper = Data::Dumper->new([$hash1, $hash2]);
$dumper->Sortkeys(1);
print $dumper->Dump;

相关问题