Perl 在Try::Tiny catch块中处理next时出错

Perl 在Try::Tiny catch块中处理next时出错,perl,exception,error-handling,try-catch,Perl,Exception,Error Handling,Try Catch,下面的代码(一个简化的示例,实际上我正在迭代对象列表并试图捕获异常)通过转到for列表中的下一项来执行错误处理。它可以工作,但给出了关于在catch子例程中使用循环控制语句的警告: use strict; use warnings; use Try::Tiny; use 5.010; NUM: for my $num (1 .. 10) { try { if ($num == 7) { die 'ugly number'; } } catch {

下面的代码(一个简化的示例,实际上我正在迭代对象列表并试图捕获异常)通过转到for列表中的下一项来执行错误处理。它可以工作,但给出了关于在catch子例程中使用循环控制语句的警告:

use strict;
use warnings;
use Try::Tiny;
use 5.010;

NUM: for my $num (1 .. 10) { 
  try { 
    if ($num == 7) { 
      die 'ugly number'; 
    } 
  } catch { 
    chomp;
    say qq/got "$_"/; 
    next NUM; 
  }; 
  say qq/number $num/; 
}
产出:

number 1
number 2
number 3
number 4
number 5
number 6
got "ugly number at testtry.pl line 9."
Exiting subroutine via next at testtry.pl line 14.
Exiting subroutine via next at testtry.pl line 14.
number 8
number 9
number 10

我可以看到两种方法来解决这个问题——用一个作用域为no warnings的块关闭此用法范围内的警告,或者将错误消息复制到一个临时变量,并在catch块外检查它/next。前者可能有我忽略的问题,而第二个则稍微扩展了错误处理。首选哪种方法,还是我忽略了另一种方法?

另一种解决方法,尽管可能只对这个简化的示例有用,而不是在整个程序中,是避免
下一个
指令将代码放在模具后,如:

use strict;
use warnings;
use Try::Tiny;
use 5.010;

for my $num (1 .. 10) { 
  try { 
    if ($num == 7) { 
      die 'ugly number'; 
    }   
    say qq/number $num/; 
  } catch { 
    chomp;
    say qq/got "$_"/; 
    #next NUM; 
  }; 
  #say qq/number $num/; 
}
它产生:

number 1
number 2
number 3
number 4
number 5
number 6
got "ugly number at script.pl line 11."
number 8
number 9
number 10

基本上,我在寻找下面的流控制。我只是不喜欢把$e的范围扩大到下一个NUM错误处理

use strict;
use warnings;
use Try::Tiny;
use 5.010;

NUM: for my $num (1 .. 10) { 
  my $e;
  try { 
    if ($num == 7) { 
      die 'ugly number'; 
    } 
  } catch { 
    chomp;
    say qq/got "$_"/; 
    $e = $_; 
  }; 
  if ($e) {
    next NUM;
  }
  say qq/number $num/; 
}

在catch块内,放置一个
无警告“退出”
。这将禁用警告。
strict
warnings
pragmas只是为了帮助您,当它们妨碍您时,您可以随意禁用它们

该页面列出了内置的警告和错误类别。您可以查看通过禁用此类别将被静音的所有消息,并决定是否值得

编辑: 您可以利用一个成功的
try
返回
undef
,但出现错误时,您会得到
catch
块的值。这使我们能够做到:

NUM: for my $num (1 .. 10) { 
  try {
    die 'ugly number' if $num == 7;
  } catch { 
    chomp;
    say qq/got "$_"/; 
    return 1;       # return some true value
  } and next NUM;   # go to next iteration here, outside the try/catch
  say qq/number $num/; 
}

然而,我发现
无警告
解决方案更优雅,也更明显。

对于这种情况,以及您试图解决的更一般的情况,正确的解决方案是将只有在没有错误的情况下才会发生的内容放入
try
块,而不是依赖显式循环控制。看起来是这样的:

for my $num (1 .. 10) { 
  try { 
    if ($num == 7) { 
      die 'ugly number'; 
    } 
    say qq/number $num/; 
  } catch { 
    chomp;
    say qq/got "$_"/;
  }; 
}
last
是实际上需要额外注意的循环控制语句,因为它所做的不仅仅是跳过循环体。但是考虑一下

try {
  for my $num (1 .. 10) { 
    try {
      die 'ugly number' if $num == 7;
      die 'early exit' if $num == 9;
      say qq/number $num/; 
    } catch {
      die $_ if /^early exit/;
      chomp;
      say qq/got "$_"/;
    }; 
  }
};

在错误处理之后,有相当多的发言权,所以第一个也是一个糟糕的选择。如果在块中返回(如果没有,则隐式返回),它将执行以下代码,这是我不想要的。@Oesor:是的。我在第二个解决方案中意识到了这一点,并将其删除。我正在考虑这一点,但我更担心的是,我会冒着比赛条件或类似的风险禁用现有收益。我可能会走这条路。事情看起来不错,但我没有意识到“和下一个NUM;”的重要性线路。不管怎样,迭代正在为定义的范围运行,所以请您解释这一行好吗?@bimleshsharma如果我们省略了
和下一个NUM
,那么将打印出
第7个
,这是我们想要避免的。因此,我们必须在执行qq/number$num/
之前跳转到下一个循环迭代。如果我们在
catch
块中执行此操作,我们将通过下一个
获得警告
退出子例程,此问题最初与此有关。请删除建议使用
无警告“退出”
。在这种情况下,警告实际上告诉了我们一些非常重要的事情,不应该沉默。(特别是,
next
内的
catch
不会跳到
for
循环的下一次迭代。)@PedroLM我不确定你在说什么。代码按照OP的预期工作,并且
next NUM
会跳到下一个迭代(特别是
say qq/number$NUM/
不会在捕获后执行)。虽然跨子程序边界使用循环控件通常是一种可怕的做法,但在这种特殊情况下是合理的,因此禁用警告是必要的。可以说,更好的选择是不使用Try::Tiny。