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

I'm developing a Perl script that will perform certain functions based upon the time of day and day of the week. Below is a minimal test case of a piece of code that I'm working on.
#!/usr/bin/perl -w use strict; my $now = 8; # we'll pretend it's between 8 and 9 PM my @hours; my %url = ( monday => { 1 => "Too early in the morning", 3 => "Someone came home from a bender", 7 => "Time for coffee", 16 => "Need to slack off the last hour of work" } ); # begin awkward code @hours = reverse (sort {$a <=> $b} keys %{$url{"monday"}}); foreach (@hours) { $now = $_, last if $_ <= $now; } # end awkward code
That code will assign "7" to $now. It seems to me that there should be a more "perlish" way to extract from the hash of hashes the greatest hash key that is less than or equal to $now. Can anyone help me come up with a clear example? (On the other hand, I don't want terribly obfuscated code).

Replies are listed 'Best First'.
RE: Hash of hashes question
by Russ (Deacon) on Jun 19, 2000 at 03:51 UTC
    My first thought was to replace the "awkward code" with:
    $now = (sort grep {$_ <= $now} keys %{$url{'monday'}})[-1];
    In other words, grep for the keys less than your test value, sort them, and take the last one.

    Note: this has nothing to do with efficiency of the code (execution speed), but it does put it on one line (and feels kinda 'perlish' to me).

    Russ

        True. Of course, in this application, there appear to be a maximum of 24 possible entries...

        Being curious, I did a quick benchmark. However, its results defy logic (my (sort grep keys)[-1] is always faster than a O(1) "max()" algorithm!)

        Since I am obviously missing something, would you be willing to post some benchmarking code showing the efficiency differences between "sort" and "max()" in this context?

        Russ

        P.S. Just for the sake of helping me find my silliness, here is what I was running. Changing the map to larger numbers (to make the source hash bigger) made no difference to the benchmark times.

        #!/usr/bin/perl -w use strict; use Benchmark; my $now = 8; # we'll pretend it's between 8 and 9 PM my %url = ( monday => { @{[map(($_,1), (1..1000))]} } ); timethese(100000, { a => q{ $now = (sort grep {$_ <= $now} keys %{$url{'monday'}})[-1]; }, b => q{ $now = ($now < $_ && $_ < 8 ? $_ : $now) for keys %{$url{'monday'} +}; } });
        The results were the same even without doing the assignment each time in b (only assign when we should, rather than each time through the loop). I left it this way for no apparent reason...