Beefy Boxes and Bandwidth Generously Provided by pair Networks
We don't bite newbies here... much

Re: Lexical closures

by backstab (Novice)
on Oct 25, 2008 at 08:33 UTC ( #719480=note: print w/replies, xml ) Need Help??

in reply to Lexical closures

Perl foreach seems to have its own scope created for each loop and then the closure snaps the current scope. The following, code without the foreach code does 444,
my $i; $i = 0; push(@flist, sub {$i * $_[0]}); $i = 1; push(@flist, sub {$i * $_[0]}); $i = 2; push(@flist, sub {$i * $_[0]});
Python must behave this way, no per loop scope. But it let me ask myself (and the monks in fact) why the following code does 024?
my $i; foreach $i (0 .. 2) { push(@flist, sub {$i * $_[0]}); }
Does not my define the scope and then putting it before the loop should not make it apply on the loop? Note: lisp can behave both ways, and maybe even more, one may not compare lisp with javascript:)

Replies are listed 'Best First'.
Re^2: Lexical closures
by GrandFather (Sage) on Oct 25, 2008 at 08:57 UTC

    The "my $i" outside the loop does not declare a lexical variable that is used as the the loop variable in the way you think it does. Perl aliases the loop variable to each element in the for list as they are iterated over so the closure is not over $i, but over the aliased list elements. Consider:

    use strict; use warnings; my @flist; my @array = 0 .. 3; for my $elt (@array) { push @flist, sub {return $elt;}; } print $_->(), "\n" for @flist; @array[0 .. 3] = 10 .. 13; print $_->(), "\n" for @flist;


    0 1 2 3 10 11 12 13

    Perl reduces RSI - it saves typing

      Yes! Thanks. I've never think about aliasing longer than the easy way to work on the elements of an array directly, that is the purpose, in most case, of the foreach loop.

      But, I'm continuing playing with that and realize aliasing also implies narrowing the scope of the variable to the loop,

      my $i = 123; foreach $i (0..3) { ; } print "$i\n"; # 123

      No? If it does not narrow it itself the final value of $i should be 3.

      If so, the funny thing is in "foreach my $i" my is not revelant as far we do not control in fact the scope of $i at this point. So, can we consider a bug, or at least an over sanity warn, the fact forces us to put it here?

        It's not a bug; it was a deliberate design decision in (I believe) 5.004. Besides, you don't have to put my there. The aliasing works with global variables too:

        use 5.010; package Foo; use vars '$i'; use strict; use warnings; sub bar { for $i (1 .. 10) { main::baz( $i ); } } package main; use strict; use warnings; use Test::More tests => 12; $Foo::i = 100; sub baz { my $expect = shift; is( $Foo::i, $expect ); } baz(100); Foo::bar(); baz(100);

        I invariably write:

        for my $i (0..3) {

        so I am reminded of the scope of the loop variable and am not tempted to think of it having larger scope.

        A very good rule to follow is to always declare lexical variables in the smallest scope possible which precludes declaring declaring loop variables outside the for loop scope

        Perl reduces RSI - it saves typing
Re^2: Lexical closures
by spurperl (Priest) on Oct 25, 2008 at 19:44 UTC
    So let me see if I get this correctly... Perl's foreach creates a new lexical scope (i.e. a new binding a.k.a. stack frame) at each iteration.

    Python's for loop doesn't do that, and hence the difference in behavior.

    Does Perl's for (my $i = 0; $i < $N; ++$i) behave the same way?

    I wonder about the tradeoffs here. It seems likely that foreach's creation of new scopes cost something. Does it make it inherenty slower than a for loop that would not create a scope ?

      No. The confusion is that Perl effectively closes over the value of the variable, not a reference to the variable.

      All languages that I know where you can declare a variable locally to the scope of a block (including a block for a loop) effectively set up a new stack frame on each entry to the block, but the loop variable is outside that block or it couldn't maintain its contents from one iteration to the next.

      Perl reduces RSI - it saves typing
        The confusion is that Perl effectively closes over the value of the variable, not a reference to the variable.
        sub make_funcs { my $val = shift; (sub {$val * $val}, sub {$val++}) } my($f3, $i3) = make_funcs(3); my($f5, $i5) = make_funcs(5); say $f3->(); say $f5->(); $i3->(); say $f3->(); say $f5->(); __END__ 9 25 16 25
        I can't explain my results in combination with your claim.

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: note [id://719480]
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others having an uproarious good time at the Monastery: (3)
As of 2020-08-10 02:51 GMT
Find Nodes?
    Voting Booth?
    Which rocket would you take to Mars?

    Results (56 votes). Check out past polls.