Iterator special variable $_

M

Mark Hobley

The iterator special variable can be used in a foreach loop as follows:

@fruit = ( "apples", "bananas", "cherries" );
foreach (@fruit) {
print "I like $_\n";
}

Is it accepted to use the special variable in a for loop as follows?

use strict;
use warnings;
for ($_=0; $_ <=9; $_++) {
print "$_\n";
}

(The above works and I get no errors.)

Would you expect nested loops using the iterator variable to work?

use strict;
use warnings;
for ($_=0; $_<=3; $_++) {
print "Loop $_\n";
for ($_=0; $_<=6; $_++) {
print "Value $_\n";
}
}

(The above does not work, but I get no warnings)

Could I fix these loops to work by changing variable scope?

Is it valid to change the scope of special variables?

Is the observed behaviour above undefined, or is there a document clarifying
this somewhere?


Mark.

--
Mark Hobley
393 Quinton Road West
QUINTON
Birmingham
B32 1QE

Telephone: (0121) 247 1596
International: 0044 121 247 1596

Email: markhobley at hotpop dot donottypethisbit com

http://markhobley.yi.org/
 
P

Paul Lalli

Mark said:
Is it accepted to use the special variable in a for loop as follows?

use strict;
use warnings;
for ($_=0; $_ <=9; $_++) {
print "$_\n";
}

(The above works and I get no errors.)

If by "accepted" you mean "does it work", then as you discovered, yes.
If, however, you mean "is this a good idea", the answer is no. Why
would you do that? What reason would you have to use a default
variable in that manner? In general, $_ should never be assigned to.
It's an implicit variable, and should be used as such. Declare a
counter variable if you need it for some reason, or just use a foreach
loop like $_ was designed to be used:

for (0..9) {
print "$_\n";
}
Would you expect nested loops using the iterator variable to work?

use strict;
use warnings;
for ($_=0; $_<=3; $_++) {
print "Loop $_\n";
for ($_=0; $_<=6; $_++) {
print "Value $_\n";
}
}

(The above does not work, but I get no warnings)

Odd, the above code works perfectly for me. It first assigns $_ to 0
and prints out the first print statement. Then it assigns $_ to 0
(again), and prints the inner loop's statement. Then it goes through
the inner loop, ending when $_ is set to 7, which is not less than or
equal to 6. Then it goes back up to the condition of the first loop,
sees that $_ is now greater than 3, and the first loop exits.

What were you expecting it to do?
Could I fix these loops to work by changing variable scope?

I have no idea what "fix" you're planning on making - the above code
does exactly what it should do.
Is it valid to change the scope of special variables?\

You cannot make a lexical variable named $_, if that's what you mean.
You can, however, localize $_, at the start of each loop. That would
result in the 0..6 loop printing out four times. Is that what you
want?
Is the observed behaviour above undefined, or is there a document clarifying
this somewhere?

The above behavior is exactly what I expect it to be. If it's not what
you expect it to be, you'll have to explain what you expect. It's
"defined" in that it's completely non-ambiguous...

Paul Lalli
 
G

Gunnar Hjalmarsson

Mark said:
Would you expect nested loops using the iterator variable to work?

