PAYATHON 2026

Perl 如何通过 subroutine reference 创建 lexical sub?

数据小陈

可以。先用 my sub foo; 声明词法子程序,再通过 \&foo = $ref; 将它绑定到已有的 subroutine reference。声明和绑定需要分开写,不能使用问题中的 my \&foo = ...。

正确写法

下面的示例适用于 Perl 5.26 及更高版本:

use v5.26;
use strict;
use warnings;

{
    use feature 'refaliasing';
    no warnings 'experimental::refaliasing';

    my $ref = sub {
        my ($a, $b) = @_;
        return $a + $b;
    };

    my sub foo;
    \&foo = $ref;

    print foo(2, 3), "\n";
}

声明和绑定完成后,就可以用 foo(2, 3) 调用 $ref 引用的子程序。refaliasing 支持对 \&sub 赋值,Perl 的实现也处理了词法子程序的绑定。参见 perlref 的引用赋值说明和 Perl 5.26 的相关实现。

如果不需要保留 $ref 变量,也可以直接写:

my sub foo;
\&foo = sub { ... };

这两行代码同样需要放在启用 refaliasing 的作用域内。

为什么原来的声明不成立?

declared_refs 允许使用 my \$x、my \@array 等引用声明语法,但 my \&foo 不是合法的词法子程序声明。词法子程序需要先用 my sub foo; 声明,再执行引用赋值。

这个方案只需要 refaliasing,不需要 declared_refs。两种功能的用途可参见 perlref 的引用声明说明。

作用域和使用限制

foo 这个词法名称只在声明之后、所在词法作用域内可见。离开代码块后,无法再通过这个词法绑定调用它;如果外部有同名的 package sub,外部的 foo() 可能解析到那个子程序。Perl 5.26 起,lexical sub 不再是实验功能,也无需显式启用 lexical_subs。参见 perlsub 的 Lexical Subroutines 说明。

绑定右侧必须是有效的 CODE reference,而且要先执行绑定,再调用 foo()。refaliasing 仍是实验功能,关闭对应警告只会隐藏提示。官方文档还列出了它与闭包配合使用时的限制,尤其不要假定在内层子程序或 eval 中重新绑定外层词法名称,会同步改变外层的绑定。参见 refaliasing 的限制说明。

如果只想在局部范围内保存并调用一个回调,使用普通词法变量即可,无需启用实验功能:

my $foo = sub {
    my ($a, $b) = @_;
    return $a + $b;
};

print $foo->(2, 3), "\n";

这种方式使用 $foo->(...) 调用,不会创建可通过 foo(...) 调用的词法子程序名称。

备注:内容仅供参考。