使用perl合并哈希输出困惑

时间:2015-11-02 04:03:58

标签: perl perl-hash

为什么合并哈希的输出会多次执行此程序?

use strict;
use warnings;
my %data1=(a=>'1',b=>'2',c=>'3');
my %data2=(d=>'4',e=>'5',f=>'6');
my %data3=(%data1,%data2);
while(my($key,$value)=each %data3)
{
    print "$key:$value\n";
}
  • 我已经验证了堆栈溢出链接(What decides the order of keys when I print a Perl hash?),但我仍然无法找到正确的解决方案。
  • 上面的代码解释了哈希的合并。
  • 我的问题是为什么哈希'每次执行Perl程序时输出都会更改。
  • 任何人都可以解释输出中的随机变化吗?

2 个答案:

答案 0 :(得分:7)

Perl中的哈希没有内在的顺序,这是哈希工作的基础。

散列中的数据存储在列表中。每个密钥都通过hash function(从中获取其名称)运行,以找出它所存储的列表中的哪个位置。 "富"可能会进入第3个插槽," bar"可能会进入第8位。让我举一个例子。

让我们说你的哈希将东西存储在8个元素长的列表中(0到7)。我们非常糟糕的哈希函数为密钥中的每个字符添加ASCII代码,然后获取列表长度的modulus。因此foo102 + 111 + 111 = 324。然后除以8并取余数:4。$hash{"foo"} = 42实际上是$hash[4] = ['foo', 42]

bar98 + 97 + 114 = 309。 309 mod 8是5. $hash{"bar"} = 23实际上是$hash[5] = ['bar', 23]。 Perl的哈希函数更复杂,但你明白了。

这样,无论哈希有多大,您都可以非常快速地添加和删除密钥。这被称为"constant time" or O(1),其中算法的速度不依赖于数据的大小。

当您要求keys %hasheach %hash时,Perl会按照他们在内部列表中的随机顺序返回密钥。这就是为什么哈希没有特别的顺序。

在旧版本的Perl中,每次运行程序时,通常会为相同的哈希获得相同的顺序,但出于安全原因,这是在Perl 5.8.1中故意更改的。现在哈希函数包含一些随机性,因此每次运行程序时,您将获得不同的键顺序。为什么?考虑当两个密钥进入同一个插槽时会发生什么。

barbaz都哈希到5.这称为"哈希冲突"。它们是正常的,并且有各种方法来处理冲突,但它们会使哈希效率降低。冲突越多,在哈希中查找密钥所需的计算能力就越强。一个好的哈希没有很多碰撞。

如果您的哈希函数是可预测的,则可以创建一个非常长的密钥列表,这些密钥将全部冲突。这将使得使用哈希使用大量的CPU能力。这可用于创建denial-of-service attack。最简单的是将病理关键列表作为HTML表单字段传递。大多数Perl程序将采用表单字段并将它们放在哈希中。因此,您可以通过制作正确的URL来严重减慢Web服务器的速度。

现在Perl在其哈希函数中包含了一些随机性,攻击者无法再创建会导致冲突的密钥列表。

如果您想订购,您必须自己添加...

for my $key (sort { $a cmp $b } keys %data3) {
    my $value = $data3{$key};
    print "$key: $value\n";
}

...或者使用不像Hash::Ordered那样的哈希值,它会保留订单项添加到哈希值。

use Hash::Ordered;

my $data = Hash::Ordered->new(d=>'4',e=>'5',f=>'6');
$data->merge( a=>'1',b=>'2',c=>'3' );

my $iterator = $data->iterator;
while( my($key, $value) = $iterator->() ) {
    print "$key: $value\n";
}

# d: 4
# e: 5
# f: 6
# c: 3
# b: 2
# a: 1

答案 1 :(得分:1)

正如前面的答案中提到的那样,哈希被认为具有随机顺序。但是,有一个名为“ Tie :: IxHash”的perl核心模块(不需要安装),该模块将您的哈希绑定为如下所示的有序哈希:

use strict;
use warnings;
use Tie::IxHash;

tie(my %data1, "Tie::IxHash",a=>'1',b=>'2',c=>'3');
@data1{'d','e','f'} = (4,5,6);
$data1{'g'} = 7;

while(my($key,$value)=each %data1)
{
    print "$key:$value\n";
}

此外,当添加条目时,如“ Hash :: Ordered”一样,此模块将保留顺序,但它允许您使用常规哈希,并且它是任何perl安装附带的核心模块。在您的情况下,当然必须绑定所有3个散列以保持顺序。否则,还有一个OO接口,它提供了更高级的功能,例如“ SortByKeys”,“ SortByValues”,“ ReOrder”等。