NOME
perlsub - Subroutine Perl
SINOSSI
Per dichiarare subroutine:
sub NOME; # Una dichiarazione "anticipata".
sub NOME(PROTO); # idem, ma con prototipi
sub NOME : ATTR; # con attributi
sub NOME(PROTO) : ATTR; # con attributi e prototipi
sub NOME BLOCCO # Una dichiarazione con definizione.
sub NOME(PROTO) BLOCCO # idem, ma con prototipi
sub NOME : ATTR BLOCCO # con attributi
sub NOME(PROTO) : ATTR BLOCCO # con prototipi ed attributi
Per definire una subroutine anonima durante l'esecuzione:
$subref = sub BLOCCO; # nessun prototipo
$subref = sub (PROTO) BLOCCO; # con prototipi
$subref = sub : ATTR BLOCCO; # con attributi
$subref = sub (PROTO) : ATTR BLOCCO; # con prototipi ed attributi
Per importare le subroutine:
use MODULO qw(NOME1 NOME2 NOME3);
Per chiamare le subroutine:
NOME(LISTA); # & e` opzionale quando si usano le parentesi.
NOME LISTA; # Parentesi opzionali se predichiarata/importata.
&NOME(LISTA); # Evita i prototipi.
&NOME; # Rende @_ corrente visibile alla subroutine chiamata.
DESCRIZIONE
Come molti linguaggi, Perl mette a disposizione la possibilità di definire delle subroutine da parte dell'utente. Queste possono essere collocate ovunque nel programma principale, caricate da altri file attraverso le parole chiave do
, require
o use
, oppure generate dinamicamente utilizzando eval
o subroutine anonime. Potete persino chiamare una funzione indirettamente utilizzando una variabile che contiene il suo nome o un riferimento di tipo CODE [CODICE, N.d.T.].
Il modello per la chiamata a funzione, e per i valori restituiti da questa, in Perl è semplice: tutte le funzioni accettano una singola lista "piatta" di scalari, e similmente restituiscono al chiamante una singola lista piatta di scalari. Qualunque array o hash in queste chiamate, e le liste di valori restituiti, verrà collassato, perdendo la propria identità -- ma potete sempre utilizzare un passaggio per riferimento per evitare questo meccanismo. Sia le liste di chiamata che quelle restituite possono contenere tanti (o pochi) scalari a vostro piacimento. (Spesso una funzione senza un'istruzione di return
esplicita è chiamata subroutine, ma non c'è una vera differenza dal punto di vista del Perl).
Qualsiasi argomento passato alla subroutine sarà disponibile nell'array @_
. Perciò, se chiamate una funzione con due argomenti, questi andrebbero a finire in $_[0]
e $_[1]
. L'array @_
è locale, ma i suoi elementi sono degli alias dei parametri scalari effettivi. In particolare, se aggiornate l'elemento $_[0]
, l'argomento corrispondente viene aggiornato (o viene segnalato un errore se non è aggiornabile). Se un argomento è un elemento di un array o di una hash che non esisteva quando la funzione è stata chiamata, l'elemento viene creato solo quando (e se) esso viene modificato o viene preso un riferimento ad esso. (In alcune versioni più antiche di Perl l'elemento veniva creato indipendentemente dal fatto che esso fosse assegnato o meno). Assegnare all'intero array @_
rimuove tale meccanismo di alias, e non aggiorna gli argomenti.
Si può utilizzare un'istruzione return
per uscire da una subroutine, specificando un valore da restituire opzionale, che verrà valutato nel contesto appropriato (lista, scalare o vuoto) dipendentemente dal contesto della chiamata alla subroutine stessa. Se non specificate alcun valore, la subroutine restituisce una lista vuota in contesto lista, il valore undef
in contesto scalare, o niente in contesto vuoto. Se restituite uno o più aggregati (array o hash), questi verranno appiattiti insieme in una grande lista indistinguibile.
Se non viene trovato nessun return
e se l'ultima istruzione è un'espressione, viene restituito il suo valore. Se l'ultima istruzione è una struttura di controllo di ciclo come foreach
o while
, il valore restituito non è specificato. La subroutine vuota restituisce la lista vuota.
Perl non ha parametri formali con nome. In pratica tutto quel che potete fare è un'assegnazione ad una lista my()
di variabili. Le variabili che non sono dichiarate private sono globali. Per i dettagli sulla creazione di variabili private consultate "Variabili Private con my()" e "Valori Temporanei con local()". Per creare ambienti protetti per un insieme di funzioni in un package separato (e probabilmente un file separato) consultate "Pacchetti" in perlmod.
Esempio:
sub max {
my $max = shift(@_);
foreach $pippo (@_) {
$max = $pippo if $max < $pippo;
}
return $max;
}
$migliore = max($lun,$mar,$mer,$gio,$ven);
Esempio:
# prendi una riga, combinando le righe di continuazione
# che iniziano con uno spazio
sub prendi_riga {
$questa_riga = $guarda_avanti; # variabili globali!
RIGA: while (defined($guarda_avanti = <STDIN>)) {
if ($guarda_avanti =~ /^[ \t]/) {
$questa_riga .= $guarda_avanti;
}
else {
last RIGA;
}
}
return $questa_riga;
}
$guarda_avanti = <STDIN>;# prendi la prima riga
while (defined($riga = prendi_riga())) {
...
}
Assegnazione ad una lista di variabili private per dare un nome agli argomenti:
sub potrebbe_essere_impostata {
my($chiave, $valore) = @_;
$Pippo{$chiave} = $valore unless $Pippo{$chiave};
}
Poiché l'assegnazione copia i valori, questo sistema ha anche l'effetto di trasformare una chiamata per riferimento in una chiamata per valore. Altrimenti una funzione è libera di effettuare modifiche di @_
sul posto e cambiare i valori del chiamante.
maiuscolizza($v1, $v2); # questa cambia $v1 e $v2
sub maiuscolizza {
for (@_) { tr/a-z/A-Z/ }
}
Non vi è consentito modificare le costanti in questo modo, ovviamente. Se un argomento fosse in realtà un letterale, e voi provaste a cambiarlo, vi ritrovereste con un'eccezione (presumibilmente fatale). Ad esempio, quanto segue non funziona:
maiuscolizza("frederick");
Sarebbe molto più sicuro se la funzione maiuscolizza
fosse scritta per restituire una copia dei suoi parametri invece che cambiarli sul posto:
($v3, $v4) = in_maiuscolo($v1, $v2); # Questa non cambia $v1 e $v2
sub in_maiuscolo {
return unless defined wantarray; # contesto vuoto, non fare niente
my @param = @_;
for (@param) { tr/a-z/A-Z/ }
return wantarray ? @param : $param[0];
}
Osservate che questa funzione (non prototipizzata) non si cura se le vengono passati scalari reali o array. Perl vede tutti gli argomenti come un'unica grande, lunga e piatta lista di parametri in @_
. Questa è un'area dove lo stile di passaggio degli argomenti di Perl risplende. La funzione in_maiuscolo()
funziona perfettamente senza cambiare la definizione di in_maiuscolo()
anche quando le passiamo questo genere di roba:
@nuova_lista = in_maiuscolo(@lista1, @lista2);
@nuova_lista = in_maiuscolo( split /:/, $var );
Non lasciatevi tentare dal fare questo, però:
(@a, @b) = in_maiuscolo(@lista1, @lista2);
Come viene appiattita la lista dei parametri di ingresso, anche quella dei valori restituiti è parimenti appiattita. Quindi tutto quel che avete ottenuto è immagazzinare tutto in @a
ed avere @b
vuota. Consultate "Passare per Riferimento" per possibili alternative.
Si può chiamare una subroutine utilizzando un prefisso &
esplicito. Nel Perl moderno il carattere &
è opzionale, come lo sono le parentesi se la subroutine è stata già dichiarata. La &
non è opzionale quando state semplicemente nominando la subroutine, come quando utilizzate defined()
o undef()
. Né è opzionale quando volete effettuare una chiamata indiretta a subroutine, con il nome della subroutine o un riferimento utilizzando i costrutti &$subref()
o &{$subref}()
, sebbene la notazione $subref->()
risolva questo particolare problema. Consultate perlref per maggiori dettagli su tutto ciò.
Le subroutine possono essere chiamate ricorsivamente. Se una subroutine è chiamata utilizzando la forma &
, la lista degli argomenti è opzionale e, se messa, non viene impostato alcun array @_
per la subroutine: l'array @_
al momento della chiamata è invece visibile direttamente dalla subroutine. Questo è un meccanismo di efficienza che i novizi potrebbero voler evitare.
&pippo(1,2,3); # passa tre argomenti
pippo(1,2,3); # idem
pippo(); # passa la lista vuota
&pippo(); # idem
&pippo; # pippo() riceve gli argomenti correnti, come pippo(@_) !!
pippo; # come pippo() SE E SOLO SE sub pippo predichiarata,
# altrimenti "pippo"
Non solo la forma &
rende la lista degli argomenti opzionale, ma disabilita anche qualsiasi controllo di prototipo possiate aver impostato. Questo effetto si ha in parte per ragioni storiche, ed in parte per avere un modo comodo per barare quando sapete quel che state facendo. Consultate Prototipi più avanti.
Le subroutine i cui nomi sono tutti in lettere maiuscole sono riservati per il nucleo di Perl, così come i moduli i cui nomi sono tutti in lettere minuscole. Una subroutine in sole lettere maiuscole è una convenzione vagamente seguita che significa che essa verrà chiamata indirettamente dal sistema di esecuzione stesso, di solito durante un evento particolare. Le subroutine che fanno cose speciali e predefinite includono AUTOLOAD
, CLONE
, DESTROY
più tutte le funzioni indicate in perltie e PerlIO::via.
Le subroutine BEGIN
, CHECK
, INIT
e END
non sono tanto subroutine quanto più blocchi di codice dal nome particolare, dei quali potete averne più d'uno in un package, e che non potete chiamare esplicitamente. Consultate "BEGIN, CHECK, INIT e END" in perlmod
Variabili Private con my()
Sinossi:
my $pippo; # dichiara $pippo locale lessicalmente
my (@pin, %co); # dichiara una lista di variabili localmente
my $pippo = "flurp"; # dichiara $pippo lessicale ed inizializzala
my @oppip = @pluto; # dichiara @oppip lessicale ed inizializzala
my $x : Pippo = $y; # simile, con attributi applicati
ATTENZIONE: l'utilizzo delle liste di attributi nelle dichiarazioni my
è ancora in evoluzione. La semantica e l'interfaccia attuali sono soggette a cambiamenti. Consultate attributes [attributi, N.d.T.] e Attribute::Handlers.
L'operatore my
dichiara le variabili elencate come confinate lessicalmente all'interno del blocco che le racchiude, della condizione (if/unless/elsif/else
), ciclo (for/foreach/while/until/continue
), subroutine, eval
o file chiamati con do/require/use
. Se viene elencato più di un elemento, la lista va posta fra parentesi. Tutti gli elementi nella lista devono essere valori plausibili "a sinistra" di un'assegnazione. Possono essere ristretti lessicalmente solo identificatori alfanumerici -- variabili del linguaggio "magiche" come $/
, al momento, devono essere local
izzate con local
.
Differentemente dalle variabili dinamiche create dall'operatore local
, le variabili lessicali dichiarate con my
sono totalmente nascoste dal mondo esterno, incluse tutte le subroutine chiamate. Questo continua ad essere vero anche se è la stessa subroutine, chiamata ricorsivamente o da qualche altra parte -- ciascuna chiamata ha la sua copia.
Questo, però, non significa che una variabile my
dichiarata in uno scope lessicale che racchiude, sarebbe invisibile; sono tagliati fuori solamente gli scope dinamici. Ad esempio, la funzione tirasux()
che segue ha accesso alla variabile lessicale $x
, perché sia il my
che la sub
si trovano nello stesso scope, presumibilmente coincidente con quello del file.
my $x = 10;
sub tirasux { $x++ }
Una eval()
, d'altra parte, può vedere variabili lessicali dello scope in cui viene valutata, ma solo finché questi nomi non vengono nascosti da dichiarazioni all'interno della eval()
stessa. Consultate perlref.
La lista dei parametri di my()
può essere utilizzata in un'assegnazione se lo si desidera, il che vi consente di inizilizzare le vostre variabili. (Se non viene dato nessun valore di inizializzazione per una determinata variabile, questa viene creata con il valore undef
). Di solito si usa questo meccanismo per dare un nome ai parametri di ingresso ad una subroutine. Qualche esempio:
$arg = "oronzo"; # variabile "globale"
$n = radice_cubica(27);
print "$arg pensa che la radice sia $n\n";
oronzo pensa che la radice sia 3
sub radice_cubica {
my $arg = shift; # il nome non conta
$arg **= 1/3;
return $arg;
}
Il my
è semplicemente un modificatore di qualcosa che voi potreste utilizzare in un'assegnazione. Per questo motivo, quando assegnate a delle variabili nella sua lista degli argomenti, my
non cambia se queste variabili sono viste come scalari o array. Quindi:
my ($pippo) = <STDIN>; # ERRORE?
my @PIPPO = <STDIN>;
danno tutti e due un contesto lista al lato destro dell'assegnazione, mentre
my $pippo = <STDIN>;
fornisce un contesto scalare. Ma quanto segue dichiara una sola variabile:
my $foo, $bar = 1; # ERRATO
Ciò ha lo stesso effetto di
my $foo;
$bar = 1;
La variabile dichiara non è introdotta (ossia, non è visibile) se non dopo l'istruzione corrente. Quindi,
my $x = $x;
può essere utilizzato per inizializzare una nuova variabile $x
con il valore della vecchia $x
, e l'espressione
my $x = 123 and $x == 123
risulta falsa a meno che la vecchia $x
non abbia il valore 123
.
I limiti lessicali delle strutture di controllo non sono esattamente definiti dalle graffe che segnano i blocchi da esse controllati; le espressioni di controllo fanno parte esse stesse dello scope. Quindi nel ciclo
while (my $riga = <>) {
$riga = lc $riga;
} continue {
print $riga;
}
la visibilità di $line
si estende dalla sua dichiarazione all'intero resto del costrutto di ciclo (inclusa la clausola continue
), ma non oltre. Similmente, nel condizionale
if ((my $risposta = <STDIN>) =~ /^si$/i) {
utente_acconsente();
} elsif ($risposta =~ /^no$/i) {
utente_dissente();
} else {
chomp $risposta;
die "'$risposta' diversa sia da 'si' che da 'no'";
}
la visibilità di $risposta
si estende dalla sua dichiarazione fino al resto del condizionale, incluse eventuali clausole elsif
e else
, ma non oltre. Consultate "Istruzioni semplici" in perlsyn per maggiori dettagli sulla visibilità di variabili nelle istruzioni contenenti modificatori.
Il ciclo foreach
ha come comportamento di default di limitare dinamicamente la visibilità della sua variabile di controllo alla stessa maniera di local
. Comunque, se la variabile di indice viene preceduta dalla parola chiave my
, o se esiste già un'altra lessicale che ha lo stesso nome nello stesso scope, allora viene creata una nuova variabile lessicale. Per questo motivo, nel ciclo
for my $i (1, 2, 3) {
una_qualche_funzione();
}
la visibilità di $i
si estende fino alla fine del ciclo, ma non oltre, rendendo quindi il valore di $i
inaccessibile da parte di una_qualche_funzione()
.
Alcuni utenti potrebbero voler incoraggiare l'uso di variabili a visibilità limitata lessicalmente. Come supporto per intrappolare l'utilizzo implicito di variabili di pacchetto, che risultano sempre globali, se inserite
use strict 'vars';
allora qualsiasi variabile menzionata da lì fino alla fine del blocco deve o riferirsi ad una variabile lessicale, o essere predichiarata con our
o use vars
, o anche deve essere completamente qualificata utilizzando anche il nome del pacchetto. Altrimenti vi ritroverete con un errore di compilazione. Un blocco più interno può essere gestito altrimenti, specificando no strict 'vars'
.
Una my
ha effetti sia durante la fase di compilazione che durante quella di esecuzione vera e propria. Durante la compilazione il compilatore ne prende nota. L'utilità principale di questo fatto è che use strict 'vars'
se ne sta calmo a questo punto, ma è anche essenziale per la generazione delle closure [letteralmente "chiusure", ma utilizzeremo il termine inglese, N.d.T.] così come dettagliato in perlref. L'inizializzazione vera e propria è ritardata fino all'esecuzione, però, e quindi viene eseguita al momento opportuno, come ad esempio ogni volta che si esegue un ciclo.
Le variabili dichiarate con my
non fanno parte di alcun pacchetto e pertanto non vengono mai qualificate con il nome del pacchetto stesso. In particolare, non vi è consentito provare a trasformare una variabile di pacchetto (o una qualche altra variabile globale) in una lessicale:
my $pack::var; # ERRORE! Sintassi non lecita
my $_; # parimenti illecita (al momento)
Infatti, una variabile dinamica (altrimenti nota come variabile di pacchetto o globale) è ancora accessibile utilizzando la notazione di qualifica piena con ::
, persino quando esiste una variabile lessicale con lo stesso nome nell'intervallo di visibilità:
package main;
local $x = 10;
my $x = 20;
print "$x e $::x\n";
Questo breve script stampa 20
e 10
.
Potete dichiarare variabili my
allo scope più esterno di un file per nascondere ciascuno di questi identificatori dal mondo esterno al file stesso. Questo modo di procedere è simile a quanto avviene per le variabili statiche in C, quando queste siano utilizzate a livello di file. Per far ciò con una subroutine è necessario utilizzare una closure (una funzione anonima che accede i lessicali racchiusi). Se volete creare una subroutine privata che non può essere chiamata al di fuori del blocco, potete dichiarare una variabile lessicale che contiene un riferimento da una subroutine anonima:
my $versione_segreta = '1.001-beta';
my $subroutine_segreta = sub { print $versione_segreta };
&$subroutine_segreta();
Finché il riferimento non viene restituito da nessuna funzione all'interno del modulo, nessun modulo al di fuori può vedere questa subroutine, perché il suo nome non è nella tabella dei simboli di alcun pacchetto. Ricordate che non è VERAMENTE chiamata $un_pacchetto_qualsiasi::versione_segreta
o altro: è solamente $versione_segreta
, non qualificata e non qualificabile.
Questo meccanismo, però, non funziona con i metodi della programmazione ad oggetti: tutti i metodi ad oggetti devono trovarsi nella tabella dei simboli di un qualche pacchetto. Consultate "Modelli di Funzione" in perlref per trovare un sistema per aggirare questo problema.
Variabili Private Persistenti
Solo perché una variabile lessicale è lessicalmente (o anche staticamente) racchiusa nel suo blocco, eval
o do
FILE, questo non significa che in una funzione lavori come una variabile statica di C. Più che altro si comporta come una variabile automatica di C, ma con in più un'operazione di garbage collection implicita.
Differentemente dalle variabili locali in C o C++, le variabili lessicali in Perl non vengono necessariamente riciclate solo perché si è usciti dal loro scope. Se qualcosa di più permanente è ancora al corrente del dato lessicale, quest'ultimo continuerà a bazzicare i dintorni. Finché qualcos'altro si riferisce ad un lessicale, questo lessicale non sarà liberato -- e questo è esattamente come dovrebbe essere. Non vi piacerebbe che la memoria fosse liberata se non quando avete finito di utilizzarla, o che di contro sia tenuta in vita quando avete finito. La garbage collection automatica si prende cura di tutti questi aspetti per voi.
Ciò significa che potete restituire o salvare riferimento a variabili lessicali, laddove invece restituire un puntatore ad una variabile automatica in C sarebbe un grave errore. Ci dà anche la possibilità di simulare le variabili statiche a livello di funzione presenti in C. Ecco un meccanismo che fornisce ad una funzione variabili private che hanno sia una limitazione di visibilità che una vita di tipo statico. Se volete creare qualcosa di simile alle variabili statiche in C, non dovete far altro che racchiudere la funzione all'interno di un blocco addizionale, e mettere le variabili statiche fuori dalla funzione ma dentro al blocco.
{
my $valore_segreto = 0;
sub dammene_un_altro {
return ++$valore_segreto;
}
}
# $valore_segreto a questo punto diventa irraggiungibile dall'esterno,
# ma mantiene il suo valore fra le varie chiamate a dammene_un_altro
Se questa funzione viene inclusa da un file separato utilizzando require
o use
, allora probabilmente va tutto bene. Se si trova tutto nel programma principale, dovrete fare in modo che il my
sia eseguito abbastanza presto, o mettendo il blocco al di sopra del programma principale, o più probabilmente mettendo BEGIN
intorno al blocco di codice stesso, per essere sicuri che venga eseguito prima che il vostro programma cominci l'esecuzione:
BEGIN {
my $valore_segreto = 0;
sub dammene_un_altro {
return ++$valore_segreto;
}
}
Consultate "BEGIN, CHECK, INIT e END" in perlmod per maggiori ragguagli sui blocchi di codice ad esecuzione particolare, ossia BEGIN
, CHECK
, INIT
e END
.
Se dichiarate allo scope più esterno (quello del file), allora le lessicali funzionano abbastanza similmente alle variabili statiche di file in C. Sono disponibili per tutte le funzioni nello stesso file che sono dichiarate dopo di esse, ma risultano inaccessibili dall'esterno di questo file. Questa strategia è utilizzata a volte nei moduli per creare variabili private che possano essere viste dall'intero modulo.
Valori Temporanei con local()
ATTENZIONE: in generale, dovreste utilizzare my
invece che local
, perché è sia più veloce che più sicuro. Eccezioni a questa regola generale includono le variabili di punteggiatura, i filehandle globali ed i formati, oltre alla manipolazione diretta della stessa tabella dei simboli di Perl. local
è principalmente utilizzata quando il valore corrente di una variabile deve essere reso visibile ad una subroutine chiamata.
Sinossi:
# localizzazione di valori
local $pippo; # rendi $pippo locale dinamicamente
local (@pin, %co); # rende locale la lista delle variabili
local $pippo = "flurp"; # rende $pippo dinamica, e la inizializza
local @oppip = @pluto; # rende $oppip dinamica, e la inizializza
local $hash{chiave} = "val";# imposta un valore locale per questa
# chiave nella hash
local ($cond ? $v1 : $v2); # vari tipi di supporto per la localizzazione
# di variabili come lvalues
# localizzazione di simboli
local *FH; # localizza $FH, @FH, %FH, &FH ...
local *merlyn = *randal; # ora $merlyn E<egrave> veramente $randal, come anche
# @merlyn E<egrave> veramente @randal, ecc
local *merlyn = 'randal'; # IDEM: promuove 'randal' a *randal
local *merlyn = \$randal; # fai alias solo di $merlyn, non di @merlyn ecc
[un lvalue, lo ricordiamo, è un'espressione che restituisce qualcosa di utilizzabile nel lato sinistro di un'assegnazione, N.d.T.]
Un local
modifica le proprie variabili elencate rendondole "locali" nel blocco racchiudente, o nella eval
, o in do FILE
ed in qualsiasi subroutine chiamata da dentro questo blocco. Un local
non fa altro che associare valori temporanei a variabili globali (nel senso di variabili di pacchetto). Non crea una variabile locale. Questo meccanismo è noto come scope dinamico; lo scoping lessicale si fa con my
, che funziona molto più come le dichiarazioni automatiche di C.
Anche alcuni tipi di lvalue possono essere localizzati: elementi di hash ed array, loro slice, condizionali (a patto che il loro risultato risulti sempre localizzabile) e riferimenti simbolici. Così come per variabili semplici, questo sistema crea nuovi valori con visibilità dinamica.
Se vengono fornite a local
più di una variabile o espressione, esse devono essere poste fra parentesi tonde. Questo operatore funziona salvando il valore corrente di tali variabili nella sua lista degli argomenti in uno stack nascosto, e ripristinandole all'uscita dal blocco, dalla subroutine o dalla eval()
. Ciò significa che le subroutine chiamate possono anche riferirsi alla variabile locale, ma non a quella globale. Se lo desiderate, potete effettaure assegnazione alla lista degli argomenti, il che vi consente di inizializzare le vostre variabili locali. (Se non viene fornito alcun sistema di inizializzazione per una data variabile, essa viene creata con valore indefinito [undef
, N.d.T.]).
Poiché local
è un operatore utilizzato a tempo di esecuzione, esso viene eseguito tutte le volte in un ciclo, Di conseguenza, è più efficiente localizzare le vostre variabili al di fuori di un ciclo.
Nota grammaticale su local()
Un local
è semplicemente un modificatore su un'espressione lvalue. Quando fate un'assegnazione da una variabile local
izzata, il local
non cambia se la sua lista è vista come scalare o come array. Quindi
local($pippo) = <STDIN>;
local @pippo = <STDIN>;
forniscono entrambe un contesto lista alla parte di destra, mentre
local $foo = <STDIN>;
fornisce un contesto scalare.
Localizzazione delle variabili speciali
Se localizzate una variabile speciale le darete un nuovo valore, ma resterà magica. Questo significa che tutti gli effetti collaterali associati a questo comportamento magico continueranno a funzionare anche con il valore localizzato.
Grazie a tutto ciò quanto segue funzionerà come ci si aspetta:
# Leggi l'intero contenuto di FILE dentro la variabile $slurp
{ local $/ = undef; $slurp = <FILE>; }
Osservate, però, che questa caratteristica limita la possibilità di localizzazione di alcuni valori; ad esempio, l'istruzione seguente provocherà la terminazione con die
a partire da perl 5.9.0, con errore Modification of a read-only value attempted [tentativo di modifica di un valore di sola lettura, N.d.T.], perché la variabile $1
è magica e di sola lettura:
local $1 = 2;
In maniera analoga, ma più complicata da riconoscere, il brano di codice che segue terminerà l'esecuzione con die
in perl 5.9.0:
sub f { local $_ = "pippo"; print }
for ($1) {
# ora $_ e` un alias di $1, dunque e` magico e di sola lettura
f();
}
Leggete la prossima sezione per trovare alternative a questa situazione.
ATTENZIONE: la localizzazione di array e hash "trattati" con tie
non funziona come descritto, al momento. Questa anomalia verrà risolta in una futura versione di Perl; nel frattempo, evitate di scrivere codice che si basi su un particolare comportamento di array o hash sotto tie
localizzati (anche se la localizzazione dei singoli elementi è a posto). Consultate ""Localising Tied Arrays and Hashes Is Broken" in perl58delta ["la localizzazione di array e hash sotto tie
non funziona", N.d.T.] per maggiori dettagli.
Localizzazione dei glob
Il costrutto
local *nome;
crea una sezione completamente nuova nella tabella dei simboli per il glob nome
, all'interno del package corrente. Ciò significa che tutte le variabili nel suo slot glob ($nome
, @nome
, %nome
, &nome
ed il filehandle nome
) vengono ripulite (e ripristinate) automaticamente.
Fra l'altro, questo significa che qualsiasi comportamento "magico" assunto da queste variabili viene perso, localmente. In altre parole, utilizzare local */
non avrà effetto sul valore interno del separatore in ingresso dei record.
È notevole il fatto che, se volete lavorare con un valore nuovo di zecca per lo scalare di default $_
, ed evitare il potenziale problema indicato in precedenza nei casi in cui $_
sia latore di un comportamento magico, dovete utilizzare local *_
invece di local $_
.
Localizzazione di elementi di tipi compositi
È il caso di fermarci un momento a spiegare cosa accade quando local
izzate un membro di un tipo composito (ossia, di un array o di una hash). In questo caso, l'elemento viene local
izzato per nome. Ciò significa che quando lo scope della local
izzazione termina, il valore salvato verrà ripristinato nell'elemento della hash la cui chiave era nominata nella chiamata a local()
. Se questo elemento era stato cancellato mentre local()
era attiva (ad esempio da una chiamata a delete()
su una hash o da una shift()
di un array), ritornerà in vita, con il possibile effetto di estendere un array o riempire gli elementi mancanti di una hash con undef
. Ad esempio, se scrivete:
%hash = ( 'Questo' => 'costituisce', 'una' => 'prova' );
@ary = ( 0..5 );
{
local($ary[5]) = 6;
local($hash{'a'}) = 'drillo';
while (my $e = pop(@ary)) {
print "$e . . .\n";
last unless $e > 3;
}
if (@ary) {
$hash{'solo una'} = 'prova';
delete $hash{'una'};
}
}
print join(' ', map { "$_ $hash{$_}" } sort keys %hash),".\n";
print "L'array ha ",scalar(@ary)," elementi: ",
join(', ', map { defined $_ ? $_ : 'undef' } @ary),"\n";
Perl stamperà
6 . . .
4 . . .
3 . . .
Questa costituisce una prova solo una prova
L'array ha 6 elementi: 0, 1, 2, undef, undef, 5
Il comportamento di local()
su membri non esistenti di un tipo composito è soggetto a cambiamenti in futuro.
Subroutine come Valori a Sinistra di un'Assegnazione
[Quando un'espressione si può utilizzare come possibile valore alla sinistra di un'assegnazione, si dice che questa restituisce un lvalue, ossia un valore a sinistra. Nel seguito utilizzeremo questo termine. N.d.T.]
ATTENZIONE: le subroutine utilizzate come lvalue sono ancora sperimentali, l'implementazione potrebbe cambiare nelle versioni future di Perl.
È possibile restituire un valore modificabile da una subroutine. Per farlo, non dovete far altro che dichiarare che la subroutine restituirà un lvalue.
my $val;
sub puo_modificare : lvalue {
# return $val; non funziona, non utilizzate la parola "return"
$val;
}
sub non_puo_modificare {
$val;
}
puo_modificare() = 5; # assegna a $val
non_puo_modificare() = 5; # ERRORE
Il contesto scalare o di lista per la subroutine e per il lato destro dell'assegnazione viene determinato come se la chiamata a subroutine fosse rimpiazzata da uno scalare. Ad esempio, considerate:
data(2,3) = prendi_data(3,4);
Entrambe le subroutine qui vengono chiamate in contesto scalare, mentre in:
(data(2,3)) = prendi_data(3,4);
ed in:
(data(2),data(3)) = prendi_data(3,4);
tutte le subroutine sono chiamate in constesto lista.
- Le subroutine lvalue sono SPERIMENTALI
-
Possono sembrare comode, ma ci sono numerosi e fondati motivi per avere cautela.
Non potete utilizzare la parola chiave
return
, dovete passare il valore in uscita prima di di uscire dallo scope della subroutine (come indicato nel commento all'esempio fatto). Di solito questo non costituisce un problema, ma vi toglie la possibilità di uscire esplicitamente da un ciclo fortemente innestato, che a volte è una via d'uscita comoda.Le subroutine lvalue violano anche l'incapsulamento. Un normale metodo mutatore può controllare l'argomento che viene fornito prima di impostare l'attributo che sta proteggendo, mentre una subroutine lvalue non ha mai questa opportunità. Considerate:
my $un_rif_ad_array = []; # protetto dai mutatori?? sub imposta_arr { # mutatore normale my $val = shift; die("mi aspettavo un array, mi hai dato ", ref $val) unless ref $val eq 'ARRAY'; $un_rif_ad_array = $val; } sub imposta_arr_lv : lvalue {# mutatore lvalue $un_rif_ad_array; } # imposta_arr_lv non puo` fermare questo errore! imposta_arr_lv() = { a => 1 };
Passare Elementi della Tabella dei Simboli (typeglob)
ATTENZIONE: il meccanismo descritto in questa sezione era, originariamente, l'unico modo per simulare un passaggio per variabile nelle versioni di Perl più datate. Nonostante funzioni ancora bene nelle versioni più moderne, il nuovo meccanismo dei riferimenti è in generale più semplice da utilizzare. Vedete più avanti.
A volte non volete passare il valore di un array ad una subroutine ma, piuttosto, il suo nome, di modo che la subroutine possa modificare l'istanza globale dell'array stesso invece che lavorare su una copia locale. In Perl potete riferirvi a tutti gli oggetti che hanno un determinato nome anteponendo un asterisco: *pippo
. Questo meccanismo è noto come un typeglob, perché l'asterisco può essere pensato come se fosse un match wildcard [carattere jolly, N.d.T.] per tutti quegli strambi caratteri di prefisso per le variabili, le subroutine e così via.
Quando valutato, il typeglob produce un valore scalare che rappresenta tutti gli oggetti che hanno il dato nome, inclusi filehandle_, formati e subroutine. Quando gli si assegna qualcosa, si fa in modo che il nome utilizzato si riferisca a qualunque *
valore gli sia assegnato. Ad esempio:
sub raddoppia_array {
local(*unqualcheary) = @_;
foreach $elem (@unqualcheary) {
$elem *= 2;
}
}
raddoppia_array(*pippo);
raddoppia_array(*pluto);
Gli scalari sono sempre passati per riferimento, ossia potete modificare gli argomenti scalari senza utilizzare questo meccanismo semplicemente utilizzando $_[0]
ecc. Potete modificare tutti gli elementi di un array passandoli come scalari, ma dovete utilizzare il meccanismo *
(o l'equivalente con i riferimenti) per utilizzare push
, pop
o cambiare la dimensione dell'array. Sarà sicuramente più rapido passare il typeglob (o un riferimento).
Anche se non volete modificare un array, questo meccanismo è utile per passare più array in un'unica LISTA, perché normalmente il meccanismo della LISTA fonderà tutti i valori dei vari array senza permettervi di distinguerli. Per saperne di più sui typeglob: "Typeglob e Filehandle" in perldata.
Quando local() ha Ancora Senso
Nonostante esista my
, ci sono ancora tre situazioni dove l'operatore local
rifulge in tutto il suo splendore. Ancor meglio, in queste tre situazioni dovete utilizzare local
invece di my
.
Dovete assegnare un valore temporaneo ad una variabile globale, specialmente
$_
.Le variabili globali, come
@ARGV
o le variabili punteggiatura, devono esserelocal
izzate conlocal()
. Il blocco di codice che segue legge il file /etc/motd e lo divide in parti separate da righe o segni di uguaglianza, che sono poste in@Campi
.{ local @ARGV = ("/etc/motd"); local $/ = undef; local $_ = <>; @Campi = split /^\s*=+\s*$/; }
In particolare, è importante
local
izzare$_
in qualsiasi routine che assegni qualcosa a tale variabile. Facendo bene attenzione agli assegnamenti impliciti nei condizionali diwhile
.Dovete creare un handle locale per file o directory, o una funzione locale.
Una funzione che abbia bisogno di un file tutto suo deve utilizzare
local()
su un typeglob completo. Quanto segue può essere utilizzato per create un nuovo elemento nella tabella dei simboli:sub coda_io { local (*LETTORE, *SCRITTORE); # non my! pipe (LETTORE, SCRITTORE) or die "pipe: $!"; return (*LETTORE, *SCRITTORE); } ($testa, $coda) = coda_io();
Consultate il modulo
Symbol
per un modo per creare elementi anonimi nella tabella dei simboli.Poiché l'assegnazione di un riferimento ad un typeglob crea un alias, questa caratteristica si può utilizzare per creare quella che risulta, di fatto, una funzione locale, o quantomeno un alias locale.
{ local *cresce = \&restringe; # solo finche' esiste questo blocco cresce(); # in realta`, chiama restringe() muove(); # se muove() cresce(), si restringe() } cresce(); # usa la cresce() reale
Consultate "Modelli di Funzione" in perlref per maggiori ragguagli sulla manipolazione di funzioni per nome, come in questo caso.
Volete modificare temporaneamente solo un elemento di un array o di una hash.
Potete
local
izzare anche un solo elemento di un aggregato. Di solito viene fatto su variabili dinamiche:{ local $SIG{INT} = 'IGNORE'; funct(); # non interrompibile } # interrompibilita` ripristinata automaticamente in questo punto
Ma funziona anche su aggregati dichiarati lessicalmente. Prima della versione 5.005 questa operazione poteva portare a comportamenti anomali in qualche occasione.
Passaggio per Riferimento
Se volete passare più di un array o hash ad una funzione -- o restituirle dalla stessa -- e volete che mantengano la loro integrità, allora avete bisogno di utilizzare un passaggio per riferimento esplicito. Prima di farlo, però, c'è bisogno che capiate i riferimenti come spiegati in perlref. Viceversa, questa sezione non avrà molto senso.
Eccovi qualche esempio semplice. Prima di tutto, passiamo un po' di array ad una funzione e chiamiamo pop
su ciascuno, restituendo una nuova lista contenente tutti gli elementi estratti:
@finali = pop_molti ( \@a, \@b, \@c, \@d );
sub pop_molti {
my $aref;
my @listarit = ();
foreach $aref ( @_ ) {
push @listarit, pop @$aref;
}
return @listarit;
}
Ecco come potete scrivere una funzione che restituisce una lista di chiavi che sono presenti in tutte le hash passate in ingresso:
@comuni = intersezione( \%pippo, \%pluto, \%dree );
sub intersezione {
my ($k, $href, %vista); # locali
foreach $href (@_) {
while ( $k = each %$href ) {
$vista{$k}++;
}
}
return grep { $vista{$_} == @_ } keys %vista;
}
Fino ad ora, stiamo solo utilizzando il normale meccanismo di restituzione di una lista. Che succede se volete passare o restituire una hash? Bene, se ne state utilizzando solamente una, o non vi interessa se si concatenano, allora la convenzione di chiamata usuale va bene, per quanto sia un po' costosetta computazionalmente.
Dove molta gente si inguaia è qui:
(@a, @b) = funzione(@c, @d);
o
(%a, %b) = funzione(%c, %d);
Questa sintassi, molto semplicemente, non funziona. Quel che fa è impostare solamente @a
o %a
, e svuotare @b
o %b
. In più, la funzione non ha ricevuto due array, o hash, separati: solo una lunga lista in @_
, come al solito.
Se riuscite a fare in modo che chiunque riesca a capirci qualcosa con i riferimenti, il codice è più pulito, per quanto non così semplice da leggere. Ecco un esempio di funzione che prende due riferimenti ad array come argomenti, restituendo gli elementi dei due array nell'ordine di quanti ciascuno ne ha in sé:
($aref, $bref) = funzione(\@c, \@d);
print "@$aref ha piu` elementi di @$bref\n";
sub funzione {
my ($cref, $dref) = @_;
if (@$cref > @$dref) {
return ($cref, $dref);
} else {
return ($dref, $cref);
}
}
A conti fatti, potete anche fare così:
(*a, *b) = funzione(\@c, \@d);
print "@$aref ha piu` elementi di @$bref\n";
sub funzione {
local (*c, *d) = @_;
if (@c > @d) {
return (\@c, \@d);
} else {
return (\@d, \@c);
}
}
Qui stiamo utilizzando i typeglob per ottenere alias nella tabella dei simboli. C'è da dire che è un po' sottile, però, e che non funziona se utilizzate variabili dichiarate con my
, perché solo le variabili globali si trovano nella tabella dei simboli (anche se sono in incognito grazie a local
).
Se state passando dei filehandle, potete utilizzare, di solito, anche il puro e semplice typeglob, come *STDOUT
, ma funziona anche con riferimenti a typeglob. Ad esempio:
balbetta(\*STDOUT);
sub balbetta {
my $fh = shift;
print $fh "her umh beh a hmmm\n";
}
$rec = ottieni_rec(\*STDIN);
sub ottieni_rec {
my $fh = shift;
return scalar <$fh>;
}
Se avete in mente di generare nuovi filehandle... potete farlo. Fate attenzione a restituire solamente il filehandle *FH
così com'è, non un suo riferimento.
sub aprilo {
my $path = shift;
local *FH;
return open (FH, $path) ? *FH : undef;
}
Prototipi
Perl supporta una forma piuttosto limitata di verifica degli argomenti durante la compilazione, mediante l'utilizzo di prototipi di funzione. Se dichiarate:
sub mia_push (\@@)
allora mia_push()
accetta gli argomenti esattamente come push()
. La dichiarazione della funzione deve essere visibile al momento della compilazione. Il prototipo ha effetto solo sull'interpretazione delle chiamate a funzione nuovo stile, dove "nuovo stile" si riferisce alla chiamata a funzione senza utilizzare il carattere &
. In altre parole, se chiamate questa funzione come se fosse parte integrante del linguaggio, essa si comporterà come tale. Se la chiamate come una subroutine vecchio stile, allora si comporterà al vecchio modo. Da questa regola, naturalmente, discende che i prototipi sono ignorati quando ci sono riferimenti a subroutine come \&pippo
o su chiamate indirette a subroutine come &{$subref}
o $ubref->()
.
Anche le chiamate ai metodi [ossia, alle funzioni associate agli oggetti, N.d.T.] non sono influenzate dai prototipi, perché la funzione da chiamare non può essere stabilita a tempo di compilazione, dal momento che questa dipende dal particolare albero di ereditarietà.
Visto che lo scopo di questa caratteristica è sostanzialmente quella di consentirvi di definire subroutine che funzionano come se fossero delle funzioni native del linguaggio, ecco i prototipi di alcune funzioni che corrispondono quasi esattamente alle relative funzioni native:
Dichiarata come Chiamata come
sub mia_link ($$) mia_link $vecchio, $nuovo
sub mia_vec ($$$) mia_vec $var, $scostamento, 1
sub mia_index ($$;$) mia_index &ottienistringa, "substr"
sub mia_syswrite ($$$;$) mia_syswrite $buf, 0, length($buf) - $off, $off
sub mia_reverse (@) mia_reverse $a, $b, $c
sub mia_join ($@) mia_join ":", $a, $b, $c
sub mia_pop (\@) mia_pop @array
sub mia_splice (\@$$@) mia_splice @array, @array, 0, @pushsudime
sub mia_keys (\%) mia_keys %{$hashref}
sub mia_open (*;$) mia_open HANDLE, $nome
sub mia_pipe (**) mia_pipe HANDLELETTURA, HANDLESCRITTURA
sub mia_grep (&@) mia_grep { /pippo/ } $a, $b, $c
sub mia_rand ($) mia_rand 42
sub mia_time () mia_time
Ogni carattere di prototipo preceduto da una barra inversa [ossia la backslash "\", N.d.T.] impone che il parametro reale debba iniziare con quel carattere. Il valore passato in @_
sarà dunque un riferimento al parametro reale passato nella chiamata alla subroutine, ottenuto cioè applicando \
all'argomento.
Potete anche imporre il backslash a molti argomenti contemporaneamente, utilizzando la notazione \[]
:
sub mio_rif (\[$@%&*])
che vi consente di chiamare mio_rif()
come segue:
mio_rif $var
mio_rif @array
mio_rif %hash
mio_rif &sub
mio_rif *glob
ed il primo argomento di mio_rif()
sarà un riferimento ad uno scalare, un array, una hash, un pezzo di codice o un glob.
Caratteri di prototipo privi di backslash hanno un significato particolare. Ciascun carattere @
o %
consuma tutti gli argomenti che rimangono, e forza la loro valutazione in un contesto di lista. Un argomento rappresentato da $
forza il contesto scalare. Un &
richiede una subroutine anonima che, se passata come primo argomento, non richiede che sia specificata la parola chiave sub
o che sia seguita da una virgola [separatrice di argomenti, N.d.T.].
Un carattere *
consente di accettare una bareword [o "parola nuda", ossia parola non inclusa fra virgolette semplici o doppie, N.d.T.], una costante, un'espressione scalare, un typeglob, o un riferimento ad un typeglob in quello slot. Il valore sarà disponibile alla subroutine o come scalare semplice, o (negli ultimi due casi) come riferimento al typeglob. Se volete convertire questi argomenti sempre in un riferimento ad un typeglob, utilizzate Symbol::qualify_to_ref()
come segue:
use Symbol 'qualify_to_ref';
sub pippo (*) {
my $fh = qualify_to_ref(shift, caller);
# ...
}
Un punto-e-virgola separa gli argomenti obbligatori da quelli opzionali. È ridondante prima di @
o %
, che in ogni caso consumano tutto il resto.
Osservate che gli ultimi tre esempi nella tabella data sono trattati in maniera particolare dal parser [il sottosistema che legge il sorgente del programma e lo trasforma in qualcosa di eseguibile, N.d.T.]. mia_grep()
è interpretata come un vero operatore di lista, mia_rand()
come un vero operatore unario con la stessa precedenza unaria di rand()
, e mia_time()
è realmente priva di argomenti, proprio come time()
. Questo significa che se scrivete
mia_time +2;
otterrete mia_time() + 2
, non mia_time(2)
, che è come sarebbe interpretata senza il prototipo.
Un aspetto interessante di &
è che vi consente di generare nuove sintassi, a patto che si trovi in posizione iniziale:
sub try (&@) {
my($try,$catch) = @_;
eval { &$try };
if ($@) {
local $_ = $@;
&$catch;
}
}
sub catch (&) { $_[0] }
try {
die "topoloso";
} catch {
/topoloso/ and print "non topoloso\n";
};
Viene stampato "non topoloso"
. (Sì, rimangono ancora questioni irrisolte riguardanti la visibilità di @_
. Per il momento ignorerò domande a riguardo. (Ma osservate che se rendiamo @_
limitata come visibilità in maniera lessicale, queste subroutine anonime possono comportarsi come chiusure... (Cavoli, non vi sembra di trovarvi in un programma Lisp? (Fate finta di niente)))).
Ecco una re-implementazione dell'operatore Perl grep
:
sub mia_grep (&@) {
my $codice = shift;
my @risultato;
foreach $_ (@_) {
push(@risultato, $_) if &$codice;
}
@risultato;
}
Qualcuno preferirebbe prototipi completamente alfanumerici. Questi sono stati lasciati fuori dai prototipi per uno scopo esplicito: aggiungere, un giorno, il supporto per parametri formali con nome. Lo scopo principale del meccanismo attuale è quello di permettere a chi scrive moduli, di dare un migliore supporto alla diagnosi per gli utilizzatori dei moduli stessi. Larry pensa che questa notazione sia abbastanza comprensibile per i programmatori Perl, e che non sia troppo intrusiva nel corpo del modulo, né che peggiori la leggibilità. Il rumore causato dalla sequenza di caratteri è incapsulato all'interno di una piccola pillolina, che è semplice da ingoiare.
Se provate ad utilizzare una sequenza alfanumerica in un prototipo, genererete un warning [messaggio di avvertimento, N.d.T.] opzionale - Illegal character in prototype... [ossia, Carattere non valido nel prototipo..., N.d.T.]. Purtroppo le versioni precedenti di Perl consentivano l'utilizzo di un prototipo a patto che la prima parte fosse un prototipo valido. Il warning potrà essere promosso ad errore fatale in una versione futura di Perl, una volta che la maggior parte del codice sia stato ripulito da questo tipo di utilizzo.
È probabilmente meglio utilizzare i prototipi per le nuove funzioni, senza andare a fare operazioni di "prototipizzazione all'indietro" per l'esistente. Dovete infatti prestare particolare attenzione alle conseguenze derivanti dalle diversità fra contesto scalare e contesto di lista. Ad esempio, se decidete che una funzione debba prendere un solo parametro, come questa:
sub funz ($) {
my $n = shift;
print "mi hai dato $n\n";
}
e qualcuno la utilizzava chiamandola con un array o un'espressione che restituisce una lista:
funz(@pippo);
funz( split /:/ );
Con il prototipo, vi viene automaticamente imposta una chiamata a scalar
di fronte all'argomento, che può riservare più d'una sorpresa. Il vecchio @pippo
, che era solito contenere un solo scalare, non viene più passato. Al suo posto, ora viene passato 1
: ossia, il numero di elementi all'interno di @pippo
. E la funzione split
viene ora chiamata in contesto scalare, per cui comincia a scrivere dentro la vostra lista dei parametri @_
. Cribbio!
Tutto ciò è molto potente, chiaramente, e dovrebbe essere utilizzato con moderazione perché il mondo sia un posto migliore.
Funzioni Costanti
Funzioni che hanno un prototipo ()
sono potenziali candidate per l'inclusione in linea [espansione diretta del codice della funzione, che evita la chiamata vera e propria, N.d.T.]. Se il risultato a valle dell'ottimizzazione e della semplificazione delle costanti è una costante o uno scalare lessicalmente circoscritto che non ha altri riferimenti, allora verrà usato quello al posto di ciascuna chiamata fatta alla funzione senza utilizzare &
. (Consultate constant.pm per un modo semplice di dichiarare la maggior parte delle costanti).
Le seguenti funzioni saranno tutte espanse in linea:
sub pi () { 3.14159 } # Non proprio, ma ci siamo vicini
sub PI () { 4 * atan2 1, 1 } # Buona come viene,
# ed e` pure espansa in linea!
sub ST_DEV () { 0 }
sub ST_INO () { 1 }
sub FLAG_PIPPO () { 1 << 8 }
sub FLAG_PLUTO () { 1 << 9 }
sub FLAG_MASCHERA () { FLAG_PIPPO | FLAG_PLUTO }
sub OPZ_TOPO () { not (0x1B58 & FLAG_MASCHERA) }
sub N () { int(OPZ_TOPO) / 3 }
sub PIPPO_IMPOSTATO () { 1 if FLAG_MASCHERA & FLAG_PIPPO }
Fate attenzione che quelle che segono non saranno espanse; poiché contengono ambiti di visibilità interni, la semplificazione delle costanti non le riduce ad una singola costante:
sub pippo_set () { if (FLAG_MASCHERA & FLAG_PIPPO) { 1 } }
sub baz_val () {
if (OPZ_TOPO) {
return 23;
}
else {
return 42;
}
}
Se ridefinite una subrouinte che risultava espandibile, riceverete un warning obbligatorio. (Potete anche utilizzare questo warning per sapere se una particolare subroutine è considerata costante o no). Il warning è considerato importante abbastanza da non essere opzionale, perché le chiamate alla funzione compilate in precedenza utilizzeranno ancora la vecchia definizione della funzione. Se avete bisogno di ridefinire la subroutine, dovete assicurarvi che non sia espansa in linea, o eliminando il prototipo ()
(che ne cambia il comportamento, quindi attenzione) o aggirando il meccanismo di espansione in qualche altro modo, come
sub non_espansa_in_linea () {
23 if $];
}
Ridefinire le Funzioni Native
Molte funzioni native possono essere ridefinite, sebbene questo approccio debba essere riservato per poche e ben dimostrate occasioni. Tipicamente questo approccio potrebbe essere utilizzato da un modulo che tenti di emulare una funzionalità nativa su un sistema non-Unix.
La ridefinizione può essere effettuata solo importando il nome da un modulo in fase di compilazione -- la pre-dichiarazione ordinaria non è sufficiente. In ogni caso, il pragma use subs
vi consente, in effetti, di predichiarare le subroutine attraverso la sintassi di importazione usuale, e questi nomi possono poi ridefinire quelli nativi:
use subs 'chdir', 'chroot', 'chmod', 'chown';
chdir $daqualcheparte;
sub chdir { ... }
Per riferirsi senza ambiguità alla forma nativa, occorre precedere il nome della funzione con il qualificatore di pacchetto speciale CORE::
. Ad esempio, CORE::open()
si riferisce sempre alla funzione nativa open()
, anche se il pacchetto corrente ha importato qualche altra subroutine chiamata &open()
da qualche altra parte. Anche se sembra una normale chiamata a funzione, non lo è: non potete prendervi un riferimento, come l'espressione scorretta \&CORE::open
può sembrare produrre.
I moduli di libreria non dovrebbero, in generale, esportare nomi di funzioni native come open
o chdir
come parte della loro lista @EXPORT
di default, perché potrebbero andarsi ad infilare nello spazio dei nomi di qualcun altro e cambiar loro la semantica in maniera inaspettata. Se il modulo, invece, include questi nomi all'interno di @EXPORT_OK
sarà allora possibile per un utente importarli esplicitamente, ma non implicitamente. Ossia, potrebbero scrivere:
use Module 'open';
e verrebbe importata la ridefinizione di open
. Ma se scrivessero
use Module;
avrebbero i nomi importati per default senza le ridefinizioni.
Il meccanismo di ridefinizione delle funzioni native viene ristretto, abbastanza intenzionalmente, al pacchetto che richiede l'importazione. Esiste però un secondo metodo che può essere talvolta utilizzato quando volete ridefinire una funzione nativa in tutto il programma, senza preoccuparvi dei limiti degli spazi dei nomi. Questo risultato può essere ottenuto importando una subroutine nello spazio dei nomi speciale CORE::GLOBAL::
. Quello che segue è un esempio che rimpiazza l'operatore glob
senza troppo pudore, con qualcosa che gestisce le espressioni regolari:
package REGlob;
require Exporter;
@ISA = 'Exporter';
@EXPORT_OK = 'glob';
sub import {
my $pkg = shift;
return unless @_;
my $sim = shift;
my $dove = ($sim =~ s/^GLOBAL_// ? 'CORE::GLOBAL' : caller(0));
$pkg->export($dove, $sim, @_);
}
sub glob {
my $pat = shift;
my @preso;
local *D;
if (opendir D, '.') {
@preso = grep /$pat/, readdir D;
closedir D;
}
return @preso;
}
1;
Ed ecco come può essere (ab)usato:
#use REGlob 'GLOBAL_glob'; # ridefinisce glob() in OGNI spazio dei nomi
package Pippo;
use REGlob 'glob'; # ridefinisce glob() solo in Pippo::
print for <^[a-z_]+\.pm\$>; # mostra tutti i moduli pragmatici
Il commento iniziale mostra un esempio controverso, persino pericoloso. Ridefinendo glob
in maniera globale, state forzando il comportamento nuovo (e sovversivo) per l'operatore glob
in ogni spazio dei nomi, senza che i moduli che agiscono nei propri spazi dei nomi ne siano a conoscenza o che possano collaborare con questo cambiamento. Ovviamente, questo va fatto con estrema cautela -- se proprio va fatto.
L'esempio REglob
dato non implementa tutto il supporto necessario a ridefinire l'operatore glob
in maniera pulita. La glob
nativa ha comportamenti differenti a seconda che appaia in contesto scalare o di lista, mentre la nostra REglob
non lo fa. Va osservato che molte funzioni native hanno un comportamento sensibile al contesto, e che queste varietà devono essere adeguatamente supportate da un rimpiazzo scritto correttamente. Per un esempio pienamente funzionante su come ridefinire glob
, studiatevi l'implementazione di File::DosGlob
nella libreria standard.
Quando ridefinite una funzione nativa, il vostro rimpiazzo dovrebbe essere consistente (se possibile) con la sintassi originale. Potete ottenere questo effetto utilizzando un prototipo adeguato. Per avere il prototipo di una funzione nativa rimpiazzabile, utilizzate la funzione prototype
passando "CORE::nome_funzione_nativa"
come parametro (per maggiori dettagli si rimanda a perlfunc/prototype
).
Osservate, comunque, che la sintassi di alcune funzioni native non può essere espressa da un prototipo (come ad esempio system
o chomp
). Se le ridefinite non sarete in grado di simulare appieno la sintassi originale.
Anche le funzioni native do
, require
e glob
possono essere ridefinite, ma per una magia particolare la loro sintassi viene mantenuta, e non avete bisogno di definire un prototipo per i loro rimpiazzi. (Nonostante ciò, non potete ridefinire la sintassi di do BLOCCO
).
require
ha una magia nera in più: se chiamate il vostro rimpiazzo require
come require Pippo::Pluto
, esso in realtà riceverà "Pippo/Pluto.pm"
in @_
. Consultate "require" in perlfunc.
Come avrete notato dall'esempio precedente, se ridefinite glob
, anche l'operatore <*>
viene rimpiazzato.
In modo simile, ridefinendo la funzione readline
si ha un rimpiazzo anche per l'operatore di I/O equivalente <FILEHANDLE>
.
Va detto, infine, che alcune funzioni native (come exists
o grep
) non possono essere ridefinite.
Caricamento automatico
Se chiamate una funzione non definita, solitamente ottente immediatamente un errore fatale, che si lamenta del fatto che la subroutine non esiste. (La stessa cosa accade per le subroutine utilizzate come metodi, quando il metodo non esiste né nel pacchetto né in alcuna delle classi base). In ogni caso, però, se viene definita una subroutine chiamata AUTOLOAD
nel pacchetto o nei pacchetti utilizzati per trovare la subroutine originale, allora quella subroutine AUTOLOAD
viene chiamata con gli argomenti che sarebbero stati passati alla subroutine originale. Il nome della subroutine originale, pienamente qualificato con il nome del pacchetto, appare magicamente nella variabile globale $AUTOLOAD
dello stesso pacchetto della routine AUTOLOAD
. Il nome non viene passato come un argomento normale perché, ehm, beh, solo perché, ecco per il motivo che...
Molte routine AUTOLOAD
caricano una definizione per la subroutine richiesta utilizzando eval()
, e poi eseguono questa subroutine utilizzando una forma particolare di goto()
, che cancella lo stack frame di AUTOLOAD
senza lasciarne traccia. (Andatevi a vedere i sorgenti del modulo standard documentato in AutoLoader, ad esempio). Ma una routine AUTOLOAD
può anche semplicemente emulare la subroutine originale senza mai definirla. Ad esempio, facciamo finta che una funzione non definita debba semplicemente chiamare system
con gli argomenti dati. Tutto ciò che dovreste fare è:
sub AUTOLOAD {
my $programma = $AUTOLOAD;
$programma =~ s/.*:://;
system($programma, @_);
}
date();
who('am', 'i');
ls('-l');
Di fatto, se predichiarate le funzioni che volete chiamare in questo modo, non avete nemmeno bisogno delle parentesi:
use subs qw(date who ls);
date;
who "am", "i";
ls -l;
Un esempio più complesso di questo meccanismo si trova nel modulo standard Shell, che può trattare una subroutine non definita come chiamata ad un programma esterno.
Sono disponibili meccanismi per aiutare chi scrive moduli a suddividere i propri moduli in file caricabili automaticamente. Potete trovare il modulo standard AutoLoader descritto in AutoLoader ed in AutoSplit, il modulo standard SelfLoader in SelfLoader, e la documentazione su come aggiungere funzioni C al codice Perl in perlxs.
Attributi delle Subroutine
Una dichiarazione o una definizione di subroutine possono avere una lista di attributi associata. Se tale lista è presente, viene suddivisa utilizzando spazi o virgole come separatori, e trattata come se fosse stato scritto use attributes
. Consultate attributes per maggiori dettagli su quali attributi sono supportati al momento.
Differentemente dalle limitazioni legate all'obsolescente use attrs
, la sintassi sub : LISTA_ATTRIBUTI
è orientata ad associare gli attributi con una pre-dichiarazione, e non semplicemente con una definizione di subroutine.
Gli attributi devono essere validi come semplici nomi di identificatore (senza altri simboli di interpunzione oltre che il carattere '_'). Possono avere una lista di parametri, che viene controllata semplicemente per assicurarsi che le parentesi ('(',')') si innestino correttamente.
Esempi di sintassi valide (anche se gli attributi sono sconosciuti):
sub fnord (&\%) : scambia(10,pippo(7,3)) : costosa;
sub plugh () : Brutto('\(") :Cattivo;
sub xyzzy : _5x5 { ... }
Esempi di sintassi non valida:
sub fnord : scambia(10,pippo(); # () non bilanciate
sub snoid : Brutto('('); # () non bilanciate
sub xyzzy : 5x5; # "5x5" non e` un identificatore valido
sub plugh : Y2::north; # "Y2::north" identificatore non semplice
sub snurt : pippo + pluto; # "+" non e` una virgola o uno spazio
La lista degli attributi viene passata come una lista di stringhe costanti al codice che li associa alla subroutine. In particolare, il secondo esempio di sintassi valida sopra riportato, al momento viene interpretato e chiamato come segue:
use attributes __PACKAGE__, \&plugh, q[Brutto('\(")], 'Cattivo';
Per maggiori dettagli sulle liste di attributi e la loro manipolazione consultate attributes e Attribute::Handlers.
CONSULTATE ANCHE
Consultate "Modelli di Funzione" in perlref per dettagli su riferimenti e chiusure. Consultate perlxs per imparare qualcosa su come chiamare funzioni C da Perl. Consultate perlembed per imparare a chiamare subroutine Perl da C. Consultate perlmod per imparare a raggruppare le funzioni in file separati. Consultate perlmodlib per sapere quali moduli di libreria sono standard. Consultate perltoot per imparare a fare chiamate ai metodi di un oggetto.
TRADUZIONE
Versione
La versione su cui si basa questa traduzione è ottenibile con:
perl -MPOD2::IT -e print_pod perlsub
Per maggiori informazioni sul progetto di traduzione in italiano si veda http://pod2it.sourceforge.net/ .
Traduttore
Traduzione a cura di Flavio Poletti.
Revisore
Revisione a cura di dree.