我可以复制哈希而不重置其"每个"迭代器吗?

mob*_*mob 15 perl

我正在使用each迭代Perl哈希:

while (my ($key,$val) = each %hash) {
   ...
}
Run Code Online (Sandbox Code Playgroud)

然后发生了一些有趣的事情,我想打印出哈希值.起初我考虑的事情如下:

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

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

perl -e '%a=(foo=>1); while(each %a){keys %a}'
perl -e '%a=(foo=>1); while(each %a){values %a}'
Run Code Online (Sandbox Code Playgroud)

没问题,我想.我可以制作哈希的副本,并打印出副本.

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

但这也行不通.这也会重置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}'
Run Code Online (Sandbox Code Playgroud)

这记录在哪里?有意义的是,perl可能需要使用相同的内部迭代器将哈希的内容推送到返回堆栈,但我也可以想象哈希实现不需要这样做.

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


这也表明您无法在each迭代中调试哈希.考虑运行调试器:

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

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


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

gpo*_*ojd 9

你试过Storable's dclone复制吗?它可能是这样的:

use Storable qw(dclone);
my %hash_copy = %{ dclone( \%hash ) };
Run Code Online (Sandbox Code Playgroud)

  • `perl -MStorable = dclone -e'%a =(foo => 1); while(每个%a){dclone \%a}'`在我尝试时不会无限循环. (3认同)
  • 聪明!`Storable`中的[`store_hash`函数](http://cpansearch.perl.org/src/AMS/Storable-2.30/Storable.xs)保存了迭代器的状态,在存储时遍历散列,然后在完成后恢复迭代器状态. (3认同)
  • @mob,而不是制作副本,你可以编写一个模块,让你保存和恢复迭代器状态.然后,您可以保存迭代器,转储原始哈希,并还原迭代器. (3认同)
  • 当哈希包含GLOB和CODE值时,这会失败.但这肯定会让我走上正轨.我只需要复制`store_hash`然后扔掉大约一半. (2认同)
  • @cjm - 很好!然后我可以扔掉90%的`store_hash`. (2认同)