Perl:"变量不会保持共享"

时间:2014-08-20 08:07:57

标签: perl variables local

我查了几个处理这个警告的答案,但他们也没有帮助我,也没有真正理解Perl在这里做的事情。这就是我想要做的事情:

sub outerSub {
  my $dom = someBigDOM;
  ...
  my $otherVar = innerSub();
  return $otherVar;

  sub innerSub {
    my $resultVar = doStuffWith($dom);
    return $resultVar;
  }
}

基本上,我有一个存储在$ dom中的大DOM对象,如果可能的话我不想在堆栈中传递。在outerSub中,正在发生需要来自innerSub的结果的东西。 innerSub需要访问$ dom。当我这样做时,我收到这个警告“变量$ dom不会保持共享”。

我不明白:

  1. 此警告是否与此有关?我的预期逻辑会在这里发挥作用还是会发生奇怪的事情?

  2. 如果它无法正常工作:是否可以这样做?使局部变量对嵌套子变得可见?或者将它作为参数传递更好?或者声明一个“我的”变量是否更好?

  3. 如果我将它作为参数推送,整个对象及其所有数据(可能有几个MB)是否会被推入堆栈?或者我可以传递类似参考的东西?或者Perl是否将该参数作为参考单独处理?

  4. "Variable $foo will not stay shared" Warning/Error in Perl While Calling Subroutine中,有人谈论了一个匿名子,这将使这成为可能。我不明白它是如何工作的,从来没有使用过这样的东西。

  5. 我根本不理解这个解释(可能导致英语不是我的第一语言):“当调用内部子程序时,它会看到外部子程序变量的值,就像它之前和期间一样。首先调用外部子例程;在这种情况下,在第一次调用外部子例程完成后,内部和外部子例程将不再共享变量的公共值。“:

  6. “外部子程序的第一次调用是完成的是什么意思”是什么意思 我的意思是:首先我叫外子。外部子调用内部子。外子当然还在运行。一旦外部子完成,内部子也将完成。那么当内部子已经完成时,这仍然适用于什么呢?那么“第一次”召唤呢?什么时候发生“第二次”通话......对不起,这个解释让我感到困惑。

    很抱歉这个问题很多。也许有人至少可以回答其中一些。

4 个答案:

答案 0 :(得分:13)

简而言之,调用第二个及以后的outerSub将具有与innerSub使用的$ dom变量不同的$ dom变量。您可以通过执行以下操作来解决此问题:

{
    my $dom;
    sub outerSub {
        $dom = ...
        ... innerSub() ...
    }
    sub innerSub {
        ...
    }
}

或通过这样做:

sub outerSub {
    my $dom = ...
    *innerSub = sub {
        ...
    };
    ... innerSub() ...
}

或者这个:

sub outerSub {
    my $dom = ...
    my $innerSub = sub {
        ...
    };
    ... $innerSub->() ...
}

所有变量最初都是预先分配的,而innerSub和outerSub共享相同的$ dom。当你离开范围时,perl会遍历在范围中声明的词法变量并重新初始化它们。因此,当第一次调用outerSub完成时,它会得到一个新的$ dom。因为命名的subs是全局的东西,innerSub不受此影响,并且一直引用旧的$ dom。因此,如果第二次调用outerSub,则其$ dom和innerSub的$ dom实际上是单独的变量。

因此,将声明移出outerSub或使用匿名子(在运行时新近绑定到词法环境)可以解决问题。

答案 1 :(得分:5)

您需要有一个匿名子程序来捕获变量:

my $innerSub = sub  {
  my $resultVar = doStuffWith($dom);
  return $resultVar;
};

示例:

sub test {
    my $s = shift;

    my $f = sub {
        return $s x 2;
    };  

    print $f->(), "\n";

    $s = "543";

    print $f->(), "\n";
}

test("a1b");

给出:

a1ba1b
543543

答案 2 :(得分:4)

如果要最小化将参数传递给subs的大小,use Perl references。缺点/特征是sub可以改变引用的param内容。

my $dom = someBigDOM;
my $resultVar = doStuffWith(\$dom);


sub doStuffWith {
   my $dom_reference = shift;
   my $dom_contents = $$dom_reference;
   #...
}

答案 3 :(得分:0)

http://www.foo.be/docs/perl/cookbook/ch10_17.htm 之后,你应该定义一个本地 GLOB 如下:

local *innerSub = sub {
    ...
} 
#You can call this sub without ->
innerSub( ... ) 

请注意,即使显示警告,结果仍与预期相同:未在内部子域中定义的变量在外部子域中被修改。我看不到这个警告是关于什么的。