Domanda

Quando leggo Programming Perl , 2nd Edition, Pagina 51, qualcosa mi confonde:

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

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

Mio lo so, perché non ci raccomandiamo di usare il mio? Finora, non riesco a vedere nulla andrà storto se usiamo my ().

Grazie!

È stato utile?

Soluzione

La risposta banale è che devi usare local perché my * FH è un errore di sintassi.

Il " giusto " (ma non molto illuminante) la risposta è che stai sbagliando. Dovresti invece utilizzare i filehandle lessicali e la forma a tre argomenti di open .

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

Per rispondere veramente a perché è necessaria una spiegazione della differenza tra variabili lessicali e globali e tra l'ambito di una variabile e la sua durata.

L'ambito di una variabile è la parte del programma in cui il suo nome è valido. L'ambito è una proprietà statica. La durata di una variabile, d'altra parte, è una proprietà dinamica. La durata è il tempo durante l'esecuzione di un programma in cui la variabile esiste e contiene un valore.

my dichiara una variabile lessicale. Le variabili lessicali hanno un campo di applicazione dal punto di dichiarazione fino alla fine del blocco (o file) allegato. Puoi avere altre variabili con lo stesso nome in ambiti diversi senza conflitti. (Puoi anche riutilizzare un nome in ambiti sovrapposti, ma non farlo.) La durata delle variabili lessicali viene gestita attraverso il conteggio dei riferimenti. Finché esiste almeno un riferimento a una variabile, il valore esiste, anche se il nome non è valido in un determinato ambito! Anche my ha un effetto di runtime: alloca una nuova variabile con il nome dato.

local è un po 'diverso. Funziona su variabili globali. Le variabili globali hanno un ambito globale (il nome è valido ovunque) e una durata dell'intera vita del programma. Ciò che local è apportare una modifica temporanea al valore di una variabile globale. Questo viene talvolta definito "scoping dinamico". La modifica inizia nel punto della dichiarazione local e persiste fino alla fine del blocco racchiuso dopo il quale viene ripristinato il vecchio valore. È importante notare che il nuovo valore non è limitato al blocco: è visibile ovunque (anche chiamato subroutine). Le regole di conteggio dei riferimenti si applicano ancora, quindi puoi prendere e conservare un riferimento a un valore localizzato dopo che la modifica è scaduta.

Torna all'esempio: * FH è una variabile globale. Più precisamente è un "typeglob" - un contenitore per un insieme di variabili globali. Un typeglob contiene uno slot per ciascuno dei tipi di variabili di base (scalare, array, hash) più alcune altre cose. Storicamente, Perl ha usato i typeglob per archiviare i filehandle e local , aiutandoli a garantire che non si fossero bloccati a vicenda. Le variabili lessicali non hanno typeglobs ed è per questo che dire my * FH è un errore di sintassi.

Nelle versioni moderne delle variabili lessicali Perl possono e devono essere usate come filehandle. E questo ci riporta al "giusto" risposta.

Altri suggerimenti

Nel tuo codice di esempio, la chiamata alla subroutine integrata open utilizza una parola nuda come handle di file, che equivale a una variabile globale. Come la risposta di Nathan Fellman , usando local localizzerà questa parola nuda nel blocco di codice corrente, nel caso in cui un'altra variabile globale con lo stesso nome sia definita altrove nello script o nel modulo. Ciò eviterà che la nuova dichiarazione venga cancellata dalla variabile globale precedentemente definita.

Questa era una pratica molto comune ai vecchi tempi di Perl, ma a partire da Perl 5.6 è molto meglio usare uno scalare (con la dichiarazione my a cui hai accennato nella tua domanda ) per definire l'handle del file e, inoltre, utilizzare la chiamata a tre argomenti per aprire .

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

A parte, si noti che per la lettura e la scrittura standard di input / output, è ancora meglio usare i due argomenti open :

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

In alternativa, puoi utilizzare IO :: File modulo per benedire l'handle del file nella classe:

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

La maggior parte del credito qui va a Damian Conway, autore dell'eccellente libro Perl Best Practices . Se prendi sul serio lo sviluppo del Perl, devi a te stesso acquistare questo libro.

Perché stai leggendo un libro obsoleto. La terza edizione è in circolazione da molto tempo! Quale versione di Perl stai usando? La seconda edizione descrive Perl 5.004 (5.4.x) o successivi.

In questi giorni, non dovresti usare la notazione typeglob per gli handle di file; usa gli "handle di file lessicali" (vedi open , credo) o FileHandle o uno dei suoi parenti invece.


Grazie a Michael Schwern e Ysth per i commenti qui incorporati.

Credo che sia perché my alloca una nuova copia della variabile nello stack e si perde quando si esce dal blocco. local salva il * FH altrove e sovrascrive il * FH esistente. Ripristina quello vecchio quando esci dalla pila. Con my il typeglob * FH esce dall'ambito quando si esce dal blocco. Con local continua a esistere, quindi puoi continuare a usarlo dopo averlo restituito.

Non ne sono sicuro al 100%, ma forse può indicarti la giusta direzione.

Vedi Filehandle localizzati qui , immagino che lo spieghi.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top