Perl - 哪些范围/闭包/环境产生这种行为?
给定一个根目录,我希望识别任何 .svn 目录和 pom.xml 的最浅父目录。
为了实现这一点,我定义了以下函数
use File::Find;
sub firstDirWithFileUnder {
$needle=@_[0];
my $result = 0;
sub wanted {
print "\twanted->result is '$result'\n";
my $dir = "${File::Find::dir}";
if ($_ eq $needle and ((not $result) or length($dir) < length($result))) {
$result=$dir;
print "Setting result: '$result'\n";
}
}
find(\&wanted, @_[1]);
print "Result: '$result'\n";
return $result;
}
..并这样调用它:
$svnDir = firstDirWithFileUnder(".svn",$projPath);
print "\tIdentified svn dir:\n\t'$svnDir'\n";
$pomDir = firstDirWithFileUnder("pom.xml",$projPath);
print "\tIdentified pom.xml dir:\n\t'$pomDir'\n";
有两种情况出现,我无法解释:
- 当 .svn 搜索成功时,内部感知到的
$result
值嵌套子例程wanted
持续到下一次调用firstDirWithFileUnder
时。因此,当 pom 搜索开始时,尽管my $result = 0;
行仍然存在,但wanted
子例程将其值视为最后一个firstDirWithFileUnder 的返回值调用。
- 如果
my $result = 0;
行被注释掉,则该函数仍然可以正确执行。这意味着 a) 外部作用域 (firstDirWithFileUnder
) 仍然可以看到$result
变量以便能够返回它,并且 b) print 显示wanted
仍然看到上次的$result
值,即它似乎已经形成了一个闭包,该闭包在第一次调用firstDirWithFileUnder
后仍然持续存在。
有人可以解释发生了什么,并建议我如何在进入外部范围时正确地将 $result 的值重置为零?
Given a root directory I wish to identify the most shallow parent directory of any .svn directory and pom.xml .
To achieve this I defined the following function
use File::Find;
sub firstDirWithFileUnder {
$needle=@_[0];
my $result = 0;
sub wanted {
print "\twanted->result is '$result'\n";
my $dir = "${File::Find::dir}";
if ($_ eq $needle and ((not $result) or length($dir) < length($result))) {
$result=$dir;
print "Setting result: '$result'\n";
}
}
find(\&wanted, @_[1]);
print "Result: '$result'\n";
return $result;
}
..and call it thus:
$svnDir = firstDirWithFileUnder(".svn",$projPath);
print "\tIdentified svn dir:\n\t'$svnDir'\n";
$pomDir = firstDirWithFileUnder("pom.xml",$projPath);
print "\tIdentified pom.xml dir:\n\t'$pomDir'\n";
There are two situations which arise that I cannot explain:
- When the search for a .svn is successful, the value of
$result
perceived inside the nested subroutinewanted
persists into the next call offirstDirWithFileUnder
. So when the pom search begins, although the linemy $result = 0;
still exists, thewanted
subroutine sees its value as the return value from the lastfirstDirWithFileUnder
call. - If the
my $result = 0;
line is commented out, then the function still executes properly. This means a) outer scope (firstDirWithFileUnder
) can still see the$result
variable to be able to return it, and b) print shows thatwanted
still sees$result
value from last time, i.e. it seems to have formed a closure that's persisted beyond the first call offirstDirWithFileUnder
.
Can somebody explain what's happening, and suggest how I can properly reset the value of $result
to zero upon entering the outer scope?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
使用
警告
然后使用诊断
会产生这些有用的信息,包括解决方案:$result
是词法范围的,这意味着每次调用&firstDirWithFileUnder
时都会分配一个全新的变量。sub Want { ... }
是一个编译时子例程声明,这意味着它由 Perl 解释器编译一次并存储在包的符号表中。由于它包含对词法作用域$result
变量的引用,因此 Perl 保存的子例程定义将仅引用$result
的第一个实例。第二次调用&firstDirWithFileUnder
并声明一个新的$result
变量时,这将是与$result
内的$result
完全不同的变量代码>&想要。您需要将
sub Wanted { ... }
声明更改为词法作用域的匿名 sub:并调用
File::Find::find
如下所示,< code>$wanted 是子例程的运行时声明,并且在每次单独调用时都会使用对
$result
的当前引用重新定义&firstDirWithFileUnder
。更新:此代码片段可能具有指导意义:
典型输出:
请注意,编译时
$foo
始终引用相同的变量SCALAR(0xac18c0)
,并且这也是函数第一次运行的运行时$foo
。本示例中包含最后一行
&foo
,push @baz,\$foo
,这样$foo
就不会产生垃圾在&foo
末尾收集。否则,第二个和第三个运行时$foo
可能会指向相同的地址,即使它们引用不同的变量(每次声明变量时都会重新分配内存)。Using
warnings
and thendiagnostics
yields this helpful information, including a solution:$result
is lexically scoped, meaning a brand new variable is allocated every time you call&firstDirWithFileUnder
.sub wanted { ... }
is a compile-time subroutine declaration, meaning it is compiled by the Perl interpreter one time and stored in your package's symbol table. Since it contains a reference to the lexically scoped$result
variable, the subroutine definition that Perl saves will only refer to the first instance of$result
. The second time you call&firstDirWithFileUnder
and declare a new$result
variable, this will be a completely different variable than the$result
inside&wanted
.You'll want to change your
sub wanted { ... }
declaration to a lexically scoped, anonymous sub:and invoke
File::Find::find
asHere,
$wanted
is a run-time declaration for a subroutine, and it gets redefined with the current reference to$result
in every separate call to&firstDirWithFileUnder
.Update: This code snippet may prove instructive:
Typical output:
Note that the compile time
$foo
always refers to the same variableSCALAR(0xac18c0)
, and that this is also the run time$foo
THE FIRST TIME the function is run.The last line of
&foo
,push @baz,\$foo
is included in this example so that$foo
doesn't get garbage collected at the end of&foo
. Otherwise, the 2nd and 3rd runtime$foo
might point to the same address, even though they refer to different variables (the memory is reallocated each time the variable is declared).