Re: hash issue

2013-04-19 Thread Jim Gibson

On Apr 19, 2013, at 6:45 AM, Peter Scott wrote:

> On Wed, 17 Apr 2013 11:47:49 -0700, Jim Gibson wrote:
>> 1. You should not modify a hash while you are iterating through it with
>> the each() function. The each() function uses an internal data structure
>> that persists from one call of each to the next. That internal data
>> structure can be modified if you add or delete elements (as you are
>> doing). 
> 
> So the documentation has warned.  Yet it should probably be updated, since 
> that behavior has been demonstrated to be safe: https://groups.google.com/
> forum/?hl=en&fromgroups#!topic/comp.lang.perl.misc/53Lfj8IM0JQ and there 
> is specific code in the internals to make it safe: https://
> groups.google.com/forum/?hl=en&fromgroups=#!topic/comp.lang.perl.moderated/
> _J9aO8pdAVc

Thanks for weighing in on the subject. It looks like deleting elements is OK, 
but not adding them.

In reference one, Mark-Jason Dominus (MJD) claims that this is safe, and no one 
was able to come up with a counter-example. In fact, even the "perldoc -f each" 
entry mentions this case:

"If you add or delete a hash's elements while iterating over it, entries may be 
skipped or duplicated--so don't do that.  Exception: It is always safe to 
delete the item most recently returned by 'each()', so the following code works 
properly:

while (($key, $value) = each %hash) {
  print $key, "\n";
  delete $hash{$key};   # This is safe
}
"

Perhaps this should be extended to say that deleting any element, not just the 
last one returned by each, is safe.

However, the posted program was adding elements to the hash while iterating 
over it, and this can lead to unexpected or erroneous results. Quoting MJD from 
the first reference:

"Of course, you must not add keys, since it's well-known that this can cause 
duplications and omissions, and easy to demonstrate."

Here is a cleaned-up version of the posted program with added print statements 
to show what is happening:


#!/usr/bin/perl
use strict;
use warnings;

my @words = qw( a b a );
my %names;

my $step = 0;
foreach my $x (@words) {
  $step++;
  print "\nStep $step: starting loop with key $x\n";
  if ( ! %names ) {
print "  adding first key $x\n";
$names{$x} = 1;
  }else{
my $found;
print "  searching hash for $x\n";
my $substep = 0;
while( my ($key, $value) = each %names ) {
  $substep++;
  print "$step.$substep: checking against ($key,$value)\n";
  if( $x eq $key ) {
print "  incrementing key $x\n";
$names{$key} += 1;
$found = 1;
last;
  }else{
print "  adding key $x\n";
$names{$x} = 1;
  }
}
  }
}

print "\nHash entries: (", scalar keys %names, ")\n";
while ( my ($key2, $value2) = each %names ) {
   print "  $key2 => $value2\n";
}
__END__

Which produces the output:


Step 1: starting loop with key a
  adding first key a

Step 2: starting loop with key b
  searching hash for b
2.1: checking against (a,1)
  adding key b
2.2: checking against (b,1)
  incrementing key b

Step 3: starting loop with key a
  searching hash for a
3.1: checking against (a,1)
  incrementing key a
3.2: checking against (b,2)
  adding key a

Hash entries: (2)
  a => 1
  b => 2
__END__


This program contains several logic errors, including continuing to search the 
hash for elements after it has found the one in question. This is also not the 
way to test a hash for the existence of a key entry. Use 'exists' for that, or 
just increment the value and let the hash autovivify the element. Nevertheless, 
this type of logic could be used for more complicated hash traversals.

Looking at the output, you can see that in step 2, the program is searching the 
hash for the key 'b'. When the first key found is 'a', the program adds the 
element pair ('b',1) to the hash and continues to search. The new element with 
key 'b' is returned by the each() function in step 2.2.

Would anyone expect that the newly-added element would be returned by each()? 
Not the person who wrote the original version of this program, because it 
resulted in the value for the key 'b' being 2 instead of the expected 1.

I think the advice to be careful about modifying a hash while traversing over 
it is still valid. Changing element values and deleting elements are probably 
OK, but do not add elements to the hash.


--
To unsubscribe, e-mail: beginners-unsubscr...@perl.org
For additional commands, e-mail: beginners-h...@perl.org
http://learn.perl.org/




Re: hash issue

2013-04-19 Thread Peter Scott
On Wed, 17 Apr 2013 11:47:49 -0700, Jim Gibson wrote:
> 1. You should not modify a hash while you are iterating through it with
> the each() function. The each() function uses an internal data structure
> that persists from one call of each to the next. That internal data
> structure can be modified if you add or delete elements (as you are
> doing). 

So the documentation has warned.  Yet it should probably be updated, since 
that behavior has been demonstrated to be safe: https://groups.google.com/
forum/?hl=en&fromgroups#!topic/comp.lang.perl.misc/53Lfj8IM0JQ and there 
is specific code in the internals to make it safe: https://
groups.google.com/forum/?hl=en&fromgroups=#!topic/comp.lang.perl.moderated/
_J9aO8pdAVc

-- 
Peter Scott
http://www.perlmedic.com/ http://www.perldebugged.com/
http://www.informit.com/store/product.aspx?isbn=0137001274
http://www.oreillyschool.com/certificates/perl-programming.php

-- 
To unsubscribe, e-mail: beginners-unsubscr...@perl.org
For additional commands, e-mail: beginners-h...@perl.org
http://learn.perl.org/