Непоследовательный (глупый?) Доступ к данным в 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);

Таким образом, вы можете быть уверены, с какой структурой вы имеете дело.

Другие вопросы по тегам