因为each 不允许您像for 循环那样修改项目。 each 只返回哈希的下一个键和值。当您说$h{uc $k} = $h{$k} * 2; 时,您正在哈希中创建新值。为了得到你想要的行为,我可能会说
for my $k (keys %h) {
$h{uc $k} = $h{$k};
delete $h{$k};
}
如果哈希很大并且您担心将所有键存储在内存中(这是each 的主要用途),那么您最好说:
my %new_hash;
while (my ($k, $v) = each %h) {
$new_hash{uc $k} = $v;
delete $h{$k};
}
然后使用%new_hash 而不是%h。
至于为什么有些键会被多次处理,而有些则不会,首先我们必须看看the documentation for each:
如果在迭代哈希时添加或删除元素,条目可能会被跳过或重复——所以不要这样做。
这很好,它告诉我们会发生什么,但不告诉我们为什么。要了解为什么我们必须创建一个正在发生的事情的模型。当您将值分配给散列时,键将通过hash function 转换为数字。然后这个数字被用来索引到一个数组中(在 C 级别,而不是 Perl 级别)。出于我们的目的,我们可以使用一个非常简单的模型:
#!/usr/bin/perl
use strict;
use warnings;
use Data::Dumper;
my %hash_function = (
a => 2,
b => 1,
A => 0,
B => 3
);
my @hash_table;
{
my $position = 0;
sub my_each {
#return nothing if there is nothing
return unless @hash_table;
#get the key and value from the next positon in the
#hash table, skipping empty positions
until (defined $hash_table[$position]) {
$position++;
#return nothing if there is nothing left in the array
return if $position > $#hash_table;
}
my ($k, $v) = %{$hash_table[$position]};
#set up for the next call
$position++;
#if in list context, return both key an value
#if in scalar context, return the key
return wantarray ? ($k, $v) : $k;
}
}
$hash_table[$hash_function{a}] = { a => 1 }; # $h{a} = 1;
$hash_table[$hash_function{b}] = { b => 2 }; # $h{b} = 2;
while (my ($k, $v) = my_each) {
# $h{$k} = $v * 2;
$hash_table[$hash_function{uc $k}] = { uc $k => $v * 2 };
}
print Dumper \@hash_table;
对于这个例子,我们可以看到,当键 "A" 被添加到哈希表时,它被放在其他键之前,所以它不会被第二次处理,但是键 "B" 确实 放置在其他键之后,因此my_each 函数在第一次通过时会看到它(作为键"a" 之后的项目)。