I'm having difficulty understanding the behaviour of the map function in the Perl script below
my @list = qw/A C T G/;
my @curr = (0, undef, undef, 1);
my @res = map { $list[ $_ ] } @curr;
print @res; #prints AAAC
The value of @res
is AAAC
. I had expected it be just AC
as the 2 middle values of @curr
are undef
.
My understanding of map is that each item from the list becomes the value of $_
in turn. So I can understand how $list[0]
returns A and how $list[1]
returns C. I can't understand why/how the undef
values have returned A? To my mind $list[undef]
would not be a value?
I'm missing something obvious here probably, but would be really grateful for some help
The script that I take this code from is below. I'm stepping through it in the debugger, and I can see this behaviour in the last line returned by sub gen_permutate
my @DNA = qw/A C T G/;
my $seq = gen_permutate(14, @DNA);
while ( my $strand = $seq->() ) {
print "$strand\n";
}
sub gen_permutate {
my ($max, @list) = @_;
my @curr;
return sub {
if ( (join '', map { $list[ $_ ] } @curr) eq $list[ -1 ] x @curr ) {
@curr = (0) x (@curr + 1);
else {
my $pos = @curr;
while ( --$pos > -1 ) {
++$curr[ $pos ], last if $curr[ $pos ] < $#list;
$curr[ $pos ] = 0;
}
}
return undef if @curr > $max;
return join '', map { $list[ $_ ] } @curr;
};
}
J
This is a part of "DWIM" -- an array index need be a number, so what is passed in is converted to one, as best as the interpreter can do/guess. In case of undef
at least it's clear -- becomes a 0
.
But it's really a programming error, or at least sloppiness.
To do what you expect:
my @res = map { $list[ $_ ] } grep { defined } @curr;
or, in one iteration over the list
my @res = map { defined $_ ? $list[$_] : () } @curr;
Here the empty list, indicated by ()
, gets flattened into the return list, thus disappearing; so we filter inside map
as well. But the point of that ternary is to be able to put something meaningful instead, and if some input need be just removed then having an explicit grep
first is clearer.
NOTE With use warnings;
in place you'd hear all about it
Use of uninitialized value $_ in array element at ...
(and it would convert it to a number)
Each script really must begin with use warnings;
(and use strict;
). Sometimes the biggest part in inheriting older scripts is to stick use warnings;
on top right away (and work through the screenfulls of messages :(.