Почему подпрограмма должна быть написана после объявления переменных, используемых в ней?

#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 посмотрит в этих местах:

  1. Лексические ( 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
      
  2. Переменные пакета

    Это глобальные переменные (необъявленные или объявленные с 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 , by entire file , вы имеете в виду все, что объявлено до объявления субпрограммы?

2. @John Да и нет. Во время компиляции он будет знать только те переменные, которые были объявлены до той строки, в которой они находятся. Это отличается от объявления подпрограммы. Во время выполнения значение также может быть установлено дальше в коде, но когда вы обращаетесь к нему в подраздел, он все равно обращается к нему.

3. Спасибо, что поделились всеми этими знаниями!

4. Когда они сказали «Он будет просматривать всю область действия файла», они оговорились. Область видимости — это место, где видна переменная. В результате ваш вопрос не имеет смысла. /// Они также перечислены our как package var , но на самом деле они лексические и, следовательно, неправильно упорядочены в списке. /// В настоящее время я исправляю эти проблемы.