Home > Back-end >  Please explain to me how map works in this case
Please explain to me how map works in this case

Time:08-16

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;
  •  Tags:  
  • perl
  • Related