Perl-哪些范围/封闭/环境正在产生这种行为? [英] Perl - What scopes/closures/environments are producing this behaviour?
问题描述
给出一个根目录,我希望确定任何.svn目录和pom.xml中最浅的父目录.
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;
}
..并这样称呼:
$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:
- 成功搜索.svn时,嵌套子例程
wanted
中感知到的$result
的值将保留到firstDirWithFileUnder
的下一个调用中.因此,当pom搜索开始时,尽管行my $result = 0;
仍然存在,但wanted
子例程将其值视为最后一次firstDirWithFileUnder
调用的返回值. - 如果注释了
my $result = 0;
行,则该功能仍将正确执行.这意味着a)外部范围(firstDirWithFileUnder
)仍然可以看到$result
变量以能够返回它,并且b)打印显示wanted
从上次仍然看到$result
值,即它似乎具有形成了一个闭合,该闭合一直持续到firstDirWithFileUnder
的第一次调用之后.
- 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
.
有人可以解释正在发生的事情,并建议我在进入外部示波器时如何将$result
的值正确重置为零吗?
Can somebody explain what's happening, and suggest how I can properly reset the value of $result
to zero upon entering the outer scope?
推荐答案
使用warnings
然后使用diagnostics
会产生此有用的信息,包括解决方案:
Using warnings
and then diagnostics
yields this helpful information, including a solution:
变量"$ needle"将不会在-----第12行(#1)保持共享状态
Variable "$needle" will not stay shared at ----- line 12 (#1)
(W闭包)一个内部(嵌套的)命名子例程正在引用一个 在外部命名子例程中定义的词汇变量.
(W closure) An inner (nested) named subroutine is referencing a lexical variable defined in an outer named subroutine.
当调用内部子程序时,它将看到 在 first 之前和期间,外部子例程的变量 调用外部子例程;在这种情况下,第一次调用 外部子例程已完成,内部和外部子例程将不会 不再共享该变量的公共值.换句话说, 变量将不再共享.
When the inner subroutine is called, it will see the value of the outer subroutine's variable as it was before and during the first call to the outer subroutine; in this case, after the first call to the outer subroutine is complete, the inner and outer subroutines will no longer share a common value for the variable. In other words, the variable will no longer be shared.
通常可以通过创建内部子例程来解决此问题 匿名,使用sub {}语法.当内部匿名订阅者表示 在外部子例程中创建引用变量,它们 会自动反弹到此类变量的当前值.
This problem can usually be solved by making the inner subroutine anonymous, using the sub {} syntax. When inner anonymous subs that reference variables in outer subroutines are created, they are automatically rebound to the current values of such variables.
$result
具有词法范围,这意味着每次调用&firstDirWithFileUnder
都会分配一个新变量.
sub wanted { ... }
是 compile-time 子例程声明,这意味着它由Perl解释器一次编译并存储在包的符号表中.由于它包含对词法范围的$result
变量的引用,因此Perl保存的子例程定义将仅引用$result
的第一个实例.第二次调用&firstDirWithFileUnder
并声明一个新的$result
变量,它将与&wanted
中的$result
完全不同.
$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
.
您需要将sub wanted { ... }
声明更改为一个词法范围的匿名子项:
You'll want to change your sub wanted { ... }
declaration to a lexically scoped, anonymous sub:
my $wanted = sub {
print "\twanted->result is '$result'\n";
...
};
并以File::Find::find
find($wanted, $_[1])
在这里,$wanted
是子例程的运行时声明,并且在每次单独调用&firstDirWithFileUnder
时都使用当前引用$result
对其进行重新定义.
Here, $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:
sub foo {
my $foo = 0; # lexical variable
$bar = 0; # global variable
sub compiletime {
print "compile foo is ", ++$foo, " ", \$foo, "\n";
print "compile bar is ", ++$bar, " ", \$bar, "\n";
}
my $runtime = sub {
print "runtime foo is ", ++$foo, " ", \$foo, "\n";
print "runtime bar is ", ++$bar, " ", \$bar, "\n";
};
&compiletime;
&$runtime;
print "----------------\n";
push @baz, \$foo; # explained below
}
&foo for 1..3;
典型输出:
compile foo is 1 SCALAR(0xac18c0)
compile bar is 1 SCALAR(0xac1938)
runtime foo is 2 SCALAR(0xac18c0)
runtime bar is 2 SCALAR(0xac1938)
----------------
compile foo is 3 SCALAR(0xac18c0)
compile bar is 1 SCALAR(0xac1938)
runtime foo is 1 SCALAR(0xa63d18)
runtime bar is 2 SCALAR(0xac1938)
----------------
compile foo is 4 SCALAR(0xac18c0)
compile bar is 1 SCALAR(0xac1938)
runtime foo is 1 SCALAR(0xac1db8)
runtime bar is 2 SCALAR(0xac1938)
----------------
请注意,编译时间$foo
始终引用相同的变量SCALAR(0xac18c0)
,并且这也是运行时间$foo
函数第一次运行.
Note that the compile time $foo
always refers to the same variable SCALAR(0xac18c0)
, and that this is also the run time $foo
THE FIRST TIME the function is run.
此示例中包括了&foo
的最后一行,push @baz,\$foo
,因此,$foo
不会在&foo
的末尾收集垃圾.否则,第二个和第三个运行时$foo
可能指向相同的地址,即使它们引用了不同的变量(每次声明该变量时也会重新分配内存).
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).
这篇关于Perl-哪些范围/封闭/环境正在产生这种行为?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!