尝试通过Perl中的词法绑定来本地化外部包变量

时间:2011-10-17 20:20:08

标签: perl variables internals lexical-scope

这是一个很长的标题,但我担心在不失去问题的真正含义的情况下我不能说出一个字。我将简要介绍一下我首先想要实现的目标,然后对我为什么希望以这种方式完成这一过程进行漫长的漫游。如果您打算直接回答问题标题,请跳过漫步: - )

简要说明

假设存在一个lexicalize内置函数,它正是我想要的,这里有:

package Whatever;
{
  lexicalize $Other::Package::var;
  local $var = 42;
  Other::Package->do_stuff; # depends on that variable internally
}

为什么和whynots

对于一些上下文,我是对第三方模块进行白盒测试。

我想要变量本地化,因为我希望它只在有限的时间内更改,然后再将测试转移到其他地方。 local是我发现这样做的最佳方式,而不依赖于知道模块对初始值的选择。

我想要一个词汇绑定,主要有两个原因:

  1. 我不想污染测试文件的更高级命名空间。
  2. 我想要比完全限定名称短的东西。为了简洁起见,它不在示例代码中,但我使用的标识符比显示的更多,计算和更新相对于之前的值。
  3. 我无法正常使用our,因为它不会从另一个包中获取变量:“没有包名称允许变量$ Other :: Package :: var in”my“。”暂时作弊输入Other :: Package的范围不会削减它:如果我使用一个块({ package Other::Package; our $var }),那么绑定的持续时间不会长到有用;如果我不这样做(package Other::Package; our @var; package main)那么我需要知道并复制以前的包名,这样就无法过多地移动那段代码。

    在询问此问题的上一个形式之前做我的作业时,我发现了Lexical::Var,这似乎完全我需要的东西。唉:“不能通过参考进行本地化。”

    我也尝试过基于my *var形式的直觉,但不断遇到语法错误。我今天学到了更多关于范围和约束力的知识: - )

    我想不出为什么我想要的东西不可能,但我找不到正确的咒语。我要求一个不幸的未实现的边缘案例吗?

4 个答案:

答案 0 :(得分:3)

有几种方法可以做到这一点:

  • 下式给出:

    {package Test::Pkg;
        our $var = 'init';
        sub method {print "Test::Pkg::var = $var\n"}
    }
    
  • 将当前包中的包变量别名为目标包。

    {
        package main;
        our $var;
    
        Test::Pkg->method; #     Test::Pkg::var = init
    
        local *var = \local $Test::Pkg::var;  
            # alias $var to a localized $Test::Pkg::var
    
        $var = 'new value';
    
        Test::Pkg->method; #    Test::Pkg::var = new value
    }
    
    Test::Pkg->method;     #    Test::Pkg::var = init
    
  • 通过glob引用进行本地化:

    {
        package main;
    
        my $var = \*Test::Pkg::var;  # globref to target the local
    
        Test::Pkg->method; #     Test::Pkg::var = init
    
        local *$var = \'new value';  # fills the scalar slot of the glob
    
        Test::Pkg->method; #    Test::Pkg::var = new value
    }
    
    Test::Pkg->method;     #    Test::Pkg::var = init
    
  • 绑定目标包中的词法,让它溢出到当前包中:

    {
        package Test::Pkg;  # in the target package
        our $var;           # create the lexical name $var for $Test::Pkg::var
    
        package main;       # enter main package, lexical still in scope
    
        Test::Pkg->method; #     Test::Pkg::var = init
    
        local $var = 'new value';
    
        Test::Pkg->method; #    Test::Pkg::var = new value
    }
    
    Test::Pkg->method;     #    Test::Pkg::var = init
    

最后一个示例使用一个稍微笨拙的双包声明来在一个包中创建一个词法并将其放入另一个包中。在其他情况下,此技巧对local非常有用:

 no strict 'refs';
 local ${'Some::Pkg::'.$name} = 'something';
 use strict 'refs';

答案 1 :(得分:2)

如果我找到了你,你所需要的只是我们的。如果我不这样,请原谅我。

以下是工作示例:

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

package Some;

our $var = 42;
sub do_stuff { return $var }

package main;

{
    local $Some::var = 43;
    print "Localized: " . Some->do_stuff() . "\n";
};

print "Normal: " . Some->do_stuff() . "\n";

但是如果你尝试本地化某个包的私有(my)变量,你可能做错了。

答案 2 :(得分:2)

我不太确定我是否理解正确。这有帮助吗?

#!/usr/bin/env perl

{
    package This;
    use warnings; use strict;
    our $var = 42;

    sub do_stuff {
        print "$var\n";
    }
}

{
    package That;
    use warnings; use strict;

    use Lexical::Alias;
    local $This::var;

    alias $This::var, my $var;

    $var = 24;
    print "$var\n";

    This->do_stuff;
}

package main;

use warnings; use strict;

This->do_stuff;

答案 3 :(得分:2)

package Other::Package;

$var = 23;

sub say_var {
  my $label = shift;
  print "$label: $Other::Package::var\n";
}

package Whatever;
Other::Package::say_var("before");
{
  local $Other::Package::var = 42;
  local *var = \$Other::Package::var;
  Other::Package::say_var("during");
  print "\$Whatever::var is $var inside the block\n";
}
Other::Package::say_var("after");
print "\$Whatever::var is ",
    defined($var) ? "" : "un", "defined outside the block\n";

输出:

before: 23
during: 42
$Whatever::var is 42 inside the block
after: 23
$Whatever::var is undefined outside the block

local $Other::Package::var在块的持续时间内暂时使原始值为42,并且local *var = \$Other::Package::var使当前包中的$var临时为$Other::Package::var的别名块。

当然,其中一些不符合strict,但我避免使用our,因为如果两个软件包都在同一个文件中,our会混淆问题(如果它们可能是你复制粘贴这个样本) - our声明可能会泄漏到它所使用的包中,进入同一文件中的后续包中:)