О доступе к массиву массивов - PullRequest
1 голос
/ 23 декабря 2011

Однажды я прочитал следующий пример о «массиве массивов». АОА - двумерный массив

Следующий сегмент кода, как утверждается, печатает все это с ссылками

for $aref ( @AoA ) {
  print "\t [ @$aref ],\n";
}

И, как утверждается, следующий сегмент кода печатает все это с индексами

for $i ( 0 .. $#AoA ) {
  print "\t [ @{$AoA[$i]} ],\n";
}

За что здесь стоит $ aref? Как понять определения @$aref и @{$AoA[$i]}? Спасибо.

Ответы [ 4 ]

5 голосов
/ 23 декабря 2011

$aref означает «ссылка на массив», то есть ссылку на массив.

my $my_aref = \@somearray;

Вы можете создать массив из ссылки на массив со следующим синтаксисом:

@{$my_aref}

@{$my_aref} - @somearray.(Это не копия, это действительно тот же массив.)

Во втором примере $AoA[$i] является ссылкой на массив, и вы разыменовываете его с тем же синтаксисом: @{$AoA[$i]}.

См. perlreftut для дополнительных объяснений и примеров.

1 голос
/ 23 декабря 2011

Вся загадка с многомерными структурами в Perl довольно легко понять, когда вы поймете, что есть только три типа переменных, с которыми нужно иметь дело. Скаляры, массивы и хэши.

  • Скаляр - это одно значение, оно может содержать что угодно, но только один в то время.
  • Массив содержит ряд скалярных значений, упорядоченных по фиксированной числовой индекс.
  • Хеш содержит скалярные значения, проиндексированные ключами из строк.

И все массивы, хэши или скаляры действуют таким образом. Многомерные массивы ничем не отличаются от одного измерения.

Это также очень кратко выражено в perldata :

Все данные в Perl - это скаляр, массив скаляров или хеш скаляры. Скаляр может содержать одно значение в любом из трех различные ароматы: число, строка или ссылка. В общем, преобразование из одной формы в другую является прозрачным. Хотя скаляр не может напрямую содержать несколько значений, он может содержать ссылку на массив или хеш, который в свою очередь содержит несколько значений.

Например:

my @array = (1, 2, 3);

Здесь $array[0] содержит 1, $array[1] содержит 2 и т. Д. Как и следовало ожидать.

my @aoa = ( [ 1, 2, 3 ], [ 'a', 'b', 'c' ] );

Здесь $array[0] содержит ссылку на массив. Если вы распечатаете его, он скажет что-то вроде ARRAY(0x398a84). Не волнуйся! Это все еще скалярное значение. Откуда нам это знать? Потому что массивы могут содержать только скалярные значения.

Когда мы делаем что-то вроде

for $aref ( @AoA ) {
  print $aref;  # prints ARRAY(0x398a84) or similar
}

Это ничем не отличается от

for $number ( @array ) {
  print $number;
}

$aref и $number являются скалярными значениями. Все идет нормально. Найдите минутку и закрепите это знание: Массивы могут содержать только скалярные значения.


Теперь следующая часть просто знает, как обращаться со ссылками. Это задокументировано в perlref и perlreftut .

Ссылка является скалярным значением. Это адрес места в памяти. Это место содержит некоторые данные. Чтобы получить доступ к фактическим данным, нам нужно разыменовать ссылку.

В качестве простого примера:

my @data = (1, 2, 3);
my $aref = \@data;   # The backslash in front of the sigil creates a reference
print $aref;         # print something like ARRAY(0xa4b6a4)
print @$aref;        # prints 123

Добавление символа перед ссылкой указывает Perl разыменовать скалярное значение в тип данных, которые представляет символ. В этом случае массив. Если вы выбрали неправильный символ для типа ссылки, Perl выдаст ошибку, такую ​​как:

Not a HASH reference

В приведенном выше примере у нас есть ссылка на конкретное именованное местоположение. И @$aref, и @data имеют одинаковые значения. Если мы изменим значение на одно, это повлияет на оба, потому что адрес в ячейке памяти идентичен. Давайте попробуем это:

my @data  = (1, 2, 3);
my $aref  = \@data;
$$aref[1] = 'a';         # dereference to a scalar value by $ sigil
# $aref->[1] = 'a'       # does the same thing, a different way
print @data;             # prints 1a3
print @$aref;            # prints 1a3

