我正在使用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
(或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 %a
或x %a
),即可更改程序的输出。
更新:我上传了Hash::SafeKeys
作为此问题的一般解决方案。感谢@gpojd指出我正确的方向,感谢@cjm提出的建议使解决方案更加简单。
答案 0 :(得分:9)
您是否尝试过Storable's dclone
进行复制?它可能是这样的:
use Storable qw(dclone);
my %hash_copy = %{ dclone( \%hash ) };
答案 1 :(得分: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
,除非您计划按键或其他方式进行排序。
答案 2 :(得分: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
?)上行:
答案 3 :(得分:1)
不是真的。 each
非常脆弱。它将迭代状态存储在迭代的散列本身上,当需要它时,它会被perl的其他部分重用。更安全的是忘记它存在,并且总是从keys %hash
的结果迭代你自己的列表,因为列表上的迭代状态作为for
循环本身的一部分被词汇存储,所以免受其他事物的腐败。