in reply to Re: for loops
in thread for loops, closures

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

Replies are listed 'Best First'.
RE (tilly) 3 (closures): for loops
by tilly (Archbishop) on Aug 21, 2000 at 00:41 UTC
    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.
        Exactly. See my example at RE (tilly) 3: for loops, closures to see how you can demonstrate this by modifying the array elements after you create the closure. From perlsyn:

        The foreach loop iterates over a normal list value and sets the variable VAR to be each element of the list in turn.
        It is directly aliased. If you modify the element that you are iterating over, you modify the elements of the array, if you modify the elements of the array you modify the element that you are iterating over, and if you create a closure that temporary aliasing becomes longer-lived.
      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