Perl:映射到列表的第一个元素

ind*_*ess 7 arrays perl hash undef map

任务:使用map构建哈希,其中keys是给定数组@a的元素,值是某些函数f($ element_of_a)返回的列表的第一个元素:

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

好的,直到f()返回一个空列表(对f()来说绝对正确,在这种情况下我想分配undef).可以使用以下代码重现该错误:

my %h = map {$_ => ()[0]} @a;
Run Code Online (Sandbox Code Playgroud)

错误本身听起来像"散列分配中奇数个元素".当我重写代码时:

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

要么

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

Perl根本没有抱怨.

那么我该如何解决这个问题 - 当返回的列表为空时,获取f()返回的列表的第一个元素?

Perl版本是5.12.3

谢谢.

rua*_*akh 7

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

my @arr = ()[0];
my $size = @arr;
print "$size\n";
Run Code Online (Sandbox Code Playgroud)

打印0.所以$_ => ()[0]大致相当于$_.

要修复它,您可以使用该scalar函数强制标量上下文:

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

或者您可以undef在列表的末尾附加一个明确的:

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

或者你可以将函数的返回值包装在一个真正的数组中(而不仅仅是一个平面列表):

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

(我个人最喜欢最后一个选项.)


在"切片"perldata下的"切片"下记录了一个空列表切片的特殊行为:

一个空列表的片仍然是一个空列表.[...]这使得编写在返回空列表时终止的循环变得容易:

while ( ($home, $user) = (getpwent)[7,0]) {
    printf "%-8s %s\n", $user, $home;
}
Run Code Online (Sandbox Code Playgroud)

  • @Zaid:我们没有足够的信息可以肯定地说,但我倾向于不同意.`f`旨在返回值列表; 大概是这行代码丢弃了除第一个值之外的所有代码,是异常而不是规则,如果对`f`的每次其他调用必须明确检查它返回`undef`的情况,可能会导致维护问题.并将其转换回空列表.(注意,OP写道"它绝对正确""f()[to]返回[]一个空列表".这意味着`f`目前有一个有意义的,有凝聚力的定义.) (2认同)