7

任务:使用 map 构建哈希,其中键是给定数组 @a 的元素,值是某个函数 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

谢谢。

4

3 回答 3

7

我刚刚玩了一下,似乎()[0]在列表上下文中,被解释为一个空列表而不是一个undef标量。例如,这个:

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

打印0。so$_ => ()[0]大致相当于 just $_

要修复它,您可以使用该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;
}
于 2012-01-20T18:16:34.143 回答
0

我赞同 Jonathan Leffler 的建议——如果可能的话,最好的办法是从根本上解决问题:

sub f {

    # ... process @result

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

undef要规避空列表问题,显式是必要的。

于 2012-01-20T21:55:14.430 回答
0

首先,非常感谢所有回复者!现在我觉得我应该提供实际任务的实际细节。

我正在解析一个包含一组元素的 XML 文件,每个看起来像这样:

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

我的目标是为包含以下键和值的元素创建 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>and<attr_2>元素->getChildNodes返回一个包含一个引用(对XML::DOM::Text类对象)的列表,而 for<attr_3>它返回一个空列表。对于<attr_1>and<attr_2>我应该通过$child_ref[0]->getNodeValue, 而对于<attr_3>我应该undef放入结果哈希中,因为那里没有文本元素。

因此,您会看到无法控制f函数(现实生活中的方法)的实现 :-) 我编写的结果代码是(为子例程提供了元素、和的引用列表):->getChildNodesXML::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;
}
于 2012-01-21T18:48:21.353 回答