子程序的块参数

时间:2019-07-17 14:14:05

标签: perl

我一直在研究采用块参数的Test::Warn软件包。 warning_like方法具有签名warning_like BLOCK REGEXP, TEST_NAME。我本以为在这种情况下,括号与其他子例程一样是可选的,因此以下内容应等效:

warning_like {bar()} qr/at Foo.pm line 5/, "Testname";
warning_like({bar()} qr/at Foo.pm line 5/, "Testname");

但是第二个带方括号的电话导致错误:

syntax error at t/testfile.t line 34, near "} qr/at Foo.pm line 5/"
Execution of t/testfile.t aborted due to compilation errors.

看起来该块改变了函数的调用方式。 warning_likeaccording to the docs具有以下签名的map类似; map BLOCK LISTmap EXPR,LIST。在这种情况下,方括号没有区别。

my @arr = ('a', 'b' ,'c', 'd', 'e');

my @mapped_block_1 = map( {uc($_)} @arr);
my @mapped_block_2 = map( uc, @arr);
my @mapped_expr_1  = map {uc($_)} @arr;
my @mapped_expr_2  = map uc, @arr;

这些之间有什么区别,为什么括号会影响warning_like而不是map的调用方式?为什么在将块用作参数的参数之间不需要逗号?

谢谢

1 个答案:

答案 0 :(得分:1)

它们可以有所不同,因为warning_like ...是子调用,而map ...是操作员调​​用。

不同之处在于,处理map的两个调用约定所需的代码是合理的,但是处理包含&的任意原型(可能多次)需要妥协。


perlfunc导致以下问题:

  

本节中的函数可以用作表达式中的术语。它们分为两大类:列表运算符和命名一元运算符。

map是运算符,就像+一样。作为运算符,其语法独立于子调用的语法。

因此,这说明了两种语法为何可以不同的原因,但您还询问了为什么它们不同。

对于初学者来说,map允许使用括号是有意义的,因为它将提供与其他“功能”的一致性。因此,问题是为什么sub name(&)不允许使用name BLOCK语法。为此,我不得不猜测,但我相信这是要避免极其复杂的解析器代码。

让我们从如何调用原型为&的子程序开始。

mysub BLOCK       # Parens forbidden.
mysub sub BLOCK   # Parens allowed
mysub \&NAME      # Parens allowed
mysub \&BLOCK     # Parens allowed
mysub \&$NAME     # Parens allowed
mysub \&$BLOCK    # Parens allowed

Perl可以创建与这些规则完全匹配的解析规则。但是该子对象可以具有&@&$&$$&&等原型,以此类推。拥有处理所有这些规则的规则是不可行的。相反,解析器仅处理以下内容:

mysub BLOCK LIST   # Parens forbidden.
mysub LIST         # Parens allowed

如果子名称后没有{,则解析器首先将代码解析为一个简单的列表表达式,然后执行后续检查以查看生成的内容是否与原型匹配。您可以从错误消息中看到Perl做到了这一点。

请考虑以下内容:

$ perl -we'sub mysub(&) { ... } mysub("a'
Can't find string terminator '"' anywhere before EOF at -e line 1.

$ perl -we'sub mysub(&) { ... } mysub({});'
Type of arg 1 to main::mysub must be block or sub {} (not anonymous hash ({})) at -e line 1, near "})"
Execution of -e aborted due to compilation errors.

在第一个代码段中,即使其中不允许使用字符串文字,Perl也会给出一个错误,就好像它在解析字符串文字一样。

在第二个代码段中,我们可以看到{}已成功解析为匿名哈希构造函数,即使那儿是不允许的。


顺便说一句,这也解释了为什么只有sub的第一个参数时&才可以省略。

sub mysub2(&&);
sub mysub3($&);

mysub2 BLOCK BLOCK         # Not ok
mysub2 BLOCK sub BLOCK     # ok
mysub2 BLOCK \&...         # ok

mysub3 EXPR, BLOCK         # Not ok
mysub3 EXPR, sub BLOCK     # ok
mysub3 EXPR, \&...         # ok

如果&...的原型始终被解析为BLOCK LIST(如果以{开头)或LIST(否则),则无法支持{{ 1}}。