The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.

NAME

Maptastic - all map, all the time. Maperiffic baby, yeah!

SYNOPSIS

 use Maptastic qw(:perly);

 @a = (1, 2, 3);
 @b = qw(Mary Jane);
 @c = ('A' .. 'E');
 %d = ( smokey => 1,
        cheese => 6,
        fire   => 7,
        plant  => 3.5 );

 @spliced = map_shift { [ @_ ] } (\@a, \@b, \@c);

 @mixed  = map_for { [ @_ ] } (\@a, \@b, \@c);

 %hashed = map_each { ( $_[0] > 4 ? @_ : () ) } \%d;

Results after the above

 # map_shift / mapcaru
 @spliced = ([1,     "Mary", "A"],
             [2,     "Jane", "B"],
             [3,     undef,  "C"],
             [undef, undef,  "D"],
             [undef, undef,  "E"]);

 # map_for / mapcar
 @mixed   = ([1,     "Mary", "A"],
             [2,     "Jane", "B"], # some LISPs stop here
             [3,             "C"],
             [               "D"],
             [               "E"]);

 # map_each
 %hashed = ( cheese => 6,
             fire   => 7 );

DESCRIPTION

This module defines two maptabulous new varieties of that long-favourite map (see "map" in perlfunc). Two of these maps are more maplicious than map itself - because unlike vanilla map, it maps more than a single list! Mapendous!

But the mappy feast does not stop there! No, to satisfy your ever-growing map cravings, there's a mapdiddlyumtious version of the original map that iterates over hashes! Mapnificent!

Iterator versions

Despite just how mapfect code looks with the flexmapible mapower of map, sometimes, you don't want to process amapn entire list via map at once.

To cater for these specialist map tastes, our maxperts have come up with a great new flavour for all map-like functions: iterators.

An iterator is an object that returns the next item from its list when asked. There are many ways of `asking' an iterator for it's next value, as well as different semantics for `rewinding' the iterator to the beginning, if possible.

But don't worry, Maptastic is so mapscendant that it's looked at all[*] of the modules on that mapreme Perl source repository, CPAN, and therefore accepts the following semantics for iterators:

Object::Iterate style

If the object to be mapped over understands the method __next__, then Object::Iterate style iteration is performed.

ref CODE style

If the object to be mapped is a CODE reference (even blessed), then it is assumed that calling the code reference will perform the iteration. With these semantics, if undef is ever returned, the iterator is assumed to be `spent', and is unlinked; just in case subsequent calls re-start the iterator.

SPOPS style

Iterator function: get_next

misc. styles

Other styles of iteration are automatically detected: is the object implements a ->NEXT() or ->next() method, these are used as the iterator method.

filehandles

A filehandle is a type of iterator - so the "readline" method is accepted too.

FUNCTIONS

map and friends

mapcar { code } \@list, \@list, \@list...
map_for { code } \@list, \@list, \@list...
map_foreach { code } \@list, \@list, \@list...

"mapcar" originated in LISP (the LISt Processing language). So did the Perl built-in function "map". "car" is an old term coming from the term "Contents of the Address part of the Register", so there. This function is also available as `map_for' or `map_foreach' (because with for, you stop at the end of the list).

Note that the exact behvaviour of `mapcar' apparently varied from LISP to LISP, so the version given here is the one that was widely publicised on PerlMonks.

mapcaru { code } \@list, \@list, \@list...
map_shift { code } \@list, \@list, \@list...

"mapcaru" is a version that works similarly to `mapcar', but puts undef (hence the u) into locations in the input array where the input list has no elements. This function is also available as `map_shift' (because with `shift', you get undef out if there was nothing in the list).

map_each { code } \%hash, \%hash, ...

"map_each" is a version of `map' that works on hashes. It does not work like mapcar or mapcaru, it is a simple map for hashes. Supplying multiple hashes iterates over all of the hashes in sequence.

imapcar [TODO] ...
imap_for ...
imap_foreach ...

Returns an iterator version of mapcar (a CODE reference)

map's cousins

While not as mapxy as our star, this group of functions will be found alongside map and imap in many a code fragment.

iter($iter, [ ], ...)

This function simply returns an iterator that iterates over the input list; it is exactly the same as:

   imap { $_ } (...)
slurp($iter, [ ], ...)

This function is the opposite of iter; it takes iterators, gets them to spit values out until they are finished (or all of VM runs out, your machine starts swapping and eventually crashes, esp. on Linux). See "ulimit" in bash.

filter

To save you from having to put unsightly `$_' at the end of your map blocks, eg

   @a = ( filter { s{.*/(.*)}{} }
          split /\0/,
          `find . -type f -print0` );

   for (@a) {
       # do something with each filename
   }
ifilter

Of course the above is much better written iteratively:

   use IO::Handle;

   open FIND, "find . -type f -print0 |";
   FIND->input_record_seperator("\0");

   $iter = ifilter { s{.*/(.*)}{} } \*FIND;

   while ( my $filename = $iter->() ) {
       # do something with each filename
   }
igrep { BLOCK }, [...]

Iterative `grep'

A version of `split' that uses a scalar context m//g loop to return an iterator over a string.

eg, here is a tokeniser that tokenizes a moronically small sub-set of XML:

   my $iter = isplit qr/<[^>]*>|[^<]*/, $string;

Each call to $iter->() would return the next tag or CDATA section of the string, assuming that the input didn't come from the real world. $1, $2, etc are available as per normal with this function; though if the iterator is called in list context, they are returned as a list (yay!).

sub isplit($@) { my $regex = shift; my $iter = iter @_; my ($string, $pos); my $result = bless sub { while (1) { unless (defined $string) { defined($string = $iter->()) or return; } if (defined (my $ok = ($string =~ m/$regex/g))) { if (wantarray) { # nasty! but only way to be sure... return ($& =~ m/$regex/); } else { return $ok; } } } }, __PACKAGE__;

    return $result;
}

EXPORTS

Everything in the module is exported by default. If you prefer, you can get just `mapcar' and `mapcaru' using the import tag :lisp;

   use Maptastic ':lisp';

   my @a = mapcar { do_this(@_) } \@b, \@c;

The other functions (`map_shift', etc) may be imported using the import tags :perly (or :perl or :perlish);

   use Maptastic ':perly';

   my @b = map_for { do_that(@_) } \@b, \@c;

SEE ALSO

"map" in perlfunc, perldata, Object::Iterate, Maptastic::DBI

AUTHOR

Original implementation of mapcar by tye. See http://www.perlmonks.org/index.pl?node_id=22609

Packaged for CPAN by The Map Junky <samv@cpan.org>

This module was somewhat inspired by an MJD talk at YAPC::Europe.