each
чревато опасностью.У каждого хеша есть свой итератор.Он использует это для таких вещей, как each
, keys
и values
.
each(%ConnectionsIP)
, перебирает каждую пару в %ConnectionsIP
, возвращая по одной паре ключ / значение за раз.Он использует итератор %ConnectionsIP
, чтобы запомнить, где он находится.
keys %ConnectionsIP
перебирает все ключи %ConnectionsIP
, возвращая их все в виде массива.При этом сбрасывает итератор .
Каждый хеш или массив имеет свой внутренний итератор, доступ к которому осуществляется каждым, ключами и значениями.Итератор неявно сбрасывается, когда каждый достигает конца, как только что описано;это может быть явно сброшено, вызывая ключи или значения в хэше или массиве.Если вы добавляете или удаляете элементы хеша во время итерации по нему, влияние на итератор не определено;
my $i = 0;
while (grep { $ConnectionsIP{$_} eq '0' } keys %ConnectionsIP){
while ((my $key, my $value) = each (%ConnectionsIP)){
print "KEY: $key, VAL: $value\n";
$ConnectionsIP{$key} = $i++;
next;
}
}
Это безопасно, потому что вы вызываете keys
, сбрасывая итератор.А затем выполните полную итерацию с each
.Звонок на keys
не мешает звонить на each
.
my $i = 0;
while (grep { $ConnectionsIP{$_} eq '0' } keys %ConnectionsIP){
while ((my $key, my $value) = each (%ConnectionsIP)){
print "KEY: $key, VAL: $value\n";
$ConnectionsIP{$key} = $i++;
if (grep { $ConnectionsIP{$_} eq '1' } keys %ConnectionsIP){
print "I have 1!\n";
sleep 10;
}
next;
}
}
Это небезопасно.Пока each
выполняет итерацию по %ConnectionsIP
, вы вызываете keys %ConnectionsIP
, перезагружая итератор each
.
Правило: не смешивайте keys
и values
с each
.
Есть несколько способов это исправить.Вы можете использовать Hash :: StoredIterator , который позволяет вам независимо перебирать хеш.
use Hash::StoredIterator qw{
iterator
};
my $i = 0;
while (grep { $ConnectionsIP{$_} eq '0' } keys %ConnectionsIP) {
# Create an iterator independent of the shared one in %ConnectionsIP
my $connections_iter = iterator %ConnectionsIP;
while (my($key, $value) = $connections_iter->()){
print "KEY: $key, VAL: $value\n";
$ConnectionsIP{$key} = $i++;
if (grep { $ConnectionsIP{$_} eq '1' } keys %ConnectionsIP){
print "I have 1!\n";
}
next;
}
}
Или, и это лучше, вы можете переделать свой код, чтобы не делать так много циклов внутрипетли внутри петель.Для выполнения вашего кода потребуется n^3
, т. Е. По мере того, как количество ключей в %ConnectionsIP
будет увеличивать время, которое будет указано в кубе.3 - 27. 4 - 64. 5 - 125.
К счастью, ваш код выглядит так, будто это всего лишь некоторый код для проверки этого поведения.