Answers:
经验法则是使用最适合您需要的功能。
如果您只想要键,并且不打算读取任何值,请使用keys():
foreach my $key (keys %hash) { ... }
如果只需要这些值,请使用values():
foreach my $val (values %hash) { ... }
如果需要键和值,请使用each():
keys %hash; # reset the internal iterator so a prior each() doesn't affect the loop
while(my($k, $v) = each %hash) { ... }
如果打算以任何方式更改哈希键,但在迭代过程中删除当前键,则不得使用each()。例如,以下代码可以使用keys()来创建具有双倍值的新的大写键集:
%h = (a => 1, b => 2);
foreach my $k (keys %h)
{
$h{uc $k} = $h{$k} * 2;
}
产生预期的结果哈希:
(a => 1, A => 2, b => 2, B => 4)
但是使用each()做同样的事情:
%h = (a => 1, b => 2);
keys %h;
while(my($k, $v) = each %h)
{
$h{uc $k} = $h{$k} * 2; # BAD IDEA!
}
以难以预测的方式产生错误的结果。例如:
(a => 1, A => 2, b => 2, B => 8)
但是,这是安全的:
keys %h;
while(my($k, $v) = each %h)
{
if(...)
{
delete $h{$k}; # This is safe
}
}
所有这些都在perl文档中进行了描述:
% perldoc -f keys
% perldoc -f each
使用时应注意的一件事each
是,它具有在哈希表中添加“状态”的副作用(哈希表必须记住“下一个”键是什么)。当使用上面发布的代码片段之类的代码一遍又一遍地遍历整个哈希时,通常这不是问题。但是,当在处理所有键之前each
与诸如之类的语句一起使用
last
或return
从while ... each
循环中退出时,将很难发现问题(根据经验说);
在这种情况下,哈希将记住它已经返回了哪些键,并且当您each
下次使用它(也许是完全无关的代码)时,它将继续在该位置。
例:
my %hash = ( foo => 1, bar => 2, baz => 3, quux => 4 );
# find key 'baz'
while ( my ($k, $v) = each %hash ) {
print "found key $k\n";
last if $k eq 'baz'; # found it!
}
# later ...
print "the hash contains:\n";
# iterate over all keys:
while ( my ($k, $v) = each %hash ) {
print "$k => $v\n";
}
打印:
found key bar
found key baz
the hash contains:
quux => 4
foo => 1
键“ bar”和“ baz”发生了什么?它们仍然在那里,但是第二个each
从第一个停止的地方开始,直到哈希结束时停止,所以我们在第二个循环中再也看不到它们。
each
可能导致您出现问题的地方是它是一个真实的,不受范围限制的迭代器。举例来说:
while ( my ($key,$val) = each %a_hash ) {
print "$key => $val\n";
last if $val; #exits loop when $val is true
}
# but "each" hasn't reset!!
while ( my ($key,$val) = each %a_hash ) {
# continues where the last loop left off
print "$key => $val\n";
}
如果需要确保each
获取所有键和值,则需要确保先使用keys
或values
(使用,这会重置迭代器)。请参阅每个文档。
使用每种语法将阻止立即生成整个键集。如果您要对具有数百万行的数据库使用绑定哈希,这可能很重要。您不想一次全部生成整个键列表并耗尽您的物理内存。在这种情况下,每个都充当迭代器,而键实际上在循环开始之前就生成了整个数组。
因此,“每个”唯一真正有用的地方是散列非常大(与可用内存相比)。除非您对手持数据收集设备或内存较小的程序进行编程,否则只有在哈希自身不存在于内存中时,才有可能发生这种情况。
如果内存不是问题,则通常映射或键范式更为流行,更易于阅读。
关于此主题的一些其他想法:
values
返回别名,这意味着修改它们将修改哈希的内容。这是设计使然,但在某些情况下可能不是您想要的。each
。这是不是真正keys
的each
是一个迭代器,而keys
返回一个列表。我可能会对此一口咬,但我认为这是个人喜好。我找不到文档中对each()的引用不同于keys()或values()的引用(除了显而易见的“它们返回不同的事物”的答案。事实上,文档声明使用相同的迭代器,并且它们都返回实际的列表值,而不是它们的副本,并且在使用任何调用对其进行迭代时修改哈希值是不好的。
话虽如此,我几乎总是使用keys(),因为对我而言,通常是更多自我记录,可以通过散列本身访问键的值。当值是对大型结构的引用并且哈希的键已存储在结构中时,有时会使用values(),此时该键是多余的,我不需要它。我想我在Perl编程的10年中两次使用了each()2次,两次可能都是错误的选择=)