Почему мы не можем инициализировать массивы состояний/хэши в контексте списка?
Существует ограничение на массивы и хеши как переменные состояния. Мы не можем инициализировать их в контексте списка с Perl 5.10:
So
state @array = qw(a b c); #Error!
Почему так? Почему это не разрешено?
Мы можем использовать массивы состояний и инициализировать их таким образом
state @numbers;
push @numbers, 5;
push @numbers, 6;
но почему бы не сделать это напрямую с помощью state @numbers = qw(5 6);
Почему Perl не позволяет?
Ответы
Ответ 1
В соответствии с perldiag поддержка инициализации контекста списка запланирована для будущей версии:
- Инициализация переменных состояния в контексте списка в настоящее время запрещена
(F) В настоящее время реализация "состояния" допускает только инициализацию скалярных переменных в скалярном контексте. Перепишите state ($a) = 42
как state $a = 42
, чтобы перейти из списка в скалярный. Конструкции, такие как state (@a) = foo()
, будут поддерживаться в будущей версии perl.
В соответствии с это сообщение об изменении, которое сделало это ошибкой:
В настоящее время запретить все инициализацию списка инициализации переменных состояния, поскольку четкая семантика в Perl 6 неясна. Лучше сделать это синтаксическая ошибка, чем иметь одно поведение сейчас, но изменить его позже. [Я считаю, что это консенсус. Если нет, оно будет отменено]
Вместо этого вы можете использовать arrayref:
state $arrayRef = [qw(a b c)];
Обратите внимание, что ваш пример
state @numbers;
push @numbers, 5;
push @numbers, 6;
означает не то же самое, что state @numbers = qw(5 6)
будет (если он сработает). A state
переменная инициализируется только один раз, но ваш код будет нажимать 5 и 6 на массив каждый раз, когда этот код будет выполнен.
Ответ 2
Ужасное обходное решение:
state @array;
state $array_is_initialized;
unless ($array_is_initialized) {
$array_is_initialized = 1;
@array = (1,2,3);
}
Ответ 3
Это просто не было написано, потому что это было тяжело, и заставить его работать на скаляры считалось более важным. Там opcheck (Perl_ck_sassign
in op.c
), который распознает, когда левая часть присваивания является padsv
op, ссылаясь на вновь объявленную переменную state
и обертывает ее специальным once
op, который гарантирует, что что присваивание происходит только один раз, но даже не пытается распознать назначения списков, возможно, из-за сложности разбиения конструкции типа (state $a, my $b, state $c) = (1, 2, 3)
. Забавно, но похоже, что state @a = qw(blah blah blah)
будет достаточно простым, и, очевидно, это менее патологический случай, чем другой вариант назначения списка.
Ответ 4
Другая работа вокруг:
{
my @array = qw(1 2 3);
sub foo {
print join(", ", @array);
}
}
foo();
Обеспечивает аналогичный результат с использованием состояния (в этом случае он привязывает его к функции, не разрушая его при выходе из функции). Использует тот факт, что объявления функций глобальны в Perl.
Источник
Ответ 5
Я не думаю, что есть хороший ответ на ваш вопрос. Я не могу сказать точно, но я предполагаю, что perl просто никогда не поддерживал одноразовую инициализацию, которая будет подразумеваться вашим первым кодом (я предполагаю, что это то, что вы хотели).
Вы можете сделать:
state @array;
@array = qw( a b c );
Выполнение этого просто будет таким же, как использование "моей" переменной, а не "состояния".