Мы также можем иметь анонимные данные. Если бы мы были заинтересованы только в создании массива массивов, нас бы не интересовал @data, и мы могли бы пропустить его, выполнив следующее:

my $aref = [ 1, 2, 3 ];

Скобки вокруг списка чисел создают анонимный массив. $aref по-прежнему содержит данные того же типа: ссылка. Но в этом случае $aref - это единственный способ получить доступ к данным, содержащимся в ячейке памяти. Теперь давайте создадим еще несколько скалярных значений, например:

my $aref1 = [ 1, 2, 3 ];
my $aref2 = [ 'a', 'b', 'c' ];
my $aref3 = [ 'x', 'y', 'z' ];

Теперь у нас есть три скалярные переменные, которые содержат ссылки на анонимные массивы. Что если мы поместим их в массив?

my @aoa = ($aref1, $aref2, $aref3);

Если бы мы хотели получить доступ к $aref1, мы могли бы сделать print @$aref1, но мы могли бы также сделать

print @{$aoa[0]};

В этом случае нам нужно использовать расширенную форму разыменования: @{ ... }. Поскольку perl не любит неоднозначность, нам необходимо различать @{$aoa[0]} (взять ссылку в $aoa[0] и разыменование в виде массива) и @{$aoa}[0] (взять ссылку в $aoa и разыменование в виде массива и принять это массив первого значения).

Выше мы могли бы использовать @{$aref}, поскольку оно идентично @$aref.

Так что, если мы заинтересованы только в построении массива массивов, нас на самом деле не интересуют также $aref1 скаляры. Итак, давайте вырезать их из процесса:

my @aoa = ( [ 1, 2, 3 ], [ 'a', 'b', 'c' ], [ 'x', 'y', 'z' ]);

Тад!Это массив массивов.

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

for my $scalar ( @aoa ) {
  print @$scalar;  # prints 123abcxyz
}

На этот раз я использовал другое имя переменной, просто чтобы сделать точку.Этот цикл берет каждое значение из @aoa - которое все еще является только скалярным значением - разыменовывает его как массив и печатает его.

Или мы можем получить доступ к @aoa через его индексы

for my $i ( 0 .. $#aoa ) {
  print @{$aoa[$i]};
}

И это все, что нужно!

1 голос
/ 23 декабря 2011

Perl не имеет многомерных массивов. Один помещает массивы в другие массивы для достижения того же результата.

Ну, почти. Значения массивов (и хэшей) являются скалярами, поэтому нельзя поместить массив в другой массив. Что вместо этого вместо размещения ссылки на массив.

Другими словами, «массив массивов» - это сокращение от «массива ссылок на массивы». Каждое значение @AoA является ссылкой на другой массив, учитывая «иллюзию» двумерного массива.


Ссылка взята из использования [ ] или эквивалентного. [ ] создает анонимный массив, затем создает ссылку на этот массив, а затем возвращает ссылку. Вот откуда ссылка.

Распространенные способы построения AoA:

my @AoA = (
   [ 'a', 'b', 'c' ],
   [ 'd', 'e', 'f' ],
);

my @AoA;
push @AoA, [ 'a', 'b', 'c' ];
push @AoA, [ 'd', 'e', 'f' ];

my @AoA;
$AoA[$y][$x] = $n;

Имейте в виду, что

$AoA[$y][$x] = $n;

это сокращение от

$AoA[$y]->[$x] = $n;

и это эквивалентно следующему благодаря автовивификации:

( $AoA[$y] //= [] )->[$x] = $n;
1 голос
/ 23 декабря 2011

«Массив массивов» на самом деле не является массивом массивов.Это больше массив ссылок на массивы.Каждый элемент в базовом массиве является ссылкой на другой массив.Таким образом, когда вы хотите переключаться между элементами в базовом массиве, вы получаете ссылки на массив.Это то, что присваивается $aref в первом цикле.Затем к ним обращаются путем предварительного ожидания с символом @, поэтому @$aref - это массив, на который ссылается ссылка на массив $aref.

То же самое работает для второго цикла.$AoA[$i] является $i -ым элементом массива @AoA, который является ссылкой на массив.Отмена ссылки на него с помощью предварительного ожидания с символом @ (и добавление {} для ясности и, возможно, для приоритета) означает, что @{$AoA[$i]} это массив, на который ссылается ссылка на массив $AoA[$i].

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