This is part of The Pile, a partial archive of some open source mailing lists and newsgroups.
To: sfpug@sf.pm.org
From: "Randy J. Ray" <rjray@redhat.com>
Subject: Re: [sf-perl] Deriving a subset with map {}
Date: Thu, 13 Dec 2001 17:33:18 -0800
>>>>> "Chris" == Chris Palmer <cecibean@bitstream.net>
>>>>> wrote the following on Thu, 13 Dec 2001 17:04:39 -0800
Chris> Here's a little problem I've got. I want to use map {} to get a subset
Chris> of an array. Is map just not the right thing for it?
No, it isn't. You want grep().
Remember it this way: map {} always gives you at least as many items out as
were sent in (often more). grep() only ever gives you as many items out as went
in, and often less:
@course_names = grep(-d "$SRC_DIR/$_", @dir);
===
To: sfpug@sf.pm.org
From: Jared Rhine <jared@wordzoo.com>
Subject: [sf-perl] Deriving a subset with map {}
Date: Thu, 13 Dec 2001 17:46:23 -0800
[Chris == sfpug@sf.pm.org on Thu, 13 Dec 2001 17:04:39 -0800]
Chris> Method 1: @course_names = map { $_ if -d "$SRC_DIR/$_" } @dir;
Return an empty list for items you want skipped.
@cn = map { -d "$SRC_DIR/$_" ? $_ : () } @dir
map is a superset of grep (recommended by Ben) and very powerful. Study it
a while and you'll find all sorts of fancy usages.
grep is fine for this particular use, but is insufficient if you want to
modify the values along the way.
===
To: sfpug@sf.pm.org
From: Paul Makepeace <Paul.Makepeace@realprogrammers.com>
Subject: Re: [sf-perl] Deriving a subset with map {}
Date: Thu, 13 Dec 2001 18:23:36 -0800
On Thu, Dec 13, 2001 at 05:46:23PM -0800, Jared Rhine wrote:
> [Chris == sfpug@sf.pm.org on Thu, 13 Dec 2001 17:04:39 -0800]
>
> Chris> Method 1: @course_names = map { $_ if -d "$SRC_DIR/$_" } @dir;
>
> Return an empty list for items you want skipped.
>
> @cn = map { -d "$SRC_DIR/$_" ? $_ : () } @dir
It's not clear to me why anyone would do this over grep. TIMTOWTDI!
> map is a superset of grep (recommended by Ben) and very powerful.
I'm not sure I'd call it a superset; their semantics are quite
different.
> grep is fine for this particular use, but is insufficient if you want to
> modify the values along the way.
grep will modify the values along the way although with a few caveats
the user should be aware of (perldoc -f grep). But those caveats apply
to map in any case.
E.g., this find the filename component of .jpg files,
$ perl -lane 'print for grep s/\.jpg$//, @F'
foo.bmp bar.jpg baz.gif
bar
$
===
To: sfpug@sf.pm.org
From: Jared Rhine <jared@wordzoo.com>
Subject: [sf-perl] Deriving a subset with map {}
Date: Thu, 13 Dec 2001 17:50:10 -0800
[Randy == sfpug@sf.pm.org on Thu, 13 Dec 2001 17:33:18 -0800]
>>>>> "Chris" == Chris Palmer <cecibean@bitstream.net>
>>>>> wrote the following on Thu, 13 Dec 2001 17:04:39 -0800
Randy> Remember it this way: map {} always gives you at least as many
Randy> items out as were sent in (often more).
This isn't a true statement. It's a general list transformation construct,
borrowed from the powerful functional-programming 'reduce' pattern.
Perlfunc states:
Evaluates BLOCK or EXPR in list context, so each element of LIST may
produce zero, one, or more elements in the returned value.
===
To: sfpug@sf.pm.org
From: Jared Rhine <jared@wordzoo.com>
Subject: [sf-perl] Deriving a subset with map {}
Date: Thu, 13 Dec 2001 19:21:09 -0800
[Paul == sfpug@sf.pm.org on Thu, 13 Dec 2001 18:23:36 -0800]
Paul> TIMTOWTDI!
Definitely. So somebody needed to point out how to do it with map :)
Perhaps I was distracted by the original poster's use of map.
Paul> It's not clear to me why anyone would do this over grep.
I wouldn't personally use it for this particular case either, since the
original poster only wanted to select from the list. As I mentioned:
Jared> grep is fine for this particular use...
But in many other cases, map is clearly a better choice.
Paul> I'm not sure I'd call it a superset; their semantics are quite
Paul> different.
Ok, personal choice of semantics then, with which I respectfully disagree.
I do call it a superset, as grep can be implemented with map, but in general
not the other way around (without copying your original array if you don't
want it touched):
@w = grep { xxx } @y ---> @w = map { xxx ? $_ : () } @y
xyz(\@y, [map { xxx ? reverse $_ : uc $_ } @y]) ---> ??? in grep?
Paul> grep will modify the values along the way although with a few
Paul> caveats the user should be aware of (perldoc -f grep).
Ok, but we're saying slightly different things. Both map and grep can futz
with $_ if they want. But map can produce a different output list without
futzing with $_, leading to perlfunc's "bizarre results". map seems a
better semantic match for situations where you want to transform the list,
not just select on it. You're free to disagree, as Perl style doesn't
mandate either approach. To quote a wise sage named Paul, TIMTOWTDI!
Bottom line: I think map is under-used, probably because it wasn't present
in perl 4 and it's origin is from functional programming, not imperative
style. map is a _general_ list manipulation construct, while grep is a
_specialized_ list manipulation construct.
My apologies to the list for now having veered off from the original topic.
I hope the discussion of map has been at least moderately interesting for
those who weren't really aware of it before.
Below are excerpts from a few of my programs which use map, although each
could be solved with other approaches. (Please, I'm not looking for style
critiques; most of these were one-offs, written as quickly as possible):
-- begin --
my @rpms = map {
chomp;
my @me;
foreach (split '-') {
/^\d.*\./ ? last : push @me, $_;
}
join '-', @me;
} `rpm -qa`;
my @dirs = map { "$root/$_" } qw(bin htdocs logs lib var);
@nameservers = map { lc $_ } @nameservers;
my @uniqerrs = map {
my $errs = ${$G::err_ref}{$_};
"(Msg x${errs}) ". $_;
} (sort bynum (keys %{$G::err_ref}));
my @unsponsored = map {
my ($name,$passwd,$uid,$gid, $quota,$comment,$gcos,$dir,$shell,$expire) = getpwnam($_);
"${_}\t$gcos";
} (sort keys %unsponsored);
my $rewrite = defined $rewrite_ref ? join("\n", map { " RewriteRule $_" } @{$rewrite_ref}) : '';
foreach my $dir ((map { "$root/$_"} qw(apache apache/bin apache/logs
apache/conf apache/var var var/mason)), $conf->{docroot}) {
mkpath($dir,1,0775);
chown $uid, $gid, $dir;
}
-- end --
===
To: sfpug@sf.pm.org
From: Paul Makepeace <Paul.Makepeace@realprogrammers.com>
Subject: Re: [sf-perl] Deriving a subset with map {}
Date: Thu, 13 Dec 2001 20:21:46 -0800
On Thu, Dec 13, 2001 at 07:21:09PM -0800, Jared Rhine wrote:
> I do call it a superset, as grep can be implemented with map, but in general
> not the other way around
Yup, you're right -- this is true it is a superset.
> @w = grep { xxx } @y ---> @w = map { xxx ? $_ : () } @y
>
> xyz(\@y, [map { xxx ? reverse $_ : uc $_ } @y]) ---> ??? in grep?
Indeed. You could make a mess of everything by prefixing (my @t = @y)
and using the alias but that would at the bounds of sense :)
> futzing with $_, leading to perlfunc's "bizarre results". map seems a
> better semantic match for situations where you want to transform the list,
> not just select on it.
I'd also add IME that once folks "get" the map trick they tend to
initially overuse it -- I personally tend to shy away from big chunks of
code in map. My general rule (again, just my own metric) is as soon as
I'm defining temp vars *in the block* the time to switch to for/foreach
is probably well overdue.
> My apologies to the list for now having veered off from the original topic.
Nah -- these things are a great way to exercise & I already learnt
something...
> Below are excerpts from a few of my programs which use map, although each
> could be solved with other approaches. (Please, I'm not looking for style
> critiques; most of these were one-offs, written as quickly as possible):
>
> -- begin --
>
> my @rpms = map {
> chomp;
> my @me;
> foreach (split '-') {
> /^\d.*\./ ? last : push @me, $_;
> }
> join '-', @me;
> } `rpm -qa`;
foreach in a map!
I think your other examples are great but I couldn't let this one go, :-)
my @rpms;
push @rpms, /^(.+?)[-\d.]+$/ for `rpm -qa`;
my @rpms = map { /^(.+?)[-\d.]+$/; $1 } `rpm -qa`;
(I don't know rpm all that well [debian here] but I think that's
equivalent)
What other language has those kinds of nuances, on a regular basis?
> @nameservers = map { lc $_ } @nameservers;
These are my favorite examples of map usage -- pure list transformation.
You can even go so far as omitting the dreaded $_
@a = map {lc} @a;
Lovely. Perl rocks!
Here's a cool trick I use in a content handler, modified from some of
Randal's code,
$SIG{__WARN__} = sub {
return unless $debug;
my $now = localtime;
CORE::warn
join "", map { "[$now] ". __PACKAGE__. ": $_\n" }
split /\n/, join "", @_;
};
Heh,
Paul
===
To: sfpug@sf.pm.org
From: Chris Palmer <cecibean@bitstream.net>
Subject: Re: [sf-perl] Deriving a subset with map {}
Date: Thu, 13 Dec 2001 21:03:02 -0800
On 13 Dec, Paul Makepeace wrote:
> My general rule (again, just my own metric) is as soon as I'm defining
> temp vars *in the block* the time to switch to for/foreach is probably
> well overdue.
It's de rigueur in functional languages. Recently I've had a (re-)
infatuation with Lisp and (newly) with Miranda, which is the only reason
I even attempted that dealie with map this afternoon. I did some Lisp in
school, but not so much that functional programming is not still a major
brain-twister (which of course is precisely why I'm infatuated with
functional languages).
> > My apologies to the list for now having veered off from the original
> > topic.
>
> Nah -- these things are a great way to exercise & I already learnt
> something...
We're still firmly in relevant territory. I hope.
> What other language has those kinds of nuances, on a regular basis?
Lisp is fairly kinky, in that it at times resembles a functional
language, an assembly language and an object language. It's not as
extreme as Perl ("A different paradigm for every line!"), and you don't
feel dirty afterwards like with C++.
> Here's a cool trick I use in a content handler, modified from some of
> Randal's code,
>
> $SIG{__WARN__} = sub {
> return unless $debug;
> my $now = localtime;
> CORE::warn
> join "", map { "[$now] ". __PACKAGE__. ": $_\n" }
> split /\n/, join "", @_;
> };
Why is the 'CORE::' necessary? Have you defined a warn in the module,
outside this snippet?
===
To: sfpug@sf.pm.org
From: Paul Makepeace <Paul.Makepeace@realprogrammers.com>
Subject: Re: [sf-perl] Deriving a subset with map {}
Date: Thu, 13 Dec 2001 21:22:13 -0800
On Thu, Dec 13, 2001 at 09:03:02PM -0800, Chris Palmer wrote:
> > Here's a cool trick I use in a content handler, modified from some of
> > Randal's code,
> >
> > $SIG{__WARN__} = sub {
> > return unless $debug;
> > my $now = localtime;
> > CORE::warn
> > join "", map { "[$now] ". __PACKAGE__. ": $_\n" }
> > split /\n/, join "", @_;
> > };
>
> Why is the 'CORE::' necessary? Have you defined a warn in the module,
> outside this snippet?
It's not strictly at all. It was left over from some other fooling
around, IIRC. As it stands, it's one of those judgement calls on whether
it's making it explicit (since we're modifying where warn "goes" in a
sense) or "overdocumenting" and perhaps confusing people. Just in case
it isn't confusing enough :-)
===