11

我有许多高阶实用程序函数,它们接受代码引用并将该代码应用于某些数据。其中一些函数需要在子例程执行期间本地化变量。一开始,我是用来确定要本地化到哪个包中的,其方式与此示例函数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)
    };

所以我的最终问题是,这是否是在这种情况下确定调用者包裹的最佳方式?还有其他我没有想到的方法吗?我当前的解决方案是否有一些错误正在等待发生?

4

2 回答 2

5

首先,您可以使用以下内容并且不需要任何更改:

sub reduce_ref (&$) { @_ = ( $_[0], @{$_[1]} ); goto &reduce; }

但总的来说,以下确实正是您想要的:

B::svref_2object($code)->STASH->NAME

你想要 sub's 的$aand$b变量__PACKAGE__,所以你想知道 sub's __PACKAGE__,而这正是它返回的内容。它甚至修复了以下问题:

{
   package Utils;
   sub mk_some_reducer {
      ...
      return sub { ... $a ... $b ... };
   }
}

reduce(mk_some_reducer(...), ...)

它不能解决所有问题,但如果不使用参数而不是$aand ,这是不可能的$b

于 2011-07-12T17:17:55.743 回答
1

如果有人需要它们,以下是我最终决定使用的功能:

require B;
use Scalar::Util 'reftype';
use Carp 'croak';

my $cv_caller = sub {
    reftype($_[0]) eq 'CODE' or croak "not code: $_[0]";
    B::svref_2object($_[0])->STASH->NAME
};

my $cv_local = sub {
    my $caller = shift->$cv_caller;
    no strict 'refs';
    my @ret = map \*{$caller.'::'.$_} => @_;
    wantarray ? @ret : pop @ret
};

这将用作:

my ($ca, $cb) = $code->$cv_local(qw(a b));

在原始问题的背景下。

于 2011-09-12T21:34:47.847 回答