Iterator special variable $_

Discussion in 'Perl Misc' started by Mark Hobley, Jan 7, 2007.

  1. Mark Hobley

    Mark Hobley Guest

    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/
    Mark Hobley, Jan 7, 2007
    #1
    1. Advertising

  2. Mark Hobley

    Paul Lalli Guest

    Mark Hobley wrote:
    > 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
    Paul Lalli, Jan 7, 2007
    #2
    1. Advertising

  3. Mark Hobley wrote:
    > 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";
    }
    }

    --
    Gunnar Hjalmarsson
    Email: http://www.gunnar.cc/cgi-bin/contact.pl
    Gunnar Hjalmarsson, Jan 7, 2007
    #3
  4. Mark Hobley

    Uri Guttman Guest

    >>>>> "MH" == Mark Hobley <> writes:

    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

    --
    Uri Guttman ------ -------- http://www.stemsystems.com
    --Perl Consulting, Stem Development, Systems Architecture, Design and Coding-
    Search or Offer Perl Jobs ---------------------------- http://jobs.perl.org
    Uri Guttman, Jan 7, 2007
    #4
  5. Mark Hobley

    Mark Hobley Guest

    In alt.perl Paul Lalli <> wrote:

    > 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/
    Mark Hobley, Jan 8, 2007
    #5
  6. Mark Hobley

    Mark Hobley Guest

    In alt.perl Gunnar Hjalmarsson <> wrote:

    > 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/
    Mark Hobley, Jan 8, 2007
    #6
  7. Mark Hobley wrote:
    > In alt.perl Gunnar Hjalmarsson <> wrote:
    >
    >> 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?


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

    --
    John W. Kennedy
    "The blind rulers of Logres
    Nourished the land on a fallacy of rational virtue."
    -- Charles Williams. "Taliessin through Logres: Prelude"
    John W. Kennedy, Jan 8, 2007
    #7
  8. Mark Hobley wrote:
    > 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
    Jürgen Exner, Jan 8, 2007
    #8
  9. Jürgen Exner wrote:
    > Mark Hobley wrote:
    >>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.


    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.

    --
    Gunnar Hjalmarsson
    Email: http://www.gunnar.cc/cgi-bin/contact.pl
    Gunnar Hjalmarsson, Jan 8, 2007
    #9
  10. Mark Hobley

    Uri Guttman Guest

    >>>>> "JE" == J.AN|rgen Exner <> writes:

    JE> Mark Hobley wrote:
    >> 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.


    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

    --
    Uri Guttman ------ -------- http://www.stemsystems.com
    --Perl Consulting, Stem Development, Systems Architecture, Design and Coding-
    Search or Offer Perl Jobs ---------------------------- http://jobs.perl.org
    Uri Guttman, Jan 8, 2007
    #10
  11. Mark Hobley

    Verizon Guest

    "Mark Hobley" <> wrote in message
    news:...
    > 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
    Verizon, Jan 10, 2007
    #11
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Hendrik Maryns
    Replies:
    18
    Views:
    1,401
  2. Bobo

    Special map iterator

    Bobo, Feb 9, 2004, in forum: C++
    Replies:
    2
    Views:
    406
  3. KK

    Special iterator

    KK, Dec 16, 2005, in forum: C++
    Replies:
    3
    Views:
    284
    mlimber
    Dec 16, 2005
  4. Derek Basch
    Replies:
    8
    Views:
    128
    Ben Morrow
    Aug 12, 2006
  5. Mathematisch

    please help with creating a special iterator

    Mathematisch, Oct 18, 2010, in forum: Perl Misc
    Replies:
    4
    Views:
    101
    Xho Jingleheimerschmidt
    Oct 19, 2010
Loading...

Share This Page