【问题标题】:Perl `*foo{THING}`, subs and parenthesisPerl `*foo{THING}`、subs 和括号
【发布时间】:2015-09-17 12:11:38
【问题描述】:

除了经典的函数调用

sub foo { ... };
foo 1, 2, 3;

Perl 允许定义闭包

my $foo = sub { ... };
$foo->(1, 2, 3);

(在这种情况下,词法本地)

即使我找不到它的非黑客用途,Perl 也允许这样做:

*main::foo = sub { ... }

这获得了与第一种形式几乎相同的效果,除了我必须在参数周围加上括号:

foo(1, 2, 3);

如果我不放括号,我会收到以下错误:

Number found where operator expected at - line 6, near "&main::foo 1"
    (Missing operator before  1?)
syntax error at - line 6, near "main::foo 1"
Execution of - aborted due to compilation errors.

...如果我使用 &foo 1, 2, 3 语法调用它也会发生这种情况,明确表示它是关于一个函数的。

所以我的问题是:

  • 为什么第一种形式可以省略括号,而第三种形式却不能?

  • 第三种形式有目的吗?在我看来,在包的命名空间中注入回调是一种不好的做法,因为它会使事情变得隐含且不可检测。

感谢您的任何回答。

【问题讨论】:

  • 也许 perl 必须首先查看函数定义,而 *main::foo = sub { ... } 不会这样做。同样eval.in/434959也不起作用。
  • 第三种形式在测试代码时有时很有用——它可以用来存根或替代昂贵的调用。 Test::MockModule 基本上就是这样做的。有更好的方法(例如依赖注入),但有时您无法控制代码。

标签: perl


【解决方案1】:

*main::foo = sub { 在运行时评估,而错误在编译时抛出。正常的sub 在编译时被解析,然后Perl 解析器可以将其名称识别为裸字,但您仍然不能在声明之前使用裸字形式:

foo 1;  # Number found where operator expected...
sub foo {
    say shift;
}
foo 1; # No error here.

您可以通过将代码封装到 BEGIN 块中来安排代码在编译时进行:

BEGIN {
    *main::foo = sub { say shift };
}
foo 1; # No error!

【讨论】:

  • 您是对的,foo(1) 在您的代码中有效,而 foo 1 则无效。怀疑类似的原因,我也尝试了&foo 1 版本(即在编译时告诉编译器foo 是一个函数)。它不起作用,但我想这只是一个限制。对*main::foo = sub {...} 表单的用途有任何想法吗?
  • 主要是能够执行*somepackage::somefunction = sub { ... }; 以允许挂钩包子(模拟/测试等)的副作用。而main 本身就是一个包。
  • @Dacav:在某些情况下使用符号表可能很有用 - 生成 getter/setter、模拟等。
  • 谢谢你们的回答和嘲笑的想法!确实不错
【解决方案2】:
sub foo { ... }

大致相当于

BEGIN { *foo = sub { ... }; }

如果在编译 sub 调用时已经声明了 sub,则可以省略参数周围的括号。如果您遇到错误,则在声明 sub 之前遇到对 sub 的调用(这会在执行分配给 *foo 时发生)。

  • *foo = sub { ... }; 在语句执行时声明 sub。
  • BEGIN { *foo = sub { ... }; } 在语句执行时声明 sub,即在编译后立即声明。
  • sub foo { ... } 在编译语句时声明 sub。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-04-15
    • 1970-01-01
    • 1970-01-01
    • 2012-06-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多