Почему эта ссылка не работает как одна строка? - PullRequest
1 голос
/ 14 января 2020

Дано:

my @list1 = ('a');
my @list2 = ('b');
my @list0 = ( \@list1, \@list2 );

, затем

my @listRef = $list0[1]; 
my @list    = @$listRef;    # works

, но

my @list    = @$($list0[1]); # gives an error message

Я не могу понять, почему. Чего мне не хватает?

Ответы [ 5 ]

5 голосов
/ 14 января 2020

Существует одно простое правило разыменования, которое охватывает это. Слабое выражение:

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

Специфический c case от perlreftut

Вы всегда можете использовать ссылку на массив в фигурных скобках вместо имени массива. [...]

Тогда в вашем случае это должно быть

my @list = @{ $list0[1] };

(не index [2], поскольку в вашем @list0 есть два элемента). для удобства чтения.

Попытка @$($list0[2]) является синтаксической ошибкой, во-первых, потому что ( (после $) не допускается в идентификаторе (имя переменной), что, вероятно, следует за $.

Блок {}, хотя разрешен после $ и будет оценен, и должен давать скалярную ссылку в этом случае, чтобы быть разыменованной этим $ перед этого; но тогда первый @ будет ошибочным. Это становится грязным, если толкнуть. Хотя точные правила (все еще) немного неясны, см. Анализ идентификаторов в perldata .

Ранее @$listRef в целом является правильным синтаксисом. Но это относится к скалярной переменной $listRef (которая лучше должна быть ссылкой на массив, поскольку она там разыменовывается в массив), и в этом примере такого нет - у вас есть переменная массива @listRef.

Таким образом, при действующем use strict; компиляция также не удастся выполнить.


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

@$ar;            # list of elements
$ar->[$index];   # specific element
@$ar[@indices];  # slice -- list of some elements, like @$ar[0,2..5,-1]
$ar->@[0,-1];    # slice, with new "postfix dereferencing" (stable at v5.24)
$#$ar;           # last index in the anonymous array referred by $ar

См. Ломтики в perldata и Postfix ссылка нарезки в perlref

2 голосов
/ 15 января 2020

Вам нужно

@{ $list0[1] }

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

@NAME    # If you have the name
@BLOCK   # If you have a reference

Это означает, что

my @array1 = 4..5;
my @array2 = @array1;

и

my $array1 = [ 4..5 ];
my @array2 = @{ $array1 }

эквивалентны.

Когда единственная вещь в блоке - это простой скаляр ($NAME или $BLOCK), вы можете опустить curlies. Это означает, что

@{ $array1 }

эквивалентно

@$array1

Вот почему @$listRef работает, и поэтому @{ $list0[1] } нельзя упростить.


См. Perl Синтаксис разыменования .

2 голосов
/ 14 января 2020

У вас много чего происходит и несколько уровней непреднамеренных ссылок, поэтому давайте go пройдем через него:

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

my @list0 = ( \@list2, \@list2 );

Затем вы запрашиваете элемент с индексом 2, который является отдельным элементом, и сохраняете это в массиве:

my @listRef = $list0[2];

Однако, нет элемента с индексом 2, потому что Perl индексируется с нуля. Значение в @listRef не определено. Кроме того, вы запросили один элемент и сохранили его в массиве вместо скаляра. Это, вероятно, не то, что вы имели в виду.

Вы говорите, что следующая строка работает, но я не думаю, что вы знаете это, потому что она не даст вам ожидаемого значения, даже если вы не получили ошибку , Что-то еще происходит. Вы не объявили или не использовали переменную $listRef, поэтому Perl создает ее для вас и присваивает ей значение undef. Когда вы пытаетесь разыменовать его, Perl использует «автовивификацию» для создания ссылки. Это процесс, в котором Perl помогает создать справочную структуру для вас, если вы начинаете с undef:

my @list    = @$listRef;    # works

В этом массиве нет ничего, поэтому @list должно быть пустым.

Исправьте это, чтобы получить последний элемент с индексом 1, и исправьте его так, чтобы вы присваивали единственное значение (ссылку) скалярной переменной:

my $listRef = $list0[1];

Data :: Dumper удобен здесь:

use Data::Dumper;

my @list2 = qw(a b c);
my @list0 = ( \@list2, \@list2 );

my $listRef = $list0[1];

print Dumper($listRef);

Вы получите вывод:

$VAR1 = [
          'a',
          'b',
          'c'
        ];

Perl имеет некоторые функции, которые могут отлавливать такие ошибки именования переменных и помогут вам отследить проблемы. Добавьте это в начало вашей программы:

use strict;
use warnings;

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

И последние Perls имеют новую функцию, называемую разыменование постфикса , которая позволяет писать разыменования слева направо:

my @items = ( \@list2, \@list2 );

my @items_of_last_ref = $items[1]->@*;
1 голос
/ 14 января 2020

Вопрос не полный и неясен желаемый результат.

OP пытается получить доступ к элементу $list0[2] массива @list0, который не существует - массив имеет элементы с индексами 0 и 1.

Возможно @listRef должно быть $listRef вместо этого в посте.

Ниже мое видение описанной проблемы

#!/usr/bin/perl

use strict;
use warnings;

use feature 'say';

my @list1 = qw/word1 word2 word3 word4/;
my @list2 = 1000..1004;

my @list0 = (\@list1, \@list2);

my $ref_array = $list0[0];

map{ say } @{$ref_array};

$ref_array = $list0[1];

map{ say } @{$ref_array};

say "Element: " . @{$ref_array}[2];

output

word1
word2
word3
word4
1000
1001
1002
1003
1004
Element: 1002
0 голосов
/ 14 января 2020
my @list    = @$@listRef;    # works

Сомневаюсь, что работает. Это может не привести к синтаксической ошибке, но это точно так же, как ад делает не то, что вы думаете. На этот раз

my @list0 = ( \@list2, \@list2 );

определяет массив с 2 элементами, и вы получаете доступ к

my @listRef = $list0[2];

третьему элементу. Таким образом, @listRef - это массив, содержащий один элемент undef. Следующий код также не имеет смысла.

Если вопрос не является чисто академическим c (уже ответил zdim), я предполагаю, что вы хотите, чтобы второй элемент @list был выделен в отдельный массив, я бы написать

my @list = @{ $list0[1] };
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...