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

Why do hash keys have different order when printing?

我想使用相同的密钥构建多个散列,并在打印时让这些密钥具有相同的顺序。因此,在下面的示例中,$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的键是无序的。我希望他们有相同的顺序,但不需要排序的顺序。我希望如果我再次 运行 代码,我可以获得相同的打印输出。

根据答案的建议,我设置了两个环境变量:

PERL_HASH_SEED=0x00 PERL_PERTURB_KEYS=0

然后当我重复运行代码时我可以得到相同的输出。

您的 perl 没有问题,散列未排序。

如果你想按键排序,你需要做类似的事情:

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

hash2 也一样...

打印哈希时,有几个不同的相关顺序概念:"insertion order"、"sort order" 和 "random"。请参阅 ENVIRONMENT section of the perlrun 文档以了解如何控制此行为以及默认使用哈希随机化的原因。

至少十年来,perl 中的哈希并没有保证键的顺序。最近,哈希随机化已成为一般安全 "hardening" 工作的一部分。哈希值被随机化是有充分理由的。有关详细信息,请参阅 perlsec discussion of algorithmic complexity attacks。您会在 Perl 安全文档中注意到 perl-5.18 中添加了进一步的增强功能 - 如果您看到与以前版本相比不同的行为,则可能是由于最近的这些更改。

此外,sorting your hash keys in a deterministic way, there are other approaches you can take to ordering your hashes: Hash::Ordered 就是一个例子。 Hash::Ordered 文档对许多其他模块的优缺点进行了很好的讨论。

虽然哈希是按键值对排列的标量的“无序篮子”;数组 标量 [1][= 的“有序序列” 71=]。 “slice”是访问 "several elements of a list, an array, or a hash simultaneously" 的方式。切片使用 @ 标记,因为操作 returns 是多个值的列表 - 使用 @ 我们得到 "ordered sequence"。结果是在散列上施加一种 "order" 的一种方法是使用切片来访问它:

# 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,其中 returns 键的顺序是随机的。解决方案是使用 ccurse 的 sort 键,在这个人为的示例中,我们将它们存储在 @sort_order 中,并使用它来 "slice" 从哈希中提取我们想要的内容,我们想要的方式:

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

多田!!当您想要将键和值存储在散列中但以有序方式访问该数据时,切片会很有用。当你想对散列进行切片时,请记住“@”;正如 perldata 所说:"you use an '@' ... on a hash slice ... [because] you are getting back ...a list"。列表是有序的。


[1] 哈希 "unordered baskets" 和数组 "ordered sequence" 的定义来自 Mike Friedman (FRIEDO) 关于 Arrays vs. Lists in Perl 的优秀文章。

更多参考资料

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;