After split
, I get an @digits
array with values of 1 2 4 8 16 32 64
. However, I cannot understand the mechanism of its processing in the block if ($digits [-1] == 4) {}
. After which, only 4 6 4
values remain in the same array. Yes, I see that this code works correctly, but how are unnecessary values remove from the array without processing an it?
my @input_numbers = qw(1 2 4 8 16 32 64);
my @result = map {
my @digits = split //, $_;
if ($digits[-1] == 4) {
@digits;
} else {
( );
}
} @input_numbers;
print join(" ", @result); # 4 6 4
CodePudding user response:
A map works by taking one element of its input list at a time, running the code in the block with that element in $_
, and returning whatever the last evaluated statement (which returns) does. These returned values, after processing all elements of the input list, comprise its output list.
So the @digits
in your block is at every iteration assigned (strings with) the digits of the currently processed element, not all digits of all elements.† They are then either returned in a list, or an empty list is returned which thus "disappears" in the overall return list.‡
Since you return the list with these digits from the block when the last one is 4
, then when your map
block processes 4
(third element in the array) it returns 4
, and when it processes 64
it returns the list with its digits, 6
and 4
. Altogether 4 6 4
.
† It may also help realizing that the block in the map { ... } LIST
syntax stands for a subroutine with such a body, which is executed for every element. This is afforded by use of prototypes. Note that the prototypes are rightly advised against in casual use but for this they are needed.
This cannot be fully implemented in pure Perl, but here is a simple example
sub a_bit_like_map :prototype(&$) {
my ($code, @in) = @_;
my @res;
foreach my $el (@in) {
local $_ = $el;
push @res, $code->($el);
}
return @res
}
Now this can be called as either of
my @mapped = a_bit_like_map( sub { ... }, LIST );
my @mapped = a_bit_like_map { ... } LIST;
This :prototype
attribute is necessary in newer Perls, and in which signatures are enabled, since without it the syntax would collide with that of signatures.
With older Perls you'll see them used as sub func(&@) { ... }
.
‡ A way to use map
as a filter, too
CodePudding user response:
There are two things going on here. map
evaluates its code in list context.
Second, a block returns its last evaluated expression.
In your map
, the last evaluated expression comes from either the if
or else
blocks because there are no other statements past that structure. If the if
case, it returns @digits
, which adds one or more new items to map
's output list.
In the else
case, the last evaluated expression is ( )
, which is the empty list. This adds no new elements to the output list, thus "excluding" the results from that particular input element.
I'd probably write this task as a list pipeline. The grep
selects the elements that we know we want to process, then the map
processes everything it gets:
my @result = map { split // } grep { /4\z/ } @input_numbers;