假装一个自动加载的函数有一个块原型
Pretending an Autoloaded Function has a Block Prototype
假设我有一个以 sub 作为参数的子例程。
sub foobar {
my $block = shift;
$block->();
}
那我就可以这样调用了
foobar(sub { print "Hello :)\n" });
如果我用适当的原型声明它,我也可以使用块语法。
sub foobar(&) {
my $block = shift;
$block->();
}
foobar { print "Hello :)\n" };
现在,假设我的函数实际上并不存在并且是一个 AUTOLOAD,例如
sub AUTOLOAD {
$AUTOLOAD =~ s/.*:://;
if ($AUTOLOAD eq "foobar") {
my $block = shift;
$block->();
} else {
die("No such function $AUTOLOAD");
}
}
我们仍然可以显式调用它sub
foobar(sub { print "Hello :)\n" });
我们可以通过预先声明子例程来消除不必要的括号。
use subs 'foobar';
foobar sub { print "Hello :)\n" };
但是如果我的函数实际上不存在并且没有原型,我就无法恢复块语法。我希望能够做到这一点
foobar { print "Hello :)\n" };
假设我事先知道名称 foobar
并且可以根据需要预先声明(就像我对 use subs 'foobar'
所做的那样),有什么方法可以让 Perl 相信 foobar
需要块参数,尽管只能通过 AUTOLOAD?
访问 foobar
注意:这不是生产代码,永远不会。我问是出于好奇,并可能在编程难题/挑战中使用这样的技巧,因此可读性和可维护性不是问题。
子程序的原型——尤其是 &
——会影响调用的解析和编译方式。因此必须知道调用何时被解析和编译。
所以唯一的选择就是提前声明潜艇。缺点是名字必须事先知道
BEGIN { say "Compilation phase."; }
say "Execution phase.";
sub foo(&@); # Declaration with prototype.
sub AUTOLOAD {
our $AUTOLOAD =~ s/.*:://;
say "Autoloading $AUTOLOAD.";
my $sub = sub(&@) {
my $block = shift;
say "Calling \"block\".";
$block->(@_)
};
no strict qw( refs );
*$AUTOLOAD = $sub;
goto &$AUTOLOAD;
}
foo { say "<@_>" } qw( a b c ); # ok. Compiled as: foo(sub { say "<@_>" }, qw( a b c ));
foo { say "<@_>" } qw( a b c ); # ok. Compiled as: foo(sub { say "<@_>" }, qw( a b c ));
bar { say "<@_>" } qw( a b c ); # BAD! Compiled as: do { say "<@_>" }->bar(qw( a b c ));
输出:
Compilation phase.
Execution phase.
Autoloading foo.
Calling "block".
<a b c>
Calling "block".
<a b c>
<>
Can't locate object method "bar" via package "1" (perhaps you forgot to load "1"?) at a.pl line 26.
相关:
请注意,如果名称是动态获取的,您也不会倒霉,只要您可以在编译时获取它即可。
BEGIN {
my ($name, $proto) = ( "foo", '$@' );
eval "sub $name($proto);";
}
假设我有一个以 sub 作为参数的子例程。
sub foobar {
my $block = shift;
$block->();
}
那我就可以这样调用了
foobar(sub { print "Hello :)\n" });
如果我用适当的原型声明它,我也可以使用块语法。
sub foobar(&) {
my $block = shift;
$block->();
}
foobar { print "Hello :)\n" };
现在,假设我的函数实际上并不存在并且是一个 AUTOLOAD,例如
sub AUTOLOAD {
$AUTOLOAD =~ s/.*:://;
if ($AUTOLOAD eq "foobar") {
my $block = shift;
$block->();
} else {
die("No such function $AUTOLOAD");
}
}
我们仍然可以显式调用它sub
foobar(sub { print "Hello :)\n" });
我们可以通过预先声明子例程来消除不必要的括号。
use subs 'foobar';
foobar sub { print "Hello :)\n" };
但是如果我的函数实际上不存在并且没有原型,我就无法恢复块语法。我希望能够做到这一点
foobar { print "Hello :)\n" };
假设我事先知道名称 foobar
并且可以根据需要预先声明(就像我对 use subs 'foobar'
所做的那样),有什么方法可以让 Perl 相信 foobar
需要块参数,尽管只能通过 AUTOLOAD?
foobar
注意:这不是生产代码,永远不会。我问是出于好奇,并可能在编程难题/挑战中使用这样的技巧,因此可读性和可维护性不是问题。
子程序的原型——尤其是 &
——会影响调用的解析和编译方式。因此必须知道调用何时被解析和编译。
所以唯一的选择就是提前声明潜艇。缺点是名字必须事先知道
BEGIN { say "Compilation phase."; }
say "Execution phase.";
sub foo(&@); # Declaration with prototype.
sub AUTOLOAD {
our $AUTOLOAD =~ s/.*:://;
say "Autoloading $AUTOLOAD.";
my $sub = sub(&@) {
my $block = shift;
say "Calling \"block\".";
$block->(@_)
};
no strict qw( refs );
*$AUTOLOAD = $sub;
goto &$AUTOLOAD;
}
foo { say "<@_>" } qw( a b c ); # ok. Compiled as: foo(sub { say "<@_>" }, qw( a b c ));
foo { say "<@_>" } qw( a b c ); # ok. Compiled as: foo(sub { say "<@_>" }, qw( a b c ));
bar { say "<@_>" } qw( a b c ); # BAD! Compiled as: do { say "<@_>" }->bar(qw( a b c ));
输出:
Compilation phase.
Execution phase.
Autoloading foo.
Calling "block".
<a b c>
Calling "block".
<a b c>
<>
Can't locate object method "bar" via package "1" (perhaps you forgot to load "1"?) at a.pl line 26.
相关:
请注意,如果名称是动态获取的,您也不会倒霉,只要您可以在编译时获取它即可。
BEGIN {
my ($name, $proto) = ( "foo", '$@' );
eval "sub $name($proto);";
}