3

我对 perl 的效率和可读性有疑问

我有一个变量可以取几个值之一(5-6)。有时我想检查这是否是一个特定的值,有时我想检查它是否是几个选择之一。我在我的代码中的许多地方(在不同的函数中)都做出了这种决定,我想让它尽可能“严格”。

例如,说

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
}

这当然不是我的真实代码,并且使用有意义的变量名,我的 if 语句会变得很长并且包含几行。

任何帮助表示赞赏!

4

6 回答 6

7

List::MoreUtils模块具有any类似于短路的功能grep

use List::MoreUtils qw/any/;

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

也就是说,您仍然可以使用grep,但它总是测试所有元素,而any在第一个匹配元素之后中止:

say "correct mode" if grep { $_ eq $mode } qw/one two three/;
于 2013-11-06T21:36:04.260 回答
3

一个主意。

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

否则我喜欢使用空格:

if (
    'one' eq $mode or
    'two' eq $mode or
    'three' eq $mode
) {
}
于 2013-11-06T21:33:18.710 回答
3

有许多选项 (TMTOWDI)。这里有两个最简单的:

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

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

通过事先进行一些设置,您可以使其更高效。这样做一次:

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

然后您的支票变得简单:

if ($modes{$mode}) { ... }
于 2013-11-06T21:37:29.413 回答
3

这对于smart match来说是一项不错的工作,但是该运算符已被标记为实验性的,并且可能会从 Perl 的更高版本中删除。

List::Utilany运营商。但是,要非常小心。这List::Util是至少从 Perl 5.8.8 开始的标准 Perl 模块。不幸的是,niceany运算符不包括在内。您需要更新此模块才能使用any. 但是,first操作员可能已经足够好,这是软件包的一部分:

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";
    }
}

既然你use List::Util qw(first);在你的程序中,用户应该意识到它first来自List::Util包,他们可以用perldoc List::Util它来查找它。

您也可以只使用grep并忘记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";
    }
}

您不应该做的是使用复杂的正则表达式或 if/else 链。这些不一定更清楚,并使您的程序更难理解:

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

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

如果您决定更改有效的值列表,则必须通过整个程序来搜索它们。这就是为什么我将它们设为常数。程序中只有一处需要修改。

于 2013-11-06T22:33:35.507 回答
1

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

if ($mode ~~ ['one', 'two', 'three'])
于 2013-11-06T22:00:59.297 回答
0

如果您要检查很多很多组合,请考虑将 $mode 转换为编号位:

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

只检查“一个”,if ($modeN == 1) {...

要检查“一”、“二”或“三”,if ($modeN & 7) {...

要检查“二”或“三”,if ($modeN & 6) {...

要检查“一”或(“二”和“三”),if ($modeN & 1 || &modeN & 6) {...

那对你有用吗?

于 2013-11-06T21:36:27.117 回答