Style question: map versus foreach

  • Thread starter LaDainian Tomlinson
  • Start date
L

LaDainian Tomlinson

Hi,

I wrote a short script the other night to search for C errno definitions by
their integer values. It works fine, but I was originally using map() in
void context and figured that was bad practice. Is this true at all, or are
there any advantages to using map() (speed-wise or otherwise) rather than a
foreach loop even in void context? The script follows:

--- BEGIN CODE ---
#!/usr/bin/perl

use warnings;
use strict;
use File::Find;

my @errnos;
my $errnum = 21;

find(\&wanted, qw(/usr/include));

$\ = "\n";
foreach (@errnos){
open ERRNO, "<", $_ or die "Couldn't open $_: $!";
#map { chomp && print if /\b$errnum\b/ } <ERRNO>; #this?
foreach (<ERRNO>){ chomp && print if /\b$errnum\b/ }; #or this?
close ERRNO or die "Couldn't close $_: $!";
}

sub wanted { push(@errnos, $File::Find::name) if /errno.h$/ }
--- END CODE ---

Thanks,

Brandan L.
 
T

Tassilo v. Parseval

Also sprach LaDainian Tomlinson:
I wrote a short script the other night to search for C errno definitions by
their integer values. It works fine, but I was originally using map() in
void context and figured that was bad practice. Is this true at all, or are
there any advantages to using map() (speed-wise or otherwise) rather than a
foreach loop even in void context? The script follows:

[...]

Depends on the version of perl you employ. When you are using 5.8.1
map() no longer bothers to return a list in void context which makes it
almost (but not quite, as I remember) fast as a foreach-loop.

For older perls, there is a measurable difference in speed favouring
foreach. If in doubt, use the Benchmark module to get some figures for
your machine.

Tassilo
 
R

Roy Johnson

LaDainian Tomlinson said:
#map { chomp && print if /\b$errnum\b/ } <ERRNO>; #this?
foreach (<ERRNO>){ chomp && print if /\b$errnum\b/ }; #or this?

Or maybe
print grep (chomp && /\b$errnum\b/) <ERRNO>;
?
 
A

Anno Siegel

Tassilo v. Parseval said:
Also sprach LaDainian Tomlinson:
I wrote a short script the other night to search for C errno definitions by
their integer values. It works fine, but I was originally using map() in
void context and figured that was bad practice. Is this true at all, or are
there any advantages to using map() (speed-wise or otherwise) rather than a
foreach loop even in void context? The script follows:

[...]

Depends on the version of perl you employ. When you are using 5.8.1
map() no longer bothers to return a list in void context which makes it
almost (but not quite, as I remember) fast as a foreach-loop.

For older perls, there is a measurable difference in speed favouring
foreach. If in doubt, use the Benchmark module to get some figures for
your machine.

That's efficiency. As for style, that depends very much upon whom
you ask. Some want to ban map in void context entirely, others say
there's nothing wrong with it. I tend to avoid it since (namely 5.1.1
or so) the statement-modifying "for" does the same thing.

Anno
 
T

Tassilo v. Parseval

Also sprach Anno Siegel:
Tassilo v. Parseval said:
Also sprach LaDainian Tomlinson:
I wrote a short script the other night to search for C errno definitions by
their integer values. It works fine, but I was originally using map() in
void context and figured that was bad practice. Is this true at all, or are
there any advantages to using map() (speed-wise or otherwise) rather than a
foreach loop even in void context? The script follows:

[...]

Depends on the version of perl you employ. When you are using 5.8.1
map() no longer bothers to return a list in void context which makes it
almost (but not quite, as I remember) fast as a foreach-loop.

For older perls, there is a measurable difference in speed favouring
foreach. If in doubt, use the Benchmark module to get some figures for
your machine.

That's efficiency. As for style, that depends very much upon whom
you ask. Some want to ban map in void context entirely, others say
there's nothing wrong with it. I tend to avoid it since (namely 5.1.1
or so) the statement-modifying "for" does the same thing.

I think map() allows for some suggestive notations if one rememebers
that it applies a chunk of code to each element of a list. Someone with
an affinity to functional programming might like idioms such as

local $\ = " " and map print() => qw(Just another Perl hacker,);

On the other hand, this is likely to confuse a rather
imperatively-minded programmer.

Tassilo
 
L

LaDainian Tomlinson

Abigail said:
LaDainian Tomlinson ([email protected]) wrote on MMMDCCXX September MCMXCIII
in <URL:<> Hi,
<>
<> I wrote a short script the other night to search for C errno definitions by
<> their integer values. It works fine, but I was originally using map() in
<> void context and figured that was bad practice. Is this true at all, or are
<> there any advantages to using map() (speed-wise or otherwise) rather than a
<> foreach loop even in void context? The script follows:


I'm curious, what made you think that map () in void context was bad
practice?

