Поскольку 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
.
Почему некоторые ключи обрабатываются более одного раза, а другие - нет, сначала мы должны обратиться к документации для each
:
Если вы добавляете или удаляете элементы хеша во время итерации, записи могут быть пропущены или дублированы - такне делай этого.
Хорошо, он говорит нам, чего ожидать, но не почему.Чтобы понять, почему мы должны создать модель того, что происходит.Когда вы присваиваете значение хешу, ключ превращается в число с помощью хеш-функции .Этот номер затем используется для индексации в массив (на уровне 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"
).