Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Perl: mapping to lists' first element

Task: to build hash using map, where keys are the elements of the given array @a, and values are the first elements of the list returned by some function f($element_of_a):

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

All the okay until f() returns an empty list (that's absolutely correct for f(), and in that case I'd like to assign undef). The error could be reproduced with the following code:

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

the error itself sounds like "Odd number of elements in hash assignment". When I rewrite the code such that:

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

or

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

Perl does not complain at all.

So how should I resolve this — get first elements of list returned by f(), when the returned list is empty?

Perl version is 5.12.3

Thanks.

like image 806
indexless Avatar asked Jan 20 '12 18:01

indexless


People also ask

How do I get the first element of a list in Perl?

A list is a collection of scalar values. We can access the elements of a list using indexes. Index starts with 0 (0th index refers to the first element of the list).

How do I get the last element of an array in Perl?

Perl provides a shorter syntax for accessing the last element of an array: negative indexing. Negative indices track the array from the end, so -1 refers to the last element, -2 the second to last element and so on.

How do I declare an array in Perl?

Array Creation: In Perl programming every array variable is declared using “@” sign before the variable's name. A single array can also store elements of multiple datatypes.


1 Answers

I've just played around a bit, and it seems that ()[0], in list context, is interpreted as an empty list rather than as an undef scalar. For example, this:

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

prints 0. So $_ => ()[0] is roughly equivalent to just $_.

To fix it, you can use the scalar function to force scalar context:

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

or you can append an explicit undef to the end of the list:

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

or you can wrap your function's return value in a true array (rather than just a flat list):

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

(I like that last option best, personally.)


The special behavior of a slice of an empty list is documented under “Slices” in perldata:

A slice of an empty list is still an empty list. […] This makes it easy to write loops that terminate when a null list is returned:

while ( ($home, $user) = (getpwent)[7,0]) {
    printf "%-8s %s\n", $user, $home;
}
like image 197
ruakh Avatar answered Sep 20 '22 01:09

ruakh