【问题标题】:How can I cleanly handle error checking in Perl?如何干净地处理 Perl 中的错误检查?
【发布时间】:2009-08-03 16:44:38
【问题描述】:

我有一个管理错误检查的 Perl 例程。根据先前的成功,大约有 10 种不同的检查,其中一些是嵌套的。这些通常不是我需要croak/die 的特殊情况。此外,一旦发生错误,就没有必要再进行其余的检查了。

但是,我似乎想不出一个巧妙的方法来解决这个问题,除非使用类似于以下可怕的黑客攻击:

sub lots_of_checks
{

 if(failcond)
 {
  goto failstate:
 }
 elsif(failcond2)
 {
  goto failstate;
 }

 #This continues on and on until...

 return 1; #O happy day!

 failstate:

 return 0; #Dead...
}

我希望能够做的事情是这样的:

do
{
 if(failcond)
 {
  last;
 }
 #...
};

【问题讨论】:

  • 为什么不立即返回而不是使用 goto 失败状态?看起来不漂亮,但气味更少。
  • 我喜欢坚持“单进单出”。这很可能是不可能的......我正在尝试探索替代方案。
  • 在 Perl 社区中,跳出循环/子/等通常被认为是更好的形式。早期的。它清楚地表明该块的其余部分不适用于指定的条件。
  • 是的。 Perlfolk 喜欢保护条款并且不关心单出口,这是他们更出色的品质之一。

标签: perl


【解决方案1】:

空的 return 语句是从 Perl 子返回 false 比返回 0 更好的方法。后一个值实际上在列表上下文中为真:

sub lots_of_checks {
    return if fail_condition_1;
    return if fail_condition_2;
    # ...
    return 1;
}

【讨论】:

  • 这和return undef if cond;一样吗?
  • @Paul,空白返回在标量上下文中给出 undef,空列表是列表上下文。
【解决方案2】:

或许你想看看以下关于 perl5 中异常处理的文章:

【讨论】:

    【解决方案3】:

    你绝对可以做你喜欢的事。

    Check: {
        last Check
            if failcond1;
        last Check
            if failcond2;
        success();
    }
    

    【讨论】:

      【解决方案4】:

      为什么不使用异常?任何不应遵循正常代码流程的情况都是例外。使用“return”或“goto”实际上是一回事,只是更“不是你想要的”。

      (你真正想要的是延续,“return”、“goto”、“last”和“throw”都是特殊情况。虽然 Perl 没有完整的延续,但我们确实有转义延续;参见 @ 987654321@)

      在您的代码示例中,您编写:

      do
      {
       if(failcond)
       {
        last;
       }
       #...
      };
      

      这可能与以下内容相同:

      eval {
         if(failcond){
             die 'failcond';
         }
      }
      

      如果你想狡猾并忽略其他异常:

      my $magic = [];
      eval {
          if(failcond){
              die $magic;
          }
      }
      if ($@ != $magic) {
          die; # rethrow
      }
      

      或者,您可以使用上面提到的 Continuation::Escape 模块。但 没有理由忽略异常;这是完全可以接受的 以这种方式使用它们。

      【讨论】:

        【解决方案5】:

        鉴于你的例子,我会这样写:

        sub lots_of_checks {
            local $_ = shift;  # You can use 'my' here in 5.10+
        
            return if /condition1/;
            return if /condition2/;
            # etc.
        
            return 1;
        }
        

        注意裸露的return 而不是return 0。这通常更好,因为它尊重上下文;标量上下文中的值为undef,列表上下文中的值为()(空列表)。

        如果你想坚持一个单一的出口点(这有点不像 Perlish),你可以在不诉诸 goto 的情况下做到这一点。正如last 的文档所述:

        ... 块本身在语义上与执行一次的循环相同。 因此,“last”可用于提前退出此类区块。

        sub lots_of_checks {
            local $_ = shift;
            my $all_clear;
        
            {
                last if /condition1/;
                last if /condition2/;
                # ...
                $all_clear = 1; # only set if all checks pass
            }
        
            return unless $all_clear;
            return 1;
        }
        

        【讨论】:

        • 来自 perlmonks:如果 $_ 被绑定或别名为绑定的某个对象,则本地 $_ 无法正常工作,并且它不保护 pos($_)。在这种情况下,local $_ = shift; 非常不寻常,我更喜欢my ($arg) = @_; for ( $arg ) { /condition1/ etc etc },因为无论如何你都必须使用卷曲。
        • 我避免使用伪开关语法,认为它可以通过保持与第一个示例的对称性来使第二个示例中的机制更清晰。至于本地化$_,很遗憾local 的行为与typeglobs 纠缠不清。在尝试本地化导出的包变量之前,我遇到了问题。 “安全”(和丑陋)的替代方案是导出/本地化整个 glob。循环中的实现是什么——local $_ 或local $*?我可以看到任何一个导致问题...
        • @Michael Carman 好吧,不幸的是,我只知道local 以这种方式使用可能存在问题。无论如何,我尽量避免$_,在大多数情况下坚持使用词法变量,主要是因为我懒得去查找这类问题。
        • @Sinan Unur:我想你的意思是local *_而不是local $*
        • @daotoad 我终于找到了。 perlmonks.org/?node_id=561931顺便说一句,它出现的背景也很有趣:perlmonks.org/?node_id=561895
        【解决方案6】:

        如果您想保持单进/单出结构,您可以稍微修改其他建议以获得:

        sub lots_of_checks
        {
            goto failstate if failcond1;
        
            goto failstate if failcond2;
        
            # This continues on and on until...
        
            return 1; # O happy day!
        
            failstate:
        
            # Any clean up code here.
        
            return; # Dead...
        }
        

        IMO,Perl 对语句修饰符形式“return if EXPR”的使用使得保护子句比在 C 中更具可读性。当您第一次看到该行时,您知道您有一个保护子句。这个特性经常被贬低,但在这种情况下我很喜欢它。

        将goto 与语句修饰符一起使用可以保持清晰,减少混乱,同时保留您的单一退出代码样式。当我在例程验证失败后需要进行复杂的清理工作时,我使用过此表单。

        【讨论】:

          猜你喜欢
          • 2021-07-14
          • 1970-01-01
          • 1970-01-01
          • 2021-10-22
          • 2012-04-20
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多