fREWdiculous!
15 Mar
Hopefully everyone reading this blog knows the function map. Map maps one array onto another with a simple function. For example, if I had a list of names at my old school and I wanted a list of emails I could do something like this:
1 2 | my @names = ('frew schmidt', 'bob barr', ); # etc... my @emails = map { s/\s+//; "$_\@letu.edu" } @names; |
I think that’s pretty great. I thought it would be cool to reinvent the wheel and implement map in Perl 6. One of them will be implemented the typical way and the other will be the Perl 6 (as far as I can tell) way.
Here’s the obvious way:
1 2 3 4 5 6 7 8 9 10 | sub map1(Code $fn, @list) { my @new_list; for @list { @new_list.push($fn($_)); } return @new_list; } map1({ $_ * 2 },[ 1,2,3,4,5 ]).perl.say; map1(sub ($f) { $f + 2 },[ 1,2,3,4,5 ]).perl.say; |
Pretty simple. We make a new list; iterate over the original list and push the value returned from the code onto the new list. But look at how much we have to think about the new list! The important part is the operation, not the list, or at least that’s what I think.
With that in mind, map round 2:
1 2 3 4 5 6 7 8 9 10 | sub map2(Code $fn, @list) { gather { for @list { take $fn($_); } } } map2({ $_ ** 2 },[ 1,2,3,4,5 ]).perl.say; map2(sub ($f) { $f ** 2 },[ 1,2,3,4,5 ]).perl.say; |
Gather/take, as mentioned previously, abstracts the idea of creating a new list.
And as for a question that Sol mentioned previously about gather and take:
… is gather / take just syntactic sugar, or is it implementing lazy evaluation, or is it even opening up the possibility of threading?
I think that gather/take is really just a pretty syntax. I think that it depends on what is inside of your gather for what is possible. You could even iterate over the contents of a file inside of a gather, in which case I am sure you couldn’t do threads. I guess lazy evaluation is theoretically possible though… But that would depend both on what is inside of the gather and what uses it.
6 Responses for "Implementing map with Perl 6 and Perl 6"
Actually, gather/take is more than just syntactic sugar. It is one of the ways of building lazy lists in Perl 6. See http://perlgeek.de/blog-en/perl-5-to-6/12-lazyness.writeback (Moritz blog) for more details.
You could also just use a &-sigil for the closure parameter in your signature.
sub map1 (&fn, @list) { … }
Aha! That’s the explanation I was looking for, j1n3l0. Thanks!
Actually map does much more: it also respects the arity (number of required parameters) of the block. See http://github.com/rakudo/rakudo/blob/master/src/setting/Any-list.pm for the Perl 6 implementation that Rakudo uses right now.
my @emails = map { $_ =~ s/\w+//; “$_\@letu.edu” } @names;
Really? How… spacious.
I suspect you meant:
my @emails = map { s/\s+//; “$_\@letu.edu” } @names;
or even the safer:
my @emails = map { tr/[a-z]//cd; “$_\@letu.edu” } @names;
(to be strict, your example above just left their last name in $_, but with leading whitespace as well. Maybe you meant that. The bad thing about regexes like that is that the intent of the programmer is hard to divine. All you can see is that the code smells bad and is obviously doing _something_ wrong – but not what the correct thing is)
Leave a reply