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

Hi Monks,

I've just written some code to do the following:

$item will contain a string in the format 'X:Y'.
First I test if 'X:Y' exists in the hash, and if so, put the hash's value into $level.
Failing that, I test if 'X:*' exists in the hash, and if so, put the hash's value into $level.
Failing that, I test if '*' exists in the hash, and if so, put the hash's value into $level.
Failing that, I set $level = 0.

Here's the code I wrote to achieve the above:

if ($level = $access{$item}) # Assignment & test for existance {} # Not very tidy looking, IMHO elsif ($item =~ /^(.+):.+/ and $level = $access{"$1:*"} ) # Assignmen +t & test for existance {} # Not very tidy looking, IMHO else { unless ($level = $access{'*'}) # Assignment & test for existance { $level = 0 } }

The above code seems to be doing the job, and I'm not totally unhappy with it, but I'm wondering if there's a tidier or more concise way to write it, e.g. avoiding the empty '{}', and anything else you can suggest.

Note, the above was written without strict or warnings, and I'll be leaving it that way, despite the fact that it's against your religion. 8)

Thanks for your time.
Tel2

Replies are listed 'Best First'.
Re: Tidy up conditions
by martin (Friar) on Mar 18, 2015 at 23:49 UTC
    You could use the short-cut behaviour of boolean operators like this:
    $level = $access{$item} || $item =~ /^([^:]+):/ && $access{"$1:*"} || $access{'*'} || 0;
    Note that this assumes your %access hash does not contain boolean false values, as your initial solution did. To actually check for the presence of these keys in the hash, the exists function will help.
      Thanks heaps for that, Martin!  Awesome answer!  Concise, yet...functional.

      I don't understand your concern about boolean false values, though. Could you give me an example which would make your code fail, please?

      Thanks again.
      Tel2

        my %hash = (0=>'that was false', ''=>'oops false too', 42=>'So very true');

        The first two keys there evaluate to false, and would cause a problem with the code.

Re: Tidy up conditions
by jeffa (Bishop) on Mar 18, 2015 at 23:37 UTC

    When you specify 'X:*' i am assuming this is not a regular expression. With that caveat, perhaps this is cleaner (or even clearer) for you?

    use strict; use warnings; my %hash1 = ( 'X:Y' => 10, 'X:*' => 1, '*' => 1 ); my %hash2 = ( 'X:Z' => 1, 'X:*' => 20, '*' => 1 ); my %hash3 = ( 'A:B' => 1, 'A:*' => 1, '*' => 30 ); print find_level( 'X:Y', \%hash1 ), $/; print find_level( 'X:Y', \%hash2 ), $/; print find_level( 'X:Y', \%hash3 ), $/; sub find_level { my ($item, $hash) = @_; for ( $item, join( ':', (split ':', $item)[0], '*' ), '*' ) { return $hash->{$_} if exists $hash->{$_}; } return 0; }

    jeffa

    L-LL-L--L-LL-L--L-LL-L--
    -R--R-RR-R--R-RR-R--R-RR
    B--B--B--B--B--B--B--B--
    H---H---H---H---H---H---
    (the triplet paradiddle with high-hat)
    
      Nice work, jeffa!
      And your assumption was correct, about the '*'.

      Thanks.
      Tel2