I guess I don't like discarding return values. It seems like a bad habit to
get into. At least in this case, it could have easily been avoided with a
solution like Roy's (printing the list returned by grep() or map() rather
than printing in the map() itself).

Thank you all for the help,

Brandan L.
 
B

Ben Morrow

LaDainian Tomlinson said:
I guess I don't like discarding return values. It seems like a bad habit to
get into.

From 'info gcc':

| Coming as I do from a Lisp background, I balk at the idea that there
| is something dangerous about discarding a value. There are
| functions that return values which some callers may find useful; it
| makes no sense to clutter the program with a cast to `void' whenever
| the value isn't useful.

*Everything* returns a value[1]: a statement such as

my $i = 5;

returns $i, which you then discard.

Ben

[1] OK, with a couple of exceptions, such as 'package'...
 
D

Darin McBride

Ben said:
From 'info gcc':

| Coming as I do from a Lisp background, I balk at the idea that there
| is something dangerous about discarding a value. There are
| functions that return values which some callers may find useful; it
| makes no sense to clutter the program with a cast to `void' whenever
| the value isn't useful.

I suppose it all depends on what you're trying to do. And it depends
on what choices you have. For example, as long as we're on gcc (which
usually also does C++), using ++i vs i++ can have huge impacts on
speed. Both return a value, but that value is usually discarded. So
which one to use?

In Perl, we have the same choice with "use overload" to some degree
(perl is smart enough to do the Right Thing if your increment function
is done right).

If the 'i' object is a heavy type where copying it can be expensive,
then you definitely want to use "++i" because that can then return a
reference to i after the increment is finished. However, "i++" will
copy itself, increment itself, and then return the copy (by value, not
reference).

Thus, even when discarding a return value, it can be important to use
the right version. If you're discarding the return from ++, then use
the preincrement - it can be tons faster. And where it isn't, it can't
be any slower than postincrement.

Similarly with map vs for/foreach. If you're discarding the return
value from map, then why bother at all? It's much clearer to use
foreach instead. And, as reported, foreach is still marginally faster
than map in cases where you ignore the return from map. Clarity AND
speed - what more can one ask for?

Now, if you're writing another JAPH sig or competing for obsfucated
code, map may make more sense. But for any code that might need
maintenance, write the code that means what you want it to do. In this
case, foreach.

I think I use map more than foreach, but that's because I'm constantly
rewriting my arrays, or converting arrays to hashes, or converting
hashes to arrays or other nonsense. I use the return values. I figure
that this is probably faster than writing it in foreach, and besides,
that's what I mean to do - completely clear.
*Everything* returns a value[1]: a statement such as

my $i = 5;

returns $i, which you then discard.