Yes, if you localize it.
use strict;
use warnings;
for ($_=0; $_<=3; $_++) {

for (local $_ = 0; $_<=3; $_++) {
print "Loop $_\n";
for ($_=0; $_<=6; $_++) {

for (local $_=0; $_<=6; $_++) {
print "Value $_\n";
}
}

But this would be a more readable and Perlish solution IMO:

foreach my $loop ( 0..3 ) {
print "Loop $loop\n";
foreach my $val ( 0..6 ) {
print "Value $val\n";
}
}
 
U

Uri Guttman

MH> The iterator special variable can be used in a foreach loop as follows:

it is not the 'iterator' variable. a better name is the default
variable. it is used in many functions as the default argument, for some
i/o ops, regexes, etc.

my rule is to avoid $_ in general as i prefer named variables
(preferably lexicals) as they document the code better. i only use $_
when there is no other choice (map/grep) or where is provides a distinct
advantage (e.g. see Sort::Maker which uses it to pass in the records in $_
for key extraction).

uri
 
M

Mark Hobley

In alt.perl Paul Lalli said:
What were you expecting it to do?

I was expecting a nested loop with the inner iterator not affecting the outer
loop, ie the inner iterator scope is local to the inner loop.

Having done some googling, it appears that nesting a foreach loop would have
worked in the expected manner, because the iterator is preserved on entry and
exit from the loop, but this does not happen with a for loop.
You can, however, localize $_, at the start of each loop.

Ok. Thanks for the input.

Regards,

Mark.

--
Mark Hobley
393 Quinton Road West
QUINTON
Birmingham
B32 1QE

Telephone: (0121) 247 1596
International: 0044 121 247 1596

Email: markhobley at hotpop dot donottypethisbit com

http://markhobley.yi.org/
 
M

Mark Hobley

In alt.perl Gunnar Hjalmarsson said:
foreach my $loop ( 0..3 ) {

Ok, that is clever, but what if I wanted 1 to 50000?
Wouldn't that generate 50000 temporary values in memory to feed the loop, as
opposed to just one value iterating 50000 times?

Regards,

Mark.

--
Mark Hobley
393 Quinton Road West
QUINTON
Birmingham
B32 1QE

Telephone: (0121) 247 1596
International: 0044 121 247 1596

Email: markhobley at hotpop dot donottypethisbit com

http://markhobley.yi.org/
 
J

John W. Kennedy

Mark said:
Ok, that is clever, but what if I wanted 1 to 50000?
Wouldn't that generate 50000 temporary values in memory to feed the loop, as
opposed to just one value iterating 50000 times?

Theoretically, yes, but, in practice, perl has been smart enough not to
do that for ages.
 
J

Jürgen Exner

Mark said:
Having done some googling, it appears that nesting a foreach loop
would have worked in the expected manner, because the iterator is
preserved on entry and exit from the loop, but this does not happen
with a for loop.

You are mistaken. From "perldoc perlsyn":
The "foreach" keyword is actually a synonym for the "for" keyword, so
you can use "foreach" for readability or "for" for brevity.

jue
 
G

Gunnar Hjalmarsson

Jürgen Exner said:
You are mistaken.

Don't think so.
From "perldoc perlsyn":
The "foreach" keyword is actually a synonym for the "for" keyword, so
you can use "foreach" for readability or "for" for brevity.

A foreach loop is a foreach loop no matter which keyword you use.
 
U

Uri Guttman

JE> You are mistaken. From "perldoc perlsyn":
JE> The "foreach" keyword is actually a synonym for the "for" keyword, so
JE> you can use "foreach" for readability or "for" for brevity.

i think he means the difference between the foreach loop (over a list) and
the c style for loop. perlsyn states that the foreach loop variable is
localized unless it is declared right there with my. this happens for
named or $_. the c style for loop doesn't do anything and you have to
localize or use my vars yourself (even for $_).

The "foreach" loop iterates over a normal list value and sets the vari$B!>(B
able VAR to be each element of the list in turn. If the variable is
preceded with the keyword "my", then it is lexically scoped, and is
therefore visible only within the loop. Otherwise, the variable is
implicitly local to the loop and regains its former value upon exiting
the loop. If the variable was previously declared with "my", it uses
that variable instead of the global one, but it$B!G(Bs still localized to
the loop. This implicit localisation occurs only in a "foreach" loop.

now on top of that, you can use 'for' OR 'foreach' for either style of
loop. it is what is inside the () that actually determines the loop
style. the docs seem to use 'foreach' for the list loop and 'for' for
the c style loop. i do the same thing (but i RARELY use c style for
loops).

uri
 
V

Verizon

Mark Hobley said:
Would you expect nested loops using the iterator variable to work?

use strict;
use warnings;
for ($_=0; $_<=3; $_++) {
print "Loop $_\n";
for ($_=0; $_<=6; $_++) {
print "Value $_\n";
}
}

What you seem to want here is something like:

for(0..3)
{
print "Loop 0: $_\n" ;
for(0..6) # Perl automatically localizes $_, but now you can't see
the count for the outer loop
{
print "Loop 1: $_\n" ;
}
}

Perl will localize the scope of $_ for you and the $_ of the outer loop will
be maintained. $_ should not be thought of as an iterator, although it can
contain the value associated with one.

If you assign things to $_ all bets are off. You should avoid assigning to
the default variable ($_) - think of it as a global read-mostly even though
it isn't necessarily global and there are many cases where you will want to
modify it, such as in substitutions. "Never" is probably more appropriate
than "should", but "never" has its limitations too and Perl lets you do all
sorts of things that you shouldn't - that is why we love it so much!

If you need to be able to use the iterator from the outer loop within the
inner loop, you're better off assigning it to a local variable. In this
case you can do it the usual Perl way or use your iterator expression:

foreach my $outer (0..3) # or: for (my $outer = 0 ; $outer < 4 ;
$outer++)
{
print "Loop 0: $outer\n" ;
foreach my $inner (0..6) # etc.
{
print "Loop 1: $outer/$inner\n" ;
}
}

Craig Arnold
 

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

Staff online

Members online

Forum statistics

Threads
473,769
Messages
2,569,577
Members
45,052
Latest member
LucyCarper

Latest Threads

Top