Foreach

P

perler wannabe

Hi,

I read from a book that "for" is actually "foreach". And foreach is
preferable in Perl. I can understand foreach is better if the counting
is from 1 to n. But what if the count start from 2 or 3 or more, how
can we use foreach in this case?

Thanks
 
J

Jürgen Exner

perler wannabe said:
I read from a book that "for" is actually "foreach".

Yes, they are synonyms.
And foreach is preferable in Perl.

Preferable to what? To for? How so if they are synonyms?
I can understand foreach is better

Better than what?
if the counting
is from 1 to n. But what if the count start from 2 or 3 or more, how
can we use foreach in this case?

for my $i (3..n) {
.....
}

jue
 
P

Peter Makholm

Jürgen Exner said:
Yes, they are synonyms.


Preferable to what? To for? How so if they are synonyms?

The perlsyn manual page have a scetion called 'For Loops' describing
C-style for loops and a section called 'Foreach loops' describing the
'iteration over a list value' functionality.

It also says that the"foreach" _keyword_ is actually a synonym for the
"for" _keyword_ and that Perl executes a "foreach" statement more
rapidly then i would the equivalent "for" loop.


So based on the official documentation the terminologi is murky at
best and it is not quite unacceptable to talk about 'foreach being
prefeaable to for'

//Makholm
 
H

Helmut Richter

It's more accurate to say that 'iteration over a list' executes more
rapidly than a C-style loop, regardless of which keyword one happens
to use for either one.

That is, if I write "for my $i (1..1000000000)" I can be sure that perl
will not start by building a list of a billion entries?
 
U

Uri Guttman

HR> That is, if I write "for my $i (1..1000000000)" I can be sure that perl
HR> will not start by building a list of a billion entries?

that used to be the case in much older perls but today (i dunno when the
change was made but it was a good while ago) it will do an internal
iteration of large ranges.

uri
 
X

Xho Jingleheimerschmidt

Helmut said:
That is, if I write "for my $i (1..1000000000)" I can be sure that perl
will not start by building a list of a billion entries?

Only if you are sure you perl is not ancient.

But even small variations will cause problems.

for ("a", 1..100000000) will build the entire list. So will
for (reverse 1..100000000).

At least in 5.8.8

Xho
 
H

Helmut Richter

so you can use foreach for readability or 'for' for brevity.

Or "foreach" for lists and "for" for C-style loops. So you see already at
the keywords what to look for.
 
W

Wolf Behrenhoff

Or "foreach" for lists and "for" for C-style loops. So you see already at
the keywords what to look for.

sln actually cited perldoc perlsyn.

I only use for, both for lists and for C-style loops, although I don't
remember when I had to use a C-style the last time. Must be long ago.
C-style and list style for look so much different that I don't need two
different keywords for them.

I don't like C-style loops. One reason is that one has to look at the
comparison operator, often it is either < or <=. Sometimes it is hard to
spot if you are off by 1 with an index variable. In C++ there is
BOOST_FOREACH to get something similar to list-style for loops in Perl.

Wolf
 
K

Keith Thompson

Sherm Pendley said:
They're equivalent, yes.


I think it's more accurate to say that Perl-style looping over lists
is preferable over C-style loops with three arguments. Iterating over
the items in a list is a very common operation in Perl, and Perl-style
loops provide a more elegant way of doing that.

Agreed, in most cases. But sometimes it's useful (or even necessary) to
know inside the loop where you are in the loop, for example if you want
to treat the first last item specially.

For example, if I want to print each command-line argument on a separate
line, I might write (assume the usual "use strict; use warnings;" and so
forth):

foreach my $arg (@ARGV) {
print "$arg\n";
}

But if I want to print all the arguments on one line, as /bin/echo
does, a simple foreach doesn't work; I have to know when I'm
processing the last argument:

foreach my $i (0 .. $#ARGV) {
print "$ARGV[$i]";
if ($i < $#ARGV) {
print " ";
}
else {
print "\n";
}
}

(Or I might use a C-style for loop instead of the foreach.)

In short, one limitation of using foreach to iterate over a list is
that, inside the loop, the only information you have is the value
of the current element. Sometimes that's all you need, but in
the cases where you need more, you have to re-structure the loop.
(Though I could have added "my $arg = $ARGV[$i];" if I had a lot
of references to the current argument.)

Is there a cleaner idiom for this kind of thing that I'm not
thinking of? I suspect the answer is no.

(Yes, I could have used join() here; this is a simplified example
of a more general issue.)
 
T

Ted Zlatanov

KT> Agreed, in most cases. But sometimes it's useful (or even necessary) to
KT> know inside the loop where you are in the loop, for example if you want
KT> to treat the first last item specially.

As a general problem this is well-known; many algorithms and constructs
are stateless (they work at any offset during the iteration) while
others aren't. I'm not sure of the technical term :)

