Perl 我是否可以复制哈希而不重置其“值”;每一个;迭代器?

Perl 我是否可以复制哈希而不重置其“值”;每一个;迭代器?,perl,Perl,我使用每个迭代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" } } } 但这

我使用
每个
迭代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" }
   }
}
但这是行不通的,因为每个人都知道在散列上调用
键(或
)会重置用于
每个
的内部迭代器,我们可能会得到一个无限循环。例如,这些脚本将永远运行:

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" }
   }
但这也不行。这也会重置每个
迭代器。事实上,在列表上下文中使用
%hash
似乎会重置其
每个
迭代器。因此,这些也将永远持续下去:

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可能需要使用相同的内部迭代器将散列的内容推送到返回堆栈上,这是有道理的,但我也可以想象不需要这样做的散列实现

更重要的是,有什么方法可以满足我的需求吗?要在不重置每个迭代器的情况下访问哈希的所有元素


这也意味着您不能在每次迭代中调试
中的散列。考虑运行调试器:

%a = (foo => 123, bar => 456);
while ( ($k,$v) = each %a ) {
    $DB::single = 1;
    $o .= "$k,$v;";
}
print $o;
只需检查调试器停止的哈希(例如,键入
p%a
x%a
),即可更改程序的输出



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

这个散列有多大?迭代需要多长时间,这样您才会关心访问的时间

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

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" }
}
尽管没有理由不在打印输出代码中使用
每个
,除非您计划按键或其他方式排序。

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

use Storable qw(dclone);
my %hash_copy = %{ dclone( \%hash ) };

不要忘记,当您输入
while
循环时,已经定义了
键%hash
。您可以简单地将密钥保存到数组中供以后使用:

my @keys = keys %hash;

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

    if (something_interesting_happens()) {

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

  • 不太优雅(主观)
  • 如果修改了
    %hash
    ,它就不起作用了(但是为什么一开始就要使用
    每个
好处:

  • 它通过避免散列复制使用更少的内存

    • 不太可能<代码>每个
都非常脆弱。它将迭代状态存储在迭代散列本身上,当perl的其他部分需要时,该状态将被重用。更安全的方法是忘记它的存在,而总是从
keys%hash
的结果中迭代您自己的列表,因为列表上的迭代状态是作为
for
循环本身的一部分进行词汇存储的,因此不会被其他事物损坏。

perl-MStorable=dclone-e'%a=(foo=>1);而(每个%a){dclone\%a}'
在我尝试时不会无限循环。聪明!保存迭代器的状态,在存储哈希时对其进行迭代,然后在完成时恢复迭代器状态。当哈希包含GLOB和CODE值时,此操作失败。但这肯定让我走上了正确的道路。我只需要复制
store\u hash
,然后扔掉大约一半。@mob,您可以编写一个模块来保存和恢复迭代器状态,而不是复制。然后,您可以保存迭代器,转储原始散列,并还原迭代器。@cjm-Nice!然后我可以扔掉90%的
store\u hash
。最近遇到了同样的问题。发现Hash::SafeKeys速度非常慢,取决于Hash大小,而Hash::StoredIterator+默认键的性能要好得多。@AlexandrEvstigneev-谢谢,这非常有趣。我将更新
Hash::SafeKeys