Silly push tricks

J

J Krugman

I would like to write something like

push &test($_) ? @sheep : @goats, $_ for @bleats;

but I get the error

Type of arg 1 to push must be array (not null operation)

Of course, I can break down and roll out something like

for ( @bleats ) {
if ( &test($_) ) {
push @sheep, $_
}
else {
push @goats, $_
}
}

....but, I'm curious, is there a way I can coax push into accepting
the one-line construct?

Thanks,

-Jill
 
S

Shawn Corey

J said:
I would like to write something like

push &test($_) ? @sheep : @goats, $_ for @bleats;

but I get the error

Type of arg 1 to push must be array (not null operation)

Of course, I can break down and roll out something like

for ( @bleats ) {
if ( &test($_) ) {
push @sheep, $_
}
else {
push @goats, $_
}
}

...but, I'm curious, is there a way I can coax push into accepting
the one-line construct?

Thanks,

-Jill


map{&test($_)?push@sheep,$_:push@goats,$_}@bleats;
 
G

Greg Bacon

: I would like to write something like
:
: push &test($_) ? @sheep : @goats, $_ for @bleats;
:
: but I get the error
:
: Type of arg 1 to push must be array (not null operation)

The compiler used to accept this back in the day, but now we have
to be a little more explicit:

push @{ &test($_) ? \@sheep : \@goats }, $_ for @bleats;

Hope this helps,
Greg
 
B

Bill Smith

--snip--
map{&test($_)?push@sheep,$_:push@goats,$_}@bleats;
I think that it is poor style to use 'map' when the return value is not
used. Use 'foreach' instead. My extra parens may not be necessary, but
I do not trust my memory of precedence rules.

Bill

&test($_)?push(@sheep,$_):push(@goats,$_) foreach @bleats;
 
U

Uri Guttman

SC> map{&test($_)?push@sheep,$_:push@goats,$_}@bleats;

avoid map in a void context.

avoid & for calling subs

ever heard of whitespace?

uri
 
S

Shawn Corey

Bill said:
--snip--


I think that it is poor style to use 'map' when the return value is not
used. Use 'foreach' instead. My extra parens may not be necessary, but
I do not trust my memory of precedence rules.

Bill

&test($_)?push(@sheep,$_):push(@goats,$_) foreach @bleats;
That's like saying all subroutines must return a value. Perl, more than
one way to do things.
 
T

Tore Aursand

That's like saying all subroutines must return a value.

No, it's not. Consider the following statement from 'perldoc';

What's wrong with using grep or map in a void context?

The problem is that both grep and map build a return list,
regardless of the context. This means you're making Perl go to
the trouble of building a list that you then just throw away.
If the list is large, you waste both time and space. If your
intent is to iterate over the list then use a for loop for this
purpose.
Perl, more than one way to do things.

Sure, but that doesn't mena you shouldn't follow good practices.


--
Tore Aursand <[email protected]>

"Whenever I see an old lady slip and fall on a wet sidewalk, my first
instinct is to laugh. But then I think, what if I was an ant, and she
fell on me. Then it wouldn't seem quite so funny." -- Jack Handey
 
C

Charlton Wilbur

[using map in void context]

A> Poor style? Why?

Because

map { fn($_) } @list;

and
fn ($_) foreach @list;

(where fn is a function with side-effects) do the same thing, but the
latter is better style.

The issue that map can be inefficient when called in void context is
orthogonal to the style issue.

Charlton
 
S

Sam Holden

[using map in void context]

A> Poor style? Why?

Because

map { fn($_) } @list;

and
fn ($_) foreach @list;

(where fn is a function with side-effects) do the same thing, but the
latter is better style.

Well, they aren't exactly same. The first calls fn() in a list context,
while the second calls it in a scalar context...
 
C

Charlton Wilbur

A> Beside that they are different (the context in which fn is
A> called differs) you don't give *any* argument why one is better
A> style than the other.

map means "generate a new list by applying this block to each element
of this other list." foreach means "execute this block once for each
element of the list." When you use them interchangeably, you confuse
that meaning, even though the code does (largely) the same thing.

I mean, if we saw this code:

my $i = 1;
while ($i <= 30)
{
fn ($i);
$i++;
}

we'd consider it bad style. It does the same thing as

for (my $i=1; $i <= 30; $i++)
{
fn ($i);
}

or

fn ($_) foreach (1..30);

but the clarity of them varies. This is why we say "it's bad style"
and not "it's incorrect code."

As a parallel example, I also use 'for' for C-style for (;;) loops and
'foreach' for iterating over a list with a variable; I'd say swapping
those two, even though they're synonymous to the interpreter, is also
bad style.

A> But that's the usual argument against using map in void
A> context.

It's certainly not *my* usual argument.

Charlton
 
T

Tassilo v. Parseval

Also sprach Abigail:
Bill Smith ([email protected]) wrote on MMMDCLIV September MCMXCIII
in <URL::}
:} :} --snip--
:} > map{&test($_)?push@sheep,$_:push@goats,$_}@bleats;
:} >
:} I think that it is poor style to use 'map' when the return value is not
:} used.

Poor style? Why? Return values of print and assignment are usually not
used either, but noone considers that poor style.

The fact that map builds and discards a list when it's used in void
context is a bug in *perl*. It's not poor style of the Perl programmer;
the fault lies with the perl programmers.

