Nested subroutines and Scoping in Perl

2019-01-18 03:26发布

问题:

I'm writing Perl for quite some time now and always discovering new things, and I just ran into something interesting that I don't have the explanation to it, nor found it over the web.

sub a {
   sub b {
     print "In B\n";
   }
}
b();

how come I can call b() from outside its scope and it works?

I know its a bad practice to do it, and I dont do it, I use closured and such for these cases, but just saw that.

回答1:

Subroutines are stored in a global namespace at compile time. In your example b(); is short hand for main::b();. To limit visibility of a function to a scope you need to assign an anonymous subroutines to a variable.

Both named and anonymous subroutines can form closures, but since named subroutines are only compiled once if you nest them they don't behave as many people expect.

use warnings;
sub one {
    my $var = shift;
    sub two {
        print "var: $var\n";
    }
}
one("test");
two();
one("fail");
two();
__END__
output:
Variable "$var" will not stay shared at -e line 5.
var: test
var: test

Nesting named subroutines is allowed in Perl but it's almost certainly a sign that the code is doing someting incorrectly.



回答2:

The following prints 123.

sub a {
    $b = 123;
}

a();
print $b, "\n";

So why are you surprised that the following does too?

sub a {
    sub b { return 123; }
}

a();
print b(), "\n";

Nowhere is any request for $b or &b to be lexical. In fact, you can't ask for &b to be lexical (yet).

sub b { ... }

is basically

BEGIN { *b = sub { ... }; }

where *b is the symbol table entry for $b, @b, ..., and of course &b. That means subs belong to packages, and thus can be called from anywhere within the package, or anywhere at all if their fully qualified name is used (MyPackage::b()).



回答3:

The "official" way to create nested subroutines in perl is to use the local keyword. For example:

sub a {
    local *b = sub {
        return 123;
    };
    return b();  # Works as expected
}

b();  # Error: "Undefined subroutine &main::b called at ..."

The perldoc page perlref has this example:

sub outer {
    my $x = $_[0] + 35;
    local *inner = sub { return $x * 19 };
    return $x + inner();
}

"This has the interesting effect of creating a function local to another function, something not normally supported in Perl."



回答4:

Subroutines are defined during compile time, and are not affected by scope. In other words, they cannot truly be nested. At least not as far as their own scope is concerned. After being defined, they are effectively removed from the source code.