Использовать лексическую переменную, объявленную в цикле For в блоке продолжения - PullRequest
3 голосов
/ 28 марта 2019

У меня проблемы с компиляцией одного из моих модулей с тем, что выглядит как проблема с изменяемой областью видимости, но я не понимаю, почему.

Пример кода :

PATH: foreach my $path (@paths) {
    open(my $file, '<', $path) or next PATH;

    my %properties;
    LINE: while (<$file>) {
        $_ =~ /$property_regex/ or next LINE;
        $properties{$1} = $2;
    }

    foreach (@property_keys) {
        unless ($properties{$_}) {
            # do stuff
            next PATH;
    }

    if ( $irrelevant_condition ) {
        # do stuff
        next PATH;
    }

    foreach my $new_var (@new_list) {
        # do stuff (does not iterate PATH loop)
    }
} continue {
    if (defined $file) { close($file) or die; }
}

В переводе на урезанный код выше я получаю ошибку:

Глобальному символу "$ file" требуется явное имя пакета в строке 25

А именно, похоже, он жалуется на использование $file в блоке continue внизу. Как видите, $file объявлено как лексическая переменная в строке 2 внутри внешнего цикла foreach (обозначено PATH).

Однако, исходя из perldoc для continue , я ожидал бы, что $file все еще будет находиться в области видимости:

[...] он всегда выполняется непосредственно перед тем, как условная оценка собирается снова быть оценена, точно так же, как третья часть цикла for в C. Таким образом, его можно использовать для увеличения переменной цикла, даже когда цикл было продолжено [...]

Чтобы иметь возможность увеличивать переменную цикла, разве блок продолжения не должен обрабатываться как часть той же лексической области, что и цикл, к которому он присоединен?

Чего мне не хватает?


Примечание. Этот модуль является классом Moo, поэтому, хотя у меня нет явного оператора use strict, , когда вы используете Moo, мы включаем строгие и предупреждения .

Ответы [ 2 ]

4 голосов
/ 28 марта 2019

Ваша переменная $path все еще находится в области видимости внутри continue BLOCK, но содержимое внутри BLOCK for находится вне области видимости, потому что вы достигли конца этого блока (вы нажали конечную скобку)/ вышли из брекетов).Переменная $path, однако, не находится внутри фигурных скобок, поэтому может быть видна внутри continue BLOCK (даже если она не видна после этого).

Если синтаксис был таким:

for (...)
{
 $x = stuff();
 continue { more_stuff($x) }
}

тогда я бы ожидал, что $x будет видимым.IIRC, в perl 6 есть такие вещи, но в perl 5 continue BLOCK находится вне блока цикла и поэтому не видит лексических переменных внутри этого блока.

3 голосов
/ 28 марта 2019

Гарантируется, что переменная цикла была объявлена ​​до оценки блока continue, поэтому переменная может быть предоставлена ​​блоку continue.

Но любая часть блока цикла может быть пропущена (например, с использованием next), поэтому Perl не знает во время компиляции, какая из переменных блока цикла будет объявлена ​​при вводе блока continue, поэтому он не может сделать ни один из них доступным для блока continue.


Если вы не удовлетворены тем, что дескриптор файла закрывается автоматически (например, из-за того, что вы хотите обнаружить ошибки записи), вы можете использовать защиту области вместо блока continue.

use Sub::ScopeFinalizer qw( scope_finalizer );

for my $qfn (@qfns) {
   my $fh;
   my $guard = scope_finalizer {
      if ($fh) {
         close($fh)
            or die("Error writing to \"$qfn\": $!\n");
      }
   };

   open($fh, '>', $qfn)
      or die("Can't create \"$qfn\": $!\n");

   ...
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...