2012-04-17 26 views
14

Escribo Perl por bastante tiempo y siempre descubro cosas nuevas, y me encontré con algo interesante que no tengo la explicación ni lo encontré en la web.Subrutinas anidadas y Scoping en Perl

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

¿cómo es que puedo llamar b() desde fuera de su alcance y funciona?

Sé que es una mala práctica hacerlo, y no lo hago, utilizo closured y tal para estos casos, pero acabo de ver eso.

+0

Solo puede aplicar este tipo de ámbito con OOP –

+0

algo así como cierres le ayudarán;) no es lo mismo que está preguntando, pero otra buena implementación de subrutinas. – gaussblurinc

+1

Perl v5.20 tiene [léxicas llamadas subrutinas ] (http://www.effectiveperlprogramming.com/2015/01/named-lexical-subroutines/) –

Respuesta

15

Las subrutinas se almacenan en un espacio de nombre global en tiempo de compilación. En su ejemplo b(); es mano corta para main::b();. Para limitar la visibilidad de una función a un ámbito, debe asignar una subrutina anónima a una variable.

Tanto las subrutinas con nombre como las anónimas pueden formar cierres, pero como las subrutinas nombradas solo se compilan una vez si las anida, no se comportan como muchas personas esperan.

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 

anidamiento nombrados subrutinas está permitido en Perl pero es casi seguro que una señal de que el código está haciendo calle detrás incorrectamente.

+0

Ese es un ejemplo interesante. ¿Cómo se explica que no actualice '$ var' la segunda vez? Curiosamente, si elimina la línea 'one (" test ")', se queja de la variable indefinida, pero luego 'one (" fail ")' *** *** está permitido actualizar '$ var'. ¿Comportamiento indefinido? – TLP

+0

Creo que encontré la respuesta en [perlref] (http://perldoc.perl.org/perlref.html#Function-Templates). – TLP

+5

@TLP, 'sub two {}' es 'BEGIN {* two = sub {}}', por lo que captura el '$ var' que existía cuando se compiló. 'my' es básicamente un' nuevo' que ocurre en la salida del alcance, por lo que 'two''s' $ var' se diferencia de 'one''s' $ var' después de la primera ejecución de '$ one'. – ikegami

2

Las subrutinas se definen durante el tiempo de compilación y no se ven afectadas por el alcance. En otras palabras, no se pueden anidar realmente. Al menos no en lo que respecta a su propio alcance. Después de definirse, se eliminan efectivamente del código fuente.

+1

Bueno, ellos se pueden anidar, pero no con ámbito léxico. –

+0

@briandfoy parece que hay más a él que sabía. . encuentran una explicación muy críptico en [perlref] (http://perldoc.perl.org/perlref.html#Function-Templates) ¿es este comportamiento intencional Qué uso posible podría tener – TLP

+0

@briandfoy -.?? "asumo por 'se puede anidar', quiere decir 'puede colocarse físicamente dentro del código de otro sub'; en contraposición a 'puede tener la semántica afectadas por dicha colocación anidada'", correcto? – DVK

5

Las siguientes impresiones 123.

sub a { 
    $b = 123; 
} 

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

¿Por qué te sorprende que lo siguiente también lo haga?

sub a { 
    sub b { return 123; } 
} 

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

En ninguna parte es cualquier solicitud de $b o &b ser léxica. De hecho, no puede pedir que &b sea léxico (aún).

sub b { ... } 

es básicamente

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

donde *b es la entrada de la tabla de símbolos para $b, @b, ..., y por supuesto &b. Eso significa que los subs pertenecen a los paquetes, y por lo tanto se pueden llamar desde cualquier lugar dentro del paquete, o en cualquier lugar si se usa su nombre completamente calificado (MyPackage::b()).

3

La forma "oficial" para crear subrutinas anidadas en perl es usar la palabra clave local. Por ejemplo:

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

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

La página perldoc perlref tiene este ejemplo:

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

"Esto tiene el efecto interesante de crear una función local a otra función, algo que no es apoyado normalmente en Perl."

Cuestiones relacionadas