2016-05-03 6 views
13

In Perl 5.20, un ciclo for sembra essere in grado di modificare una variabile con ambito modulo ma non una variabile lessicale in ambito genitore.per loop non modifica la variabile `my` ma modifica` our` variabile

#!/usr/bin/env perl 
use strict; 
use warnings; 

our $x; 

sub print_func { 
    print "$x\n"; 
} 

for $x (1 .. 10) { 
    print_func; 
} 

stampe da 1 a 10 come ci si aspetterebbe, ma il seguente non lo fa:

#!/usr/bin/env perl 
use strict; 
use warnings; 

my $x; 

sub print_func { 
    print "$x\n"; 
} 

for $x (1 .. 10) { 
    print_func; 
} 

emette il seguente avviso: 10 volte

Use of uninitialized value $x in concatenation (.) or string at perl-scoping.pl line 8. 

cosa sta succedendo qui? So che le subroutine perl non possono essere annidate (e hanno sempre scope del modulo) e quindi sembra logico che non siano in grado di chiudere le variabili my. Sembra che in questo caso, perl in modalità strict dovrebbero rifiutare il secondo programma con un messaggio simile al seguente:

Global symbol "$x" requires explicit package name at perl-scoping.pl line 6. 
Global symbol "$x" requires explicit package name at perl-scoping.pl line 9. 

Vale a dire dovrebbe rifiutare la subroutine perché la variabile libera non è dichiarata da nessuna parte e il ciclo for perché la variabile non è stata dichiarata.

Perché Perl si comporta in questo modo?

risposta

17

È 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.

+0

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? –

+2

@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

+0

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? –

Problemi correlati