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

Hi,
I have execute following code.

#!/usr/bin/perl foo(); bar(); for (1..3) { my $x; sub foo { print ++$x } sub bar { print ++$x } print ++$x; }

Result: 12311

Could you please tell me how the result is coming like this??

Thanks,
Shine.

Replies are listed 'Best First'.
Re: Trying to understand closures
by davido (Cardinal) on Nov 23, 2006 at 05:26 UTC

    If I told you, I would be the one earning the grade on your quiz, not you. ;)

    Oh what the heck, who can resist?

    $x exists within a lexical scope accessible by the subroutines foo() and bar(). When foo() is called, it pre-increments $x (so it becomes 1) and prints that.

    Next, bar() is called on the same $x, pre-incrementing it and printing 2.

    Next, the loop begins. Ignore the subroutine declarations for now, they've already taken place. We've now entered the lexical scope in which foo() and bar() already pre-incremented $x when we called them earlier. On the first iteration of the loop we'll pre-increment it again, and print 3. That's probably the most counterintuitive step.

    Now, the loop iterates for the second time. This time we get a new lexical $x; one that hasn't been pre-incremented. And thus, 1 gets printed.

    Then we iterate one more time, and get a new lexical $x again, and thus 1 gets printed.

    What's cool is that the first lexical $x, the one that foo() and bar() incremented, and that got incremented on the first iteration of the loop still exists. If you call foo() after the loop, you'll get 4, indicating that foo() is still aware of and acting on the first lexical $x

    As the title of your post indicates, you're playing with closures here. I know closures are discussed in the Camel book. Probably the best discussion of closures in the POD is contained in perlref, I think I recall that the first time through it I found the Camel book's discussion clearer.


    Dave

      We've now entered the lexical scope in which foo() and bar() already pre-incremented $x when we called them earlier. On the first iteration of the loop we'll pre-increment it again, and print 3. That's probably the most counterintuitive step.

      It certainly is counterintuitive! Maybe I'm fuzzy brained tonight, but I am not clear why the first iteration of the loop would be different from the second and third iterations. In other words, I would expect the output to be 12111. Why does a new lexical $x get created the second and third times through the loop, but not the first? The statment my $x; is encountered all three times.

        A new lexical gets created on each iteration. But the subroutines live in the first iteration, it appears. That's why if you call foo() after the loop, you still get 4. Remember that my has both a compiletime and a runtime effect. When foo() and bar() are defined (as part of the compilation phase), the lexical scope in which they are created is ascertained at that time, and $x clearly lives in the same scope. It's a little shocking that a loop's block works the same as a bare block, but once you accept that, the rest fits together.

        Another way to think of it, and probably a more accurate way of thinking of it is that the lexical scope in which foo() and bar() are associated with $x is masked by the 2nd and 3rd iteration of the loop, when a new lexical $x of the same name is generated, thus masking previous $x's.


        Dave

Re: Trying to understand closures
by Anonymous Monk on Nov 23, 2006 at 18:38 UTC
    Try this:
    #!/usr/bin/perl
    foo();
    bar();
    for (1..3) {
        my $x;
        sub foo { print ++$x, "f\n" }
        sub bar { print ++$x, "b\n" }
        print ++$x, "m\n";
    }
    foo();
    bar();
    resulting in 
    1f
    2b
    3m
    1m
    1m
    4f
    5b
    
    foo and bar hold the first incarnation of x in the loop. The second and third incarnation of my $x is initialised with 0 so you get the 1m 1m lines in the midd.
Re: Trying to understand closures
by gam3 (Curate) on Nov 24, 2006 at 04:18 UTC
    What I think is confusing you is at what point $x is getting initialized. Becuse you do not explicitly intialize it it does not get set to 0 on the first iteration. Try the code:
    #!/usr/bin/perl foo(); bar(); for (1..3) { my $x=0; sub foo { print ++$x } sub bar { print ++$x } print ++$x; }
    With this code you get the more expected output of 12111.

    If seems that perl is "optimizing" your loop in some very odd manner.

    It may be (I did not look a the perl source) that perl is unrolling the loop so that it ends up looking something like:

    foo(); bar(); { my $x; sub foo { print ++$x; } sub bar { print ++$x; } print ++$x; { my $x; for (2..3) { print ++$x; undef $x; } } }
    That is: one $x is used for the closure and the first time through the loop and a different $x is used for the other times throught the loop.

    Update: updated the code of the second fragment to make it more clear what I think perl is doing. Added some text as well.

    -- gam3
    A picture is worth a thousand words, but takes 200K.

      No, no optimization was involved. Don't confuse my $x with undef $x. my $x does NOT change the value of $x. Aside from returning $x's value, all my $x does is set a flag to clear $x at the end of the current scope (i.e. at the end of the loop pass).

      Can you guess what the following snippet prints?

      for (1..4) { my $x; print ++$x; } print "\n"; for (1..4) { goto SKIP if $_ == 2; my $y; SKIP: print ++$y; } print "\n"; for (1..4) { my $z if $_ != 2; print ++$z; }

      At the end of every pass of the first loop, $x gets cleared, so the output of that loop is 1111.

      At the end of every pass *except the second* of the second loop, $y gets cleared, so the output of that loop is 1121.

      The third loop is identical to the second one. It also outputs 1121.

      Updated: Reworded for clarity. No new content.
      Updated: Added code.

        my $x does actually change the value of $x .

        Update: That's wrong. I don't know why I wrote that. As far as I know my creates a new variable so the later references to $x actually refer to a different instance of a variable even though they have the same name.

        For example this code:
        print __LINE__ . ": $x\n"; my $x = 123; print __LINE__ . ": $x\n"; my $x = 456; print __LINE__ . ": $x\n"; my $x; print __LINE__ . ": $x\n";
        Prints out:
        1: 3: 123 5: 456 7:
        Can you guess what the following snippet prints?
        for (1..4) { my $x; print ++$x, " ", \$x, "\n"; sub { $y = $x }; }
        And this?
        for (1..4) { my $x; print ++$x, " ", \$x, "\n"; sub bob { $y = $x }; }
        And now the answer: In the first case there is no closure, so only one $x is created, but in the second case there is a closure so 2 different $xs are created as you can see from the output.

        for example:

        1 SCALAR(0x8160300)
        1 SCALAR(0x8160300)
        1 SCALAR(0x8160300)
        1 SCALAR(0x8160300)
        
        1 SCALAR(0x81649e8)
        1 SCALAR(0x814cd38)
        1 SCALAR(0x814cd38)
        1 SCALAR(0x814cd38)
        
        -- gam3
        A picture is worth a thousand words, but takes 200K.