http://www.perlmonks.org?node_id=68776

greenFox has asked for the wisdom of the Perl Monks concerning the following question:

I feel like I should add a disclaimer to this post, there could be some mistakes due to misunderstanding... please be gentle :) I was playing with some code, reduced here to-
use strict; my $var=0; for $var (1..9){ print $var; } print "\n", $var, "\n";

expecting it to output

        123456789
	9

what it actually outputs is

	
	123456789
	0

I got all excited thinking I had found a bug :) A quick search showed that it was in fact a "feature", the for loop localises the variable, Dominus describes it well here. Admittedly I know little of the wonders of perl internals but as a user of the language I agree with Dominus that "lexically scoping" (NB1) the index variable is a mistake, perl lets the programmer hang themselves in countless other ways without quibble and to get what I originally wanted I have to write-

my $var1=0; for my $var2 (1..9){ print $var2; $var1 = $var2; } print "\n", $var1, "\n";

or some other gymnastics?

am I right in thinking that it is this way so that you can write nested foreach loops without $_ clobbering itself all over the place? If that is the only reason why not check to see if the index variable is $_ and only localise then?

And just when I thought I had finally "got it" I tried this-

use strict; my $var; for ($var=1; $var < 10; ++$var){ print $var; } print "\n", $var, "\n";

following everything I had discovered so far I expected to get

        123456789
        0

++ to any-one who can guess what it actually prints because on my system (v5.6, rh7.0) it prints-

	123456789
	10

Not only is $var not localised by the for loop but it gets mysteriously incremented AFTER the loop has finished (at this point I did think that "duh! its because of when the ++ is happening" but combinations of $var++ and <= all behave the same), is *this* a feature as well?

NB1. As an aside I had to go look up lexical (Oxford paperback) because while I know what was meant from context I didn't know what it meant, lexical means "words of a language", in this case I interpreted it as the language is perl and the variable is scoped by the word "foreach". The camel (v2) defines lexical scoping as "variables whach are visible only from their point of declaration down to the end of the block in which they are declared", I should have looked in the camel first :)

--
my $chainsaw = 'Perl';

Replies are listed 'Best First'.
Re: (s)coping with foreach
by Dominus (Parson) on Apr 01, 2001 at 07:37 UTC
    "lexical" means "pertaining to the text".

    lexical variables are so-called because you can figure out their scope just by looking at the program text, with no thought about what it is doing or what order it is executed in or anything else pertaining to the running of the program. All you need to look at is the arrangement of curly braces on the page.

    Hope this helps.

(jeffa) Re: (s)coping with foreach
by jeffa (Bishop) on Apr 01, 2001 at 07:40 UTC
    You best off keeping $var in the scope of the loop:
    for my $var (0..9){ #UPDATE: I changed 1-9 to 0-9 print $var; } #actually, this is better . . . but I digress . . . print (0..9);
    No need to declare $var and assign zero to it like you did in the first example. There really is no need to keep $var around after you have finished the loop. If you need to remember what it was, look at the array you were looping through. Rarely are you going to say (1..9) in any serious program, unless you love playing catch-up to scalability:
    my @numbers = (0..9); foreach my $n (@numbers) { print $n; } print "\n", scalar(@numbers), "\n";
    will produce
    0123456789 10
    As for why this:
    my $var; for ($var=1; $var < 10; ++$var){ print $var; } print "\n", $var, "\n";
    yields $var = 10 after it finishes, the reason is because that's how a C-style for loop works. Think about it. It _HAS_ to be 10, because $var increments 1 at a time . . .
    and in order for the loop to stop . . . .
    $var has to be equal to 10 . . . ;)

    Jeff

    R-R-R--R-R-R--R-R-R--R-R-R--R-R-R--
    L-L--L-L--L-L--L-L--L-L--L-L--L-L--
    

      The somewhat strange syntax was to demonstrate the localisation of the index variable which I wasn't aware of :) As to using why you would want to do this, if you exit the loop at any point before the final iteration the count of elements may not be useful but the last value of the index variable will tell you when you left... no doubt there are better ways to do the same thing but I wasn't playing with production code I was just playing and I noticed something I didn't expect :)

      the c-style loop still bothers me, OK I can now see why it is greater than the last increment but it seems wrong that the variable contains this value outside of the loop when you have explicitly asked it to stop before you got there! Of course if I used the loop as intended I would never have noticed... or if the index variable was localised as in the perl style loop.

      --
      my $chainsaw = 'Perl';

        ...the last value of the index variable will tell you when you left...
        then try this:
        my @numbers = (0..9); foreach my $i (0..$#numbers) { print $numbers[$i]; stopped($i),last if $i > 4; } sub stopped { my $index = shift; print "Stopped at $index\n"; }

        Jeff

        R-R-R--R-R-R--R-R-R--R-R-R--R-R-R--
        L-L--L-L--L-L--L-L--L-L--L-L--L-L--
        
      i think the point of that last bit was that "foreach" and "for" act differently. Foreach localizes the $var and for doesn't. And that after people keep telling you that they are the same thing and you can use "for" for "foreach" =)

      --
      $you = new YOU;
      honk() if $you->love(perl)

        Says extremely:
        i think the point of that last bit was that "foreach" and "for" act differently.
        Definitely wrong. for and foreach are completely identical. Once Perl finishes compiling your program, it can't even remember which one you used.

        If you look in toke.c, you will see:

        case KEY_for: case KEY_foreach: ... code here that handles 'for' and 'foreach'
        This is in the vicinity of line 4145.
        Err,

        Do they? According to the camel:

        "The foreach keyword is actually a synonym for the for keyword, so you can use foreach for readability or for for brevity."

        - p100, Programming Perl, 2nd edition

        .02

        cLive ;-)

        Well that wasn't the point I was trying to make :) I tried this though
        my $var=0; foreach ($var=1; $var < 10; ++$var){ print $var; } print "\n$var \n";

        it appears that when used with a c style loop for/foreach does not localise the index variable... and for the record I have never written a loop with the index variable other than the normal syntax of for (my $var;... :-)

        --
        my $chainsaw = 'Perl';