Объявление наборов анонимных массивов в Perl - PullRequest
6 голосов
/ 29 октября 2019

Я пытаюсь сгенерировать массивы для каждого из агрегатов ячеек в suduko. Кажется, я решил проблему, но не понимаю, что делают мои более ранние альтернативы.

Я получаю ожидаемый ответ, если напишу, например:

@row = ( [], [], [], [], [], [], [], [], [] ) ;

Я ожидал

@row = ( [] ) x 9 ;

вести себя так же. Я также попробовал, что получилось лучше

@row = ( [] x 9 ) ;

Только первый элемент выглядит странным с этим, в двух массивах. С первой отклоненной формой я получаю все 81 элемент в каждом массиве

Интересно, была ли последняя форма действительно допустимой?

# prob.pl - Show problem with repeat anonymous arrays
#
# ###################################################

@row = ( [] x 9 ) ;
@col = ( [] x 9 ) ;
@box = ( [] x 9 ) ;

for ( $i = 0 ; $i < 81 ; $i ++ ) {
   push( @{$row[ row($i) ]}, $i ) ;
   push( @{$col[ col($i) ]}, $i ) ;
   push( @{$box[ box($i) ]}, $i ) ;
}

for ( $i = 0 ; $i < 9 ; $i ++ ) {
   print STDERR "\@{\$row[$i]} = @{$row[$i]}\n" ;
   print STDERR "\@{\$col[$i]} = @{$col[$i]}\n" ;
   print STDERR "\@{\$box[$i]} = @{$box[$i]}\n" ;
}

sub row {
   my( $i ) = @_ ;

   int( $i / 9 ) ;
}

sub col {
   my( $i ) = @_ ;

   $i % 9 ;
}

sub box {
   my( $i ) = @_ ;

   int( col( $i ) / 3 ) + 3 * int( row( $i ) / 3 ) ;
}

Ответы [ 2 ]

3 голосов
/ 29 октября 2019

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


Часть 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.

* обратите внимание, что для этой второй части я не уверен, что именно так и происходит, и это только мое (несколько образованное) предположение. Пожалуйста, не верьте мне на слово и не стесняйтесь поправлять меня, если вы знаете лучше.

2 голосов
/ 29 октября 2019

Вторая форма создает 9 копий одной и той же ссылки на массив. Третья форма создает один элемент массива, состоящий из строки типа «ARRAY (0x221f488)», объединенной 9 раз. Либо создайте 9 отдельных массивов с помощью, например, push @row, [] для 1..9;или положитесь на автовивификацию.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...