如果我有一堆(键,值)对的Perl哈希,迭代所有键的首选方法是什么? 我听说使用each可能会以某种方式产生意想不到的副作用。 那么,这是真的,并且是以下两种方法中最好的方法之一,还是有更好的方法?

# Method 1
while (my ($key, $value) = each(%hash)) {
    # Something
}

# Method 2
foreach my $key (keys(%hash)) {
    # Something
}

===============>>#1 票数:192 已采纳

经验法则是使用最适合您需求的功能。

如果您只想要键并且不打算读取任何值,请使用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()创建一组具有doubled值的新大写键的代码可以正常工作:

%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

===============>>#2 票数:25

在使用each时,你应该注意的一点是,它具有向你的哈希添加“状态”的副作用(哈希必须记住“下一个”键是什么)。 当使用像上面发布的代码片段一样遍历整个哈希值的代码时,这通常不是问题。 但是,在处理所有密钥之前,您将遇到难以追踪的问题(我从经验中说出来),当将eachlast语句一起使用或returnwhile ... each循环退出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

发生了什么事键“栏”和巴兹?“他们还在那里,但第二个each地方的第一个不放过开始,并且当它到达哈希年底停止,所以我们从来没有看到他们在第二循环中。

===============>>#3 票数:20

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

如果您需要确保eacheach获得所有键和值,则需要确保首先使用keysvalues (因为它会重置迭代器)。 请参阅每个文档

===============>>#4 票数:13

使用每种语法将阻止一次生成整组密钥。 如果您对具有数百万行的数据库使用绑定哈希,这可能很重要。 您不希望一次生成整个键列表并耗尽物理内存。 在这种情况下,每个都充当迭代器,而键实际上在循环开始之前生成整个数组。

因此,“每个”实际使用的唯一地方是散列非常大(与可用内存相比)。 只有在散列本身不存在于内存中时,才会发生这种情况,除非您正在编写手持数据采集设备或内存较小的内容。

如果内存不是问题,那么通常地图或键范例是更容易阅读和更容易阅读的范例。

===============>>#5 票数:6

关于这个主题的一些杂项想法:

  1. 任何哈希迭代器本身都没有什么不安全的。 什么是不安全的是在迭代时修改哈希的键。 (修改值是完全安全的。)我能想到的唯一潜在副作用是values返回别名,这意味着修改它们将修改哈希的内容。 这是设计的,但在某些情况下可能不是您想要的。
  2. John 接受的答案很好,只有一个例外:文档很清楚,在迭代哈希时添加密钥是不安全的。 它可能适用于某些数据集,但根据哈希顺序会对其他数据集失败。
  3. 如前所述,删除each返回的最后一个密钥是安全的。 对于keys 不是这样,因为each keys都是迭代器,而keys返回列表。

===============>>#6 票数:3

我可能会被这个人咬伤,但我认为这是个人偏好。 我在文档中找不到任何引用,每个()与keys()或values()不同(除了显而易见的“它们返回不同的东西”的答案。实际上,文档声明使用相同的迭代器,它们都是返回实际的列表值而不是它们的副本,并且在使用任何调用迭代它时修改哈希是不好的。

所有这一切,我几乎总是使用keys(),因为对我来说,通常更自我记录通过哈希本身访问密钥的值。 当值是对大型结构的引用并且哈希的密钥已经存储在结构中时,我偶尔会使用values(),此时密钥是冗余的,我不需要它。 我想我在Perl编程的10年中已经使用了每次()2次,这可能是两次错误的选择=)

===============>>#7 票数:3

我也总是使用方法2。 使用每个的唯一好处是,如果您只是读取(而不是重新分配)散列条目的值,则不会经常取消引用散列。

===============>>#8 票数:2

我通常使用keys ,我想不起上次使用或读取each的使用。

不要忘记map ,这取决于你在循环中做了什么!

map { print "$_ => $hash{$_}\n" } keys %hash;

===============>>#9 票数:-1

我woudl说:

  1. 使用大多数人最容易阅读/理解的东西(所以关键,通常,我认为)
  2. 在整个代码库中使用您始终如一的决定。

这有两个主要优点:

  1. 发现“常见”代码更容易,因此您可以重新考虑函数/方法。
  2. 未来的开发人员更容易维护。

我认为在每个密钥上使用密钥并不昂贵,因此在代码中不需要两个不同的构造用于同一个东西。

  ask by Rudd Zwolinski translate from so

未解决问题?本站智能推荐: