È un comportamento confuso, ma documentato, probabilmente derivante dalla cattiva decisione di rendere il ciclo iteratore variabile implicitamente localizzato globale piuttosto che lessicale. Da Foreach Loops in perlsyn.
Se la variabile è preceduta della parola my
, allora è lessicale scope, ed è quindi visibile solo all'interno del ciclo. Altrimenti, la variabile è implicitamente locale al ciclo e riprende il suo valore precedente all'uscita dal ciclo. Se la variabile è stata precedentemente dichiarata con my
, utilizza quella variabile anziché quella globale, ma è ancora localizzata nel ciclo.
Per dirla in altro modo, l'iteratore ciclo è sempre localizzata al ciclo. Se è globale, si comporta come se fosse stato dichiarato local
all'interno del blocco del ciclo. Se è un lessico, allora si comporta come se fosse stato dichiarato con my
all'interno del blocco del ciclo.
Applicare questo ai due esempi aiuterà a capire cosa sta succedendo.
our $x;
sub print_func {
print "$x\n";
}
for $x (1 .. 10) {
print_func;
}
C'è un implicito local $x
su quel ciclo. local
avrebbe dovuto essere chiamato temp
. Sovrascrive temporaneamente il valore di una variabile globale per la durata del suo ambito, ma è ancora globale. Ecco perché print_func
può vederlo.
Il vecchio valore viene ripristinato al termine dell'ambito. Puoi vedere questo se aggiungi un print $x
dopo il ciclo for.
use v5.10;
our $x = 42;
for $x (1 .. 10) {
say $x;
}
say $x; # 42
Vediamo il codice che coinvolge lessicali (my
variabili).
my $x;
sub print_func {
print "$x\n";
}
for $x (1 .. 10) {
print_func;
}
Che cosa sta realmente accadendo qui è di avere due variabili lessicali entrambi chiamati $x
. Uno è ambito del file, uno è limitato al ciclo. L'interno $x
sul ciclo for ha la precedenza sull'esterno $x
. Questo è noto come "shadowing".
I lessici non possono essere visualizzati al di fuori del loro ambito fisico. print_func()
vede solo l'esterno non inizializzato $x
.
Ci sono alcuni suggerimenti stilistici da questo.
Passa sempre i parametri nelle tue funzioni.
In realtà, print_func
dovrebbe prendere una discussione. Quindi non ti devi preoccupare delle complicate regole di scoping.
sub print_func {
my $arg = shift;
print "$arg\n";
}
for $x (1..10) {
print_func($x);
}
Usare sempre for my $x
.
Non fare affidamento sulle complicate regole di ambito del ciclo for
implicite. Dichiara sempre l'iteratore di loop con my
.
for my $x (1..10) {
print_func($x);
}
Evitare globali.
Dal momento che è difficile dire cosa sta accedendo a un globale, non usarli. Se pensate di aver bisogno di un globale, scrivete invece una funzione per controllare l'accesso a un file lessicale con scope.
my $Thing = 42;
sub get_thing { return $Thing }
sub set_thing { $Thing = shift; return }
dichiarare le variabili vicino a dove sono usate.
Gli stili di codifica olde faranno cose come dichiarare tutte le loro variabili nella parte superiore del file o della funzione. Questa è una presa da lingue molto, molto, molto vecchie che richiedevano che le variabili venissero dichiarate solo in determinati luoghi. Perl e la maggior parte delle lingue moderne non hanno questa restrizione.
Se dichiarate tutte le variabili contemporaneamente, è difficile sapere a cosa servano, ed è difficile sapere che cosa sta usando o che lo influenza. Se lo dichiari vicino al suo primo utilizzo, limita ciò che può influenzarlo e rende più ovvio per quale motivo.
Sembra che per $ x (...) {...} 'sia equivalente a' do {local $ x; per $ x (...) {...}}; ', ma quando faccio la sostituzione ottengo un' Can not localize variabile lessicale $ x in perl-scoping.pl' ... Perché non è quello errore attivato anche nella versione originale? –
@GregoryNisbet Questo non funzionerà perché 'local' non funziona su lessicali e presumo che tu abbia' my $ x' già in ambito. Altrimenti la tua analogia è fondamentalmente corretta * se non c'è '$ x' in ambito, o se è globale *. Il comportamento di 'for $ x (...) {...}' cambia se c'è già un lessico '$ x' in scope. In questo caso 'per $ x' dichiara un nuovo lessico all'interno del ciclo, quindi è più come' do {my $ x; per $ x (...) {...}}; '. Questo è il motivo # 1398 perché dovresti essere sempre esplicito e scrivere 'per il mio $ x'. – Schwern
Immagino che adesso stia cercando di capire perché le potenze Perl che non hanno deciso di rifiutare 'for $ x (...) {...}' in modalità rigorosa o facciano emettere un avvertimento. Sai se è venuto fuori prima? –