perlsub - Subroutine Perl
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.
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 perlmod/``Pacchetti''.
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 C<&> 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 L<Prototipi> piE<ugrave> avanti.
X<&>
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 the PerlIO::via manpage.
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 perlmod/``BEGIN, CHECK, INIT e END''
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
the Attribute::Handlers manpage.
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 perlsyn/``Istruzioni semplici''
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
perlref/``Modelli di Funzione'' per trovare un sistema per aggirare
questo problema.
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 perlmod/``BEGIN, CHECK, INIT e END'' 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.
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.
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.
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 perl58delta/``Localising Tied Arrays and Hashes Is Broken
[''la localizzazione di array e hash sotto tie non funziona``, N.d.T.]
per maggiori dettagli.
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 $_ .
È 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.
[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 };
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:
perldata/``Typeglob e Filehandle''.
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
essere local izzate con local() . 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 di while .
-
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 perlref/``Modelli di Funzione'' 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.
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;
}
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 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 $];
}
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 perlfunc/require.
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.
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.
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 the Attribute::Handlers manpage.
Consultate perlref/``Modelli di Funzione'' 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.
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/ .
Traduzione a cura di Flavio Poletti.
Revisione a cura di dree.
Mon Jun 11 22:02:19 2012
|