Ответ 1
Это запутанное, но документированное поведение, по-видимому, связано с плохим решением сделать переменную итератора цикла неявной локализованной глобальной, а не лексической. Из Циклы Foreach в perlsyn.
Если переменной предшествует ключевое слово
my
, то она лексически ограничена и, следовательно, отображается только внутри цикла. В противном случае переменная неявно локальна для цикла и возвращает свое прежнее значение после выхода из цикла. Если ранее переменная была объявлена с помощьюmy
, она использует эту переменную вместо глобальной, но она все еще локализована в цикле.
Другими словами, итератор цикла всегда локализуется в цикле. Если он глобальный, то он действует так, как он был объявлен local
внутри блока цикла. Если он лексический, то он действует так, как он был объявлен с my
внутри блока цикла.
Применение этого к вашим двум примерам поможет понять, что происходит.
our $x;
sub print_func {
print "$x\n";
}
for $x (1 .. 10) {
print_func;
}
В этом цикле есть неявный local $x
. local
действительно должен был быть назван temp
. Он временно переопределяет значение глобальной переменной в течение всего срока ее действия, но она по-прежнему является глобальной. Вот почему print_func
может видеть это.
Старое значение восстанавливается, когда его область действия заканчивается. Вы можете увидеть это, если вы добавите print $x
после цикла for.
use v5.10;
our $x = 42;
for $x (1 .. 10) {
say $x;
}
say $x; # 42
Посмотрите на свой код, включающий лексики (my
variables).
my $x;
sub print_func {
print "$x\n";
}
for $x (1 .. 10) {
print_func;
}
Что действительно происходит здесь, у вас есть две лексические переменные, называемые $x
. Один из них имеет область видимости, одна из них привязана к циклу. Внутренний $x
в цикле for имеет прецедент над внешним $x
. Это известно как "затенение".
Лексики не могут быть замечены за пределами их физического охвата. print_func()
видит только внешний неинициализированный $x
.
Там некоторые стилистические выдержки из этого.
Всегда передавать параметры в свои функции.
В действительности, print_func
должен принять аргумент. Тогда вам не придется беспокоиться о сложных правилах определения области.
sub print_func {
my $arg = shift;
print "$arg\n";
}
for $x (1..10) {
print_func($x);
}
Всегда используйте for my $x
.
Не полагайтесь на сложные неявные правила проверки цикла t221. Всегда объявляйте итератор цикла с помощью my
.
for my $x (1..10) {
print_func($x);
}
Избегать глобальных привязок.
Так как трудно сказать, какой доступ к глобальному, не используйте их. Если вы когда-нибудь думаете, что вам нужен глобальный, напишите функцию вместо этого, чтобы контролировать доступ к лексическому файлу с лексикой.
my $Thing = 42;
sub get_thing { return $Thing }
sub set_thing { $Thing = shift; return }
Объявите переменные рядом с тем, где они используются.
В старых стилях кодирования будут выполняться такие вещи, как объявить все их переменные в верхней части файла или функции. Это удержание с очень, очень, очень старых языков, требующих, чтобы переменные были объявлены только в определенных местах. Perl и большинство современных языков не имеют такого ограничения.
Если вы сразу объявляете свои переменные, вам сложно понять, для чего они нужны, и трудно понять, что с ними связано или влияет на него. Если вы объявите его близким к его первому использованию, которое ограничивает то, что может повлиять на него, и делает его более очевидным, для чего оно предназначено.