http://www.perlmonks.org?node_id=1008163

Redei has asked for the wisdom of the Perl Monks concerning the following question:

I'm puzzling over the use of map with user-defined functions. Here's some sample code:
sub my_uc { my $c = shift; return uc($c); } my @a = ('a'..'c'); @a = map { uc($_) } @a; print "==>@a<== { uc(\$_) }\n"; @a = map { my_uc($_) } @a; print "==>@a<== { my_uc(\$_) }\n"; @a = map uc, @a; print "==>@a<== uc,\n"; @a = map my_uc, @a; print "==>@a<== my_uc,\n";
The output is:
==>A B C<== { uc($_) } ==>A B C<== { my_uc($_) } ==>A B C<== uc, ==> <== my_uc,
Apparently Perl doesn't support the syntax map function, array for user-defined functions. Or perhaps with this syntax it expects to find the function's argument in $_. So here's my second try:
sub my_new_uc { my $c = shift // $_; return uc($c); } sub my_uc { my $c = shift; return uc($c); } my @a = ('a'..'c'); @a = map my_new_uc, @a; print "==>@a<== my_new_uc,\n"; @a = map my_uc, @a; print "==>@a<== my_uc,\n"; @a = map my_new_uc, @a; print "==>@a<== my_new_uc,\n";
Surprisingly, my new function sometimes works, and sometimes not. Here's the output from that second code fragment:
==>A B C<== my_new_uc, ==> <== my_uc, ==> <== my_new_uc,
The information on map in perlfunc doesn't say anything about using your own functions except in the form map { function($_) } @array and I couldn't find any help in other tutorials or FAQs. Does anyone know what is happening in my second example, and why $_ sometimes is used in map and sometimes not?

Replies are listed 'Best First'.
Re: Problems with map(function, array)
by tobyink (Canon) on Dec 10, 2012 at 22:41 UTC

    Nothing to do with map really. uc has magic!

    In the following construct, uc is getting called with no parameters:

    my @caps = map uc, @lower;

    As per perlfunc, when called with no parameters, uc operates on $_. So you need to make your function act the same (as indeed you did in your second experiment).

    From Perl 5.10 and above, there's a better technique to write such functions:

    sub my_uc (_) { # note underscore in parentheses my $c = shift; return uc($c); }

    And now my_uc has the same magic as uc.

    perl -E'sub Monkey::do{say$_,for@_,do{($monkey=[caller(0)]->[3])=~s{::}{ }and$monkey}}"Monkey say"->Monkey::do'

      Thank you very much. That is just what I needed. Now, instead of

      sub twice { 2 * (shift // $_) }

      I'll use something like

      sub twice(_) { 2 * shift }

Re: Problems with map(function, array)
by LanX (Saint) on Dec 10, 2012 at 21:21 UTC
    works for me:

    DB<121> sub add { $_ + $_[0] } DB<122> map add(2), 1..3 => (3, 4, 5)

    > Surprisingly, my new function sometimes works, and sometimes not. Here's the output from that second code fragment:

    No surprise, you're deleting @a in the process, please repeat your experiments always with freshly reinitialized input.

    Cheers Rolf

      You're right when you say I should have used freshly reinitialised input. But where does map destroy the input array?

      EDIT: Okay, I've got it now. Thanks for your help.
        The array is not exactly deleted, but the elements are reduced to empty strings:

        DB<139> sub my_uc { my $c = shift; return uc($c); } DB<140> my_uc() DB<141> @a = map my_uc, a..c => ("", "", "")

        keep in mind no argument means $c is undef!

        Cheers Rolf

      My problem is what happens when I don't use $_:
      sub twice { 2 * $_[0] } @a = map twice, 1..3; print "@a\n"; ==> 0 0 0

        Your original code with strict and warnings:

        use strict; use warnings; sub twice { 2 * $_[0] } my @a = map twice, 1..3; print "@a\n";
        gives the warning "Use of uninitialized value $_[0] in multiplication (*) at map1.pl line 3".

        This is how it should be written:

        use strict; use warnings; sub twice { 2 * $_[0] } my @a = map twice($_), 1..3; print "@a\n";
        which produces 2 4 6 as expected.

        BTW, Conway, in Perl Best Practices, chapter 8, recommends always using the block form of map, not the expression form that you are using. The expression form might be faster than the block form though (if that matters, you should benchmark it).

        the LIST elements are ALWAYS passed via $_ per iteration.

        you call twice w/o parameters so $_[0] is undef and undef*2 == 0!

        take again a look at my "add"-example to distiguish between parameters and iteration value

        Cheers Rolf