Perl尽可能干净地调用具有显式附加范围的子例程引用

我希望能够写出如下内容……

call_with_scope({
    x => 47,},sub {
    printf "$x\n";
    printf "$y\n";
});

其中$y绑定在包含表达式的环境中(词法或动态,具体取决于符号).

我找到了一种方法,但它不需要在包含call_with_scope(…)的表达式中生效严格的“变量”,并且call_with_scope的实现在将控制转移到回调之前使用eval创建本地绑定.

有没有办法避免在调用站点要求没有严格的“变量”,或者在不诉诸eval的情况下引用和更改局部变量的值?

为了完整起见,下面的代码片段实现了call_with_scope并打印47然后打印48.

#!/usr/bin/env perl
use strict;
use warnings;

sub call_with_scope {
    my ($env,$func) = @_;
    my %property;
    my @preamble;
    foreach my $k (keys %$env) {
        $property{$k} = $env->{$k};
        # deliberately omitted: logic to ensure that ${$k} is a well-formed variable
        push @preamble,"local \$$k = \$property{'$k'};";
    }
    # force scalar context
    do {
        my $str = join('','no strict "vars";',@preamble,'$_[1]->();');
        return scalar(eval($str));
    };
}                        

do {
    no strict 'vars';
    local $x;
    my $y = 48;
    call_with_scope(
        {
            x => 47,sub {
            printf "$x\n";
            printf "$y\n";
        }
    );
};

解决方法

I’m trying to write something kind of like Test::LectroTest … except that instead of using a source filter and comments like in Property { ##[ x <- Int,y <- Int ]## <body> } … I want to write something like Property({x => gen_int,y => gen_int},sub { <body> }) where $x and $y inside body get their values when an “instantiation” of a property test is performed.

您可以通过在调用者的包中将$x和$y定义为全局变量来实现此目的.

no strict 'refs';
my $caller = caller;
for my $var (keys %$properties) {
    *{$caller.'::'.$var} = $properties->{$var};
}
$code->();

但这不容易本地化.使用全局变量污染调用者的命名空间可能会导致测试之间出现神秘的数据泄露.通常,在测试库中使用尽可能少的魔法;用户将有足够的自己奇怪的魔法来调试.

相反,提供一个返回属性函数.例如,p.

package LectroTest;

use Exporter qw(import);
our @EXPORT = qw(test p);
our $P;

sub test {
    my($props,$test) = @_;

    local $P = $props;
    $test->();
}

sub p {
    return $P;
}

测试看起来像:

use LectroTest;

test(
    { x => 42 },sub { print p->{x} }
);

相关文章

忍不住在 PerlChina 邮件列表中盘点了一下 Perl 里的 Web 应用框架(巧的是 PerlBuzz 最近也有一篇相关...
bless有两个参数:对象的引用、类的名称。 类的名称是一个字符串,代表了类的类型信息,这是理解bless的...
gb2312转Utf的方法: use Encode; my $str = "中文"; $str_cnsoftware = encode("utf-8...
  perl 计算硬盘利用率, 以%来查看硬盘资源是否存在IO消耗cpu资源情况; 部份代码参考了iostat源码;...
1 简单变量 Perl 的 Hello World 是怎么写的呢?请看下面的程序: #!/usr/bin/perl print "Hello W...
本文介绍Perl的Perl的简单语法,包括基本输入输出、分支循环控制结构、函数、常用系统调用和文件操作,...