The difference is that this "returns" a single scalar. Map allocates
and returns an array (or at least, that's the concept). Big
difference.
Ben

[1] OK, with a couple of exceptions, such as 'package'...
 
D

Darin McBride

Abigail said:
Darin McBride ([email protected]) wrote on MMMDCCXXII
September MCMXCIII in <URL:""
"" The difference is that this "returns" a single scalar. Map allocates
"" and returns an array (or at least, that's the concept). Big
"" difference.


Wrong. First of all, from the language point of view, what a function
returns is determined by the *CONTEXT*, not the function. As Randal
likes to chant "there are no lists in scalar context". So, a function
in void context (it being a special case of scalar context) simply
*cannot* return a list (arrays are never returned by Perl anyway).

No, you're right. I suppose you overlooked the "that's the concept"
part of that paragraph. Context rules in perl. But that doesn't mean
that context rules in the maintainer's head when trying to decipher
what was written.
Secondly, the fact than in older versions of Perl map was implemented
inefficiently was a bug in perl, that now is luckely fixed. But it's

That it being fixed is "lucky" is somewhat debatable. That doesn't
make map in void context any more clear than before - only not as bad
for runtime speed vs before the "fix".

Note that the fix is not necessarily goodness. It may have a speed
penalty on array-context map's if it means that all of my 80 map's need
to check their context (which is never void) just so that one of your
map's (in void context) can be zillions faster. That's an overall
negative to perl's speed since I can't speed up my map's any more, but
you could have simply converted to foreach to get an even better (even
if only slightly better) speed increase.
pretty lame to call a certain style of Perl programming "bad practise",
just because the implementation was inefficient. But that's no longer
an issue anymore.

I suppose you also missed pretty much any of my post that you didn't
quote. My point, as opposed to others, is that even without the speed
penalty, there is a stylistic benefit to using foreach when you may
otherwise wish to use map in void context.

It's simply more *obvious* to use map for array contexts and foreach in
void contexts. And that, regardless of any speed difference, is enough
reason to discourage map in void context.
 
T

Tassilo v. Parseval

Also sprach Abigail:
LaDainian Tomlinson ([email protected]) wrote on MMMDCCXXII September MCMXCIII
in <URL:## "Abigail" wrote:
## >
## > I'm curious, what made you think that map () in void context was bad
## > practice?
##
## I guess I don't like discarding return values. It seems like a bad habit to
## get into. At least in this case, it could have easily been avoided with a
## solution like Roy's (printing the list returned by grep() or map() rather
## than printing in the map() itself).

print returns a value, which is discarded in void context..
Assignment returns a value, which is discarded in void context.
s/// returns a value, which is discarded in void context.
pop returns a value, which is discarded in void context.
map returns a value, which is discarded in void context.


Do you think that using any of these five operations in void context is
bad practise? If not, could you indicate why using some operations in
void context is fine, but map in void context is bad practise? Because
I really fail to see what map has done to get this special status.

It's a historical thing. It used to always generate a return-list and so
it was bad. It's questionable of course whether this a stylistic
question at all or maybe whether it wasn't simply a shortcoming (or even
a bug) in the implementation. For me it's the latter.

Perhaps some day the same optimization happens for grep (I looked at it
but it's a little more complicated than the map-case). Then these three

$_++ for @a;
map $_++, @a;
grep $_++, @a;

will all behave very similarily in speed although the third version
looks indeed kind of strange since it looks like a conceptual abuse of
grep.

Tassilo
 
T

Tassilo v. Parseval

Also sprach Abigail:
Tassilo v. Parseval ([email protected]) wrote on
MMMDCCXXIII September MCMXCIII in <URL:<>
<> Perhaps some day the same optimization happens for grep (I looked at it
<> but it's a little more complicated than the map-case). Then these three
<>
<> $_++ for @a;
<> map $_++, @a;
<> grep $_++, @a;
<>
<> will all behave very similarily in speed although the third version
<> looks indeed kind of strange since it looks like a conceptual abuse of
<> grep.


Actually, they will all be slightly different:

sub f {
print wantarray ? "LIST" : defined wantarray ? "SCALAR" : "VOID"
}

f () for 1; # VOID.
map f () => 1; # LIST.
grep f () => 1; # SCALAR.

You are right. The different contextes map() and grep() impose onto their
first argument is something I always forget about. Fortunately it
shouldn't matter in my example (unless $_ holds an object with some
fancy ++-overloading).

Thanks for the reminder,
Tassilo
 
B

Ben Morrow

Tassilo v. Parseval ([email protected]) wrote on
MMMDCCXXIII September MCMXCIII in
<URL:<> Perhaps some day the same optimization happens for grep (I looked at it
<> but it's a little more complicated than the map-case). Then these three
<>
<> $_++ for @a;
<> map $_++, @a;
<> grep $_++, @a;
<>
<> will all behave very similarily in speed although the third version
<> looks indeed kind of strange since it looks like a conceptual abuse of
<> grep.

Actually, they will all be slightly different:

sub f {
print wantarray ? "LIST" : defined wantarray ? "SCALAR" : "VOID"
}

f () for 1; # VOID.
map f () => 1; # LIST.
grep f () => 1; # SCALAR.

I like that :) it appeals to my simple mind...

It also gives a specific use for map in void context: to call a
function in list context but discard the result.

Ben
 
A

Anno Siegel

Tassilo v. Parseval said:
Also sprach Abigail:
LaDainian Tomlinson ([email protected]) wrote on MMMDCCXXII September MCMXCIII
in <URL:## "Abigail" wrote:
[...]
print returns a value, which is discarded in void context..
Assignment returns a value, which is discarded in void context.
s/// returns a value, which is discarded in void context.
pop returns a value, which is discarded in void context.
map returns a value, which is discarded in void context.


Do you think that using any of these five operations in void context is
bad practise? If not, could you indicate why using some operations in
void context is fine, but map in void context is bad practise? Because
I really fail to see what map has done to get this special status.

It's a historical thing. It used to always generate a return-list and so
it was bad. ...

(bad to use it in void context, I assume)

Historically, the concept of "map" functions was introduced with LISP,
every other language has it from there. Interestingly, the original
LISP "map" doesn't return anything, it is a procedure that is only called
for its side effects. Calling it anywhere *but* in void context is useless.
(You need "maplist" if you want a return value; the real ancestor of other
"map" functions is LISPs "mapcar".)

So the original inventors of "map" didn't share the intuition that "map"
has to return an image.

Anno
 
D

Darin McBride

Abigail said:
Darin McBride ([email protected]) wrote on MMMDCCXXIII
September MCMXCIII in <URL:__ Abigail wrote:
__
__ No, you're right. I suppose you overlooked the "that's the concept"
__ part of that paragraph. Context rules in perl. But that doesn't mean
__ that context rules in the maintainer's head when trying to decipher
__ what was written.

If you're going to assume the maintainer doesn't know basic concepts
of Perl, all bets are off. Then one might want to avoid hashes and
regexes too.

True to a point. However, there is no cleaner way to do hashes or
regexes. There is a clearer way to do map in void context: foreach.
And that's the crux, too. If there was no foreach, then it would be
obvious to all comers that map in void context is used for a particular
purpose.

However, the general rule is to use map for return values and foreach
when you don't need the return value. And that's what most people are
likely to think. Try reading perldoc perlstyle a bit... a few tidbits:

o Just because you CAN do something a particular way
doesn't mean that you SHOULD do it that way. Perl is
designed to give you several ways to do anything, so
consider picking the most readable one.

The main point of map is the return value. The main point of foreach
is the iteration over an array (no return value implied).

o Avoid using grep() (or map()) or `backticks` in a void
context, that is, when you just throw away their
return values. Those functions all have return val-
ues, so use them. Otherwise use a foreach() loop or
the system() function instead.

I think this is the whole debate. Unfortunately, there's no real
explicit justification here.
__ That it being fixed is "lucky" is somewhat debatable. That doesn't
__ make map in void context any more clear than before - only not as bad
__ for runtime speed vs before the "fix".
__
__ Note that the fix is not necessarily goodness. It may have a speed
__ penalty on array-context map's if it means that all of my 80 map's
need
__ to check their context (which is never void) just so that one of your
__ map's (in void context) can be zillions faster. That's an overall
__ negative to perl's speed since I can't speed up my map's any more, but
__ you could have simply converted to foreach to get an even better (even
__ if only slightly better) speed increase.

Of course, map already had to check context. It wasn't that map left
its intermediar list on the stack if it was called in void or scalar
context.

It's just an extra check. I'm sure it was something like (simplified
for CODE case only):

sub map {
my $code = shift;
my @r;
foreach (@_) {
my @a = $code->();
if (wantarray) {
push @r, @a;
}
else {
$r[0] += scalar @a;
}
}
wantarray ? @r : $r[0];
}

Now it's more like:

sub map {
my $code = shift;
my @r;
foreach (@_) {
my @a = $code->();
if (defined wantarray) {
if (wantarray) {
push @r, @a;
}
else {
$r[0] += scalar @a;
}
}
}
wantarray ? @r : $r[0];
}

That's just an extra level of checks that isn't needed. Of course,
there are other ways to write this - some simpler, others faster. And,
of course, it's probably all in native (C) code anyway. Probably using
a case statement - which has its own overhead.
__ I suppose you also missed pretty much any of my post that you didn't
__ quote. My point, as opposed to others, is that even without the speed
__ penalty, there is a stylistic benefit to using foreach when you may
__ otherwise wish to use map in void context.
__
__ It's simply more *obvious* to use map for array contexts and foreach
in
__ void contexts. And that, regardless of any speed difference, is
enough
__ reason to discourage map in void context.

"Obvious" is something subjective. *YOU* might find one way of doing
something more obvious than doing it another way. But that doesn't
mean it's true for everyone. Could you please explain why you find
map in void context so non-obvious? Do you think map in general is
non-obvious, or do you only get confused when the context is void?
What about other functions in void context? Are they non-obvious too?
Or is it only map that's non-obvious?

Given that map and foreach do almost exactly the same thing with only
the desired contexts being different, it makes sense to me that one
would use the version one desires based on that context difference.
Thus, when I see map in void context, the first thought in my mind is
"they didn't get the return from map!". Then, after scrutinising the
map's block (as it's usually a block not an expression), I may figure
out that they intended to have void (e.g., only doing it for the side
effect). And then, if that's not the source of the problem, I can go
on.

However, if they had used foreach to begin with, I might have been able
to skip over the code altogether knowing that "ignoring the return" is
exactly what was intended.
I've had this discussion before, and people sometimes claim they find
map in void context hard to understand but when I point out to them they
happily use other function in void context without a problem, and ask
them why they special case 'map', they don't know.

I do know. It's that there is another, clearer way to do it. There is
no other function other than 'print' to print to a filehandle. So use
it.
Is there anyone who can explain why map in void context is confusing,
but other functions aren't?

Simply have your code say what you mean rather than trick the virtual
machine into doing what you want by side effect. In general, side
effects make code harder to understand.
 
L

LaDainian Tomlinson

Abigail said:
LaDainian Tomlinson ([email protected]) wrote on MMMDCCXXII September MCMXCIII
in <URL:## "Abigail" wrote:
## >
## > I'm curious, what made you think that map () in void context was bad
## > practice?
##
## I guess I don't like discarding return values. It seems like a bad habit to
## get into. At least in this case, it could have easily been avoided with a
## solution like Roy's (printing the list returned by grep() or map() rather
## than printing in the map() itself).

print returns a value, which is discarded in void context..
Assignment returns a value, which is discarded in void context.
s/// returns a value, which is discarded in void context.
pop returns a value, which is discarded in void context.
map returns a value, which is discarded in void context.

You are correct, and I know there are loads of others too, not just in Perl.
Do you think that using any of these five operations in void context is
bad practise? If not, could you indicate why using some operations in
void context is fine, but map in void context is bad practise? Because
I really fail to see what map has done to get this special status.

No, I don't (except for map() :), and no, I can't. It's simply a judgment
call in most cases whether or not to capture a return value. _Everyone_
knows that you should always, yes *always*, check the return value of
open(), and close(), and system() and various other such functions with
"important" return values. Failing to do so has a high probability of
affecting the way your code works.

However, most of the time you don't necessarily need to know the number of
substitutions made on a string by s/// or the success of the last print
operation, and sometimes you just need to pop the last value on an array.
In these cases, functions are the simplest way to perform such operations,
so the choice is either to capture the return value and do nothing with it,
or just ignore it altogether. And it makes more sense to ignore it. In my
example, there was an easy way to avoid discarding the return value while
still achieving the same behavior. I _personally_ prefer to avoid returns
in void context when possible and plausible. I can't quantify the
importance of return values or draw a line to separate the good ones from
the bad ones, but I still need to make a (possibly informed) decision
between a few different ways of doing something.

Which is why I asked in the first place, and I've gotten a lot of good
insight. So thank you folks.

Brandan L.
 
D

Darin McBride

Abigail said:
Darin McBride ([email protected]) wrote on MMMDCCXXIII
September MCMXCIII in <URL::: Abigail wrote:
::
:: > If you're going to assume the maintainer doesn't know basic concepts
:: > of Perl, all bets are off. Then one might want to avoid hashes and
:: > regexes too.
::
:: True to a point. However, there is no cleaner way to do hashes or
:: regexes. There is a clearer way to do map in void context: foreach.

But could you give an objective reason why foreach is "clearer" than
map (after defining what "clearer" is)? Or is it your own preference?

<sigh> I thought I had. Let me try again:

The only real difference between map and foreach is that map returns a
value, while foreach doesn't [1]. Thus, if you intend on ignoring the
return, don't create it in the first place and just use foreach.

[1] Foreach actually returns the same way any block does - the last
statement executed in the last time through the loop is the return
value. But this is not generally useful. It only makes perl more
consistant.
What makes foreach cleaner than while? Or is while cleaner than
foreach? Which one ought to go? Or can we have foreach, for, while,
until, goto, and bare blocks, but not map in void context?

While vs foreach is another discussion (I wish we'd stick to a single
topic, but that's not generally possible on usenet, I suspect). If the
only data I have to work with is whether we're in void context or not,
then neither while nor foreach are different. However, they have other
strengths which would come up should we actually debate those. Again,
use whichever one makes the most readable sense.

Until vs while is easy - again, the most readable one wins. If your
assertion is positive, use while, if it is negative, use until.

And now we're really straying off the topic of this thread.
You can do loops in the following ways:

[ If you're trying to be pedantic, you missed some. ]
1 C style for

Good for when you have a C-style index.
2 Perl style foreach

Good for when you have a perl-style index.
3 for statement modifier

Good when what you're trying to do is an action, and it happens to be
looped, rather than what you're trying to do is loop, and during the
loop you want to do something. Use the right loop construct for what
you're trying to do rather than try to jimmy the wrong construct into
doing what you want (which is what you have to do in most other
languages with limited choices).
4 while
5 until

These are the same as each other with the minor difference noted above
- one's assertion is positive while the other is negative. Good when
you're waiting for a condition to be false/true, and until that
happens, you want to do stuff.
6 bare blocks

Hmmm - there is probably a bit more needed here (redo?) I would
definitely not recommend this.

Good if you're making regex substitutions.

Good if you're trying to find many of the same thing in a given string.

Not generally good for looping - there is usually a better construct
for what you're trying to do.

Good if you're mapping one space (array) onto another space (array).

Good for finding a subset of a given array.
Don't pick on map in void context. Could you please pick one, and make
a case against the other 10, and not just map?

Use the one that fits the job you have for it. Both map and m// are
there to create an array. If you don't want the array, I fail to see
why you'd use m//, but for map, foreach is exactly equivalent in the
void context. Use it.
:: The main point of map is the return value. The main point of foreach
:: is the iteration over an array (no return value implied).
::
:: o Avoid using grep() (or map()) or `backticks` in a void
:: context, that is, when you just throw away their
:: return values. Those functions all have return val-
:: ues, so use them. Otherwise use a foreach() loop or
:: the system() function instead.
::
:: I think this is the whole debate. Unfortunately, there's no real
:: explicit justification here.

Which makes it just the personal preference of one man, doesn't it?

No - it means that the perlstyle authors (who are probably more
proficient in perl than the both of us put together) thought this would
be obvious and unneeding of justification.
:: Given that map and foreach do almost exactly the same thing with only
:: the desired contexts being different, it makes sense to me that one
:: would use the version one desires based on that context difference.
:: Thus, when I see map in void context, the first thought in my mind is
:: "they didn't get the return from map!".

Ah, yes. Logical. There are thousands and thousands of user defined
functions whose return value isn't collected. There are many Perl
defined functions and operators whose return value isn't collected.

Noone ever makes a fuss. But if the function of which the return

I do. If there are two user defined functions that do exactly the same
thing, but one allocates and returns an array, and the other does not
but has the same desired side effects, I would expect people to use the
appropriate one depending on whether they want the array returned or
not. I don't see any difference.
value isn't collected is called "map", hundreds of Perl programmer
brains go into overload, and the programmers start running around
like chickens without their heads. "They didn't get the return from
map! They didn't get the return from map! Keep your daughters inside!
Keep your daughters inside! The end of times in coming! The antichrist
has arrived!"

I'm not sure that the Six Degrees of Godwin game would have anticipated
this as "legitimate topic drift".
I really don't get it. map in void context isn't doing anything else
than the countless of other functions in void context that are called
because of their side effects. Yet it sparks gigabytes of discussions.

Only because there is an exact duplicate of map functionally that is
there for void context.
:: Then, after scrutinising the
:: map's block (as it's usually a block not an expression), I may figure
:: out that they intended to have void (e.g., only doing it for the side
:: effect). And then, if that's not the source of the problem, I can go
:: on.

But if the function wasn't called "map", but "zeebleflup", and it was
used in void context, would you get confused as well? What if it was
called "apply_this_function_over_the_elements_of_a_list"?

Well, if it were called "zeebleflup", I'd be looking for zeebleflup's
definition to understand what the heck it is, and why someone thought
that this was a brilliant name to give a function.

You see, I'm not worried about some "map" crusade. I'm looking for
readability. I'd be just as curious about why someone calls map in
void context as someone calling $my_circle->get_radius() in void
context. If someone was looking at the return to a function that
obviously shouldn't have one, I'd be just as curious (although I can't
think of an example function name that would cause me to think this
offhand).
:: However, if they had used foreach to begin with, I might have been
:: able to skip over the code altogether knowing that "ignoring the
:: return" is exactly what was intended.

Again, why do you assume that if map was used in void context the
programmer was in error, and you have to confirm yourself it wasn't the
case, and you don't have this Pavlov reaction with other functions? What
makes map trigger this behaviour?

Who said I don't have this reaction to any other function? Perhaps
some of your previous sparring partners are that way, but you paint me
unfairly with the same brush. If I were into the same ad hominum style
debate, I'd start wondering about your knee-jerk reactions...
:: Simply have your code say what you mean rather than trick the virtual
:: machine into doing what you want by side effect.

If I write:

map {BLOCK} LIST;

I write *exactly* what I mean. I want to map an operation over a list.
It simple can't be named simpler.

If we were writing LISP where that's what map means, that's fine. But
in perl, if you want to "map an operation over a list", that's called
"foreach".
:: In general, side
:: effects make code harder to understand.

Then you shouldn't be programming in Perl. Because in Perl, you can't
do much useful without side effects.

Hmmm... I dunno - I've managed to do a lot of useful stuff without side
effects in perl.
 
T

Tassilo v. Parseval

Also sprach Darin McBride:
Abigail said:
Darin McBride ([email protected]) wrote on MMMDCCXXIII
September MCMXCIII in <URL:__ Abigail wrote:
__
__ No, you're right. I suppose you overlooked the "that's the concept"
__ part of that paragraph. Context rules in perl. But that doesn't mean
__ that context rules in the maintainer's head when trying to decipher
__ what was written.

If you're going to assume the maintainer doesn't know basic concepts
of Perl, all bets are off. Then one might want to avoid hashes and
regexes too.
[...]

The main point of map is the return value. The main point of foreach
is the iteration over an array (no return value implied).

o Avoid using grep() (or map()) or `backticks` in a void
context, that is, when you just throw away their
return values. Those functions all have return val-
ues, so use them. Otherwise use a foreach() loop or
the system() function instead.

I think this is the whole debate. Unfortunately, there's no real
explicit justification here.

The mentioning of map() needs to be scratched from perlstyle.pod as it's
no longer true. Forgot to include that in my map-patch.
Of course, map already had to check context. It wasn't that map left
its intermediar list on the stack if it was called in void or scalar
context.

It's just an extra check. I'm sure it was something like (simplified
for CODE case only):

sub map {
my $code = shift;
my @r;
foreach (@_) {
my @a = $code->();
if (wantarray) {
push @r, @a;
}
else {
$r[0] += scalar @a;
}
}
wantarray ? @r : $r[0];
}

Now it's more like:

sub map {
my $code = shift;
my @r;
foreach (@_) {
my @a = $code->();
if (defined wantarray) {
if (wantarray) {
push @r, @a;
}
else {
$r[0] += scalar @a;
}
}
}
wantarray ? @r : $r[0];
}

That's just an extra level of checks that isn't needed. Of course,
there are other ways to write this - some simpler, others faster. And,
of course, it's probably all in native (C) code anyway. Probably using
a case statement - which has its own overhead.

Much easier. This

if (items) {

had to become

if (items && gimme != G_VOID) {

If the condition evaluates to false, about 15 lines of code are skipped
and pp_mapwhile (the core of a map() statement) becomes quite a simple
operation.

Which however doesn't change the fact that foreach is faster. The
difference is marginal for 'map EXPR, LIST' (6% in favour of for).
Strangely enough, map is disproportionally slower in the 'map BLOCK
LIST' case...around 50%.
Given that map and foreach do almost exactly the same thing with only
the desired contexts being different, it makes sense to me that one
would use the version one desires based on that context difference.
Thus, when I see map in void context, the first thought in my mind is
"they didn't get the return from map!". Then, after scrutinising the
map's block (as it's usually a block not an expression), I may figure
out that they intended to have void (e.g., only doing it for the side
effect). And then, if that's not the source of the problem, I can go
on.

There's also a different syntax involved:

map BLOCK LIST
map EXPR, LIST

versus

for LIST BLOCK
STATEMENT for LIST

Particularly, you can change from EXPR to BLOCK without changing a lot
in your code when using map().
Simply have your code say what you mean rather than trick the virtual
machine into doing what you want by side effect. In general, side
effects make code harder to understand.

Depends on the standpoint. When I began with Perl, the return value of
map() was the side-effect (side-effect in the way that I didn't use this
feature). I was familiar with the idea of mapping a function to members
of a list however. That was before I learned about statement-modifiers
therefore using map() in void context was the natural choice for me (it
no longer is to this extent since these statement-modifiers somewhat
changed my habits).

Anyway, you can't make assumptions about how a programmer thinks. Or at
least you can't expect to always make the right ones.

Tassilo
 
B

Ben Morrow

Strangely enough, map is disproportionally slower in the 'map BLOCK
LIST' case...around 50%.

Presumably this is the overhead involved in setting up and tearing
down a BLOCK?

Ben
 
T

Tassilo v. Parseval

Also sprach Ben Morrow:
Presumably this is the overhead involved in setting up and tearing
down a BLOCK?

Yes, most certainly. But I would expect a similar overhead when using
blocks and foreach. Maybe it has something to do with the callback
nature of BLOCK in the map() case. Afterall, map() is a function while
foreach is not.

Tassilo
 
D

Darin McBride

Abigail said:
Darin McBride ([email protected]) wrote on MMMDCCXXIV
September MCMXCIII in <URL:mad:@ Abigail wrote:
@@
@@ > But could you give an objective reason why foreach is "clearer" than
@@ > map (after defining what "clearer" is)? Or is it your own preference?
@@
@@ <sigh> I thought I had. Let me try again:
@@
@@ The only real difference between map and foreach is that map returns a
@@ value, while foreach doesn't [1]. Thus, if you intend on ignoring the
@@ return, don't create it in the first place and just use foreach.

map in void context has never returned anything. No function in void
context has ever returned anything. How could it - if it returned
something, it couldn't have been in void context. Now, up to 2 versions
ago, map created a temporary list, even if it didn't need it. That has
now been fixed.

sub foo()
{
return qw(this is a small array);
}

foo();

And you're saying something in void context doesn't return anything?
No, I think perlstyle is right - map returns something (or at least it
did). The VM cleans it up by deallocating it immediately (or at the
end of the calling block, I'm not exactly sure when). But it is
returned.

Now with the map "fix", it won't create the array. And thus it won't
return anything. However, given that the difference between map and
foreach was solely that return difference, I don't see a point behind
the patch.
@@ > What makes foreach cleaner than while? Or is while cleaner than
@@ > foreach? Which one ought to go? Or can we have foreach, for, while,
@@ > until, goto, and bare blocks, but not map in void context?
@@
@@ While vs foreach is another discussion (I wish we'd stick to a single
@@ topic, but that's not generally possible on usenet, I suspect). If
the
@@ only data I have to work with is whether we're in void context or not,
@@ then neither while nor foreach are different. However, they have
other
@@ strengths which would come up should we actually debate those. Again,
@@ use whichever one makes the most readable sense.

The latter is a good remark. That's why I sometimes use while, sometimes
for/foreach, and sometimes map in void context. See, there is a difference
how you read them:

map ACTION DATA;
for DATA ACTION;

If I want to have the focus on the action, I prefer map. If I want to
focus on the action, I prefer for. But focus isn't the only thing that's
important for readability. I prefer shorter things before larger things.
So, if my block is large, I'll prefer to use for. But if the data is
coming from a long and complex expression, I'll favour map.

I see what you're saying - unfortunately, to me the simple perldoc on
map makes the void context of map counterintuitive: "... and returns
the list value composed of ..." And thus, those of us who do not have
any LISP background, have a certain expectation that you're using the
function according to the docs...
@@ Until vs while is easy - again, the most readable one wins. If your
@@ assertion is positive, use while, if it is negative, use until.

So, you decide on for vs while and while vs until on a case by case
basis. That's good. However, you dismiss map right away.

No - I use map more than I use for or foreach. But that's primarily
because I am mapping problem spaces from one space to another more than
I am simply looping through constructs.
@@ > :: o Avoid using grep() (or map()) or `backticks` in a void
@@ > :: context, that is, when you just throw away their
@@ > :: return values. Those functions all have return val-
@@ > :: ues, so use them. Otherwise use a foreach() loop or
@@ > :: the system() function instead.
@@ > ::
@@ > :: I think this is the whole debate. Unfortunately, there's no real
@@ > :: explicit justification here.
@@ >
@@ > Which makes it just the personal preference of one man, doesn't it?
@@
@@ No - it means that the perlstyle authors (who are probably more
@@ proficient in perl than the both of us put together) thought this
would
@@ be obvious and unneeding of justification.

Well, apparently it isn't obvious, and in dire need of a justification.
But I don't think Larry is going to do that. Some fragments of perlstyle:

Larry has his reasons for each of these things, but he
doesn't claim that everyone else's mind works the same as
his does.

And Larry probably has better knowledge of perl than the rest of us.
Being a linguist rather than a purist, he doesn't force us to do things
his way, but that doesn't mean his thoughts aren't likely to be better.

[ first element from perlstyle under the above quote deleted for space
reasons... this is getting too damned long already ;-> ]
See? Larry thinks that it's important that you mention the main point
first. So, if the main point is the action, not the data it acts upon,
you would write:

map ACTION DATA;

and not

for DATA ACTION;

None of the examples are ignoring return codes, which is also mentioned
explicitly later on in the perlstyle document (as I quoted earlier).
@@ > Ah, yes. Logical. There are thousands and thousands of user defined
@@ > functions whose return value isn't collected. There are many Perl
@@ > defined functions and operators whose return value isn't collected.
@@ >
@@ > Noone ever makes a fuss. But if the function of which the return
@@
@@ I do. If there are two user defined functions that do exactly the
same
@@ thing, but one allocates and returns an array, and the other does not
@@ but has the same desired side effects, I would expect people to use
the
@@ appropriate one depending on whether they want the array returned or
@@ not. I don't see any difference.

There are two points against that:

1) map in void context *doesn't* allocated an array whose
values remained unused.

Perhaps if you work exclusively with perl 5.8+, that might be true.
However, this doesn't work for two points:
1a) Some of us still work in shops that haven't upgraded from 5.6 yet
(no matter how hard I've pushed)
1b) map still allocates that array *conceptually* in the reader's
mind, even if no allocation actually takes place on the physical
machine.
2) it goes against all ideas of encapsulation. If in order
to use a module, I would first have to study the implementation
of its functions, why bother? I could write my own. But you
go further, you require people to first have studied the
implementation of perl before they can pick the "right way".
I presume you have carefully studied all the libraries Perl
links in as well?

Who said anything about studying the implementation? I haven't studied
the implementation of map or for either - only their docs. I *would*
expect that you would first study the perldoc on the module before
trying to use it... and if it documented two functions doing the same
thing, one that allocated and returned another object (whether ARRAY,
HASH or real object), and another that did exactly the same thing
without that allocation and return, then the point still stands.
@@ > I really don't get it. map in void context isn't doing anything else
@@ > than the countless of other functions in void context that are called
@@ > because of their side effects. Yet it sparks gigabytes of
discussions. @@
@@ Only because there is an exact duplicate of map functionally that is
@@ there for void context.

There is also an exact duplicate of 'unless' (which even takes less
characters to type). Should 'unless' be avoided as well? The functionality

Um, where did I say that? Are you fond of putting words in my mouth?
This is the same as the while/until comparison above: use if for
positive assertions, unless for negative assertions. "unless ($foo) {}"
is way more readable than "if (not $foo) {}". Use the right tool for
the job.
of while() can also exactly be duplicated with bare blocks. Should
while() be avoided? If the answers are 'no', then why is it so bad
there's another way of doing a map in void context?

Who said it was bad? I said it was goodness - so use the right tool
for the job!
@@ > Then you shouldn't be programming in Perl. Because in Perl, you can't
@@ > do much useful without side effects.
@@
@@ Hmmm... I dunno - I've managed to do a lot of useful stuff without
side
@@ effects in perl.

Really? Could you show some code? Remember, assignment and print are
only useful because of their side effects.

I respectfully must disagree with your definition of "side effects".
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,776
Messages
2,569,603
Members
45,189
Latest member
CryptoTaxSoftware

Latest Threads

Top