Какая локализация происходит в цикле foreach?

Из perldoc perlsyn на тему циклов по каждому элементу:

Если переменная была ранее объявлена ​​с помощью my, она использует эту переменную вместо глобальной, но она по-прежнему локализована в цикле.

Но рассмотрим этот пример:

use Devel::Peek;
my $x = 1;
Dump $x;
for $x ( 1 ) { Dump $x }

SV = IV(0x8117990) at 0x8100bd4
  REFCNT = 1
  FLAGS = (PADBUSY,PADMY,IOK,pIOK)
  IV = 1
SV = IV(0x8117988) at 0x8100bf8
  REFCNT = 2
  FLAGS = (IOK,READONLY,pIOK)
  IV = 1

Похоже, это не одни и те же переменные. Это ошибка в документах или я что-то упустил?


person Eugene Yarmash    schedule 11.02.2010    source источник
comment
Хорошо, а теперь какой у тебя вопрос?   -  person amarillion    schedule 12.02.2010
comment
И вопрос будет...?   -  person p.marino    schedule 12.02.2010
comment
Пока вы размышляете над этим, обязательно прочитайте это: perldoc.perl.org/perlsub.html#Temporary-Values-via-local%28%29 и, возможно, взгляните на это: stackoverflow.com/questions/2238576/   -  person p.marino    schedule 12.02.2010
comment
Я считаю, что идея этого сайта заключается в том, чтобы задавать вопросы (и давать ответы), а не использовать его как своего рода файл заметок. И даже в этом случае немного больше контекста, безусловно, поможет.   -  person p.marino    schedule 12.02.2010
comment
Вот мой собственный ответ на связанный вопрос: stackoverflow.com/questions/2273784/ Я не повторяю это в этой теме.   -  person Eugene Yarmash    schedule 18.02.2010


Ответы (3)


Каждое правило нуждается в своем исключении, и это одно из них. В цикле for, если переменная цикла является лексической (объявленной с помощью my), Perl создаст новую лексическую псевдоним для текущего элемента в цикле. Код OP можно записать следующим образом:

use Data::Alias 'alias';

my $x = 1;
for (2..3) {
    alias my $x = $_;
    # note that $x does not have dynamic scope, and will not be visible in 
    # subs called from within the loop
    # but since $x is a lexical, it can be closed over
}

Редактировать: предыдущий пример был в псевдокоде Perl, ответ изменен для ясности.

person Eric Strom    schedule 11.02.2010
comment
Perl создаст локальную версию неправильного лексического выражения. Perl не создает локальную версию лексики, такой вещи не существует, и в этом нет никакого смысла. Он создает локальную версию глобального. В случае OP лексический и глобальный оба называются $x. Вы можете увидеть это с помощью одного вкладыша: perl -wle '$x = 42; for $x (1..5) { print $main::x; }' vs perl -wle '$x = 42; for my $x (1..5) { print $main::x; }' - person Schwern; 12.02.2010
comment
И ваш код РАБОТАЕТ, если вы явно ссылаетесь на $::x (глобальный $x). В противном случае $x ссылается на объявленный лексический $x. my $x = 1; for (2..3) { local *x = \$_; print $::x; } Если лексический $x не объявлен, то $x ссылается на глобальный $x. $x = 1; for (2..3) { local *x = \$_; print $x; } Мораль этой истории: не делайте лексику и глобал одним и тем же именем. - person Schwern; 12.02.2010

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

person ysth    schedule 12.02.2010

Во-первых, поймите, что есть глобальные переменные (в пределах пакета) и есть лексические переменные. Они могут иметь одно и то же имя. С этого момента я буду ссылаться на глобальный пакет $x по его полному имени $::x, что является сокращением от «основной глобальный пакет $x».

Для обратной совместимости циклы for используют локализованные глобальные переменные, если только вы не укажете иное или $x уже объявлен лексическим (не понял этого). Итак, for $x (2..3) {} относится к локализованному $::x. for my $x (2..3) {} относится к лексическому $x. В обоих случаях они находятся внутри цикла, например:

for (2..3) {
    my $x = $_;
    ...
}

За исключением того, что $_ имеет псевдоним $x, а не копируется.

Я считаю, что приведенное выше объясняет, почему при использовании Devel::Peek вы получаете разные скаляры. Поскольку нет эквивалента local() для лексики, возможно, это объявление нового лексики в лексической панели внутри цикла for, как и в приведенном выше коде.

local($x) также изменяет базовый SV, потому что он делает что-то вроде:

my $original = $x;
my $new;
*::x = \$new;

...

*::x = \$original;

т.е. создание нового скаляра, вставка его в слот $x в таблице символов, а затем восстановление старого скаляра, когда область действия завершена.

Но это действительно только предположение. Вам придется копаться в коде, чтобы узнать.

Документы, в которых говорится, что лексика «локализована в цикле», не должны пониматься как буквальное local(). Термин local сбивает с толку, потому что другие языки и даже программисты Perl используют его взаимозаменяемо для обозначения local() и "лексической области действия". local() лучше было бы назвать temp(). Поэтому я думаю, что документы просто немного небрежны со своей терминологией.

person Schwern    schedule 12.02.2010
comment
man perlsyn: ... Если переменная была ранее объявлена ​​с помощью my, она использует эту переменную вместо глобальной, но она по-прежнему локализована в цикле. Это кажется неправильным.. - person Eugene Yarmash; 12.02.2010
comment
@Schwern perlmod: «Если имя пакета равно null, предполагается, что это «основной» пакет». - person Greg Bacon; 12.02.2010
comment
@ Эрик Я думаю, что происходит что-то смешное, потому что сравните с perl -wle '$x = 1; for $x (2..3) {print "$x vs $::x\n"}' - person Schwern; 12.02.2010
comment
@eugene Ха, я никогда не понимал, что он выбрал лексическое или глобальное значение в зависимости от того, было ли оно уже объявлено. Думал, что по умолчанию всегда используется лексический. Виноват. - person Schwern; 12.02.2010
comment
@Schwern => сравните следующее: perl -wle '$x = 1; for $x (2..3) {print "$x vs $::x\n"}' («2 vs 2», «3 vs 3»), perl -wle '$x = 1; for my $x (2..3) {print "$x vs $::x\n"}' («2 vs 1», «3 vs 1») и perl -wle 'my $x = 1; for $x (2..3) {print "$x vs $::x\n"}' («2 vs undef», «3 vs недеф»). Что меня больше всего удивило при изучении этого, так это то, что вы можете правильно закрыть $x в третьем цикле стиля (вне моего объявления). Я думал, что требуются объявления типа 2 (каждый раз новые лексические), а не одно внешнее объявление. (это не похоже на то, как работают многие другие языки) - person Eric Strom; 13.02.2010
comment
... ограничение на количество символов: например, если вы попытаетесь сделать этот тип закрытия в javascript, каждое закрытие будет закрываться вокруг одной и той же переменной, таким образом, все получат последнее назначенное значение. - person Eric Strom; 13.02.2010