Beefy Boxes and Bandwidth Generously Provided by pair Networks
Keep It Simple, Stupid
 
PerlMonks  

[Solved] How does map work?

by three18ti (Monk)
on Oct 26, 2013 at 20:07 UTC ( [id://1059839]=perlquestion: print w/replies, xml ) Need Help??

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

Hello Monks,

I'm reading Higher Order Perl and one of the examples is an "imap" function which acts like map only it takes a block and an iterator.

sub imap { my ($transform, $it) = @_; return Iterator { my $next = NEXTVAL($it); return unless defined $next; return $transform->($next); } }

That code may as well be in greek because it makes no sense to me. So I figured I'd try to write my own map function to see how it works.

#!/usr/bin/env perl use 5.010; use strict; use warnings; sub foo(&@) { my $coderef = shift; my @params = @_; my @ret; while(@params) { push @ret, $coderef->($_); } return @ret; } my @a = ( "1", "2", "3", "4", ); my @b = foo { $_ ** 2 } @a;

But that produces the error "Use of uninitialized value $_ in exponentiation (**) at coderef.pl line 19."

I know when I pass parameters to a subroutine or closure they are passed in in @_, so I tried to shift the parameter off @_, but that produces the same error (based on my understanding of perl, this second attempt should work... "Should" is funny word).

my @b = foo { $_ = shift; $_ ** 2 } @a; my @b = foo { my $var = shift; $var ** 2 } @a;

What gives? How does map/grep work it's magic? And why doesn't my attempt work? I realize that all of the useful map functions have probably already been written, so this is more of an exercise in understanding, how could I write my own map function?

Thanks Monks!

Edit: Seems my problem was with my use of while instead of for, I thought while set $_, but I guess not.

Here's the version that works:

#!/usr/bin/env perl use 5.010; use strict; use warnings; sub foo(&@) { my $coderef = shift; my @params = @_; my @ret; for(@params) { push @ret, $coderef->(); } return @ret; } my @a = ( "1", "2", "3", "4", ); my @b = foo { $_ ** 2 } @a;

Replies are listed 'Best First'.
Re: How does map work?
by BrowserUk (Patriarch) on Oct 26, 2013 at 20:25 UTC
    But that produces the error "Use of uninitialized value $_ in exponentiation (**) at coderef.pl line 19."

    The problem is your use of $_.

    You pass it to the coderef as an argument, but you never set it!

    And within the coderef -- ie. subroutine -- the first argument passed is aliased to $_[0] as with all subroutines.

    To be able to use $_ within the coderef, you need to set it before calling the block. Try:

    sub foo(&@) { my $coderef = shift; my @ret; for( @_ ) { ## let the for loop do the localisation and aliasing push @ret, $coderef->(); ## No arg } return @ret; }

    With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.
      Awesome! Thanks! using for instead of while definitely solved my problem.
Re: How does map work?
by LanX (Saint) on Oct 26, 2013 at 20:21 UTC
    $_ does not belong to @_, you have to set this global variable explicitly and not pass it.

    Eg by changing while to for (another bug in your code btw is to think that while automatically sets $_)

    Im pretty sure HOP already shows how to emulate map! Sorry no code am typing on my mobile... :-)

    Cheers Rolf

    ( addicted to the Perl Programming Language)

      thanks for the assist. Indeed my use of while was incorrect

      Indeed HOP does doe a version of map that takes an iterator, and I'm sure it will just take plugging away at HOP some more, it starts to make sense after a few read throughs...

        Hmm seems like HOP doesn't show how to reimplement plain map.

        But as a side note, perlsub has an example for grep :

        And here’s a reimplementation of the Perl "grep" operator: sub mygrep (&@) { my $code = shift; my @result; foreach $_ (@_) { push(@result, $_) if &$code; } @result; }

        HTH!

        Cheers Rolf

        ( addicted to the Perl Programming Language)

        There is one instance where while sets $_ (or at the very least has the appearance to do so), and that's in the special while( <$filehandle> ) {...} construction (or ... while <$filehandle> for that matter, I believe).

Re: [Solved] How does map work?
by Laurent_R (Canon) on Oct 26, 2013 at 21:47 UTC

    Although the code below is quite similar to what BrowserUk has posted, I post here two versions of the "my_map" function that I wrote a couple of weeks ago for a tutorial that I am in the course of writing in French on functional programming in Perl. The first one works similarly to Perl's map function in the sense that if the code block modifies $_, then the original array is modified:

    sub my_map (&@){ my $code_ref = shift; my @d ; push @d, $code_ref->($_) for @_; return @d; }

    The second one is more like a pure functional version of map having no side-effect on the original array:

    sub my_map (&@){ my $code_ref = shift; my @d = @_; $_ = $code_ref->($_) for @d; return @d; }
      may I ask, why you pass $_ as argument?

      Cheers Rolf

      ( addicted to the Perl Programming Language)

        Well, yes, you are right, this is not needed. But this was an example for a tutorial, I wanted it to be explicit to explain clearly what is going on. Thank you for your remark, I need to add at least one example without $_ in my tutorial.

Re: [Solved] How does map work?
by Bloodnok (Vicar) on Oct 28, 2013 at 11:48 UTC
    Hmm,

    Altho' perfectly correct, methinx that the simple for loop construct you use is more normally written as

    push @ret, $coderef->() for @params;

    In fact, simpler still would be use the incoming arg list directly and to use parens for the non-core call to foo (as per PBP) ...

    #!/usr/bin/env perl use 5.010; use strict; use warnings; sub foo(&@) { my $coderef = shift; my @ret; push @ret, $coderef->() for @_; return @ret; } my @a = ( "1", "2", "3", "4", ); my @b = foo( sub { $_ ** 2 }, @a);
    Yet another product of an idle moment ...

    A user level that continues to overstate my experience :-))

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://1059839]
Front-paged by ww
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others about the Monastery: (7)
As of 2024-03-19 11:48 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found