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

问题描述 投票:0回答:3

我想使用相同的键构建多个哈希值,并且在打印它们时使键具有相同的顺序。因此,在下面的示例中,

$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
        };

i.e 哈希值具有不同且意外的顺序。我的 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哈希的键是未排序的顺序。我希望它们具有相同的顺序,但不需要排序的顺序。我希望如果我再次运行代码能够得到相同的打印输出。

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

PERL_HASH_SEED=0x00 PERL_PERTURB_KEYS=0

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

perl sorting data-structures hash perl-data-structures
3个回答
12
投票

打印哈希时,有一些相关的不同顺序概念:“插入顺序”、“排序顺序”和“随机”。请参阅 perlrun

 文档的 ENVIRONMENT 部分,了解控制此行为的方法以及默认使用哈希随机化的原因。

至少十年来,perl 中的哈希值并不能保证键的顺序。最近,哈希随机化已成为一般安全“强化”工作的一部分。哈希值被随机化是有充分理由的。有关更多详细信息,请参阅

perlsec
算法复杂性攻击的讨论。您会在 Perl 安全文档中注意到,
perl-5.18
中添加了进一步的增强功能 - 如果您看到与以前版本相比不同的行为,可能是由于这些最新的更改所致。

除了以确定性方式明确对哈希键进行排序之外,您还可以采用其他方法来对哈希进行排序:

Hash::Ordered
就是一个例子。
Hash::Ordered
文档对许多其他模块的优缺点进行了很好的讨论。

虽然哈希是按键值对排列的标量的“无序篮子”;数组标量[1]的“有序序列”。 “slice”是“同时访问列表、数组或散列的多个元素”的方式。切片使用

@
符号,因为该操作返回多个值的列表 - 并且使用
@
我们得到“有序序列”。结果是,对哈希施加某种“顺序”的一种方法是使用切片来访问它:

# 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

田田!!当您想要将键和值存储在哈希中但以有序方式访问该数据时,切片会很有用。当你想对哈希进行切片时,请记住“

@
”;正如
perldata
所说:“你在哈希片上使用
'@'
...[因为]你要返回......一个列表”。而且列表是有序的。


[1] 哈希作为“无序篮子”和数组作为“有序序列”的定义来自 Mike Friedman (FRIEDO) 关于 Arrays vs. Lists in Perl 的优秀文章。

更多参考资料


4
投票

你的 Perl 没有任何问题,哈希值未排序。

如果你想按键排序,你需要这样做:

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

对于 hash2 也是同样的事情...


3
投票

G。西托的回答是正确的。但是,如果您想从 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;
© www.soinside.com 2019 - 2024. All rights reserved.