为什么“尝试"不会导致未定义的子程序错误? [英] Why does "try" not cause an undefined subroutine error?

查看:39
本文介绍了为什么“尝试"不会导致未定义的子程序错误?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

有几次我遇到了忘记在脚本中加载 Try::Tiny 模块的情况,但我仍然使用它的 try-catch 块,像这样:

#!/usr/bin/env perl使用严格;使用警告;尝试 {call_a('x');} 抓住 {死实际上死$ _";};子调用_a {死是的,我会";}

出于某种原因,该脚本运行良好,没有提示没有 try.没有未定义的子程序错误.这让我想知道为什么我提出的异常没有被捕获.

为什么这会默默工作,没有错误?

编辑

我也查看了符号表:

对键 %main:: 说$_: %main::{ $_ }";

发现没有try.我还尝试在上面的脚本中将其称为 main::try ,它也没有引起任何错误.

解决方案

这是由于间接对象语法,是 这个例子.

间接对象表示法" 允许代码

PackageName->method(@args);

写成

method PackageName @args;

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

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

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

使用严格;使用警告;使用功能说";试试 { call_a( 'x' ) }抓住 {死真的死了";#say不要死";};子调用_a {死是的,它死了";#说不死";}

as perl -MO=Deparse script.pl 应该显示非常接近的运行内容:

<前>使用警告;使用严格;使用功能说";尝试 {call_a('x')} 做 {死真的死了"}->抓住;子调用_a {使用警告;使用严格;使用功能说";die '是的,它死了';}undef_sub.pl 语法正常

嵌套的间接对象语法对于 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.

如果我们把例子改成

会更有趣

使用严格;使用警告;使用功能说";试试 { call_a( 'x' ) }抓住 {#die "真的死了";说不要死";};子调用_a {#die "是的,它死了";说不死";}

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

使用警告;使用严格;使用功能说";尝试 {call_a('x')} (抓住 {说不要死"});子调用_a {使用警告;使用严格;使用功能说";说不死";}undef_sub.pl 语法正常

现在采用直接的 method {} args 语法(args 本身由 Deparse 以间接对象表示法显示为出色地).等效代码为

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

call_a() 首先运行的地方,在它返回之后,接下来运行 try 方法调用中的参数列表的代码.我们没有遇到 die 而实际的运行就像

<前>没有死不要死不能在没有包或对象引用的情况下调用方法catch"...

所以现在方法catch"确实出现了问题.

感谢 ikegami 的评论

<小时>

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

使用严格;使用警告;使用功能说";开始 {包捕获;sub catch { say "In ", (caller(0))[3] };$INC{"Catch.pm"} = 1;};使用捕捉;试试 { call_a( 'x' ) }抓住 {说不要死";抓住";};sub call_a { 说不死"}

现在我们有了等价物

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

输出

<前>没有死不要死在 Catch::catch 中在 undef_sub.pl 第 14 行,如果没有包或对象引用,则无法调用方法try".

A couple of times I've ran into the situation where I've forgotten to load the Try::Tiny module in my script and I've still used its try-catch block, like this:

#!/usr/bin/env perl

use strict; 
use warnings;

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


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

For some reason, the script works fine without giving any hints that there is no try. No Undefined subroutine errors. This makes me wonder why my raised exceptions are not caught.

Why does this work silently, without an error?

EDIT

I looked into symbol table as well:

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

and found there no try. Also I tried to call it as main::try in the script above, and it caused also no errors.

解决方案

This is due to the indirect-object syntax, and is a more elaborate variation on this example.

The "indirect object notation" allows code

PackageName->method(@args);

to be written as

method PackageName @args;

So the "try" and "catch" words don't matter. The interesting bit here is the more involved and extended syntax, with two parts, each in this indirect object notation.

The code in question in fact has method BLOCK LIST form, but that also goes by indirect object syntax into (do BLOCK)->method(LIST), where do BLOCK needs to produce a name of a package or a blessed (object) reference for a meaningful method call. This is seen below in Deparse output.

Using B::Deparse compiler backend (via O module) on this code

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

as perl -MO=Deparse script.pl should show a very close approximation of what runs:

use warnings;
use strict;
use feature 'say';
try {
    call_a('x')
} do {
    die 'ACTUALLY die'
}->catch;
sub call_a {
    use warnings;
    use strict;
    use feature 'say';
    die 'Yes it dies';
}
undef_sub.pl syntax OK

The nested indirect object syntax is apparently too much for Deparse which still leaves method BLOCK LIST form in the output. The equivalent code can be spelled out as

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

what in this case is more simply

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

Thus the original code is interpreted as valid syntax (!) and it is the contents of the block after try (call_a('x')) that runs first --- so the program dies and never gets to go for the "method" try.

It gets more interesting if we change the example to

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

with no die-ing anywhere. Run it with -MO=Deparse to see

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

which is now in a straight-up method {} args syntax (with args itself shown by Deparse in an indirect object notation as well). The equivalent code is

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

where first the call_a() goes and, after it returns, then the code for the argument list in the try method call runs next. We aren't running into a die and an actual run goes as

no die
NO DONT die
Can't call method "catch" without a package or object reference at ...

So now a problem with the method "catch" does come up.

Thanks to ikegami for comments


If the block above were to return a name of a package (or object reference) which does have a method catch then the try would finally be attempted as well

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

Now we have the equivalent

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

with the output

no die
NO DONT die
In Catch::catch
Can't call method "try" without a package or object reference at undef_sub.pl line 14.

这篇关于为什么“尝试"不会导致未定义的子程序错误?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