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

试图通过Perl中的词法绑定本地化外部包变量,perl,variables,internals,lexical-scope,Perl,Variables,Internals,Lexical Scope,这是一个很长的标题,但是我恐怕我不能把一个词都去掉而不失去这个问题的真正含义。首先,我会快速描述一下我想要实现的目标,然后漫无目的地讲述我为什么希望这样做。如果你想按照问题的标题直接回答,请跳过漫无边际的问题:-) 快速描述 假设存在一个符合我要求的词汇化内置代码,下面是: package Whatever; { lexicalize $Other::Package::var; local $var = 42; Other::Package->do_stuff; # depen

这是一个很长的标题,但是我恐怕我不能把一个词都去掉而不失去这个问题的真正含义。首先,我会快速描述一下我想要实现的目标,然后漫无目的地讲述我为什么希望这样做。如果你想按照问题的标题直接回答,请跳过漫无边际的问题:-)

快速描述 假设存在一个符合我要求的
词汇化
内置代码,下面是:

package Whatever;
{
  lexicalize $Other::Package::var;
  local $var = 42;
  Other::Package->do_stuff; # depends on that variable internally
}
为什么和为什么不 有一点背景知识,我正在白盒测试一个第三方模块

我希望变量本地化,因为我希望在测试转移到其他测试之前,只在有限的时间内对其进行更改<代码>本地是我发现的最好的方法,无需知道模块对初始值的选择

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

  • 我不想污染测试文件的高级名称空间
  • 我想要比完全限定名称短的名称。为了简洁起见,示例代码中没有这个标识符,但我使用的标识符比所显示的要多很多,并进行了相关的计算和更新
  • 我不能得体地使用
    我们的
    ,因为它不会从另一个包中获取变量:“在“我们的”中变量$Other::package::var不允许有包名。”“临时输入Other::package的作用域并不能阻止它:如果我使用块(
    {package Other::package;我们的$var}
    )那么绑定的持续时间不够长,无法发挥作用;如果我没有(
    package Other::package;我们的@var;package main
    ),那么我需要知道并复制上一个包的名称,这样可以防止代码移动过多

    在问之前的问题之前做作业时,我发现
    词法::Var
    ,这似乎正是我所需要的。唉:“无法通过引用进行本地化。”

    我也尽了最大的努力来理解基于
    我的*var
    的表单,但总是遇到语法错误。今天,我学到了更多关于范围界定和绑定的知识:-)


    我想不出为什么我想要的不可能,但我找不到正确的咒语。我是在要求一个不幸的未实施的edge案例吗?

    如果我没弄错,您所需要的就是我们的。如果我没有,请原谅我

    下面是一个工作示例:

    #!/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";
    

    但是如果您试图本地化某个包的private(
    my
    )变量,您可能做错了什么。

    我不太确定我是否理解正确。这有用吗

    #!/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;
    

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

    • 鉴于:

      {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
      
    • 通过全局引用进行本地化:

      {
          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
      
    最后一个示例使用了一个稍微笨拙的双包声明,在一个包中创建一个词汇表,然后将其放入另一个包中。在其他情况下,此技巧对
    本地
    非常有用:

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

    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
    的别名


    当然,这其中有些不符合
    的严格要求,但我避免使用
    我们的
    ,因为
    我们的
    会混淆问题,如果两个包都在同一个文件中(就像复制粘贴此示例时一样)
    我们的
    声明可能会泄漏到它所使用的包中,进入同一文件中的以下包:)

    它确实有效:我目前已经在使用它的一个变体,因为缺少更好的东西。这不能满足我自己的要求的地方是它没有创建新的(较短的)词汇绑定:在块内部,我们坚持使用变量的完全限定的
    $Some::var
    形式,而不是更好的普通
    $var
    。我实际的包名比这个长了一点;这就是我首先提出问题的原因。旁注:这可能是您在一个代码示例中复制粘贴多个文件的副作用,但顶部附近的
    our$var=42
    行可能会污染您以后想在目标块中尝试的任何内容。最好将包
    中的一些
    包装在块中。词法::别名。。。环顾四周时还没找到那个。看起来很有希望。尝试一下,我很快会告诉你的。有趣的东西。你只是让我对内部的理解比我想的多了一点。就我对问题的表述而言,它是有效的(因此我将投票并接受)。因为我太笨了,漏掉了太多,这并不是我想要的,但我现在只怪我自己。我漏掉的是:我的
    local
    行实际上是
    local$pkg::var=f($pkg::var)
    <代码>本地
    必须在别名之前完成,否则它会破坏别名,所以我要写三次巨大的完全限定名,这比我的目标有点高。谢谢你的回答!这并不完全是我想要的,因为它将别名泄漏到了词法范围之外的*whater::var(我认为是whater中的helper函数)。谢谢你抽出时间来帮助我,非常感谢。@JB。啊,在这种情况下,词法::别名就是您想要的(正如Sinan所说)。我试图避免这种情况,因为这是一种狡猾的野兽。在霍布斯(SO order)之后阅读你的答案,同样的评论也适用。非常感谢你的帮助@JB.=>添加了一些不会污染名称空间的示例。您的第二个示例,除了提醒我不想重复的内容之外
    before: 23
    during: 42
    $Whatever::var is 42 inside the block
    after: 23
    $Whatever::var is undefined outside the block