#perl #scope #subroutine
#perl #область видимости #подпрограмма
Вопрос:
Давайте предположим, что у нас есть этот код, почему он завершается ошибкой с явным именем пакета, поскольку функция вызывается только после объявления $value
?
use strict;
use warnings;
sub print_value{
print "n$value";
}
my $value = 2;
print_value();
Это не удается во время компиляции с :
Global symbol "$value" requires explicit package name at file.pl line 5.
Execution of file.pl aborted due to compilation errors.
И этот код работает отлично:
use strict;
use warnings;
my $value = 2;
print_value();
sub print_value{
print "n$value";
}
Игнорируйте бесполезность этого стиля программирования или устаревший стиль кодирования, просто сосредоточьтесь на контексте.
Комментарии:
1. Я отредактировал заголовок вопроса, может быть, теперь он немного понятнее
2. Даже без
strict
этого я все равно не стал бы «работать» — sub все равно будет использовать пакет$value
, а не лексический, объявленный позже.
Ответ №1:
Это из-за области видимости. Вот где в вашей программе видна ваша переменная. Ваша $value
лексическая переменная, потому что вы объявили ее с my
помощью . Это означает, что она существует внутри определенной области видимости (и всех тех, что ниже нее). В обоих ваших примерах эта область представляет собой весь файл (который также можно назвать глобальной областью).
Perl просматривает ваш код в два этапа. Первый — это время компиляции, где он проверяет синтаксис и загружает зависимости (например use
, операторы). На этом этапе он будет искать доступность $value
внутри функции.
Perl посмотрит в этих местах:
-
Лексические (
my
иour
) переменные, находящиеся в области видимости.Переменная находится в области видимости (т. Е. Переменная видна), если код, который ссылается на нее, следует за объявлением, и если он находится в том же блоке, что и объявление, или блок, вложенный в этот блок. Сам файл является блоком, а завитки образуют остальные.
Если в области видимости имеется несколько лексических переменных с одинаковым именем, самые последние объявленные скрывают остальные. Это означает, что переменная, объявленная в самой функции, будет использоваться перед переменной, объявленной вне функции.
my $i; # --------- $i is in scope (visible) here my $x; # | ------- $x is in scope (visible) here while (...) { # | | my $j; # | | ---- $j is in scope (visible) here my $x; # | | -- This different $x is in scope (visible) here ... # | v v } # | | sub foo { # | | my $j; # | | ---- This different $j is in scope (visible) here my $x; # | | -- This third $x is in scope (visible) here ... # | v v } # | | ... # v v
-
Переменные пакета
Это глобальные переменные (необъявленные или объявленные с
use vars
помощью ).Perl будет искать в пространстве имен, объявленном последним «пакетом» в области видимости (по умолчанию
main
), за исключением «суперглобальных» переменных. Это относится к символьным переменным (например$_
,$$
, , и т.д.), Которые Perl ищет,main
а не к текущему пакету.
Поскольку $value
она не была объявлена, Perl принимает ее за переменную пакета $main::value
(поскольку main
это пакет по умолчанию).
use strict; # | Code here will use package var $main::value
use warnings; # |
# |
sub print_value{ # |
print "n$value"; # |
} # |
# v
my $value = 2; # | Code here will use this lexical var $value
print_value(); # v
Ничего из этого не происходит, потому что вы strict
включили. Только тот факт, что вам нужно объявлять переменные либо с my
our
помощью, либо с использованием полного имени, объясняется use strict
.
Если бы у вас не было strict
и не объявлялось переменной with my
, ваша программа работала бы. $value
в этом случае это будет переменная пакета.
В вашем втором примере вы объявили $value
перед подпрограммой, поэтому Perl знает во время компиляции, что в $value
этой области будет доступно, поэтому он не жалуется.
use strict; # | Code here will use package var $main::value
use warnings; # |
# v
my $value = 2; # | Code here will use this lexical var $value
print_value(); # |
# |
sub print_value{ # |
print "n$value"; # |
} # v
Однако лучшим подходом было бы передать переменную в качестве аргумента print_value
.
use strict;
use warnings;
sub print_value{
my $arg = shift;
print "n$arg";
}
my $value = 2;
print_value($value);
Теперь Perl видит $arg
, что внутри небольшой области видимости есть. Он не знает своего значения, но это и не обязательно. И $value
также уже объявлен перед его использованием.
Никогда не используйте переменные из глобальной области видимости (весь файл) внутри функций. Всегда передавайте их в качестве аргументов 1.
Вот несколько соответствующих ссылок:
1) если вы не хотите создавать одноэлементный или какой-либо другой тип замыкания
Комментарии:
1. Когда вы говорите
It will look at: ... the entire file scope
, byentire file
, вы имеете в виду все, что объявлено до объявления субпрограммы?2. @John Да и нет. Во время компиляции он будет знать только те переменные, которые были объявлены до той строки, в которой они находятся. Это отличается от объявления подпрограммы. Во время выполнения значение также может быть установлено дальше в коде, но когда вы обращаетесь к нему в подраздел, он все равно обращается к нему.
3. Спасибо, что поделились всеми этими знаниями!
4. Когда они сказали «Он будет просматривать всю область действия файла», они оговорились. Область видимости — это место, где видна переменная. В результате ваш вопрос не имеет смысла. /// Они также перечислены
our
как package var , но на самом деле они лексические и, следовательно, неправильно упорядочены в списке. /// В настоящее время я исправляю эти проблемы.