Re: Using an "outer" lexical in a sub?
by Joost (Canon) on Nov 01, 2006 at 20:22 UTC
|
Perl doesn't really make a distinction between "normal" blocks and subroutine blocks with regard to lexical scoping rules. You can even take and copy a reference to a subroutine that accesses lexicals in its outer scope that way. This is by design. See also perlfaq7 (what's a closure?), perlsub and perlref.
use strict;
sub make_ref {
my $name = shift;
my $i = 99;
return sub {
print "ref $name: ",$i++,"\n";
}
}
my $ref1 = make_ref("sub1");
my $ref2 = make_ref("sub2");
for (1 .. 10) {
$ref1->();
$ref2->() if $_ % 2 == 0;
}
output:
ref sub1: 99
ref sub1: 100
ref sub2: 99
ref sub1: 101
ref sub1: 102
ref sub2: 100
ref sub1: 103
ref sub1: 104
ref sub2: 101
ref sub1: 105
ref sub1: 106
ref sub2: 102
ref sub1: 107
ref sub1: 108
ref sub2: 103
| [reply] [d/l] [select] |
|
|
Very interesting... each copy of that referenced subroutine which gets returned by make_ref() gets its own "freeze-dried" copy of $i...
I've got some reading to do. Thank you, and thanks also for the link to perlfaq7.
| [reply] |
|
|
Hm... I just tried something which, to me, looks like it should behave the same as the above code, but it won't do the same trick:
#!/usr/bin/perl
use strict;
use warnings;
my $ref1;
my $ref2;
{
my $n = 10;
$ref1 = sub { print( $n++, "\n" ) };
$ref2 = sub { print( $n++, "\n" ) };
}
$ref1->(); # --> 10
$ref1->(); # --> 11
$ref1->(); # --> 12
print "\n";
$ref2->(); # --> 13 Huh?
$ref2->(); # --> 14 ?
$ref2->(); # --> 15 ?
I'd expect that first call to $ref2->(); to start up with its own squirreled-away $n (starting at 10). Why doesn't it?
| [reply] [d/l] |
|
|
sub test() {
my $n = 10;
{ print( $n++, "\n" ) }
{ print( $n++, "\n" ) }
print "\$n is now $n\n;
}
Now, you can see that both blocks access the same variable (i.e. $n will be 12 at the end of test()).
If you run test() again, my $n = 10; will effectively be a new variable $n but if you still have a subref somewhere that accesses the old $n (i.e. a reference to a to one or more of the inner blocks) that code ref will hang on to its variables and new ones will be created if necessary.
Note that in my earlier post the outer subroutine is called twice and that will create the new lexical variable.
| [reply] [d/l] [select] |
|
|
|
|
| [reply] [d/l] |
Re: Using an "outer" lexical in a sub?
by ikegami (Patriarch) on Nov 01, 2006 at 20:16 UTC
|
As you discovered, lexical vars can be globals too :)
Now try the following snippets:
#!/usr/bin/perl
#use strict;
use warnings;
sub foo { $n++; }
$n = 10;
print "$n\n"; # 10
foo();
print "$n\n"; # 11
#!/usr/bin/perl
use strict;
use warnings;
sub foo { $n++; } # Compile-time (strict) error!
our $n = 10;
print "$n\n"; # 10
foo();
print "$n\n"; # 11
#!/usr/bin/perl
use strict;
use warnings;
# "our $n;" disables "use strict 'vars';" for $n for the
# remainder of the lexical scope in which it is located.
sub foo { our $n; $n++; }
our $n = 10;
print "$n\n"; # 10
foo();
print "$n\n"; # 11
#!/usr/bin/perl
use strict;
use warnings;
sub foo { my $n; $n++; } # Different $n!
my $n = 10;
print "$n\n"; # 10
foo();
print "$n\n"; # 10
#!/usr/bin/perl
use strict;
use warnings;
sub foo { $n++; } # Compile-time (strict) error!
my $n = 10;
print "$n\n";
foo();
print "$n\n";
#!/usr/bin/perl
use strict;
use warnings;
my $n = 10;
sub foo { $n++; }
print "$n\n"; # 10
foo();
print "$n\n"; # 11
| [reply] [d/l] [select] |
|
|
#!/usr/bin/perl
use strict;
use warnings;
# "our $n;" disables "use strict 'vars';" for $n for the
# remainder of the lexical scope in which it is located.
sub foo { our $n; $n++; }
our $n = 10;
print "$n\n"; # 10
foo();
print "$n\n"; # 11
Well, this example helps me somewhat (though seemingly (to me) not directly with what I was originally confused about). Thank you. I'd previously always written "our $n = $whatever;" assuming that it atomically meant, "create a package global named $n with $whatever value". Looking more closely at our though, I see that that statement is really doing *2* separate things:
- Letting me refer to $main::n as just plain $n.
- Setting $n to something ($n springs into existence at *this* point).
(Should've read the docs on our more carefully -- they're very good.)
So, in your example above, I now see that "our $n;" in sub foo isn't defining anything -- just telling perl that I'll be referring to $main::n in here as "$n". :)
| [reply] [d/l] |
|
|
Pragmas (such as use strict), my variables, our declerations (but not package variables), package statements, etc all have lexical scope. A lexical scope spans (includes) its child lexical scope.
use strict;
sub f {
# strict still in effect in this child block
if (...) {
# strict still in effect in this child block
for (...) {
# strict still in effect in this child block
{
# strict still in effect in this child block
}
}
}
}
my $n;
sub f {
# $n is still accessible in this child block
if (...) {
# $n is still accessible in this child block
for (...) {
# $n is still accessible in this child block
{
# $n is still accessible in this child block
}
}
}
}
{
use strict;
# strict still in effect
}
# strict no longer in effect
{
my $n;
# $n accessible.
}
# $n no longer accessible.
| [reply] [d/l] [select] |
|
|
|
|
Letting me refer to $main::n as just plain $n.
Or $Whatever::The::Current::Package::Is::n ofcourse :-)
our() is a fairly recent introduction addition to perl. In the olden days, you'd have to use vars, which just switches off strict 'vars' for specific variables.
For additional fun and confusion see local.
| [reply] |
|
|
|
|
|
Re: Using an "outer" lexical in a sub?
by GrandFather (Saint) on Nov 01, 2006 at 20:14 UTC
|
$n1 is simply a global. It is global to everything (including subroutines and nested blocks) from the point where it is declared onward.
Actually no language I can think of (of the small number I've used) hides global variables from subroutines/functions/class members. Is there a bigger picture here that is leading to your confusion?
Update: Fletch is quite right that I'm playing fast and loose with nomenclature here. C's 'file scope' is the notion I intended by "global". My appologies for any confusion caused.
DWIM is Perl's answer to Gödel
| [reply] |
|
|
It's a lexical at the outermost scope (what'd probably be referred to as "file scope" in C-land) and it's visibility is limited to everything lexically within the file it's declared in (after the point at which the declaration occurs, of course).
I think you're getting --'d here because calling a file scope'd lexical a "global", the term usually in Perl usage referring to package variables which live in the truly global symbol table, is only likely to further confuzzle people.
Update: Someone else below has them called "file statics" in C; I've heard both terms I think.
| [reply] |
Re: Using an "outer" lexical in a sub?
by cmeyer (Pilgrim) on Nov 01, 2006 at 20:04 UTC
|
You are learning about closures. :)
Have a good read at perlsub, and pay close attention to all the talk about lexical scoping.
-Colin.
WHITEPAGES.COM | INC
| [reply] |
Re: Using an "outer" lexical in a sub?
by derby (Abbot) on Nov 01, 2006 at 20:36 UTC
|
From perlsub:
If declared at the outermost scope (the file scope), then lexicals work somewhat like C’s file statics. They are available to all func‐
tions in that same file declared below them, but are inaccessible from outside that file. This strategy is sometimes used in modules to
create private variables that the whole module can see.
So
my $var;
sub foo {
$var++;
}
is similar (from a scope perspective) to:
sub foo {
my $var;
{
$var++;
}
}
Trying to avoid this *unintended* global issue is why
you see some perl code with subroutines called main or
with all the subs defined at the beginning of a file and
the driver portion at the end:
sub foo {
...
}
sub bar {
...
}
my $var = 0;
foo();
bar();
or
main();
sub main {
my $var = 0;
foo();
bar();
}
sub foo {
...
}
sub bar {
...
}
I neither condemn nor condone these approaches - just pointing out why some devs do what they do; although the reverse definition always seemed too pascally to me.
| [reply] [d/l] [select] |
Re: Using an "outer" lexical in a sub?
by imp (Priest) on Nov 01, 2006 at 20:41 UTC
|
The lexical variable in your example is in the package scope.
Compare this:
package foo;
our $n = 1;
package main;
print "n = ", $foo::n , $/; # n = 1
With this:
package foo;
my $n = 1;
package main;
print "n = ", $foo::n , $/; # nothing
A related topic is the behaviour of closures, which was mentioned by several other monks above. One of the less obvious behaviours of closures can be seen in the following example:
sub foo {
my $n = 1;
print $n;
bar();
print $n;
bar();
sub bar {
$n++;
}
}
foo();
foo();
#output 1211
The inner sub 'bar' keeps a reference to the instance of '$n' that existed during the first call to 'foo'. People frequently run into this problem when they first start using mod_perl with Apache::Registry, as documented here:
mod_perl / Apache::Registry accidental closures
| [reply] [d/l] [select] |
Re: Using an "outer" lexical in a sub?
by holcapek (Sexton) on Nov 02, 2006 at 08:40 UTC
|
There is something called package scope. In each point in time, perl code is in exactly one package scope.
There is something pretty different called lexical scope. In each point of time, perl code can be in one or more lexical scopes. Lexical scopes are distributed into nested lexical scopes.
my $outer = 1; # lexical file scope
{
my $inner = 1;
# here I can see both $inner and $outer
# as $outer is "inherited" from the outer
# lexical scope
{
my $inner = 2;
# this $inner is completely different $inner
# than the $inner in the direct outer scope
# but still I can see $outer
}
# now I can see $inner that is equal to 1
}
# now I can see only $outer
For additional reading, I recommend perlsub, namely "Private Variables via my()".
| [reply] [d/l] |