Использование warnings
, а затем diagnostics
дает эту полезную информацию, включая решение:
Переменная «$ needle» не будет использоваться совместно в ----- строке 12 (# 1)
(W замыкание) Внутренняя (вложенная) подпрограмма с именем ссылается на
Лексическая переменная, определенная во внешней подпрограмме с именем.
Когда вызывается внутренняя подпрограмма, она увидит значение
переменная внешней подпрограммы, как это было до и во время first
вызов внешней подпрограммы; в этом случае после первого звонка
внешняя подпрограмма завершена, внутренняя и внешняя подпрограммы не будут
больше разделяют общее значение для переменной. Другими словами,
переменная больше не будет общедоступна.
Эту проблему обычно можно решить, сделав внутреннюю подпрограмму
анонимный, используя синтаксис sub {}. Когда внутренние анонимные подпрограммы
ссылочные переменные во внешних подпрограммах созданы, они
автоматически возвращаются к текущим значениям таких переменных.
$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
- это объявление времени выполнения для подпрограммы, и оно переопределяется с текущей ссылкой на $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
. В противном случае 2-я и 3-я среды выполнения $foo
могут указывать на один и тот же адрес, даже если они ссылаются на разные переменные (память перераспределяется при каждом объявлении переменной).