readey has asked for the wisdom of the Perl Monks concerning the following question:

Here's a quickie, and maybe I'm gonna be acused of being a nerd but I am interested in mechanism of how the following %hash sort works so, given the following;
foreach $key (sort { ($a) <=> ($b) } keys %hash) { #block of code to operate on %hash; }
My question is this; Does the foreach loop sort everything out first BEFORE going thru foreach $key to operate on the loop? If this is the case then it would seem (for whatever perverse reason you can think of) possible to do this
foreach $key (sort{($a)<=>($b)} keys %hash)
to yield a sorted hash. And I suppose that the foreach loop actually has to operate twice, once to sort the %hash then again to actually iterate over the keys. Hopefully this makes sense to all the helpful monks out there.

Replies are listed 'Best First'.
(Efficient sorting in Perl) Re: sort mechanism
by arhuman (Vicar) on Apr 06, 2001 at 13:05 UTC

    OeufMayo once gave me a useful link about efficient sorting in Perl.

    You may find there several useful tricks and facts that everybody (IMHO) should know :
    • The Schwartzian Transform from our beloved merlyn.
    • The Orcish Maneuver.
    • The packed-string sortkey.
    • A 'quite deep' approach of the sort function.
    • The Sort::Records module.
    • ...
    Please just read it, you won't regret it...


    "Only Bad Coders Badly Code In Perl" (OBC2IP)
Re: sort mechanism
by ChOas (Curate) on Apr 06, 2001 at 13:01 UTC
    Hi!

    I hope this helps, this is what happens:

    1: keys %Hash returns a list of all the keys in the hash
    2: sort sorts that list
    3: The foreach goes over the sorted list, presenting you the keys
    of the hash in sorted order

    foreach $key (sort{($a)<=>($b)} keys %hash)
    is a perfectly valid way to get a list of the keys of the hash in
    a sorted order (provided they are numeric)

    btw the block that you mentioned does not operate on the hash
    in any real sense, you use the keys of the hash in there
    and yes, you can use those to access the hash.

    GreetZ!,
      ChOas

    print "profeth still\n" if /bird|devil/;
Re: sort mechanism
by kal (Hermit) on Apr 06, 2001 at 12:56 UTC

    Your first foreach says "Iterate over every key in the hash, in order". Your second one says the same thing. The hash itself is not changed.

    I'm also unsure what you mean by a "sorted hash" - if you mean, the next time you do a foreach on it's keys, they come out in order, nope. hashes are always unsorted - that is, the ordering is dicatated by need to access values quickly. If you want a "sorted hash", the nearest thing I can think of is to do your foreach, push the values onto an array and then do a foreach on the array.

Re: sort mechanism
by satchboost (Scribe) on Apr 06, 2001 at 20:16 UTC
    This brings up an interesting question. Reading the Library notes on sort seem to indicate that it will return a list (of sorted values).
    So, the following code does what you'd think it does: print (3, 2, 1) ? "yes" : "no";
    However, if you change it to: print sort (3, 2, 1) ? "yes" : "no"; you get "no", not "yes".

    Why does this behavior happen? I'm obviously not understanding something about the sort function.

      I just bumped into this behavior when asking the confused question in Evaluation Disorder. As it turns out, sort always returns false in a scalar context, not the length of the returned list, as one might assume.
         MeowChow                                   
                     s aamecha.s a..a\u$&owag.print
        My question then becomes where would this be documented? How would I known this without playing around? (Granted, the playing was still fun and enlightening, but that's not the point.)
Re: sort mechanism
by McD (Chaplain) on Apr 06, 2001 at 19:47 UTC
    One nitpick that I'll comment on because it got me thinking (always a challenging task first thing in the morning... good lord, it's noon...):

    I think the sort function you provided is only doing what you want by coincidence. When you say:

    sort { ($a) <=> ($b) } keys %hash

    The ()'s around $a and $b force the <=> operator to use list context. If we know that $a and $b will always be single (numeric) elements (as in this case), this will produce the correct behavior - but if they were lists, things would be different.

    In list context, I believe <=> compares the two lists element by element - damn if I can find where that's documented, though. Brother monks? Anybody got a pointer?.

    Disclaimer: Again, this is trivia, won't affect the code shown, and I need more coffee.

    Peace,
    -McD

      No that doesn't work. The parens basically are no-ops there. a scalar is just a scalar. You would need @{$a} to explode the array.

      update (GOOD band names =)

      --
      $you = new YOU;
      honk() if $you->love(perl)

        Um... right. I don't think I was completely clear - what I was trying to point out was that <=> has different behavior when comparing lists than when comparing scalars.

        Although comparing two lists of a single scalar each is, as you said, exactly like comparing the naked scalars.

        So what have we learned?

        1. I shouldn't node before coffee, no matter what the hour.
        2. "Naked Scalars" would be a pretty good name for a rock and roll band.
        3. So would "Spaceship Operator."
        4. I still can't find where <=>'s behavior in list context is documented.
        Peace,
        -McD