Indice

Capitolo 7.
Scoping, Name Spaces, Packages e Moduli






   1. Lo scope (campo di visibilità) delle variabili (my, local)

Lo scope di una variabile è il campo di visibilità all'interno del quale la variabile è raggiungibile (modificabile, risolvibile, interpolabile, ditela un po' come vi torna più congeniale).

     Prendiamo il caso più semplice per fare un primo esempio. Ammettiamo di avere un programma di n righe di codice, all'interno del quale viene utilizzata una variabile. Diciamo anche che in questo programma non ci sono costrutti if, while, for e loro modificazioni. La visibilità della variabile in questione sarà estesa dalla riga di codice all'interno della quale viene definita, sino all'ultima riga del programma. Vediamo un esempio:


Primo esempio di Scoping Fig. 1
 
    1:  #!/usr/bin/perl
    2: 
    3:  #
    4:  # Definiamo la variabile
    5:  #
    6:  $int = 5;
    7: 
    8:  print $int;
    9:  exit;
 

In questo caso è (forse) evidente che la visibilità della variabile abbia l'estensione citata in precedenza. Complichiamo un po' le cose:


Scoping dentro blocchi Fig. 2
 
    1:  #!/usr/bin/perl
    2: 
    3: 
    4:  $int = 10;
    5: 
    6:  for ( $int = 0; $int < 5; $int++ ) {
    7:    print "Dentro il ciclo \$int vale $int\n";
    8:  }
    9: 
   10:  print "Fuori dal ciclo \$int vale $int\n";
 

In questo caso lo scope della variabile $int si estende ancora a tutto lo script. Cosa avviene all'esecuzione dello script?
Dentro il ciclo $int vale 0
Dentro il ciclo $int vale 1
Dentro il ciclo $int vale 2
Dentro il ciclo $int vale 3
Dentro il ciclo $int vale 4
Fuori dal ciclo $int vale 5
Modifichiamo un minimo l'esempio precedente...


Limitare lo scoping Fig. 3
 
    1:  #!/usr/bin/perl
    2: 
    3: 
    4:  $int = 10;
    5: 
    6:  for ( my $int = 0; $int < 5; $int++ ) {
    7:    print "Dentro il ciclo \$int vale $int\n";
    8:  }
    9: 
   10:  print "Fuori dal ciclo \$int vale $int\n";
 

...e vediamo cosa succede all'esecuzione dello script:
Dentro il ciclo $int vale 0
Dentro il ciclo $int vale 1
Dentro il ciclo $int vale 2
Dentro il ciclo $int vale 3
Dentro il ciclo $int vale 4
Fuori dal ciclo $int vale 10
Attenzione! Fuori dal ciclo $int vale 10!! Perché? Se fate attenzione noterete che abbiamo introdotto prima della variabile $int una parola chiave: my. Questo qualificatore di variabile consente di specificare variabili "localizzate". Questo genere di variabili ha scope ridotto al più ristretto blocco di codice all'interno del quale la variabile è stata dichiarata. In questo caso particolare, lo scope di $int è ridotto alla durata del ciclo for. All'uscita dal ciclo for la variabile $int dichiaratavi scompare, riportando, per così dire, alla luce la variabile $int dichiarata in precedenza.

     Una ulteriore proprietà del modificatore my è rendere la variabile estranea a qualsiasi Name Space (vedi il paragrafo successivo dedicato).

     Contraltare di my è invece local, modificatore che duplica una variabile, assegnandole scoping temporaneo, relativo al campo rispettivo, deciso secondo le medesime regole di my. La differenza consiste nel fatto che local crea una copia della variabile, con medesimo nome, ma valore dissociato da quello della variabile precedentemente dichiarata.

     Ad esempio è possibile eseguire il seguente codice:


Esempio di variabile local Fig. 4
 
    1:  #!/usr/bin/perl
    2: 
    3:  #
    4:  # Definiamo l'array
    5:  #
    6:  @array = (1,2,3,4,5);
    7: 
    8:  #
    9:  # Iteriamo su una copia locale dell'array
   10:  #
   11:  for ( my $c = 0; $c < @array; $c++ ) {
   12:    local @array = @array;
   13:    $array[$c]++;
   14:    print qq/\$array[$c] incrementato vale $array[$c]\n/;
   15:  }
 

Il significato del ciclo for può essere parafrasato come: "per $c che varia da 0 alla dimensione di @array (quindi finché $c è minore di 4), incrementando ad ogni iterazione di 1". Il blocco di codice eseguito all'interno effettua una copia di @array in un suo "clone" locale. Quindi incrementa il valore interessato di una unità e infine stampa il messaggio quotato.


   
2. Il concetto di Package e di NameSpace

