简体   繁体   中英

perl simple efficiency and readability

I have a question on efficiency and readability in perl

I have a variable that can take on one of several values (5-6). Sometimes I want to check if this is a specific value, sometimes I want to check if it is one of several choices. I am making this sort of decision in many places in my code (in different functions), and I would like to make this as 'tight' as possible.

for example, say

my $mode; # can be any of qw(one two three four five six)
if ($mode eq 'one') {
    #do code
}

if ($mode eq 'one' or $mode eq 'two' or $mode eq 'three') {
    #do more code
}

This of course is not my real code, and with meaningful variable names, my if statements are getting quite long and wrap on several lines.

Any help is appreciated!

The List::MoreUtils module has the any function, which is like a short-circuiting grep :

use List::MoreUtils qw/any/;

say "correct mode" if any { $_ eq $mode } qw/one two three/;

That said, you can still use grep , but that always tests all elements, whereas any aborts after the first matching element:

say "correct mode" if grep { $_ eq $mode } qw/one two three/;

An idea.

my %please_name_me;
$please_name_me{$_}++ for qw(one two three);
if ($please_name_me{$mode}) {
    #do something
}

Otherwise I like using whitespace:

if (
    'one' eq $mode or
    'two' eq $mode or
    'three' eq $mode
) {
}

There are a number of options (TMTOWDI). Here are two of the simplest:

if ($mode =~ /^(?one|two|three)$/) { ... }

if (grep { $mode eq $_ } qw(one two three)) { ... }

With a little setup beforehand you can make it rather more efficient. Do this once:

 my @modes = qw(one two three);
 my %modes;
 @modes{@modes} = @modes;

Then your check becomes simply:

if ($modes{$mode}) { ... }

This would be a nice job for smart match , but the operator has been marked experimental and may be removed from later versions of Perl.

List::Util has the any operator. However, be very careful. The List::Util is a standard Perl module since at least Perl 5.8.8. Unfortunately, the nice any operator isn't included. You need to update this module in order to use any . However, the first operator may be good enough and that's part of the package:

use strict;
use warnings;
use feature qw(say);
use autodie;

use List::Util qw(first);

use constant VALID_VALUES => qw(one two three four five);

for my $value ( qw(zero one two three four five six) ) {
    if ( first { $value eq $_ } VALID_VALUES ) {
        say "$value is in the list!";
    }
    else {
        say "Nope. $value is not";
    }
}

Since you have use List::Util qw(first); in your program, user should realize that first is from the List::Util package and they can use perldoc List::Util to look it up.

You can also just use grep and forget about List::Util :

for my $value ( qw(zero one two three four five six) ) {
    if ( grep { $value eq $_ } VALID_VALUES ) {
        say "$value is in the list!";
    }
    else {
        say "Nope. $value is not";
    }
}

What you shouldn't do is use a complex regular expression or a if/else chain. These aren't necessarily any clearer and make your program harder to understand:

if ( $value =~ /^(one|two|three|four|five)$/ ) {

if ( $value eq "one" or $value eq "two" or $value eq "three" ... ) {

If you decide to change the valid list of values, you would have to go through your entire program to search for them. This is why I made them a constant . There's only one place in the program where they have to be modified.

如果您使用的是Perl 5.10或更高版本,则可以使用智能匹配运算符:

if ($mode ~~ ['one', 'two', 'three'])

If you have many, many combinations to check, consider translating $mode to a numbered bit:

if 'one' -> $modeN = 1
if 'two' -> $modeN = 2
if 'three' -> $modeN = 4  etc.

To check just 'one', if ($modeN == 1) {...

To check 'one', 'two', or 'three', if ($modeN & 7) {...

To check 'two' or 'three', if ($modeN & 6) {...

To check 'one' OR ('two' AND 'three'), if ($modeN & 1 || &modeN & 6) {...

Does that work for you?

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM