Почему мы не можем инициализировать массивы состояний/хэши в контексте списка?

Существует ограничение на массивы и хеши как переменные состояния. Мы не можем инициализировать их в контексте списка с 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 );

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