http://qs321.pair.com?node_id=444830


in reply to Turning foreach into map?

I have never used map before...

Once you start, you won't stop! You'll wonder how you could program without it.

...and I have read the map function page on http://perldoc.perl.org/functions/map.html, but I don't seem to quite understand.

As a first approximation, you can imagine map as being defined something like this:

sub my_map { my $sub = shift; my @out; for my $element ( @_ ) { push @out, $sub->( $element ); } return @out; }
This function my_map takes a code ref as its first argument, and returns the array consisting of applying that code ref to each one of the remaining arguments. Very useful! When programming we often want all the elements of some array, but transformed or operated on in some way. my_map (just like map) abstracts the essentials of this procedure, so that the only variables left are the actual input array and the operation that you want performed on each element.

You would use my_map like this:

sub square { $_[0] * $_[0] } my @squares = my_map( \&square, 1..3 ); # @squares is now ( 1, 4, 9 )
or if you don't want to bother defining a sub just to feed it to my_map, you can give it a code ref written just for the ocassion:
my @squares = my_map( sub { $_[0]*$_[0] }, 1..3 );
Same thing. In fact, if you have arranged your definitions so that the compiler processes it before it processes the first call to it, you can toss the parens and write
my @squares = my_map sub { $_[0]*$_[0] }, 1..3;
which is beginning to resemble the way one uses Perl's map. Since this is such a handy thing to do, for its map Perl provides some "syntactic sugar" to simplify the writing of such expressions, so that for example you don't need to bother writing "sub" before the code ref. Also, in Perl's map, the code ref doesn't get its argument passed through its @_ array like is the case with my_map's $sub; instead map's the code ref picks up its argument from $_. In other words, if we wanted my_map to mimic Perl's map more closely, we could define it like this:
sub my_map { my $sub = shift; my @out; for ( @_ ) { push @out, $sub->(); # $sub will get its arg from $_ } return @out; }
The only difference between this and the previous definition of my_map is in the first and second lines of the loop: 1) the loop variable is no longer $element, but $_; and 2) $sub is now called without arguments, since it will read its arguments from $_. Now we can write
my @squares = my_map sub { $_ * $_ }, 1..3;
and that's starting to look a lot like the standard
my @squares = map { $_ * $_ } 1..3; # BLOCK form of map

Now, watch this. If you defined a function that operated on $_, e.g.

sub twice { $_ * 2 }
you could just feed it to Perl's map as its first argument:
my @evens = map &twice, 1..10; # EXPR form of map
and you could also feed it to (the second version of) my_map
my @evens = my_map \&twice, 1..10;
Notice that now the only difference that remains between the syntax of the two calls is that with my_map you need to explicitly take the reference to the sub you are passing as the first argument, hence the extra backslash. Can we get rid of this difference too? Yes, by using prototypes. I for one never use prototypes, so I'm not up on all their ins and outs, but for the sake of this illustration
sub my_map (\&@_) { my $sub = shift; my @out; push @out, $sub->() for @_; return @out; }
(Note, aside from the prototype, there is no significant difference between this definition of my_map and the previous one; I just condensed it a little bit.) Now you can write,
my @evens = my_map &twice, 1..10;
OK, this last version of my_map does a limited impersonation of the "expression form" of map. A slight variant could be used to implement an impersonation of the "block form" of map; the only difference is in the prototype, but just to be clear, here it is in full:
sub my_map (&@) { my $sub = shift; my @out; push @out, $sub->() for @_; return @out; }
With this final version of my_map you can write:
my @evens = my_map { $_ * 2 } 1..3;
Note that even though we are using an anonymous code ref, the sub keyword is gone. This is syntactically identical to the "block form" of map.

Alas, my_map is not as versatile as map. It can't do some things that map can do, like this:

my @evens = my_map $_ * 2, 1..10; # splat!
and it can't mimic both the "expression form" and the "block form" of map. But I think that by studying my_map you'll get a pretty good feel for what map is doing. That's the important thing; the other stuff with tweaking the definition of my_map so it's usage better resembles that of map is a bit of a distraction. The important thing is to understand that map takes a function as its first argument, and returns the array obtained from applying that function to the rest of its arguments.

OK, since I've made this far, I might as well tell you, if not the whole story, at least all that I know about map, and the only thing that's left is that both map and my_map have the potential to change their inputs. If, for example, you did this (using the last version of my_map):

my @ints = 1..3; my @evens = my_map { $_ *= 2 } @ints; print "@evens\n"; print "@ints\n"; __END__ 2 3 6 2 3 6
...the input array @ints has been modified by my_map. Same thing with Perl's map, so watch out.

Last thing: you could play the same game with grep:

sub my_grep (&@) { my $sub = shift; my @out; $sub->() && push @out, $_ for @_; return @out; }

the lowliest monk