Perl: отображение на первый элемент списков - PullRequest
7 голосов
/ 20 января 2012

Задача: создать хеш с использованием map, где ключами являются элементы данного массива @a, а значениями являются first элементы списка, возвращаемого некоторой функцией f ($ element_of_a):

my @a = (1, 2, 3);
my %h = map {$_ => (f($_))[0]} @a;

Все в порядке, пока f () не вернет пустой список (это абсолютно правильно для f (), и в этом случае я бы хотел назначить undef).Ошибка может быть воспроизведена с помощью следующего кода:

my %h = map {$_ => ()[0]} @a;

сама ошибка звучит как «Нечетное количество элементов в присвоении хэша».Когда я переписываю код таким образом:

my @a = (1, 2, 3);
my $s = ()[0];
my %h = map {$_ => $s} @a;

или

my @a = (1, 2, 3);
my %h = map {$_ => undef} @a;

Perl вообще не жалуется.

Так как мне решить эту проблему - получить первые элементысписка, возвращаемого функцией f (), когда возвращаемый список пуст?

Версия Perl 5.12.3

Спасибо.

Ответы [ 3 ]

7 голосов
/ 20 января 2012

Я только что немного поиграл, и кажется, что ()[0] в контексте списка интерпретируется как пустой список, а не как undef скаляр. Например, это:

my @arr = ()[0];
my $size = @arr;
print "$size\n";

отпечатков 0. Так что $_ => ()[0] примерно эквивалентно просто $_.

Чтобы исправить это, вы можете использовать функцию scalar для принудительного скалярного контекста:

my %h = map {$_ => scalar((f($_))[0])} @a;

или вы можете добавить явный undef в конец списка:

my %h = map {$_ => (f($_), undef)[0]} @a;

или вы можете заключить возвращаемое значение вашей функции в истинный массив (а не просто в плоский список):

my %h = map {$_ => [f($_)]->[0]} @a;

(лично мне больше нравится этот последний вариант.)


Особое поведение фрагмента пустого списка описано в разделе «Срезы» в perldata:

Часть пустого списка все еще является пустым списком. […] Это облегчает написание циклов, которые завершаются, когда возвращается нулевой список:

while ( ($home, $user) = (getpwent)[7,0]) {
    printf "%-8s %s\n", $user, $home;
}
0 голосов
/ 21 января 2012

Во-первых, большое спасибо всем ответчикам!Теперь я чувствую, что должен предоставить реальные детали реальной задачи.

Я анализирую XML-файл, содержащий набор элементов, каждый из которых выглядит следующим образом:

<element>
    <attr_1>value_1</attr_1>
    <attr_2>value_2</attr_2>
    <attr_3></attr_3>
</element>

MyЦель состоит в том, чтобы создать Perl-хеш для элемента, который содержит следующие ключи и значения:

('attr_1' => 'value_1',
 'attr_2' => 'value_2',
 'attr_3' =>  undef)

Давайте подробнее рассмотрим элемент <attr_1>.Модуль XML::DOM::Parser CPAN, который я использую для разбора, создает для них объект класса XML::DOM::Element, давайте дадим ему имя $attr для справки.Имя элемента легко получить с помощью $attr->getNodeName, но для доступа к тексту, заключенному в теги <attr_1>, нужно сначала получить все дочерние элементы <attr_1>:

my @child_ref = $attr->getChildNodes;

For <attr_1> и <attr_2> elements ->getChildNodes возвращает список, содержащий ровно одну ссылку (на объект класса XML::DOM::Text), в то время как для <attr_3> он возвращает пустой список.Для <attr_1> и <attr_2> я должен получить значение на $child_ref[0]->getNodeValue, а для <attr_3> я должен поместить undef в результирующий хеш, поскольку там нет текстовых элементов.

Итак, вы видите, что f Реализация функции (метод ->getChildNodes в реальной жизни) не может контролироваться :-) Полученный код, который я написал (подпрограмма снабжена списком XML::DOM::Element ссылок для элементов <attr_1>, <attr_2><attr_3>):

sub attrs_hash(@)
{
    my @keys = map {$_->getNodeName} @_;  # got ('attr_1', 'attr_2', 'attr_3')
    my @child_refs = map {[$_->getChildNodes]} @_;  # got 3 refs to list of XML::DOM::Text objects
    my @values = map {@$_ ? $_->[0]->getNodeValue : undef} @child_refs;  # got ('value_1', 'value_2', undef)

    my %hash;
    @hash{@keys} = @values;

    %hash;
}
0 голосов
/ 21 января 2012

Я второе предложение Джонатана Леффлера - лучшее, что можно сделать, это решить проблему с корнем, если это вообще возможно:

sub f {

    # ... process @result

    return @result ? $result[0] : undef ;
}

Явное undef необходимо для решения проблемы пустого списка.

...