Ответ состоит из двух частей: первая часть представляет собой простое представление о том, что происходит, и что вы должны сделать, чтобы это исправить. Вторая часть пытается объяснить это странное поведение, которое вы получаете.
Часть 1 - простые объяснения
Все формы являются законными;они просто не эквивалентны и, вероятно, не делают то, что вы ожидаете. В этом случае Data::Dumper
или Data::Printer
ваши друзья:
use Data::Printer;
my @a1 = ( [] x 9 );
p @1;
Печатает что-то вроде
[
[0] "ARRAY(0x1151f30)ARRAY(0x1151f30)ARRAY(0x1151f30)ARRAY(0x1151f30)ARRAY(0x1151f30)ARRAY(0x1151f30)ARRAY(0x1151f30)ARRAY(0x1151f30)ARRAY(0x1151f30)"
]
Цитируя документ x
(«оператор повторения»,если вам нужно его найти):
В скалярном контексте или если левый операнд не заключен ни в скобки, ни в список qw //, он выполняет повторение строки.
([] x 9
вызывает x
в скалярном контексте)
С другой стороны, когда вы делаете ([])
, вы получаете что-то вроде:
[
[0] [],
[1] var[0],
[2] var[0],
[3] var[0],
[4] var[0],
[5] var[0],
[6] var[0],
[7] var[0],
[8] var[0]
]
Цитированиеснова документ:
Если x находится в контексте списка, и левый операнд либо заключен в скобки, либо в список qw //, он выполняет повторение списка. В этом случае он передает контекст списка левому операнду и возвращает список, состоящий из списка левого операнда, повторяемого число раз, указанное правым операндом.
Что здесь происходит, так это то, что []
оценивается до x
. Он создает arrayref и x 9
затем дублирует его 9 раз.
Правильные способы достижения того, что вы хотите, были бы либо вашим первым решением, либо, возможно, если вы предпочитаете что-то более краткое (но все же читаемое):
my @row = map { [] } 1 .. 9;
(поскольку тело карты вычисляется на каждой итерации, оно действительно создает 9 различных ссылок)
Или, вы можете просто не инициализировать @row
, @col
и@box
и пусть автовивификация создает массивы при необходимости.
Часть 2 - предварительное объяснение
При использовании ([] x 9)
у вас странное поведение в программе. Для простоты позвольте мне воспроизвести его на более простом примере:
use feature 'say';
@x = ([] x 5);
@y = ([] x 5);
@z = ([] x 5);
push @{$x[0]}, 1;
push @{$y[0]}, 1;
push @{$z[0]}, 1;
say "@{$x[0]}";
say "@{$y[0]}";
say "@{$z[0]}";
Эта программа выводит:
1 1
1
1 1
Интересно, удалите определение @y
(@y = ([] x 5)
) из этогопрограммы выдают:
1
1
1
Что-то подозрительное происходит. Я объясняю это двумя пунктами.
Сначала давайте рассмотрим следующий пример:
use Data::Printer;
use feature 'say';
say "Before:";
@x = "abcd";
p @x;
say "@{$x[0]}";
say "After:";
push @{$x[0]}, 5;
p @x;
say "@{$x[0]}";
say $abcd[0];
Какие выходные данные
Before:
[
[0] "abcd"
]
After:
[
[0] "abcd"
]
5
5
Когда мы делаем push @{$x[0]}, 5
, @{$x[0]}
становится @ {"abcd"}, который создает массивы @abcd
и вставляет в него 5
. $x[0]
по-прежнему является строкой (abcd
), но эта строка также является именем массива.
Second *, давайте рассмотрим следующую программу:
use Data::Printer;
@x = ([] x 5);
@y = ([] x 5);
@z = ([] x 5);
p @x;
p @y;
p @z;
Мыполучить вывод:
[
[0] "ARRAY(0x19aff30)ARRAY(0x19aff30)ARRAY(0x19aff30)ARRAY(0x19aff30)ARRAY(0x19aff30)"
]
[
[0] "ARRAY(0x19b0188)ARRAY(0x19b0188)ARRAY(0x19b0188)ARRAY(0x19b0188)ARRAY(0x19b0188)"
]
[
[0] "ARRAY(0x19aff30)ARRAY(0x19aff30)ARRAY(0x19aff30)ARRAY(0x19aff30)ARRAY(0x19aff30)"
]
@x
и @z
содержат одинаковые ссылки. Хотя это удивительно, я думаю , что это объяснимо: строка 1, [] x 5
создает arrayref, затем преобразует его в строку для выполнения x 5
, а затем он больше не использует arrayref,Это означает, что сборщик мусора может освободить свою память, а Perl может перераспределить что-то еще по этому адресу. По какой-то причине это происходит не сразу (@y
не содержит того же, что и @x
), а только при выделении @z
. Вероятно, это всего лишь результат реализации сборщика / оптимизатора мусора, и я подозреваю, что он может измениться с версии на другую.
В конце концов происходит следующее: @x
и @z
содержит один элемент, строку, которая идентична. Когда вы разыменовываете $x[0]
и $z[0]
, вы получаете один и тот же массив. Следовательно, нажатие на $x[0]
или $z[0]
приводит к вставке в один и тот же массив.
Это было бы зафиксировано с use strict
, который сказал бы что-то вроде:
Can't use string ("ARRAY(0x2339f30)ARRAY(0x2339f30)"...) as an ARRAY ref while "strict refs" in use at repl1.pl line 11.
* обратите внимание, что для этой второй части я не уверен, что именно так и происходит, и это только мое (несколько образованное) предположение. Пожалуйста, не верьте мне на слово и не стесняйтесь поправлять меня, если вы знаете лучше.