鉴于根目录,我希望识别任何.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";
有两种情况我无法解释:
$result
内感知的wanted
值将持续到firstDirWithFileUnder
的下一个调用中。因此,当pom搜索开始时,虽然行my $result = 0;
仍然存在,但wanted
子例程将其值视为上一次firstDirWithFileUnder
调用的返回值。my $result = 0;
行被注释掉,那么该函数仍然可以正常执行。这意味着a)外部范围(firstDirWithFileUnder
)仍然可以看到$result
变量能够返回它,并且b)打印显示wanted
仍然看到$result
值最后一次,也就是说它似乎形成了一个在firstDirWithFileUnder
的第一次调用之后持续存在的闭包。有人可以解释发生了什么,并建议我在进入外部范围时如何正确地将$result
的值重置为零?
答案 0 :(得分:5)
使用warnings
然后diagnostics
会产生这些有用的信息,包括解决方案:
变量“$ needle”不会在-----第12行(#1)保持共享
(W闭包)内部(嵌套)命名子例程引用a 在外部命名子例程中定义的词法变量。
当调用内部子程序时,它将看到的值 外部子例程的变量与第一个之前和期间的变量一样 调用外部子程序;在这种情况下,第一次调用之后 外子程序完成后,内子程序和外子程序都没有 更长时间共享变量的公共值。换句话说, 变量将不再被共享。
这个问题通常可以通过制作内部子程序来解决 匿名,使用sub {}语法。当内部匿名subs表示 它们是外部子程序中的引用变量 会自动反弹到这些变量的当前值。
$result
是词法范围的,这意味着每次调用&firstDirWithFileUnder
时都会分配一个全新的变量。
sub wanted { ... }
是编译时子例程声明,这意味着它由Perl解释器一次编译并存储在包的符号表中。由于它包含对词法范围$result
变量的引用,因此Perl保存的子例程定义仅引用$result
的第一个实例。第二次拨打&firstDirWithFileUnder
并声明新的$result
变量时,这将是一个与$result
内的&wanted
完全不同的变量。
您需要将sub wanted { ... }
声明更改为词法范围的匿名子:
my $wanted = sub {
print "\twanted->result is '$result'\n";
...
};
并调用File::Find::find
作为
find($wanted, $_[1])
此处,$wanted
是子例程的运行时声明,并在每次单独调用{{1}时使用当前对$result
的引用重新定义}。
更新:此代码段可能具有指导意义:
&firstDirWithFileUnder
典型输出:
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
的最后一行,&foo
,以便push @baz,\$foo
在$foo
结束时不会收集垃圾。否则,第二个和第三个运行时&foo
可能指向相同的地址,即使它们引用了不同的变量(每次声明变量时都会重新分配内存)。