La filosofia ufficiale di Perl è che non dovete entrare dove non è consentito, non perché ci sono le sbarre alle finestre ma perché sapete di non doverlo fare!

     Per questo Perl provvede i Packages, ossia spazi che delimitano la visibilità di un simbolo ad un determinato ambito. Un NameSpace è letteralmente uno spazio letterale che caratterizza la posizione di un simbolo nella tabella dei simboli. (Ricordiamo che per simbolo si intende qualsiasi "elemento atomico" dichiarato, comprendendo variabili, subroutine e filehandle). In Perl il concetto di NameSpace e di Package coincidono. Quindi di quì in avanti parleremo di Packages (termine più perlish).

     La struttura dei Packages è gerarchica (esattamente come la struttura delle directory su un filesystem). Ciascun livello è separato da un simbolo convenzionale. Originariamente questo simbolo era l'apostrofo ('), richiamando il fatto che un Package "appartiene" al Package superiore. (In Tools'Editors, Editors appartiene, nel senso di "è parte di", Tools). Successivamente il simbolo è stato sostituito da ::, conforme alla tradizione C. Quindi Tools'Editors diventa Tools::Editors. Tenete presente che la vecchia sintassi è ancora supportata per compatibilità; questo può portare ad errori subdoli, come print "This is $owner's car", in cui la variabile è $owner's, ossia la variabile s nel Package owner. Per fortuna affligge più gli anglofoni che i latini! ;-)

     La dichiarazione di un Package avviene tramite la keyword package. Se all'inizio di un file appare package Prova; tutto il contenuto del file farà parte del package Prova sino alla fine del file oppure sino ad una successiva dichiarazione di package, che attribuisce a questo secondo nuovo pacchetto il contenuto del file di lì in avanti!

     Esiste un package speciale: main il quale include qualsiasi altro package. Incluso se stesso. Quindi $main::var è assolutamente identico a $main::main::var e $main::main::main::var. Questa tecnica di autoinclusione ha permesso di scrivere il Perl Debugger, che è un normale modulo Perl il quale, quando caricato insieme al vostro programma, consente il debugging interattivo.


   
3. Il concetto di Modulo

Un modulo è l'unità minima di riutilizzabilità di codice in Perl. Il nome del file che contiene il Modulo è il nome stesso del modulo (con preservazione della capitalizzazione delle lettere), con il suffisso ".pm". Inoltre la gerarchizzazione dei Moduli si traduce in una gerarchizzazione delle directory che contiene i moduli. Il modulo Tools::Editors::Writer è rappresentata da Tools/Editors/Writer.pm. Se esiste un modulo Tools::Editors::Writer::Macros esisterà anche per conseguenza il file Tools/Editors/Writer/Macros.pm!

     Vediamo prima come utilizzare un modulo.


Come usare un modulo Fig. 5
 
    1:  #!/usr/bin/perl
    2: 
    3:  use Cwd;
    4: 
    5:  my $cwd = getcwd();
 

oppure


Come richiedere un modulo Fig. 6
 
    1:  #!/usr/bin/perl
    2: 
    3:  BEGIN { require Cwd; }
    4: 
    5:  my $cwd = Cwd->getcwd();
 

La differenza tra le due sintassi stà nel fatto che la prima richiede il modulo e ne importa tutti i simboli importabili nel namespace dal quale si stà chiamando il modulo. La seconda invece richiese esclusivamente il modulo senza importare i simboli che questo provvede. Per questo nel secondo esempio abbiamo dovuto usare l'operatore di dereferenziazione (->) per avere accesso alla sub getcwd().


   
4. Esempi di dichiarazioni di Moduli

Vediamo al volo un esempio di dichiarazione di un modulo:


Esempio di diachiarazione di un modulo Fig. 7
 
    1:  #!/usr/bin/perl
    2: 
    3:  package Tools::MyCwd;
    4:  require Cwd;
    5:  @ISA = qw/Cwd/;
    6:  @EXPORT = qw/getcwd/;
    7:  @EXPORT_OK = qw/getcwd oldcwd/;
    8: 
    9:  sub getcwd {
   10: 
   11:    [ ... ]
   12: 
   13:  }
 

Come prima cosa dichiariamo il package del nostro Modulo. (Questo comporta che il nome del file, nel quale salveremo il modulo, sia Tools/MyCwd.pl). Subito dopo richiediamo il modulo Cwd. Questo ci serve perché il nostro package vuole estendere il package Cwd. Infatti questo modulo è stato introdotto nell'array @ISA, che contiene un elenco dei moduli all'interno dei quali cercare i simboli che non vengono reperiti nel package corrente. (Attenzione, stiamo un minimo sconfinando nella programmazione orientata agli oggetti, ma come vedremo non ci passa poi tanto!)

     Successivamente definiamo gli array @EXPORT ed @EXPORT_OK. Questi due array contengono rispettivamente l'elenco dei simboli che vengono automaticamente esportati in caso di use, e l'elenco dei simboli che possono essere richiesti esplicitamente dal Modulo. Se un simbolo non figura qui e viene richiesto (es. use Tools::MyCwd qw/getcwd gettime/;), Perl segnalerà un errore!



Inizio Capitolo Indice