Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I copy a hash without resetting its "each" iterator?

Tags:

perl

I am using each to iterate through a Perl hash:

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

Then something interesting happens and I want to print out the hash. At first I consider something like:

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

But that won't work, because everyone knows that calling keys (or values) on a hash resets the internal iterator used for each, and we may get an infinite loop. For example, these scripts will run forever:

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

No problem, I thought. I could make a copy of the hash, and print out the copy.

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

But that doesn't work, either. This also resets the each iterator. In fact, any use of %hash in a list context seems to reset its each iterator. So these run forever, too:

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}'

Is this documented anywhere? It makes sense that perl might need to use the same internal iterator to push a hash's contents onto a return stack, but I can also imagine hash implementations that didn't need to do that.

More importantly, is there any way to do what I want? To get to all the elements of a hash without resetting the each iterator?


This also suggests you can't debug a hash inside an each iteration, either. Consider running the debugger on:

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

Just by inspecting the hash where the debugger stops (say, typing p %a or x %a), you will change the output of the program.


Update: I uploaded Hash::SafeKeys as a general solution to this problem. Thanks @gpojd for pointing me in the right direction and @cjm for a suggestion that made the solution much simpler.

like image 508
mob Avatar asked Jun 06 '12 19:06

mob


1 Answers

Have you tried Storable's dclone to copy it? It would probably be something like this:

use Storable qw(dclone);
my %hash_copy = %{ dclone( \%hash ) };
like image 121
gpojd Avatar answered Sep 22 '22 23:09

gpojd