Непоследовательный (глупый?) Доступ к данным в Perl 5 (также сбивает меня с толку в отношении использования сигил)
Этот вопрос о том, чтобы попросить дать некоторые объяснения тому, что происходит в системе Perl, потому что я не вижу явного смысла, хотя я кодирую уже более 25 лет. Итак, вот и история...
При попытке работать с Cyrus::IMAP::Admin
В Perl5 я пытался получить и распечатать список квот, в результате чего были возвращены несколько странно структурированные данные.
my %quotas = $client->listquota(@list[0]);
if ( $client->error ) {
printf STDERR "Error: " . $client->error . "\n";
exit 1;
}
print "root: " . $list[0] . "\n";
foreach my $quota ( keys %quotas ) {
print( $quota, " ", $quotas{$quota}[0], "/", $quotas{$quota}[1], " KiB\n" );
}
Этот код на самом деле работает как нужно, распечатывая такие вещи, как
root: user.myuser
STORAGE: 123/4567 KiB
Этот код был взят из Cyrus::IMAP::Shell
чтение похоже на это:
my %quota = $$cyrref->listquota(@nargv);
foreach my $quota (keys %quota) {
$lfh->[1]->print(" ", $quota, " ", $quota{$quota}[0], "/", $quota{$quota}[1]);
if ($quota{$quota}[1]) {
$lfh->[1]->print(" (", $quota{$quota}[0] * 100 / $quota{$quota}[1], "%)");
}
}
Этот код выглядит несколько глупо для меня для использования $quota{$quota}[0]
, В моем случае я немного переименовал переменные, чтобы отказаться от такого смешанного использования переменных с разными типами, но с одинаковыми именами.
До получения кода от Cyrus::IMAP::Admin
Я попытался понять его спецификацию и обработать результат по коду, написанному мной. Это выглядело так:
my %quotas = $client->listquota(@list[0]);
if ( $client->error ) {
printf STDERR "Error: " . $client->error . "\n";
exit 1;
}
print "root: " . $list[0] . "\n";
foreach my $quota ( keys %quotas ) {
my @sizes = @quotas{$quota};
print( $quota, " ", $sizes[0], "/", $sizes[1], "\n" );
}
Однако этот код не сработал, и я сам не нашел никакого правдоподобного объяснения. Насколько я понимаю, для переноса этого последнего примера кода в первоначально опубликованную форму потребуется заменить источник назначения в строке 11 на значения в строке 12 и изменить символ квот с @
в $
потому что я пытаюсь получить скалярный результат наконец. Этот последний код печатал ссылку на массив до слэша и ничего после него. Поэтому я должен был исправить свой код следующим образом, чтобы он заработал:
my %quotas = $client->listquota(@list[0]);
if ( $client->error ) {
printf STDERR "Error: " . $client->error . "\n";
exit 1;
}
print "root: " . $list[0] . "\n";
foreach my $quota ( keys %quotas ) {
my @sizes = @quotas{$quota};
print( $quota, " ", $sizes[0][0], "/", $sizes[0][1], "\n" );
}
Это дополнительное разыменование в строке 12 - вот что меня смущает сейчас. Почему @sizes
содержащий массив, хранящий другой массив в своем единственном первом элементе? Из-за путаницы я уже попробовал альтернативный код в строке 11 безрезультатно. Эти тесты включены
my @sizes = $quotas{$quota};
(для его эквивалентности с оригинальным кодом, размещенным выше) и
my $sizes = @quotas{$quota};
(потому что я не знаю почему). Переключение сигил здесь, похоже, не меняет семантику назначения. Но использование этого назначения, кажется, открывает другое представление о структуре данных, содержащихся в %quotas
первоначально. Какие сигилы должны иметь @sizes
соответствие содержания и структуры $quotas{$quota}
как используется в самом верхнем фрагменте кода?
2 ответа
$quotas{$quota}
обращается к одному скалярному элементу в %quotas
, @quotas{$quota}
это фрагмент хеша, который выбирает список из одного элемента из хеша.
$collection[...]
или же$collection{...}
: Единственный скалярный элементmy @array = (1, 2, 3, 4); my $elem = $array[1]; #=> 2
@collection[...]
или же@collection{...}
: список элементовmy @array = (1, 2, 3, 4); my @slice = @array[1, 3]; #=> (2, 4)
%collection[...]
или же%collection{...}
: список значений ключа, пока недоступен, кроме "blead".my @array = (1, 2, 3, 4); my %kvs = %array[1, 3]; #=> (1 => 2, 3 => 4)
Использование другого символа при ссылке на элемент в коллекции не разыменовывает элемент!
Теперь, что делает или одноэлементный список @quotas{$quota}
содержат? Это ссылка на массив [...]
,
Когда вы назначаете это скаляру, список оценивается в скалярном контексте и дает последний элемент:
my $sizes = @quotas{$quota}; my $sizes = ([...]); my $sizes = [...]; [...]
Доступ к элементу в этой ссылке на массив выглядит так
$sizes->[0]
,Когда вы присваиваете это массиву, вы создаете массив, который содержит в качестве одного элемента ссылку на этот массив:
my @sizes = @quotas{$quota}; my @sizes = ([...]); ([...])
Доступ к элементу в этой ссылке на массив выглядит так
$sizes[0][0]
потому что сначала вы должны получить ссылку на этот массив внутри массива@sizes
,
... и, кстати, то же самое происходит здесь, когда вы делаете $quotas{$quota}
, но по несколько иным причинам.
Если вы хотите разыменовать ссылку на массив, используйте фигурные скобки:
my @foo = @{$array_refernce}
который копирует содержимоеmy $elem = ${$array_reference}[0]
который обращается к элементу, так же, как$array_reference->[0]
,
Так что вы могли бы сделать
my @sizes = @{ $quotas{$quota} };
разыменовать массив и скопировать его в переменную массива. Затем вы можете получить доступ к элементу, как $sizes[0]
,
Я полагаю, что вы хотите это в своей строке 11:
my @sizes = @{ $quotas{$quota} };
Кроме того, рекомендуем вам начать использовать Data::Dumper везде.
Например
use Data::Dumper;
print 'Data structure of \%quotas: ' . Dumper(\%quotas) . qq(\n);
Таким образом, вы можете быть уверены, с какой структурой вы имеете дело.