我查了几个处理这个警告的答案,但他们也没有帮助我,也没有真正理解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不会保持共享”。
我不明白:
此警告是否与此有关?我的预期逻辑会在这里发挥作用还是会发生奇怪的事情?
如果它无法正常工作:是否可以这样做?使局部变量对嵌套子变得可见?或者将它作为参数传递更好?或者声明一个“我的”变量是否更好?
如果我将它作为参数推送,整个对象及其所有数据(可能有几个MB)是否会被推入堆栈?或者我可以传递类似参考的东西?或者Perl是否将该参数作为参考单独处理?
在"Variable $foo will not stay shared" Warning/Error in Perl While Calling Subroutine中,有人谈论了一个匿名子,这将使这成为可能。我不明白它是如何工作的,从来没有使用过这样的东西。
我根本不理解这个解释(可能导致英语不是我的第一语言):“当调用内部子程序时,它会看到外部子程序变量的值,就像它之前和期间一样。首先调用外部子例程;在这种情况下,在第一次调用外部子例程完成后,内部和外部子例程将不再共享变量的公共值。“:
“外部子程序的第一次调用是完成的是什么意思”是什么意思 我的意思是:首先我叫外子。外部子调用内部子。外子当然还在运行。一旦外部子完成,内部子也将完成。那么当内部子已经完成时,这仍然适用于什么呢?那么“第一次”召唤呢?什么时候发生“第二次”通话......对不起,这个解释让我感到困惑。
很抱歉这个问题很多。也许有人至少可以回答其中一些。
答案 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( ... )
请注意,即使显示警告,结果仍与预期相同:未在内部子域中定义的变量在外部子域中被修改。我看不到这个警告是关于什么的。