【问题标题】:Should I avoid nested if statements in Perl?我应该避免在 Perl 中嵌套 if 语句吗?
【发布时间】:2013-03-21 01:57:51
【问题描述】:

我正在做一些 Perl,看到我嵌套的“if”语句让我发疯。我设法在另一部分用防护块减少了其中一些,但我被困在这里。

您认为我可以将代码保持原样,还是有一种“正确”的方式来重构以下代码? (我也承认对 Perl 比较陌生)

这实际上是一个子程序,要求用户输入列表(外部文件)的每个参数。 $[3] 是匹配模式,$[2] 是所考虑参数的默认值(如果没有,则为 NULL),$_[1] 指定它是否是强制性的。 'next' 语句是指读取的下一个参数(while 循环)。

在大家的帮助下(谢谢!),这是最新版本。

100         if ( $input ne '' && ( $input !~ $match || $input =~ /'.+'/ ) ) {
101             print "! Format not respected. Match : /$match/ (without \' \')\n";
102             next;
103         }
104         if ( $input eq '' ) {
105             if ( $default eq 'NULL' ) {
106                 if ( $manda eq 'y' ) {
107                     print "! Mandatory parameter not filled in\n";
108                     next;
109                 }
110                 print "+ Ignoring parameter.\n";
111                 $input = '';
112             }
113             else {
114                 print "+ Using default value\n";
115                 $input = $default;
116             }
117         }

 98        if($input eq ''){
 99             if($_[2] eq 'NULL'){
100                 if($_[1] eq 'y'){
101                     print "! Mandatory parameter not filled in\n";
102                     next;
103                 }
104                 else{
105                     print "+ Ignoring parameter.\n";
106                     $input = '';
107                 }
108             }
109             else{
110                 print "+ Using default value\n";
111                 $input = $_[2];
112             }
113         }
114         elsif($input !~ $_[3] || $input =~ /'.+'/){
115                 print "! Format not respected. Match : /$_[3]/ (without \' \')\n"; 
116                 next;
117             }
118         }

【问题讨论】:

  • 究竟是什么让您对那些嵌套的 if 感到抓狂?
  • 我不知道,我只是觉得还有另一种写法……

标签: perl coding-style nested


【解决方案1】:

这是混乱的答案的一个更易读的版本:

# Set sane variable names...
my ($is_required, $default, $pattern) = @_

# Convert the external string convention for simpler evaluation...
$default = undef if $default eq 'NULL'

# Refuse problematic input before going any further...
if ($input ne '' && $input !~ $pattern || $input =~ /'.+'/) {
    print "! Format not respected. Match : /$pattern/ (without \' \')\n"; 
    next;
}


# If there was no input string...
if($input eq '') {

    # Set the default, if one was provided...
    if( $default ) {
        print "+ Using default value\n";
        $input = $default;
    } 
    # otherwise, complain if a default was required...
    else {
        if( $is_required eq 'y' ){
            print "! Mandatory parameter not filled in\n";
            next;
        }
        print "+ Ignoring parameter (no input or default provided).\n";
    }
}

重点是:

  • 如果您使用 next 退出当前循环,则不需要 else
  • 应先处理特殊情况
  • 使用命名变量可以大大提高可读性

【讨论】:

  • 谢谢。我们的回复交叉(见我的第一篇文章编辑)。顺便说一句,在我的代码中, $is_required 强制用户填写,这不是抱怨:p(虽然没有改变任何东西,你还没有阅读整个子程序)
  • 抱歉重复发帖,但我喜欢你的评论方式;而“你还没有阅读”我的意思是“我还没有发布”。
  • 谢谢,虽然我不能要求任何功劳。评论风格是 Damian Conway 的——我在阅读了他的作品后采用了它,因为我也喜欢它。 ;)
【解决方案2】:

有时有助于提高可读性的另一种方法是将部分或全部分支放在命名良好的代码引用中。这是这个想法的开始:

$format_not_respected = sub {
    return 0 if ...;
    print "! Format not respected....";
    return 1;
}
$missing_mandatory_param = sub {
    return 0 if ...;
    print "! Mandatory parameter not filled in\n";
    return 1;
}

next if $format_not_respected->();
next if $missing_mandatory_param->();
etc...

【讨论】:

  • 我不知道,谢谢!即使在我的情况下,我已经在一个子例程中(这是嵌套的 if 或子子例程,这就是问题),这对于其他脚本可能会派上用场。
  • 每次调用这个函数时都会编译几个 subs ——这真的是你想要做的吗?这是一个好主意——我放弃了你,但如果这会被调用很多,最好不要使用捕获行为。 (我认为。)
【解决方案3】:

主要关注的是保持代码的可读性。

如果您可以使用嵌套的 if 语句获得可读的代码,请继续。但始终保持常识活跃。

【讨论】:

  • 您是否必须维护我的代码,这样做会有困难吗?
  • @Isaac,没有。 (但我接受过维护难以阅读的代码的培训,所以我不是一个好的参考)。
【解决方案4】:
if($input ne '' && ($input !~ $_[3] || $input =~ /'.+'/)) {
    print "! Format not respected. Match : /$_[3]/ (without \' \')\n"; 
    next;
}
if($input eq '') {
    if($_[2] eq 'NULL') {
        if($_[1] eq 'y'){
            print "! Mandatory parameter not filled in\n";
            next;
        }
        print "+ Ignoring parameter.\n";
        $input = '';
    } else {
        print "+ Using default value\n";
        $input = $_[2];
    }
}

【讨论】:

  • 我喜欢那个!谢谢 ! (除了被拥抱的其他人:p)
