Stories
Slash Boxes
Comments
NOTE: use Perl; is on undef hiatus. You can read content, but you can't post it. More info will be forthcoming forthcomingly.

All the Perl that's Practical to Extract and Report

use Perl Log In

Log In

[ Create a new account ]

darobin (1316)

darobin
  (email not shown publicly)
http://berjon.com/

Journal of darobin (1316)

Friday January 25, 2002
12:52 PM

Perl bites

[ #2415 ]

I've been using Perl daily for five years and I think I know it fairly well (apart of course from some obscure filehandle magic or hidden things like PROPAGATE). Still, once in a while I get bitten but something that I didn't quite ignore, but that I clearly wasn't sufficiently aware of.

Today I got bitten rather bad. I have this simple code in XML::NamespaceSupport that iterated over a hash, and if the value matched a given pattern would immediately return the key (for those interested, this was in getPrefix() or get_prefix() depending on your coding preferences). Grant McLean reported a strange bug: when calling that method twice in a row, the second call would most of the time return nothing. A rather weird thing to do for a piece of code that just looks inside a hash... Here a (simplified) version of the offending code (does anyone have code2usePerlPRE script ?):

 while (my ($k, $v) = each %{$self->{someHash}}) {
     return $k if foo($v);
 }

It seems pretty innocent at first, but those of you with acute eyes will have spotted the problem: each() uses an iterator to know where it is between two calls. That iterator, is only reset when it reaches the end of the hash. But here, unless there was a failure to match the condition, the end of the hash wasn't reached. So on the first call for a given pattern it'd match, but on the second call it'd start at the pair in the hash following the one that had previously matched. If that one failed, the next call would succeed again.

I don't think that this is a problem with each() as there are good reasons for it to be so. However, one has to admit that it makes it rather easy to create a subtle bug. Perhaps a docpatch is in order?

The Fine Print: The following comments are owned by whoever posted them. We are not responsible for them in any way.
 Full
 Abbreviated
 Hidden
More | Login | Reply
Loading... please wait.
  • I never use each. Originally the reason was that it was just so different from the way I thought that I couldn't even understand it. Fortunately TMTOWTDI. Now, though, I've seen many, many people get bitten by each, especially newcomers. So I don't do it.

    My body of code is full of:

    foreach my $key (%hash) { print "$key:\t$hash{$key}\n"; }

    and similar constructs.

    --
    J. David works really hard, has a passion for writing good software, and knows many of the world's best Perl programmers
    • Yes, I'm switching a fair amount to for(each) now just to be on the safe side, only leaving each() when I know that I want it.

      I didn't use each() all that much. Mostly it was for cases when I was certain that I was going to want both the key and the value no matter what. I find it far more elegant and readable than the hash access syntax :-)

      --

      -- Robin Berjon [berjon.com]

      • I can read it now, but it just really made no sense to me at all when I first saw it. I was having trouble enough understanding the whole hash concept, I think. When people throw C<each> at a beginner, I don't see how they can handle it.

        Some Perl trainer commented on a p6 list once that after discovering many students were confused initially by using $ for single array and hash elements (why is it $hash{key} instead of %hash{key} and $array[$key] instead of @array[$key]), he was finding it easier

        --
        J. David works really hard, has a passion for writing good software, and knows many of the world's best Perl programmers
  • My perldoc -f says:
    There is a single iterator for each hash, shared by all each, keys, and values function calls in the program; it can be reset by reading all the elements from the hash, or by evaluating keys HASH or values HASH.
    So before I start a loop over each, I always have a line:
    keys %thing; # reset hash iterator

    Or:

    keys %{$self->{tiny_pies}}; # reset hash iterator
    • Is keys in void context optimized to do nothing aside from resetting the iterator? Although, I guess most hashes won't be a problem for efficiency, unless they are tied hashes, in which case you might not want such an optimization ...
      • Is keys in void context optimized to do nothing aside from resetting the iterator?

        I don't know.

      • If I'm looking in the right place (doop.c, function Perl_do_kv), it does (a call to hv_iterinit comes before the context check). I'm at patchlevel 14388.