Perl: перебор хеша не работает с вложенным grep в состоянии while - PullRequest
0 голосов
/ 15 мая 2019

У меня проблема с условием while.Мой простой хеш:

my %ConnectionsIP = (
                '10.130.20.21' => 0,
                '10.130.20.22' => 0,
                '10.130.20.23' => 0,
                '10.130.20.24' => 0
            );

Код ниже работает для меня, как я ожидал.

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

Каждая пара (ключ, значение) отображается правильно во время выполнения.Проблема заключается в следующем grep в секунде, в то время как условие:

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

Во время выполнения второго кода все еще печатается тот же ключ с возрастающим значением.В чем причина этого?Спасибо за каждый отзыв и решения:)

1 Ответ

4 голосов
/ 15 мая 2019

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.

К счастью, ваш код выглядит так, будто это всего лишь некоторый код для проверки этого поведения.

...