5

我很难理解,为什么 Perl 在这样的程序中执行花括号中的代码:

unknown_method {
    # some code
};

我的程序:

文件交易.pm:

package Transaction;
use strict;
use warnings;
use feature qw/ say /;

sub transaction(&) {
    say 'BEGIN TRANSACTION';
    eval {
        shift->()
    };
    if ( $@ ) {
        say 'ROLLBACK TRANSACTION';
        die($@);  # reraise error
    } else {
        say 'COMMIT TRANSACTION';
    }
}
1;

文件 run_properly.pl:

use feature qw/ say /;
use Transaction;
eval {
    Transaction::transaction {
        say "insert some data to DB";
        die("KnownException")
    }
};
warn $@;

文件 run_wrong.pl:

use feature qw/ say /;
# I forgot to import Transaction
eval {
    Transaction::transaction {
        say "insert some data to DB";
        die("KnownException")
    }
};
warn $@;

执行:

$ perl run_properly.pl 
BEGIN TRANSACTION
insert some data to DB
ROLLBACK TRANSACTION
KnownException at run_properly.pl line 6.

$ perl run_wrong.pl 
insert some data to DB
KnownException at run_wrong.pl line 6.

为什么 Perl 允许这样的事情?

4

3 回答 3

9

Perl 在语法上是灵活的,并且经常有不止一种语法做事。例如,调用方法。这是常规且推荐的语法:

  Foo  ->  new       (1, 2, 3);
# ^-object ^- method ^- arguments

这是间接语法:

  new       Foo       1, 2, 3;
# ^- method ^- object ^- arguments, parens are optional

这一切都很好,但是当我们想将复杂计算的结果用作具有间接对象表示法的对象时会发生什么?

# set up some constructors for demonstration purposes
*Foo::new = *Bar::new = sub {say "@_"};

# This obviously fails
# new (rand > .5 ? "Foo" : "Bar") 1, 2, 3;

解决方案是一个与格块

new {rand > .5 ? "Foo" : "Bar"} 1, 2, 3;

您可能已经知道文件句柄中的与格块:print {$handles[-1]} $_.

dative 块在方法解析之前执行,因为方法解析通常(但不是您的情况)取决于对象的类型。但是,如果块dies.


间接表示法在构造函数中仍然很流行,因为它使 Perl 看起来像 C++。但是,Perl(与 C++ 不同)没有new 运算符:它只是一个常规方法。这种灵活性可能不是一个好主意,所以no indirect如果你对此有强烈的感觉,你可以使用它来“修复”它。

于 2013-06-08T20:44:48.020 回答
1

当你有这样的调用时:

 name { ... } etc.

如果name是一个以 BLOCK 作为其参数的内置函数或带有&原型的预声明子例程,则 BLOCK(即{ ... })不会被评估,而是按原样传递给name

  • 例如grep { $_ > 9000 } @levels
  • 这适用于run_properly.pl案例

否则(例如,没有原型或不存在子例程的预声明子例程),BLOCK 被评估,然后 Perl 查找调用的子例程/方法name,并将评估结果作为参数传递给(或如果未定义name则引发错误)。name

  • 这适用于run_wrong.pl案例
  • 在这种情况下,如果您想阻止 BLOCK 被评估,您可以使用sub { ... }而不是仅仅使用。{ ... }
于 2013-06-09T09:07:21.400 回答
1

它首先执行块(因为它必须将结果传递给Transaction::transaction)。由于您die在该块内,因此它永远不会到达Transaction::transaction.

于 2013-06-08T20:35:43.370 回答