[Melbourne-pm] map -- with an iterator
Toby Corkindale
toby.corkindale at strategicdata.com.au
Thu Apr 22 00:39:32 PDT 2010
On 22/04/10 16:54, Sam Watkins wrote:
> On Thu, Apr 22, 2010 at 03:59:23PM +1000, Toby Corkindale wrote:
>> Hey all,
>> You're all familiar with the map operator, used like so:
>>
>> my @results = map { transform $_ } @input;
>>
>>
>> However I often find that instead of @input, I have $iterator,
>> where $iterator is an object supporting a ->next method.
>>
>>
>> What I would like to be able to do is something like:
>> my @results = map { transform $_ } $iterator;
>> and then have map walk through my iterator for me.
>
> ok, how about this:
>
> #!/usr/bin/perl
> use strict; use warnings;
>
> sub mapit (&$) {
> my ($sub, $it) = @_;
> my @out;
> while (defined (my $x = $it->next)) {
> push @out, $x;
> }
> return @out;
> }
>
> my $it = It->new;
>
> my @out = mapit { $_ . "foo" } $it;
>
> print "@out\n";
>
> package It;
>
> sub new {
> return bless {x=>5};
> }
>
> sub next {
> my ($self) = @_;
> my $x = $self->{x}--;
> return $x>= 0 ? $x : undef;
> }
Ah, the idea of having the iterator-aware map was to prevent creating a
temporary array containing everything in the iterator.
Eg. Say that the iterator returns a 10 Mbyte object every time you call
->next, and that you have one million items to get through.
If you have to put them all into a temporary @out array first, then you
chew up vast amounts of ram. Whereas if your iterator/map combo is
smarter, then you only use 10 Mb at a time. (If you accumulating
results, then you'll still build up an array of results, of course. But
it might be a lot smaller.. or at the very least, it's only one copy of
the results, not two.)
So, currently one tends to do:
while (my $item = $iterator->next) {
do_stuff_to($item);
}
But it would be nice, syntactically, to say
$iterator->foreach(sub { do_stuff($_) });
More information about the Melbourne-pm
mailing list