KT> Is there a cleaner idiom for this kind of thing that I'm not
KT> thinking of? I suspect the answer is no.

I usually think about the reasons why I need the first and last thing.
Sometimes I do this:

# same for the last element with pop(), obviously
process_first(shift @data);
process_rest($_) foreach @data;

Sometimes I keep the offset manually:

my $i = 0;
foreach my $x (@data)
{
...
}

Sometimes I do a Lisp-style "shift while true" loop:

# @data is a copy of the original data
while (@data)
{
my $element = shift @data;
do_first_thing() unless scalar @data;
do_general_thing();
}

Sometimes I use my own Every.pm module to do periodic actions:

foreach my $x (@data)
{
...
do_periodic_thing() if every(2);
}

....but mostly, I wish for the Common Lisp `loop' statement :) See
http://www.gnu.org/software/emacs/manual/html_mono/cl.html#Loop-Facility
for details on how that works.

Ted
 
J

Jim Gibson

Tad McClellan said:
From that, I thought you were going to present a case where a C-style
loop with three arguments was preferable over Perl-style looping over
lists...

One thing you can do with C-style for loops that you cannot do with
Perl-style loops is modify the value of the loop index inside the loop:

my $save;
for( my int $i = 0; $i < @ARGS; $i++ ) {
if( $ARGS[$i] =~ /-savenext/ ) {
$save = $ARGS[++$i];
}
}
 
K

Keith Thompson

Tad McClellan said:
From that, I thought you were going to present a case where a C-style
loop with three arguments was preferable over Perl-style looping over
lists...

No, I suppose I wasn't entirely clear. The distinction I was making
was between iterating over the elements of a list and iterating
over the indices of a list.

[...]
I have to know when I'm
processing the last argument:

foreach my $i (0 .. $#ARGV) {
print "$ARGV[$i]";


perldoc -q vars

Right, lose the quotes. (That was an editing error.)
foreach (my $i=0; $i<=$#ARGV; $i++) {
or
foreach (my $i=0; $i<@ARGV; $i++) {


I'd say that a Perl style loop "foreach my $i (0 .. $#ARGV)" was
preferable here too.
Agreed.

Were you going to present a case where a C-style loop was preferable?

No, I wasn't planning on it. :cool:}
Like in all of the code that you posted.

No, in the code I posted I need to know when I'm processing the last
item.
Were you going to present a case where a C-style loop was preferable?
Is there a cleaner idiom for this kind of thing that I'm not
thinking of?

print "$ARGV[0] "; # do something with the first arg
foreach my $i (1..$#ARGV-1) {
print "$ARGV[$i] ";
}
print "$ARGV[-1]\n"; # do something with the last arg

Hmm. Yes, that certainly works, but there's some duplication
of code. In this case the duplicated code is just a print, but as
I said this is a simple example.
You can generate a list of indexes using either style of loop, but
a Perl-style loop seems preferable for that.

You have presented a case where you need to know the index, but that
can be done with either type of loop.

[...]

More generally, here's the kind of thing I have in mind (pseudo-code):

First version:

foreach my $elem (@list) {
do_a_whole_bunch_of_stuff_with($elem); # multiple lines of code
}

In the second version, I need to add some minor functionality:

foreach my $elem (@list) {
do_a_whole_bunch_of_stuff_with($elem); # multiple lines of code
if (this is the first element) {
do_this_with($elem);
}
elsif (this is the last element) {
do_that_with($elem);
}
else {
do_the_other_thing_with($elem);
}
}

What I'm vaguely wishing for, but don't particularly expect to get,
is a clean way to detect, from inside the loop, whether I'm looking
at the first or last element *without* having to restructure the
loop to keep track of the current index.

I can't even think of a good way to design a language feature that
would support this (let alone think about how to implement it),
so I'll just live with the way it is now.
 
U

Uri Guttman

KT> No, in the code I posted I need to know when I'm processing the last
KT> item.

so use a while/shift loop instead. i stay away from indexing arrays as
much as possible as there is usually a better way to deal with things.

while( defined( my $elem = shift @array ) ) {

last_elem( $elem) unless @array ;

rest of code
}

KT> foreach my $elem (@list) {
KT> do_a_whole_bunch_of_stuff_with($elem); # multiple lines of code
KT> if (this is the first element) {
KT> do_this_with($elem);
KT> }
KT> elsif (this is the last element) {
KT> do_that_with($elem);
KT> }
KT> else {
KT> do_the_other_thing_with($elem);
KT> }
KT> }

you can just add a flag to the while/shift loop above to handle the
first element. but if all your work code is in subs, then a 3 layer set
is fine too:

my $elem = shift @array ;
do_this_first($elem);

while( defined( my $elem = shift @array ) ) {

if ( @array ) {

do_middle_elem( $elem ) ;
next ;
}

do_last_elem( $elem ) ;
}

or split the array with splice/slice and work each part separately
(untested):

my( $first, @middle ) = @array ;
my $last = pop @middle ;

do_first( $first ) ;

foreach my $elem ( @middle ) {
do_middle( $elem ) ;
}

do_last( $last ) ;

not too fugly. and better than indexing. perl6 will be able to loop over
indexes and elems together like each does for hashes.

there is even a way to do it with the range flipflop op. it has a neat
return value where the last count value it returns is in float with an
E0 on it.

this is tested and works. odd looking but fun!

my @elems = qw( a b c d ) ;

while( my $ind = (@elems .. @elems == 1) ) {

my $elem = shift @elems ;

print "IND $ind\n" ;

if ( $ind == 1 ) {
print "FIRST $elem\n" ;
next ;
}

if ( $ind =~ /e/i ) {
print "LAST $elem\n" ;
next ;
}

print "MID $elem\n" ;

}

uri
 
K

Keith Thompson

Uri Guttman said:
KT> No, in the code I posted I need to know when I'm processing the last
KT> item.

so use a while/shift loop instead. i stay away from indexing arrays as
much as possible as there is usually a better way to deal with things.

while( defined( my $elem = shift @array ) ) {

last_elem( $elem) unless @array ;

rest of code
}

Cool. Of course it destroys the array as you traverse it, which may or
may not be a problem.

[more good ideas snipped]
not too fugly. and better than indexing. perl6 will be able to loop over
indexes and elems together like each does for hashes.

Ah, yes, Perl6's "kv" operator seems to be just about exactly what
I was looking for. For example:

for @*ARGS.kv -> $index, $value {
say "index = $index, value = $value";
}

(Is Perl6 considered topical here?)

[more good stuff snipped]

As always, TMTOWTDI.
 
U

Uri Guttman

KT> No, in the code I posted I need to know when I'm processing the last
KT> item.
KT> Cool. Of course it destroys the array as you traverse it, which may or
KT> may not be a problem.

yeah, some good ideas do need to shift off the array and destroy it. one
workaround is to copy it first and work on the copy. i just don't seem
to design code than need array indexing much nor things like first and
last handling. sometimes a better design is the solution.

uri
 
M

Mladen Gogala

foreach my $arg (@ARGV) {
print "$arg\n";
}

Of course, there is a "map" equivalent, too:

map { print "$_\n"; } @ARGV;

I find that to be the most elegant way of looping through arrays. Of
course, if you need the global $_ variable, for instance for reading a
file, this trick is not applicable.
 
W

Willem

Mladen Gogala wrote:
) On Wed, 03 Nov 2010 10:34:33 -0700, Keith Thompson wrote:
)
)
)> foreach my $arg (@ARGV) {
)> print "$arg\n";
)> }
)
) Of course, there is a "map" equivalent, too:
)
) map { print "$_\n"; } @ARGV;

First: Looping through an array and doing stuff is for 'for' loops.
Map is for when you want the resulting values.

Example:
print map { "$_\n" } @ARGV;

Second: You can do the short version of for too:

Example:
print "$_\n" for @ARGV;

) I find that to be the most elegant way of looping through arrays.

If you want other people to read your code, then

for my $arg (@ARGV) { print "$arg\n"; }

is more readable (because it's like other languages) than

print "$_\n" for @ARGV;

) Of
) course, if you need the global $_ variable, for instance for reading a
) file, this trick is not applicable.

Wrong. The $_ is aliased to the elements and after the loop you get the
original value back.

So, in closing, I totally disagree with your whole post.


SaSW, Willem
--
Disclaimer: I am in no way responsible for any of the statements
made in the above text. For all I know I might be
drugged or something..
No I'm not paranoid. You all think I'm paranoid, don't you !
#EOT
 
D

Deadly Dirk

Wrong. The $_ is aliased to the elements and after the loop you get the
original value back.

I know that. The problem is that you don't have it within the loop.
So, in closing, I totally disagree with your whole post.

My post was, basically, describing my taste. It doesn't make much sense
to discuss somebody's taste. So, in closing, you can agree with me or you
can have a bad taste.

PS:
Before you explode, the last sentence was a joke.
 
M

Mladen Gogala

I know that. The problem is that you don't have it within the loop.



My post was, basically, describing my taste. It doesn't make much sense
to discuss somebody's taste. So, in closing, you can agree with me or
you can have a bad taste.

PS:
Before you explode, the last sentence was a joke.

Posted from the wrong ID. I apologize.
 

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

No members online now.

Forum statistics

Threads
473,999
Messages
2,570,243
Members
46,836
Latest member
login dogas

Latest Threads

Top