我面临与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;
}
}
在执行第二个代码期间,仍然打印具有增加值的相同键。这是什么原因呢?感谢您的反馈和解决方案:)
答案 0 :(得分:4)
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。
幸运的是,您的代码看起来只是测试这种行为的一些代码。