当我在运行时只知道它们的名字时,如何在另一个包中本地化变量?

时间:2009-04-27 13:40:54

标签: perl

我需要在另一个包中本地化一些变量,但是在传入之前我不知道它们的名字是什么。我尝试将local与typeglobs一起使用不起作用,所以我已经倒退了保存变量的值并手动恢复。有没有更好的办法?请注意,为了清楚起见,已经省略了错误检查以查看变量是否存在之前存在变量。

#!/usr/bin/perl

use strict;
use warnings;

my %orig;
for my $name (qw/foo bar baz/) {
    my $var = \${$meta::{$name}};
    $orig{$name} = $$var;
    $$var = $$var * 2;
}
meta::p();
for my $name (keys %orig) {
    my $var = \${$meta::{$name}};
    $$var = $orig{$name};
}
meta::p();

package meta;

BEGIN {
    our $foo = 1;
    our $bar = 2;
    our $baz = 3;
}

sub p { print join(" :: ", $meta::foo, $meta::bar, $meta::baz), "\n" }

我试图避免像这样的评估:

my $eval = '';

for my $name (qw/foo bar baz/) {
    $eval .= "local \$meta::$name = \$meta::$name * 2;\n";
}

eval "$eval meta::p()";
meta::p();

试图避免eval浪费时间吗?新代码是否比eval差?

注意,我也不想使用符号引用,所有代码都必须在strict下工作。当前的解决方案是有效的,所以我不是在寻找黑客来解决我正在做的事情,我正在寻找更好的解决方案(如果存在的话)。

1 个答案:

答案 0 :(得分:3)

关闭strict refs应允许您使用symbolic references执行所需操作。我无权访问使用ATM进行测试的系统,但是这样的事情应该有效:

use strict;
use warnings;

{
    no strict `refs`;
    local ${"meta::$name"} = ${"meta::$name"} * 2;

    meta::p();
}

meta::p();

<强>更新

经过一些测试,我发现了一个障碍。

虽然在单个符号引用上使用local很容易。但是,本地化一组变量名称并不是那么简单 - 循环结构(map,for等)都会围绕它们运行的​​表达式创建一个小的范围,从而终止本地化。

# This works but does not work on an array of names.
{   no strict 'refs';

    local ( ${'meta::foo'}, ${'meta::bar'}, ${'meta::baz'} );
    meta::p();
}
meta::p();


# THIS DOES NOT WORK AT ALL!
{   no strict 'refs';

    my @to_localize = map "meta::$_", qw/foo bar baz/;

    local ${$_} = $$_ * 2 for @to_localize;
    meta::p();

}
meta::p();

我能找到的唯一解决方案是使用goto,我们都知道被认为是有害的

{   no strict 'refs';

    my @to_localize = map "meta::$_", qw/foo bar baz/;

    LOCALIZER:
        my $localize_me = shift @to_localize;
        local ${$localize_me} = $$localize_me * 2;
        goto LOCALIZER if @to_localize;

    meta::p();

}
meta::p();

我愿意接受更好的想法。