【解决方案5】:

通常的做法是为数组索引定义常量,并赋予它们有意义的名称。如:

use constant MANDATORY => 1,
             DEFAULT => 2,
             PATTERN => 3;
...
if($_[DEFAULT] eq 'NULL') {
   ...
}

就嵌套而言——您应该经常尝试减少缩进(意味着保持低嵌套级别),但绝不要以保持代码易于理解为代价。我对嵌套级别没有问题,但这也只是您代码的一小部分。如果确实有问题,您可以将条件分解为单独的子例程。

【讨论】:

  • 正如我所说,这是一个子程序,我所有的变量都是本地的。因此我不能使用“使用”语句?幸运的是,嵌套在其余代码中不是问题。谢谢你的回答。
  • '使用常量'有一些缺点。使用常量创建的常量不能被插值(例如在字符串中),因为它们没有符号,并且在运行时没有词法范围。它们是包范围的并在编译时生成。如果您在运行时设置了一个常量,这将是一个问题,例如通过一个函数。 Damian Conway 所著的“Perl 最佳实践”一书中对此进行了更好的描述,并提供了出色的示例。
【解决方案6】:

如果您的逻辑需要嵌套 if 语句,那么我想它们没有问题。

但是,您可以通过

来提高代码的可读性
  1. 只使用更多的空白空间和
  2. 通过使用您自己的变量而不是直接在 @_ 上进行操作

这不是更易读吗?

 98        if ($input eq '') {
 99             if ($default eq 'NULL'){
100                 if ($input eq 'y'){
101                     print "! Mandatory parameter not filled in\n";
102                     next;
103                 }
104                 else {
105                     print "+ Ignoring parameter.\n";
106                     $input = '';
107                 }
108             }
109             else {
110                 print "+ Using default value\n";
111                 $input = $default;
112             }
113         }
114         elsif ($input !~ $foo || $input =~ /'.+'/) {
115                 print "! Format not respected. Match : /$foo/ (without \' \')\n"; 
116                 next;
117             }
118         }

【讨论】:

  • 关于第 1 点,我偶尔会使用 Perltidy。我目前正在尝试解决问题并提前调试。 2. 我读到(在 O'reilly 的书和​​其他地方)它 $_ 和 $@ 被广泛使用,没有提到任何缺点?
  • 1.通过易于阅读的代码,尝试解决问题更容易。 2. 缺点是可读性。例如,当替换您对 $_[x] 的各种用途时,我立即注意到您对这些变量所包含的内容的描述并不完全正确。
  • 1.你是绝对正确的。我喜欢它的代码,但它肯定会由其他人维护(首先是你)。 2. @_ 是一个数组,其中包含(按值)传递给函数的参数列表。正确的 ?至于 $_,我只在 r/w (into) 文件句柄时使用它。
  • 你是对的@_。但最好将 @_ 中的值转换为具有正确名称的标量变量。
【解决方案7】:

鉴于您可能会做意大利面goto,否则,绝对不会。

switch case 可能更好。

【讨论】:

  • 我吸取了教训:goto 是地狱。 ;) 从来没有想过。 Switch case 在这里并不适用,因为我正在测试 4 个不同的变量。
  • 你不是说given/when吗?
  • 不是只用'use feature'或者'use switch 'perl 6.0''实现的吗?
【解决方案8】:

如果您不喜欢所有其他的,您可以简化为以下内容。

if($input eq ''){
    $input = $_[2];
    $output = "+ Using default value\n";
    if($_[2] eq 'NULL'){
        $input = '';
        $output = "+ Ignoring parameter.\n";
        if($_[1] eq 'y'){
            $output = "! Mandatory parameter not filled in\n";
        }
    }
}
elsif($input !~ $_[3] || $input =~ /'.+'/){
    $output = "! Format not respected. Match : /$_[3]/ (without \' \')\n"; 
}
print $output;

【讨论】:

    【解决方案9】:

    我认为关注嵌套的主要(如果不是唯一的话)原因是算法复杂性。在其他情况下,您应该担心可读性和可维护性,这可以通过适当的注释和缩进来解决。

    我总是找到一个很好的可维护性练习来阅读我的旧代码,不仅可以反馈可读性,还可以反馈技术......

    【讨论】:

      【解决方案10】:

      我想你可以做逻辑组合来把它弄平:

      if(($input eq '')&&($_[2] eq 'NULL')&&($_[1] eq 'y')){
        print "! Mandatory parameter not filled in\n";
        next;
      }
      
      elsif(($input eq '')&&($_[2] eq 'NULL')){
        print "+ Ignoring parameter.\n";
        $input = '';
      }
      
      elsif($input eq ''){
        print "+ Using default value\n";
        $input = $_[2];
        next;
      }
      
      
      elsif($input !~ $_[3] || $input =~ /'.+'/){
        print "! Format not respected. Match : /$_[3]/ (without \' \')\n";
      }
      
      print $output;
      

      然后你可以用一个开关让它变得更好一点。

      【讨论】:

      • 这实际上是我以前的版本,不过 '&&' 测试不适合我。
      • 这甚至不是 Perl。 Perl 没有elseif 只有elsif
      • @Imagest,在这种情况下,可读性比微小的速度差异更重要,根据问题作者的说法,flatter 对他来说更具可读性,这就是他所要求的。 @Brad Gilbert 好吧,你抓住了我,我什至不知道珍珠,但没关系,很明显 elseif 是什么,不是吗。我给你改了答案。
      猜你喜欢
      • 2013-02-12
      • 1970-01-01
      • 2022-12-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-09-27
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多