Вопрос о вификации Perl при разыменовании неопределенной ссылки на массив

Мне трудно понять, почему работает следующее:

my $array_reference;
foreach $element (@{$array_reference}) {
# some code
}

пока не работает следующее

my $array_reference;
if (scalar (@{$array_reference}) {
    # some code here
}

Я понимаю, что perl оживляет (авто-оживляет) неопределенные ссылки. Но я все еще не понимаю, почему последний сегмент кода выдает FATAL.


person kuriouscoder    schedule 21.06.2011    source источник
comment
Хороший вопрос. Обратите внимание, что первая последовательность автоматически оживляет ссылку. Таким образом, ваш скрипт будет работать, если if идет после foreach, но не наоборот. Я думаю, что это просто неясная (недокументированная?) деталь Perl, но мне любопытно увидеть ответы.   -  person Nemo    schedule 21.06.2011
comment
nemo - я не думаю, что выполнение будет перетекать в цикл foreach для неопределенных ссылок. Может быть, я что-то упускаю   -  person kuriouscoder    schedule 21.06.2011
comment
Нет, выполнение не перетекает в цикл, потому что автооживляемый массив пуст. Но как только вы выполните foreach для неопределенной ссылки, ссылка больше не будет неопределенной... Таким образом, вы можете успешно вызвать scalar @$reference для нее.   -  person Nemo    schedule 21.06.2011
comment
Я удивлен, что первый (foreach) даже не предупреждает.   -  person Alex    schedule 21.06.2011


Ответы (3)


Разыменования автоматически оживляются в контексте lvalue (имеется в виду, когда ожидается модифицируемое значение), а foreach создает контекст lvalue.

>perl -E"$$x = 1;  say $x;"
SCALAR(0x74b024)

>perl -E"++$$x;  say $x;"
SCALAR(0x2eb024)

>perl -E"\$$x;  say $x;"
SCALAR(0x30b024)

>perl -E"sub {}->($$x);  say $x;"
SCALAR(0x27b03c)

>perl -E"for ($$x) {}  say $x;"
SCALAR(0x25b03c)

Последние два создают контекст lvalue, потому что им нужно значение, которому можно присвоить псевдонимы $_[0] и $_ (соответственно).

person ikegami    schedule 21.06.2011

В Perl есть несоответствия в этой области, но в целом код, который может изменять структуру, автоматически оживает, а код, который этого не делает, — нет. И если он не оживает автоматически, он пытается разыменовать неопределенное значение, что вызывает предупреждение или, в соответствии с use strict "refs", исключение.

person ysth    schedule 21.06.2011
comment
Но foreach не изменяет переменную; он просто читает это. Тем не менее, он самооживляется... - person Nemo; 21.06.2011
comment
@Nemo, foreach действительно принимает lvalue (изменяемые значения): foreach (@$a) { $_ = uc($_) } - person ikegami; 21.06.2011
comment
@ikegami: Ага, точно, забыл об этом. Хорошо, тогда я думаю, что это последовательно. +1 к обоим ответам. - person Nemo; 21.06.2011

Я думаю, глядя на perlref, что это ожидаемое поведение:

"Ссылки соответствующего типа могут возникнуть, если вы разыменовываете их в контексте, который предполагает, что они существуют."

То же самое с foreach происходит с push() и друзьями:

my $f;
push @$f, 1;
say @$f;

Хотя и не с новыми версиями, которые можно просто взять по ссылке:

my $f = [];
push $f, 1;
say @$f;

работает, пока

my $f;
push $f, 1;
say @$f;

нет, что я считаю разумным, поскольку push понятия не имеет, что вы на самом деле имели в виду.

Интересный вопрос заключается в том, должен ли scalar(@$undef) делать то же самое или должен предупреждать, поскольку он в конечном итоге возвращает undef, я думаю, он мог бы сразу предупредить.

person Alex    schedule 21.06.2011
comment
Ожидаемое поведение? Значение foreach предполагает, что они существуют, а scalar нет? Это похоже на серьезно недоопределенную часть языка Perl. Может ли любой из этих примеров изменить свое поведение в версии N+1? Я честно не знаю. - person Nemo; 21.06.2011