我有许多高阶实用程序函数,它们接受代码引用并将该代码应用于某些数据。其中一些函数需要在子例程执行期间本地化变量。一开始,我是用来确定要本地化到哪个包中的,其方式与此示例函数caller
中所示的方式类似:reduce
sub reduce (&@) {
my $code = shift;
my $caller = caller;
my ($ca, $cb) = do {
no strict 'refs';
map \*{$caller.'::'.$_} => qw(a b)
};
local (*a, *b) = local (*$ca, *$cb);
$a = shift;
while (@_) {
$b = shift;
$a = $code->()
}
$a
}
最初,这种技术工作得很好,但是一旦我尝试围绕高阶函数编写一个包装函数,找出正确的调用者就变得很复杂。
sub reduce_ref (&$) {&reduce($_[0], @{$_[1]})}
现在为了reduce
工作,我需要类似的东西:
my ($ca, $cb) = do {
my $caller = 0;
$caller++ while caller($caller) =~ /^This::Package/;
no strict 'refs';
map \*{caller($caller).'::'.$_} => qw(a b)
};
在这一点上,它变成了跳过哪些包的问题,再加上从不使用这些包中的函数的原则。必须有更好的方法。
事实证明,高阶函数作为参数的子程序包含足够的元数据来解决问题。我当前的解决方案是使用B
自省模块来确定传入子例程的编译存储。这样,无论在代码编译和执行之间发生什么,高阶函数总是知道要本地化到的正确包。
my ($ca, $cb) = do {
require B;
my $caller = B::svref_2object($code)->STASH->NAME;
no strict 'refs';
map \*{$caller.'::'.$_} => qw(a b)
};
所以我的最终问题是,这是否是在这种情况下确定调用者包裹的最佳方式?还有其他我没有想到的方法吗?我当前的解决方案是否有一些错误正在等待发生?