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