Для циклических и лексически ограниченных переменных - PullRequest
4 голосов
/ 13 декабря 2010

Версия # 1

use warnings;
use strict;

my $count = 4;

for $count (1..8) {
    print "Count = $count\n";
    last if ($count == 6);
}

if (not defined($count)) {
    print "Count not defined\n";
}
else {
    print "Count = $count\n";
}

Это печатает:

1
2
3
4
5
6
4

Почему? Потому что цикл for создает собственную версию $count в своем блоке с лексической областью.

Версия # 2

use warnings;
use strict;

my $count;
for $count (1..8) {
    print "Count = $count\n";
    last if ($count == 6);
}

if (not defined($count)) {
    print "Count not defined\n";
}
else {
    print "Count = $count\n";
}

1
2
3
4
5
6
Count not defined

Упс! Я хотел получить выходное значение $count, но цикл for имел свою собственную версию $count! Мне просто пришлось потратить два часа, пытаясь отследить эту ошибку.

Версия # 3

use warnings;
use strict;

for $count (1..8) {
    print "Count = $count\n";
    last if ($count == 6);
}

print "That's all folks!\n";

Это дает мне ошибку Global symbol "$count" requires explicit package name at line 5. Но, я думал, $count автоматически лексически ограничено внутри блока for. Похоже, что это происходит только тогда, когда я уже объявил лексически ограниченную версию этой переменной в другом месте.

В чем причина такого поведения? Да, я знаю о диктате Конвея, что вы всегда должны использовать my для переменной цикла for, но вопрос в том, почему интерпретатор Perl был разработан таким образом.

Ответы [ 3 ]

8 голосов
/ 13 декабря 2010

В Perl назначение переменной в цикле всегда локализовано в цикле, а переменная цикла всегда является псевдонимом зацикленного значения over (то есть вы можете изменить исходные элементы, изменив переменную цикла). Это верно как для переменных пакета (our), так и для лексических переменных (my).

Это поведение наиболее близко к динамическому определению переменных пакета в Perl (с ключевым словом local), но также имеет особую специфику для работы с лексическими переменными (объявленными в цикле или ранее).

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

for our $val (1 .. 10) {...} 

эквивалентно:

our $val;
my @list = 1 .. 10;
my $i = 0;

while ($i < @list) {
   local *val = \$list[$i++];
   # loop body
}

В чистом Perl невозможно написать расширенную лексическую версию, но если используется такой модуль, как Data::Alias:

my $val;
my @list = 1 .. 10;
my $i = 0;

while ($i < @list) {
   alias $val = $list[$i++];
   # loop body
}
5 голосов
/ 13 декабря 2010

На самом деле, в версии # 3 переменная "локализована", в отличие от лексической области действия .

Цикл "foreach" выполняет итерацию по обычному значению списка и устанавливает переменная VAR для каждого элемента списка по очереди. Если переменная ему предшествует ключевое слово "my", затем он имеет лексическую область видимости и является поэтому виден только внутри цикла. В противном случае переменная неявно локально для цикла и восстанавливает свое прежнее значение при выходе петля. Если переменная была ранее объявлена ​​как "my", она использует эта переменная вместо глобальной, но она все еще локализована в петля. Эта неявная локализация происходит только в цикле "foreach".

В любом случае вы не сможете получить доступ к переменной цикла из этого стиля for -loop вне цикла. Но вы можете использовать другой стиль (стиль C) for -loop:

my $count;
for ($count=1; $count <= 8; $count++) {
    last if $count == 6;
}
...   # $count is now 6.
4 голосов
/ 13 декабря 2010

Почему? Поскольку цикл for создает свою собственную версию $count с лексической областью внутри своего блока.

Это неправильно. Если бы вы написали for my $count (...) { ... }, это было бы правдой, но вы этого не сделали. Вместо этого, если $count уже является глобальным, он локализован - глобальный элемент, который уже существует, устанавливается на новые значения во время выполнения цикла и возвращается в исходное состояние, когда это делается. Отличие должно быть ясно из этого:

our $x = "orig";

sub foo {
    print $x, "\n";
}

foo();

for $x (1 .. 3) {
  foo();
}

for my $x (1 .. 3) {
  foo();
}

Выход

orig
1
2
3
orig
orig
orig

Первый цикл for без my изменяет значение глобального $x, который уже существует. Второй цикл for с my создает новый лексический $x, который не виден вне цикла. Они не одинаковы.

По этой же причине пример № 3 терпит неудачу - поскольку в области действия нет лексического $count, и вы не объявили, что намереваетесь коснуться пакета, глобального $count, strict 'vars' останавливает вас в вашем дорожек. На самом деле для цикла for он не ведет себя иначе, чем все остальное.

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