But noone has found it seriously enough to provide a patch. For years,
a whole chorus on people in this newsgroup chant "poor style" whenever
someone uses map in void context. But none of those sheep has ever
bothered sending a patch to p5p.

You are right, especially when considering that this change to the core
should be trivial. And so I just modified pp_ctl.c:pp_mapwhile()
accordingly to see what happens. All tests pass but I yet have to test
whether there's really no return-list built (testing whether something
is returned in void context is tricky;-).

And then I need to do some timing comparisons. If the results are
satisfying, you'll see the patch on the porters-list soon.

Tassilo
 
A

Anno Siegel

Sam Holden said:
"A" == Abigail <[email protected]> writes:

[using map in void context]

A> Poor style? Why?

Because

map { fn($_) } @list;

and
fn ($_) foreach @list;

(where fn is a function with side-effects) do the same thing, but the
latter is better style.

Well, they aren't exactly same. The first calls fn() in a list context,
while the second calls it in a scalar context...

For sensible fn() this won't matter, since the return value is discarded.
It is of course possible for a function to have different side effects
depending on context, but, well...

To my mind, the main difference between map in void context and foreach
is that map is slightly more flexible because it takes an expression
or a block. With foreach, you need an expression, so you need "do {}"
to accommodate a block.

Anno
 
T

Tassilo v. Parseval

Also sprach Abigail:
Tassilo v. Parseval ([email protected]) wrote on MMMDCLVI
September MCMXCIII in <URL:??
?? You are right, especially when considering that this change to the core
?? should be trivial. And so I just modified pp_ctl.c:pp_mapwhile()
?? accordingly to see what happens. All tests pass but I yet have to test
?? whether there's really no return-list built (testing whether something
?? is returned in void context is tricky;-).
??
?? And then I need to do some timing comparisons. If the results are
?? satisfying, you'll see the patch on the porters-list soon.


That would be great!

Done. Now it's up to the porters to either find holes in the patch
(uggh) or apply it :)-).

Tassilo
 
A

Anno Siegel

Tassilo v. Parseval said:
Also sprach Abigail:


Done. Now it's up to the porters to either find holes in the patch
(uggh) or apply it :)-).

APPLAUSE

Anno
 
S

Shawn Corey

Ben Kennedy wrote:
8< snip
Using map() or grep() like this obfuscates the intent of the code. It
certianly works, but it is not the most straightforward way of expressing
the notion of "do one thing for each element in this list" - foreach()
clearly is. Whether or not this is "poor style" depends on your point of
view, of course. I prefer code to be as unambiguous as possible, as this
makes it is easier to read, understand, and maintain. Using functions and
modifers as described in the documentation and FAQ tend to do this for me.
"Bad style" is clearly in the eye of the beholder, as someone could be
perfectly comfortable with using map to iterate over loops and would not
experience any loss in comprehension... but I'd guess that most seasoned
Perl programmers would blink once or twice.

--Ben Kennedy

Of course you have overlooked the original question: How to do this on
one line? Therefore style is relevant.
 
A

Anno Siegel

Ben Kennedy said:
Abigail said:
Charlton Wilbur ([email protected]) wrote on MMMDCLV
September MCMXCIII in <URL::)
:) [using map in void context]
:)
:) A> Poor style? Why?
:)
:) Because
:)
:) map { fn($_) } @list;
:)
:) and
:) fn ($_) foreach @list;
:)
:) (where fn is a function with side-effects) do the same thing, but the
:) latter is better style.

Beside that they are different (the context in which fn is called
differs) you don't give *any* argument why one is better style
than the other.

Using map() or grep() like this obfuscates the intent of the code. It
certianly works, but it is not the most straightforward way of expressing
the notion of "do one thing for each element in this list" - foreach()
clearly is.

The inventors of LISP (the language that introduced the map operator to
computing) thought otherwise. There, the map operation is clearly iterative.
It means: Apply this functions to successive elements (tails, actually)
of the original list. No collection of results is being done, you
need maplist for that. (The real LISP counterpart of Perl's map is
mapcar. It applies a function to successive elements (not tails), *and*
collects the results. Go figure.)

[snip style]

Anno
 
C

Charlton Wilbur

A> So, the "there is more than one way of doing it" is just a
A> marketing slogan, because in reality, there's just one way of
A> doing it, and alternatives are "bad style"?

No. There is frequently more than one way to do it, but not all of
them are equally good for all situations. There are numerous sorting
algorithms; does this mean that bogosort is as good a way of sorting a
list as mergesort? This is why a broad variety of approaches is
valuable, and I don't think that pointing out that a particular
approach is badly suited for the task at hand is in conflict with
TMTOWTDI.

A> In perl6, there won't be a 'foreach' keyword. Oh my! You will
A> be programming in bad style!

Well, for one, I probably won't be programming in perl6 at all; by the
time it arrives, I'll be in another career entirely, but that's
largely irrelevant to the world at large.

For another, the point of considering style in the first place is to
get the point of the code across to the reader or readers of the code.
If there are multiple possible ways to express something, then the
programmer ought to choose the clearest way; not doing so is poor
style. At the same time, good style varies across languages: things
that are perfectly idiomatic in C are poor style in Perl, for
instance; perl6 is going to be substantially different from perl5, and
will almost certainly have different stylistic criteria as well.

Charlton
 

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
474,135
Messages
2,570,783
Members
47,341
Latest member
hanifree

Latest Threads

Top