Вложенные подпрограммы и Scoping в Perl
Я пишу Perl довольно долгое время и всегда открываю новые вещи, и я просто столкнулся с чем-то интересным, что у меня нет объяснений и не нашел его в Интернете.
sub a {
sub b {
print "In B\n";
}
}
b();
Почему я могу вызывать b()
из-за пределов своей области действия и работает?
Я знаю, что это плохая практика, и я не делаю этого, я использую закрытые и такие для этих случаев, но просто видел это.
Ответы
Ответ 1
Подпрограммы хранятся в глобальном пространстве имен во время компиляции. В вашем примере b();
есть короткая рука для main::b();
. Чтобы ограничить видимость функции для области, вам нужно назначить подпрограммы анонимной переменной.
Обе именованные и анонимные подпрограммы могут создавать замыкания, но поскольку именованные подпрограммы только скомпилированы один раз, если вы их в гнездо, они не ведут себя так, как многие ожидают.
use warnings;
sub one {
my $var = shift;
sub two {
print "var: $var\n";
}
}
one("test");
two();
one("fail");
two();
__END__
output:
Variable "$var" will not stay shared at -e line 5.
var: test
var: test
В Perl допускаются вложенные именованные подпрограммы, но это почти наверняка признак того, что код неправильно делает.
Ответ 2
Следующие отпечатки 123
.
sub a {
$b = 123;
}
a();
print $b, "\n";
Итак, почему вы удивляетесь, что и следующее?
sub a {
sub b { return 123; }
}
a();
print b(), "\n";
Нигде ни один запрос для $b
или &b
не будет лексическим. На самом деле вы не можете просить &b
быть лексическим (пока).
sub b { ... }
в основном
BEGIN { *b = sub { ... }; }
где *b
- запись таблицы символов для $b
, @b
,... и, конечно, &b
. Это означает, что subs принадлежат к пакетам и поэтому могут быть вызваны из любого места в пакете или где угодно вообще, если используется их полное имя (MyPackage::b()
).
Ответ 3
"Официальным" способом создания вложенных подпрограмм в perl является использование ключевого слова local
. Например:
sub a {
local *b = sub {
return 123;
};
return b(); # Works as expected
}
b(); # Error: "Undefined subroutine &main::b called at ..."
Страница perldoc perlref имеет следующий пример:
sub outer {
my $x = $_[0] + 35;
local *inner = sub { return $x * 19 };
return $x + inner();
}
"Это имеет интересный эффект создания функции локально для другой функции, что обычно не поддерживается в Perl".
Ответ 4
Подпрограммы определяются во время компиляции и не зависят от области видимости. Другими словами, они не могут быть действительно вложенными. По крайней мере, это не касается их собственных возможностей. После определения они эффективно удаляются из исходного кода.