Pregunta

Cuando leí Programming Perl , 2nd Edition, página 51, algo me confunde:

sub newopen {
    my $path = shift;
    local *FH;    #not my!
    open (FH, $path) || return undef;
    return *FH;
}

$fh = newopen('/etc/passwd');

Lo sé, ¿por qué no se nos recomienda volver a usar mi? Hasta ahora, no puedo ver que algo salga mal si usamos my ().

¡Gracias!

¿Fue útil?

Solución

La respuesta trivial es que debe usar local porque my * FH es un error de sintaxis.

El " derecha " (pero no muy esclarecedor) la respuesta es que lo estás haciendo mal. Debería usar los identificadores de archivos léxicos y la forma de tres argumentos de open en su lugar.

sub newopen {
    my $path = shift;
    my $fh;
    open($fh, '<', $path) or do {
        warn "Can't read file '$path' [$!]\n";
        return;
    }
    return $fh;
}

Para responder realmente por qué se requiere una explicación de la diferencia entre las variables léxicas y globales y entre el alcance de una variable y su duración.

El alcance de una variable es la parte del programa donde su nombre es válido. El alcance es una propiedad estática. La duración de una variable, por otro lado, es una propiedad dinámica. La duración es el tiempo durante la ejecución de un programa que la variable existe y tiene un valor.

my declara una variable léxica. Las variables léxicas tienen un alcance desde el punto de declaración hasta el final del bloque (o archivo) adjunto. Puede tener otras variables con el mismo nombre en diferentes ámbitos sin conflicto. (También puede reutilizar un nombre en ámbitos superpuestos, pero no lo haga). La duración de las variables léxicas se administra mediante el recuento de referencias. Siempre que haya al menos una referencia a una variable, el valor existe, incluso si el nombre no es válido dentro de un ámbito particular. my también tiene un efecto de tiempo de ejecución: asigna una variable nueva con el nombre dado.

local es un poco diferente. Opera sobre variables globales. Las variables globales tienen un alcance global (el nombre es válido en todas partes) y una duración de toda la vida del programa. Lo que hace local es hacer un cambio temporal en el valor de una variable global. Esto se denomina a veces "alcance dinámico". El cambio comienza en el punto de la declaración local y persiste hasta el final del bloque de cierre, después de lo cual se restaura el valor anterior. Es importante tener en cuenta que el nuevo valor no está restringido al bloque, sino que está visible en todas partes (incluidas las llamadas subrutinas). Las reglas de conteo de referencias siguen vigentes, por lo que puede tomar y mantener una referencia a un valor localizado una vez que el cambio haya expirado.

De vuelta al ejemplo: * FH es una variable global. Más exactamente es un " typeglob " - un contenedor para un conjunto de variables globales. Un typeglob contiene una ranura para cada uno de los tipos de variables básicas (escalar, matriz, hash) más algunas otras cosas. Históricamente, Perl utilizaba typeglobs para almacenar identificadores de archivos y local ; su tamaño ayudaba a garantizar que no se golpearan entre sí. Las variables léxicas no tienen typeglobs, por eso decir que my * FH es un error de sintaxis.

En las versiones modernas de Perl, las variables léxicas pueden y deben usarse como identificadores de archivos. Y eso nos lleva de vuelta a la "derecha". respuesta.

Otros consejos

En su código de muestra, la llamada a la subrutina integrada open está usando una palabra simple como el identificador de archivo, que es el equivalente de una variable global. Como la respuesta de Nathan Fellman explicó, usando local localizará esta palabra desnuda en el bloque de código actual, en el caso de que otra variable global con el mismo nombre se defina en otra parte del script o módulo. Esto evitará que la variable global previamente eliminada sea eliminada por la nueva declaración.

Esta era una práctica muy común en los viejos tiempos de Perl, pero a partir de Perl 5.6 es mucho mejor usar un escalar (con la declaración de mi que indicaste en tu pregunta ) para definir su identificador de archivo y, adicionalmente, use la llamada de tres argumentos para abrir .

use Carp;
open my $error_log, '>>', 'error.log' or croak "Can't open error.log: $OS_ERROR";

Como nota aparte, tenga en cuenta que para la lectura y escritura estándar de entrada / salida, es mejor usar los dos argumentos open :

use Carp;
open my $stdin, '<-' or croak "Can't open stdin: $OS_ERROR";

Alternativamente, puede usar IO :: File módulo para bendecir el identificador de archivos a la clase:

use IO::File;
my $error_log = IO::File->new('error.log', '>>') or croak "Can't open error.log: $OS_ERROR");

La mayoría del crédito aquí va a Damian Conway, autor del excelente libro Perl Best Practices . Si se toma en serio el desarrollo de Perl, debe comprar este libro.

¿Por qué estás leyendo un libro desactualizado? ¡La 3a edición ha salido durante mucho tiempo! ¿Qué versión de Perl estás usando? La segunda edición describe Perl 5.004 (5.4.x) o más o menos.

En estos días, no debe usar la notación typeglob para los identificadores de archivo; use los 'identificadores de archivos léxicos' (vea abierto , creo) o el FileHandle , o uno de sus parientes en su lugar.


Gracias a Michael Schwern e Ysth por los comentarios incorporados aquí.

Creo que es porque my asigna una nueva copia de la variable en la pila, y se pierde al salir del bloque. local guarda el * FH existente en otro lugar y anula el * FH existente. Restaura el antiguo cuando sales de la pila. Con my , el typeglob * FH se sale del alcance al salir del bloque. Con local sigue existiendo, por lo que puede seguir usándolo después de devolverlo.

No estoy 100% seguro de esto, pero tal vez pueda orientarte en la dirección correcta.

Consulte Manijas de archivos localizados aquí , supongo que eso lo explica.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top