in reply to for loops, closures

This is just a hunch, but could it be that the $i in the last example is treated as a reference at the end, so that the sub is not evaluated until in the last block. So it is evaluated to the last value of $i, the value that triggered the exit from the construction loop.

Maybe this should be fixed with scoping $i so it can't be reached by the last block?

update:This is a bit silly, but this is the way I solved it when I had the same problem ("my $o" in second last block):

my ( @ref_list, $code_ref, $i ); #pass one #for SCALAR LIST for $i (1..8) { push( @ref_list, sub { print " \$i == $i\n" } ); } print "for SCALAR LIST\n"; while ( $code_ref = pop @ref_list ) { &$code_ref; } #pass 2 #for INIT ; TEST ; INCREMENT for ( $i = 1; $i <= 8; $i++ ) { my $o; $o=$i; push( @ref_list, sub { print " \$i == $o\n" } ); } print "for (;;)\n"; while ( $code_ref = pop @ref_list ) { &$code_ref; }
/jeorgen

Replies are listed 'Best First'.
RE: Re: for loops
by Aighearach (Initiate) on Aug 20, 2000 at 15:58 UTC

    Excellent! Okay, but, why does it work? Does that mean that for (my $i;;) gives my a ref to a lexical variable instead of the actual variable? Hm, or a ref to a copy of whatever is in the init part? That doesn't seem to be it, because moving my $i before the for (;;) doesn't fix it, either.

    Can anybody explain the scoping differences between these lines:

    for my $i (1) { print $i } for ( my $i = 1; $i; $i--) { print $i } for ( my $i = 1; $i; $i--) { my $i = $i; print $i }
    In all three, $i disapears after the closing brace. So, why does the middle one fail with closures?
    Paris Sinclair    |    4a75737420416e6f74686572
    pariss@efn.org    |    205065726c204861636b6572
    http://sinclairinternetwork.com
    
      In case my other reply didn't make it clear.

      The first case half-way works. On each pass through the loop you get an alias to a different element of the list. Therefore the different iterations are different variables, but they are still the same as what is in the list so this is dangerous.

      The second case simply breaks since you create one variable and modify it during the loop. Think of closures as working by reference and you see the issue - you get a bunch of references to the same variable so all of them point at the same value in the end. (The last value during the loop.)

      The third case works because inside the loop you explicitly create a new variable which is completely private to that iteration of the loop. This not only means that your attempted closures are different for each iteration, it means that there is nothing still out there which can mess with your private closure by reference.

      Make sense?

        if I understand correctly, what you are saying is that:
        my @coderefs; for $i (@array) { push @coderefs, sub { print $i; }; }
        is (roughly) equivilant, scope-wise, to:
        my @coderefs; for (@array) { my $i = \$_; push @coderefs, sub { print $$i; }; }
        In the second case, it is abundantly clear that the $i captured by the closure is different for each iteration, and is an alias for the underlying array element.
        The first case half-way works.

        No, it fully works. The error in pass two is a logic error. This means, it doesn't do what I wanted. The first pass does what I want, and even passes strict. My problems were: A) a failure to grok that closures were using a deeply linked reference, and not a copy, and B) a failure to understand that for (;;) is not at all the same as for(), the latter doing what I would expect of it in each case.

        Paris Sinclair    |    4a75737420416e6f74686572
        pariss@efn.org    |    205065726c204861636b6572
        http://sinclairinternetwork.com