15

each用来遍历 Perl 哈希:

while (my ($key,$val) = each %hash) {
   ...
}

然后发生了一些有趣的事情,我想打印出哈希。起初,我考虑类似:

while (my ($key,$val) = each %hash) {
   if (something_interesting_happens()) {
      foreach my $k (keys %hash) { print "$k => $hash{$k}\n" }
   }
}

但这行不通,因为每个人都知道在哈希上调用keys(or values) 会重置用于 的内部迭代器each,并且我们可能会得到一个无限循环。例如,这些脚本将永远运行:

perl -e '%a=(foo=>1); while(each %a){keys %a}'
perl -e '%a=(foo=>1); while(each %a){values %a}'

没问题,我想。我可以制作哈希的副本,然后打印出副本。

   if (something_interesting_happens()) {
      %hash2 = %hash;
      foreach my $k (keys %hash2) { print "$k => $hash2{$k}\n" }
   }

但这也行不通。这也会重置each迭代器。事实上,%hash在列表上下文中的任何使用似乎都会重置其each迭代器。所以这些也永远运行:

perl -e '%a=(foo=>1); while(each %a){%b = %a}'
perl -e '%a=(foo=>1); while(each %a){@b = %a}'
perl -e '%a=(foo=>1); while(each %a){print %a}'

这在任何地方都有记录吗?perl 可能需要使用相同的内部迭代器将哈希的内容推送到返回堆栈是有道理的,但我也可以想象不需要这样做的哈希实现。

更重要的是,有什么方法可以做我想做的事吗?在不重置each迭代器的情况下获取哈希的所有元素?


这也表明您也不能在each迭代中调试哈希。考虑在以下位置运行调试器:

%a = (foo => 123, bar => 456);
while ( ($k,$v) = each %a ) {
    $DB::single = 1;
    $o .= "$k,$v;";
}
print $o;

只需检查调试器停止的哈希值(例如,键入p %ax %a),您将更改程序的输出。


更新:我上传Hash::SafeKeys了这个问题的一般解决方案。感谢@gpojd 为我指明了正确的方向,感谢@cjm 的建议,使解决方案变得更加简单。

4

4 回答 4

9

您是否尝试过Storable dclone复制它?它可能是这样的:

use Storable qw(dclone);
my %hash_copy = %{ dclone( \%hash ) };
于 2012-06-06T20:16:28.703 回答
2

这个哈希有多大?遍历它需要多长时间,以便您关心访问的时间?

只需设置一个标志并在迭代结束后执行操作:

my $print_it;
while (my ($key,$val) = each %hash) {
    $print_it = 1 if something_interesting_happens();
    ...
}

if ($print_it) {
    foreach my $k (keys %hash) { print "$k => $hash{$k}\n" }
}

尽管也没有理由不在each打印输出代码中使用,除非您计划按键排序或其他东西。

于 2012-06-06T20:07:37.360 回答
1

并不真地。each非常脆弱。它将迭代状态存储在迭代哈希本身上,当 perl 的其他部分需要时,该状态会被重用。更安全的是忘记它的存在,并始终从结果中迭代您自己的列表keys %hash,因为列表上的迭代状态作为for循环本身的一部分存储在词法上,因此不会受到其他事物的破坏。

于 2012-06-17T15:03:54.593 回答
1

我们不要忘记当你进入循环keys %hash时已经定义了。while可以简单地将键保存到数组中以供以后使用:

my @keys = keys %hash;

while (my ($key,$val) = each %hash) {

    if (something_interesting_happens()) {

        print "$_ => $hash{$_}\n" for @keys;
    }
}

缺点:

  • 它不那么优雅(主观)
  • 如果被修改它将不起作用%hash(但是为什么要首先使用each呢?)

优势:

  • 它通过避免哈希复制使用更少的内存
于 2012-06-07T06:13:41.800 回答