Beefy Boxes and Bandwidth Generously Provided by pair Networks
There's more than one way to do things
 
PerlMonks  

Why Doesn't each() DWIM?

by enoch (Chaplain)
on May 27, 2004 at 20:41 UTC ( #357045=perlquestion: print w/ replies, xml ) Need Help??
enoch has asked for the wisdom of the Perl Monks concerning the following question:

I just started thinking about this, and I know there is probably a very good answer that just hasn't occured to me yet, but why doesn't each automatically reset after loops are exited? It seems to me that that would make each DWIM. For example, this code:
#!/usr/local/bin/perl -wl my %hash=(a=>0, b=>1, c=>2); my $key; while (defined($key=each(%hash))) { last; } while (defined($key=each(%hash))) { print($key); }
Produces the output:
b c
Even though, I would think, the person writing that code would expect:
a b c
Is there a good reason not to reset the iterator that I just haven't thought of.

Comment on Why Doesn't each() DWIM?
Select or Download Code
Re: Why Doesn't each() DWIM?
by hardburn (Abbot) on May 27, 2004 at 20:54 UTC

    It is explicilty documented that hashes never, never, never have any meaningful order to them. So your "what you would expect" output is never going to be guarenteed to happen.

    Now, for having each be localized . . . maybe. It might cause problems with tied hashes. I'll leave it to someone more familer with the internals.

    ----
    send money to your kernel via the boot loader.. This and more wisdom available from Markov Hardburn.

      hashes never, never, never have any meaningful order to them.
      Right, right. I am not asking that a hash have order; rather, I just want each call to each to iterate over the entire hash; rather than having to make an explicit reset call (my $x = keys %hash) between calls to each.
Re: Why Doesn't each() DWIM?
by Anonymous Monk on May 27, 2004 at 21:06 UTC

    From each docs:

    When the hash is entirely read, a null array is returned in list context (which when assigned produces a false (0) value), and undef in scalar context. The next call to each after that will start iterating again. 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.

    Perhaps you don't like it, but it's documented.

    If you want each to restart, you need to finish reading.

    Put a  my $x = keys %hash; between the two loops, and it will work as you expect.

      I think the question is more along the lines of "why does it work this way" as opposed to "does it work this way" or "is this behavior documented". Can you think of a good scenario where not resetting each is a desired behavior?

      Yea.

      I am just wondering if there is a reason Perl won't do it for me? Why isn't there a little Do What I Mean in there.

      Is there a technical reason why that piece of DWIMmery would be considered harmful at some edge case?

        Is there a technical reason why that piece of DWIMmery would be considered harmful at some edge case?

        I can think of one. Whether it is right or not is another matter :)

        There would be times when you wanted to return to your position in the hash. If the iterator (or hashkey pointer) was reset everytime the program left a control loop, you would have to save it in a variable and then restore it each time you went into another control structure.

        I don't see this as being a bad thing - other languages do it like this. I also don't know why it wasn't done this way rather than the way it currently is.

        Having said that, I have found this to be a problem before, but some careful data structure design worked around it just fine.

        ____________________
        Jeremy
        I didn't believe in evil until I dated it.

        Perl already does what I mean in this case. If it did what you wanted, my expectations would be violated. Also, let's say that at the end of a loop the iterator on each() was reset, how would you get the behavior where it wasn't reset when you need it?

      If you have 5.8.3 or later, it's more efficient to use keys %hash; in a void context rather than a scalar context if you only want to reset the iterator.

      This avoids the need to calculate the number of keys in the hash, which for large and/or tied hashes can potentially be an expensive operation.


      Examine what is said, not who speaks.
      "Efficiency is intelligent laziness." -David Dunham
      "Think for yourself!" - Abigail
Re: Why Doesn't each() DWIM?
by chromatic (Archbishop) on May 27, 2004 at 22:25 UTC
    why doesn't each automatically reset after loops are exited?

    How does it know when you've exited a loop?

    That's not a flippant question. Consider more complicated control flows. For example, with an iterator interface that abstracts away the hash to where you can't reset it, how is perl to tell when you want to reset the hash iterator?

    I suspect that there's no meaningful algorithm to figure this out in a language that allows you to define your own flow of control.

Re: Why Doesn't each() DWIM?
by Abigail-II (Bishop) on May 27, 2004 at 22:58 UTC
    Well, in such a case, you would have to DWIM while, not each. each has no concept of blocks or loops.

    Abigail

Re: Why Doesn't each() DWIM?
by ambrus (Abbot) on May 28, 2004 at 17:30 UTC

    Use a for (keys %hash) if you want it to get reset.

Re: Why Doesn't each() DWIM?
by thospel (Hermit) on May 29, 2004 at 12:25 UTC
    But actually, you probably don't care what the each state at the end of your loop is. You care about the state at the beginning, so you will know that you potentially process all elements. And these two are only the same if you only use each inside loops.

    However, it's perfectly ok to have code like:

    # Suppose I know the hash has at least two entries, and # it's freshly restarted # Process first entry my($tag, $val) = each %hash) ; do_something($tag, $val); #Process second entry ($tag, $val) = each %hash; do_something($tag, $val); more_processing();
    No loop here, so I don't suppose you would want to change the each semantics here.

    But now suppose more_processing calls your while loop using each, which can also be called from other points in the program. You will still have the problem of not knowing the state, even with your proposed modification to the semantics of loops/each. So you'd have to reset the hash anyways.

    So in short, I don't think your proposal would make the use of each easier, while the current sometimes useful behaviour would be lost. So all in all your proposal would be a loss.

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://357045]
Approved by blokhead
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others making s'mores by the fire in the courtyard of the Monastery: (11)
As of 2014-10-01 17:03 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    What is your favourite meta-syntactic variable name?














    Results (30 votes), past polls