【问题标题】:Why does "try" not cause an undefined subroutine error?为什么“尝试”不会导致未定义的子程序错误?
【发布时间】:2020-01-19 05:36:49
【问题描述】:

有几次我忘记在脚本中加载Try::Tiny 模块,但我仍然使用它的try-catch 块,如下所示:

#!/usr/bin/env perl

use strict; 
use warnings;

try {
  call_a( 'x' );
} catch {
  die "ACTUALLY die $_";
};


sub call_a {
  die "Yes, I will";
}

由于某种原因,脚本运行良好,但没有给出没有try 的任何提示。没有Undefined subroutine 错误。这让我想知道为什么我提出的异常没有被捕获。

为什么它会安静地工作,没有错误?

编辑

我也查看了符号表:

say "$_: %main::{ $_ }" for keys %main::; 

发现那里没有try。我也尝试在上面的脚本中将其称为main::try,它也没有导致任何错误。

【问题讨论】:

  • 在调用不存在的函数 try 之前评估参数时,代码只会死掉。
  • @nwellnhof 因此,作为对try (call_a) 的参数调用在调用try 之前运行并在此期间死亡,try 从未尝试过。我尝试了anything 而不是try,结果相同。你的分析似乎是正确的。请提交它作为答案。
  • @nwellnhof 不过,当call_a 随机死亡时,我制作了另一个版本。如果它确实死了,catch 块也会运行。如果catch 也死了,那么仍然没有错误。如果catch 不是die,我们得到错误:Can't locate object method "catch"。当然,它并不能反驳你的分析。它只是说明了如何用这个来割伤自己。
  • 更正:'如果它不死'

标签: perl


【解决方案1】:

这是由于间接对象语法造成的,并且是 this example 的更精细的变体。

"indirect object notation" 允许代码

PackageName->method(@args);

写成

method PackageName @args;

所以“try”和“catch”这两个词无关紧要。这里有趣的一点是更多的涉及和扩展的语法,有两个部分,每个部分都使用这种间接对象表示法。

有问题的代码实际上具有method BLOCK LIST 形式,但这也通过间接对象语法进入(do BLOCK)->method(LIST),其中do BLOCK 需要生成包的名称或祝福(对象)引用以获得有意义的方法调用。这在下面的Deparse 输出中可以看到。

在此代码上使用B::Deparse 编译器后端(通过O 模块)

use strict; 
use warnings;
use feature 'say';

try   { call_a( 'x' ) } 
catch { 
    die "ACTUALLY die";
    #say "NO DONT die";
};

sub call_a { 
    die "Yes it dies";
    #say "no die";
}

perl -MO=Deparse script.pl 应该显示非常接近的近似值:

使用警告; 使用严格; 使用功能“说”; 尝试 { call_a('x') } 做 { 死'实际上死' }->捕捉; 子调用_a { 使用警告; 使用严格; 使用功能“说”; die '是的,它死了'; } undef_sub.pl 语法OK

对于Deparse 而言,嵌套的间接对象语法显然太多了,它仍然在输出中留下method BLOCK LIST 形式。等价的代码可以写成

(do { call_a('x') })->try( (do { die("ACTUALLY die") })->catch() );

在这种情况下更简单

call_a('x')->try( die("ACTUALLY die")->catch() );

因此,原始代码被解释为有效语法 (!),它是 try (call_a('x')) 之后块的内容首先运行 --- 所以程序死了,然后永远不会去追求“方法”try

如果我们把例子改成

会更有趣
use strict;
use warnings;
use feature 'say';

try   { call_a( 'x' ) }
catch {
    #die "ACTUALLY die"; 
    say "NO DONT die";
};

sub call_a {
    #die "Yes it dies";
    say "no die";
}

在任何地方都没有die-ing。用-MO=Deparse运行看看

use warnings;
use strict;
use feature 'say';
try {
    call_a('x')
} (catch {
    say 'NO DONT die'
} );
sub call_a {
    use warnings;
    use strict;
    use feature 'say';
    say 'no die';
}
undef_sub.pl syntax OK

现在是直接的method {} args 语法(args 本身也由Deparse 以间接对象表示法显示)。 等效代码是

call_a('x')->try( say("NO DONT die")->catch() );

首先call_a() 去哪里,在它返回之后,接下来运行try 方法调用中的参数列表的代码。我们没有遇到die,实际运行情况如下

不死 不,不要死 不能在没有包或对象引用的情况下调用方法“catch”...

所以现在方法“catch”确实出现了问题。

感谢ikegami cmets


如果上面的块返回一个确实有方法catch的包(或对象引用)的名称,那么最终也会尝试try

use strict; 
use warnings;
use feature 'say';

BEGIN {
    package Catch;
    sub catch { say "In ", (caller(0))[3] };
    $INC{"Catch.pm"} = 1;
};

use Catch;

try   { call_a( 'x' ) } 
catch { 
    say "NO DONT die";
    "Catch";
};

sub call_a { say "no die" }

现在我们有了等价物

call_a('x')->try( do { say("NO DONT die"); 'Catch' }->catch() );

输出

不死 不,不要死 在 Catch::catch 中 无法在 undef_sub.pl 第 14 行调用没有包或对象引用的方法“try”。

【讨论】:

  • 你提到了METHODNAME PKGNAME LIST,但代码使用了METHODNAME BLOCK LIST。您可能需要澄清后者等同于(do BLOCK)->METHODNAME(LIST)。您在 Deparse 中看到 catch 的这一点,但 Deprase 继续使用 try 的间接表示法。
  • 事实上,因为 Deparse 仍然使用间接方法调用,所以我完全避免在答案中使用它。 Suggested edit
  • @ikegami 已编辑,谢谢。可能(方式)太长/罗嗦,但至少现在应该清楚
  • @zdim 谢谢你如此详尽的解释!
  • @w.k 不客气,谢谢你这么说 :) 如果需要更多/更好,请告诉我
猜你喜欢
  • 1970-01-01
  • 2021-04-22
  • 2022-11-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-01-08
相关资源
最近更新 更多