perlref - I Riferimenti in Perl e le strutture dati annidate
Questa è una documentazione completa riguardante tutti gli
aspetti dei riferimenti. Se si desidera un tutorial più breve,
con un'introduzione alle caratteristiche essenziali, date un'occhiata a perlreftut.
Prima della versione 5 di Perl era difficile rappresentare strutture
dati complesse, perché tutti i riferimenti dovevano esser
simbolici--ed era persino difficile referenziare una variabile invece
di una voce nella tabella dei simboli. Perl ora non solo rende più
semplice utilizzare dei riferimenti simbolici alle variabili, ma
vi permette anche di avere riferimenti concreti [hard reference, NdT]
a qualsiasi dato o pezzo di codice. Qualunque scalare può contenere
un riferimento concreto. Dato che gli array e gli hash contengono scalari, ora potete creare
facilmente array di array, array di hash, hash di array, array di hash
di funzioni, e così via.
I riferimenti concreti sono scaltri, tengono traccia dei contatori
dei riferimenti per voi, liberando automaticamente la cosa riferita quando
il suo contatore dei riferimenti va a zero. (I valori dei contatori dei
riferimenti di strutture dati auto-referenziate o cicliche potrebbero
non andare a zero senza un piccolo aiuto; consultate perlobj/``Two-Phased Garbage Collection''
[Garbage Collection in due fasi, NdT] in perlobj per una spiegazione dettagliata).
Se la cosa di cui si parla è un oggetto, l'oggetto viene distrutto. Consultate
perlobj per maggiori informazioni sugli oggetti. (In un certo senso, in Perl tutto
è un oggetto, ma noi solitamente riserviamo questo termine per
riferimenti a oggetti che sono stati ufficialmente sottoposti a bless
nel package della classe).
I riferimenti simbolici sono nomi di variabili o di altri oggetti,
proprio come un link simbolico in un filesystem Unix contiene
solamente il nome di un file. La notazione *glob è qualcosa di simile
ad un riferimento simbolico. (I riferimenti simbolici sono alle volte
chiamati ``riferimenti soft'', ma per favore non chiamateli così;
ci si confonde a sufficienza anche senza inutili sinonimi).
Al contrario, i riferimenti concreti sono più simili agli hard
[letteralmente ``duro'', NdT] link in un filesystem Unix: essi sono utilizzati
per accedere all'oggetto che vi sta sotto senza preoccuparsi di quale sia
il suo (altro) nome. Quando la parola ``riferimento'' è utilizzata
senza un aggettivo, come nel seguente paragrafo, si sta parlando solitamente
di un riferimento concreto.
I riferimenti sono facili da usare in Perl. C'è solo un principio di
sovrapposizione: Perl non referenzia o dereferenzia implicitamente. Quando
uno scalare contiene un riferimento, si comporta sempre come uno
scalare semplice. Non diventa magicamente un array o un hash o
una subroutine; dovete dirgli voi esplicitamente cosa fare, deferenziandolo.
I riferimenti possono esser creati in molti modi.
-
Utilizzando l'operatore backslash [barra inversa, NdT] su una variabile, una funzione o
un valore. (Questo funziona come l'operatore & (indirizzo-di) in C).
Questo crea tipicamente un altro riferimento ad una variabile,
perché c'è già un riferimento alla variabile
nella tabella dei simboli. Ma la tabella dei simboli dei riferimenti potrebbe
sparire e voi avreste ancora il riferimento che l'operatore backslash
ha restituito. Ecco alcuni esempi:
$rifscalare = \$pippo;
$rifarray = \@ARGV;
$rifhash = \%ENV;
$rifcodice = \&handler;
$rifglob = \*pippo;
Non è possibile creare un riferimento valido ad un handle di IO
(filehandle o dirhandle) utilizzando l'operatore backslash. Il massimo che
potete ottenere è un riferimento ad un typeglob, che è
in realtà una voce completa nella tabella dei simboli. Ma vedetevi la
spiegazione della sintassi *pippo{COSA} qua sotto. Comunque, potete ancora
utilizzare typeglob e riferimenti a glob come se fossero handle di IO.
-
Un riferimento ad un array anonimo può esser creato utilizzando
le parentesi quadre:
$rifarray = [1, 2, ['a', 'b', 'c']];
Qui abbiamo creato un riferimento ad un array anonimo di tre elementi il
cui elemento finale è esso stesso un riferimento a un altro array
anonimo di tre elementi. (La sintassi multidimensionale descritta in seguito
può essere utilizzata per accedere a quest'ultimo. Per esempio, dopo il
suddetto esempio, $rifarray->[2][1] dovrebbe avere il valore ``b'').
Ottenere un riferimento a una lista enumerata non è lo stesso che
utilizzare le parentesi quadre--che invece equivale a creare una lista
di riferimenti!
@lista = (\$a, \@b, \%c);
@lista = \($a, @b, %c); # stessa cosa!
Come caso speciale, \(@pippo) restituisce una lista di riferimenti al
contenuto di @pippo , non un riferimento a @pippo stesso. Nello stesso modo
per %pippo , eccetto che i riferimenti alle chiavi sono da copiare (dato che
le chiavi sono semplicemente delle stringe invece che scalari completi).
-
Un riferiemnto ad un hash anonimo può esser creato utilizzando le
parentesi graffe:
$rifhash = {
'Adamo' => 'Eva',
'Clyde' => 'Bonnie',
};
Hash anonimi e compositori di array come questi possono esser mescolati
liberamente per produrre strutture complicate quanto volete. La
sintassi multidimensionale descritta sotto va bene anche per questo. I
valori sopra sono letterali, ma le variabili e le espressioni
dovrebbero funzionare lo stesso, perché gli operatori di assegnamento
in Perl (perfino all'interno di local() o my() sono comandi eseguibili, non
dichiarazioni a tempo di compilazione.
Dato che le parentesi graffe (da ora in poi solo ``graffe'')
sono utilizzate per molte altre cose inclusi i BLOCCHI,
potreste occasionalmente dover esplicitare le graffe
all'inizio di un comando mettendovi un + o un return davanti,
di modo che Perl capisca che la graffa aperta non è
l'inizio di un BLOCCO. Vale la pena, in termini economici e mnemonici, di
utilizzare le graffe.
Per esempio, se volete che una funzione crei un nuovo hash e ne restituisca
un riferimento, avete queste opzioni:
sub hashem { { @_ } } # silenziosamente errata
sub hashem { +{ @_ } } # ok
sub hashem { return { @_ } } # ok
D'altro canto, se volete l'altro significato, potete far questo:
sub showem { { @_ } } # ambiguo (adesso funziona, ma potrebbe cambiare)
sub showem { {; @_ } } # ok
sub showem { { return @_ } } # ok
I +{ and {; all'inizio, servono a disambiguare l'espressione
per intendere o il riferimento ad un HASH o il BLOCCO.
-
Un riferimento ad una subroutine anonima può esser creato
utilizzando la parola chiave sub senza il nome della subroutine:
$rifcodice = sub { print "Boink!\n" };
Va notato il punto e virgola. Eccetto che per il codice all'interno che non
viene eseguito immediatamente, una sub {} non è proprio
una dichiarazione, essa è un operatore, do{} o eval{} .
(Ad ogni modo, non importa quante volte eseguiate quella particolare
linea (a meno che non siate in un eval("...") ), $rifcodice avrà
ancora un riferimento alla stessa subroutine anonima).
Le subroutine anonime agiscono come le closures [letteralmente
``chiusure'', NdT] con rispetto alle variabili my(),
che sono variabili lessicalmente visibili dentro lo scope corrente. La
chiusura è un concetto del mondo Lisp che dice che se voi definite
una funzione anonima in un particolare contesto lessicale, essa
pretende di essere eseguita in quel contesto anche quando viene chiamata
fuori da quel contesto.
In termini comprensibili, esso è una modo divertente di passare
argomenti ad una subroutine non solo quando la definite ma anche quando
la chiamate. È utile per impostare piccoli bit di codice da far
girare dopo, come callbacks. Potete anche farci anche cose orientate agli
oggetti, sebbene Perl fornisca già un meccanismo diverso per
farlo--consultate perlobj.
Potreste anche pensare alla chiusura come ad un modo per scrivere
un modello di subroutine [``template'' NdT] senza usare eval().
Ecco un breve esempio di come funzionano le chiusure:
sub nuovastampa {
my $x = shift;
return sub { my $y = shift; print "$x, $y!\n"; };
}
$h = nuovastampa("Salve");
$g = nuovastampa("Benvenuti");
# Il tempo passa...
&$h("mondo");
&$g("terrestri");
Questo stampa
Salve, mondo!
Benvenuti, terrestri!
Va notato in particolare che $x continua a riferire al valore passato
a nuovastampa() malgrado ``my $x'' sia uscita dallo scope
nel momento in cui la subroutine anonima è in esecuzione. Questo è tutto
ciò che una chiusura è.
A proposito, questo si applica solo a variabili lessicali. Le variabili
dinamiche continuano a funzionare come hanno sempre fatto. Le chiusure
non sono qualcosa di cui la maggior parte dei programmatori Perl
devono preoccuparsi all'inizio.
-
I riferimenti sono spesso restituiti da speciali subroutine
chiamate costruttori. Gli oggetti in Perl sono semplicemente dei riferimenti a
speciali tipi di oggetti che sembrano sapere a quale pacchetto essi
sono associati. I costruttori sono solo delle subroutine speciali
che sanno come creare quell'associazione. Fanno questo cominciando
con un normale riferimento, ed esso rimane un normale riferimento anche
quando è anche un oggetto. Il nome dei costruttori è spesso new()
e vengono chiamati indirettamente:
$rifogg = new Cagnolino (Coda => 'corta', Orecchie => 'lunghe');
Ma non E<egrave> necessario:
$rifogg = Cagnolino->new(Coda => 'corta', Orecchie => 'lunghe');
use Term::Cap;
$terminal = Term::Cap->Tgetent( { OSPEED => 9600 });
use Tk;
$main = MainWindow->new();
$menubar = $main->Frame(-relief => "raised",
-borderwidth => 2)
Quando dereferenziate uno scalare in un contesto che assume si tratti di
un riferimento di un certo tipo, un tale riferimento viene creato
automaticamente. Visto che non abbiamo ancora parlato del dereferenziamento,
non è ancora possibile mostrare alcun esempio.
-
Un riferimento può esser creato utilizzando una speciale sintassi,
affettuosamente conosciuta come sintassi *pippo{COSA}. *pippo{COSA}
restituisce un riferimento alla casella COSA in *pippo (che è la voce nella
tabella dei simboli che contiene qualunque cosa conosciuta come pippo).
$rifscalare = *pippo{SCALAR};
$rifarray = *ARGV{ARRAY};
$rifhash = *ENV{HASH};
$rifcodice = *handler{CODE};
$rifio = *STDIN{IO};
$rifglob = *pippo{GLOB};
Tutti questi sono auto-esplicativi tranne *pippo{IO}. Esso restituisce l'IO
handle, utilizzato per file handle (perlfunc/open), socket
(perlfunc/socket e perlfunc/socketpair), e directory handle
(perlfunc/opendir). Per compatibilità con le precedenti versioni
di Perl, *pippo{FILEHANDLE} è un sinonimo di *pippo{IO}, sebbene esso
sia deprecato dalla versione 5.8.0. Se sono in vigore dei warning di deprecazione,
ci saranno degli avvertimenti sul suo utilizzo.
*pippo{THING} restituisce undef se quella particolare COSA non è
già stata utilizzata, eccetto che nel caso di scalari.
*pippo{SCALAR} restituisce un riferimento ad uno scalare anonimo se $pippo non
è già stato utilizzato. Questo potrebbe cambiare in
versioni future.
*pippo{IO} è un'alternativa al meccanismo di *HANDLE dato in
perldata/``Typeglobs and Filehandles'' in perldata per passare filehandle
dentro o fuori una subroutine, o salvare dentro vaste strutture dati.
Il suo svantaggio è che non vi creerà un nuovo filehandle.
Il suo vantaggio è che correte meno rischi di sbagliare rispetto
a quanto si può fare con un assegnamento con typeglob.
(Sebbene esso combini ancora insieme file e directory handle). Ad ogni modo,
se assegnate il nuovo valore ad uno scalare invece che ad un typeglob come
abbiamo fatto nell'esempio sotto, non c'è il rischio che questo accada.
borbottio(*STDOUT); # passa l'intero glob
borbottio(*STDOUT{IO}); # passa sia file che dir handle
sub borbottio {
my $fh = shift;
print $fh "eh um bhe ah mmm\n";
}
$rec = get_rec(*STDIN); # passa l'intero glob
$rec = get_rec(*STDIN{IO}); # passa sia file che dir handle
sub get_rec {
my $fh = shift;
return scalar <$fh>;
}
Questo è quanto per creare riferimenti. Ormai vi starete probabilmente
chiedendo come utilizzare riferimenti per recuperare dati a ``lungo persi''.
Ci sono molti metodi di base.
-
Ovunque dovreste mettere un identificatore (o una serie di identificatori)
come parte di una variabile o nome di subroutine, potete rimpiazzare
l'indentificatore con un semplice scalare che contiene un riferimento
del tipo corretto:
$pluto = $$rifscalare;
push(@$rifarray, $nomefile);
$$rifarray[0] = "Gennaio";
$$rifhash{"CHIAVE"} = "VALORE";
&$rifcodice(1,2,3);
print $rifglob "output\n";
È importante capire che non stiamo dereferenziando
specificatamente $rifarray[0] o $rifhash{``CHIAVE''} qui. Il
dereferenziamento della variabile scalare accade prima che esso
faccia qualsiasi key lookup [``ricerca della chiave'' NdT].
Qualcosa di più complicato che una semplice
variabile scalare deve utilizzare i metodi 2 e 3 sotto. Comunque un
``semplice scalare'' include un identificatore che essostesso utilizza
il metodo 1 ricorsivamente. Di conseguenza, ciò stampa ``salve''
$rifrifrif = \\\"salve";
print $$$$rifrifrif;
-
Ovunque poniate un identificatore (o un insieme di identificatori)
come parte di una variabile o del nome di una subroutine, potete rimpiazzare
l'indentificatore con un BLOCCO che restituisce un riferimento di tipo corretto.
In altre parole, i precedenti esempi potrebbero esser scritti così:
$pluto = ${$rifscalare};
push(@{$rifarray}, $nomefile);
${$rifarray}[0] = "Gennaio";
${$rifhash}{"CHIAVE"} = "VALORE";
&{$rifcodice}(1,2,3);
$rifglob->print("output\n"); # sse IO::Handle e` caricato
Certo, è un po' sciocco utilizzare le graffe in questo
caso, ma il BLOCCO può contenere un'espressione arbitraria, in
particolare, l'espressione qui sotto:
&{ $dispatch{$indice} }(1,2,3); # chiama la routine corretta
Dato che è possibile omettere le graffe per il semplice caso di $$x ,
le persone spesso fanno un errore nel vedere i simboli del
dereferenziamento come degli operatori, e si meravigliano delle loro
precedenze. Se così fosse, potreste utilizzare le parentesi tonde
invece delle graffe. Non è questo il caso. Si consideri la differenza
sotto; il caso 0 è una versione stenografata del caso 1, non il caso 2:
$$rifhash{"CHIAVE"} = "VALORE"; # CASO 0
${$rifhash}{"CHIAVE"} = "VALORE"; # CASO 1
${$rifhash{"CHIAVE"}} = "VALORE"; # CASO 2
${$rifhash->{"CHIAVE"}} = "VALORE"; # CASO 3
Il caso due è anche ingannevole in quanto state accedendo ad una
variabile chiamata %rif_hash, non dereferenziandola attraverso $rifhash
all'hash che esso sta presumibilmente referenziando. Che dovrebbe essere il
caso 3.
- >>
Chiamate a subroutine e lookup di singoli elementi di array si
presentano spesso scomode nell'utilizzare il metodo 2.
Come una forma di zucchero sintattico, gli esempi per il metodo 2
potrebbero esser scritti:
$rifarray->[0] = "Gennaio"; # Elemento di un array
$rifhash->{"CHIAVE"} = "VALORE"; # Elemento di un hash
$rifcodice->(1,2,3); # Chiamata a subroutine
La parte sinistra della freccia può essere qualsiasi espressione che
restituisce un riferimento, inclusa una precedente dereferenziazione. Va notato
che $array[$x] non è la stessa cosa che $array->[$x] qui:
$array[$x]->{"pippo"}->[0] = "Gennaio";
Questo E<egrave> uno dei casi che avevamo menzionato prima nel quale i
riferimenti potevano essere creati automaticamente [ossia quando sono
utilizzati in un contesto I<lvalue>, ossia in cui possono essere
considerati alla stregua di un possibile valore a sinistra di un operatore
di assegnazione, NdT].
Prima questa istruzione, $array[$x] potrebbe non esser stato definito.
Se cosE<igrave> fosse, esso E<egrave> automaticamente definito con un riferimento
ad hash cosE<igrave> che possiamo fare un lookup di "{"pippo"}" in esso.
CosE<igrave> come "$array[$x]->{"pippo"} sarE<agrave> automaticamente definito
con un riferimento ad array in maniera che possiamo fare lookup di "[0]" in esso.
Questo processo E<egrave> chiamato I<autovivificazione>.
Un'altra cosa. La freccia è opzionale tra le graffe
qui sotto, di modo che potete scrivere quanto sopra come:
$array[$x]{"pippo"}[0] = "Gennaio";
Che, in un caso degenerativo di utilizzo di soli array ordinari, vi da
un array multidimensionale come in C:
$punteggio[$x][$y][$z] += 42;
Bene, okay, in realtE<agrave> non del tutto come un array in C. Il C non sa
come crescono i suoi array on demand [letteralmente "a richiesta" NdT].
Perl invece sE<igrave>.
Se un riferimento sembra essere un riferimento ad un oggetto, allora
ci sono probabilmente dei metodi per accedere alle cose riferite, e
dotreste probabilmente stick to (limitarvi a)(?) quei metodi se non siete
nel package della classe che li definisce. In altre parole,
siate buoni, e non violate l'incapsulamento di un oggetto senza
una ragione molto buona. Perl non forza l'incapsulamento. Qui non
siamo totalitaristi. Sebbene ci aspettiamo un po' di
civiltà di base.
Utilizzare una stringa o un numero come un riferimento produce un
riferimento simbolico, come spiegato sopra. Utilizzare un riferimento
come un numero produce un intero che rappresenta la sua locazione di
salvataggio in memoria. L'unica cosa utile che ci si può fare è
confrontare numericamente due riferimenti per vedere se si riferiscono
alla stessa locazione.
if ($rif1 == $rif2) { # confronto numerico, non costoso, di riferimenti
print "I rif 1 e 2 si riferiscono alla stessa cosa\n";
}
Utilizzando un riferimento all'interno di una stringa si produce sia il
suo tipo referente, che include qualsiasi package sottoposto a bless
come descritto in perlobj, sia l'indirizzo numerico della variabile espresso in
esadecimale. L'operatore ref() restituisce il tipo di cosa al quale il riferimento
sta puntando, senza l'indirizzo. Consultate perlfunc/ref per dettagli ed
esempi del suo utilizzo.
L'operatore bless() potrebbe essere usato per associare un
riferimento che punta ad un oggetto con un package che funzioni come un
oggetto di una classe. Si veda perlobj.
Un typeglob potrebbe essere dereferenziato nello stesso modo di un
riferimento, perché la sintassi di dereferenziazione indica sempre il
tipo di riferimento desiderato. Così ``${*pippo}'' e ``${\$pippo}'' indicano
entrambi la stessa variabile scalare.
Ecco un trucco per interpolare una chiamata a subroutine in una stringa:
print "Quella volta, la mia sub ha restituito @{[mysub(1,2,3)]}.\n";
Il modo in cui esso funziona è che quando ``@{...}'' è visto nella stringa
quotata tra doppi apici, esso viene valutato come un blocco. Il blocco crea
un riferimento ad un array anonimo contenente i risultati della chiamata
a mysub(1,2,3) . Così l'intero blocco restituisce un riferimento ad un
array, il quale è poi dereferenziato da @{...} ed incluso nella
stringa quotata tra doppi apici. Questo sotterfugio è utile anche per
espressioni arbitrarie:
print "Questo produce @{[$n + 5]} widget\n";
Abbiamo detto che i riferimenti vengono creati automaticamente se non sono
definiti, ma non abbiamo detto cosa accade se un valore usato come un
riferimento è già stato definito, ma non è un
riferimento concreto. Cioè il valore dello scalare è preso
come il nome di una variabile, anziché come link diretto ad un (possibile)
valore anonimo.
Spesso le persone si aspettano che funzioni in questo modo.
Quindi è ciò che fa.
$name = "pippo";
$$name = 1; # Imposta $pippo
${$name} = 2; # Imposta $pippo
${$name x 2} = 3; # Imposta $pippopippo
$name->[0] = 4; # Imposta $pippo[0]
@$name = (); # Pulisce @pippo
&$name(); # Chiama &pippo() (come in Perl 4)
$pack = "QUEL";
${"${pack}::$name"} = 5; # Imposta $QUEL::pippo senza eval
Questo è potente, e lievemente pericoloso, nel senso che è possibile
intendere (con la massima sincerità) di utilizzare un riferimento
reale, e invece utilizzare accidentalmente un riferimento simbolico.
Per proteggervi contro queste situazioni, potete dichiarare:
use strict 'refs';
e quindi saranno permessi solamente i riferimenti concreti per il resto del
blocco considerato. Un blocco interno potrebbe annullare ciò con
no strict 'refs';
Solamente le variabili package (globali, anche se localizzate) sono
visibili a riferimenti simbolici. Variabili lessicali (dichiarate con
my()) non sono nella tabella dei simboli, e quindi sono invisibili a
questo meccanismo. Per esempio:
local $valore = 10;
$rif = "valore";
{
my $valore = 20;
print $$rif;
}
Questo stamperà ancora 10, non 20. Ricordate che local() agisce su
variabili package, le quali sono tutte ``globali'' al package.
Una nuova caratteristica che contribuisce alla leggibilitià nel perl
versione 5.001 è che le graffe intorno ad un riferimento simbolico si
comportano più come degli apici, come se contenessero sempre una
stringa. Cioè,
$push = "pop on ";
print "${push}over";
ha sempre il senso di stampare ``pop on over'', anche se push è una parola
riservata del linguaggio. Questo è stato generalizzato per funzionare
anche fuori dagli apici, così che
print ${push} . "over";
ed anche
print ${ push } . "over";
avranno lo stesso effetto. (Questo dovrebbe esser stato un errore di
sintassi in Perl 5.000, anche se Perl 4 lo permetteva nella forma senza spazi).
Questo costrutto non è considerato essere un
riferimento simbolico quando state utilizzando strict refs:
use strict 'refs';
${ bareword }; # Va bene, significa $bareword.
${ "bareword" }; # Errore, riferimento simbolico.
Analogamente, a causa di tutto il subscripting che è stato fatto usando
parole singole, abbiamo applicato la stessa regola a qualsiasi
bareword [letteralmente ``parola nuda'', NdT] che è usata per
effettuare l'indicizzazione di un hash. Quindi ora,
invece di scrivere
$array{ "aaa" }{ "bbb" }{ "ccc" }
potete scrivere solamente
$array{ aaa }{ bbb }{ ccc }
e non preoccupatevi se le parole nelle graffe sono parole riservate del linguaggio.
Nel raro caso in cui desideriate fare qualcosa come
$array{ shift }
potete forzare l'interpretazione a parola riservata aggiungendo qualcosa
che la renda più di una semplice bareword:
$array{ shift() }
$array{ +shift }
$array{ shift @_ }
La direttiva C<use warnings> o lo switch B<-w> vi avviseranno qualora
interpretassero una parola riservata come una stringa. Ma non vi
avvertiranno ulteriormente circa l'uso di parole scritte in lettere
minuscole, perchE<eacute> la stringa E<egrave> effettivamente
posta fra virgolette.
AVVERTIMENTO: Questa sezione descrive una caratteristica sperimentale. I
dettagli potrebbero cambiare senza preavviso nelle versioni future.
NOTA: L'implementazione corrente degli pseudo-hash visibile all'utente
(il bizzarro utilizzo del primo elemento dell'array) è
sconsigliato a partire da Perl 5.8.0 e sarà rimosso in Perl 5.10.0, e
tale caratteristica sarà implementata differentemente. Non solo l'attuale
interfaccia è piuttosto bruttina, ma l'implementazione corrente
rallenta abbastanza visibilmente l'utilizzo normale di array ed hash. La
direttiva 'fields' rimarrà disponibile.
A partire dalla versione 5.005 di Perl, potete utilizzare un
riferimento ad array in alcuni contesti che di solito richiederebbero
un riferimento ad hash. Questo vi permette di accedere agli
elementi di un array utilizzando nomi simbolici, come se essi fossero i
campi di una struttura.
Per far in modo che questo funzioni, l'array deve contenere delle
informazioni in più. Il primo elemento dell'array deve essere un
riferimento ad un hash che consenta di mappare i nomi dei campi
agli indici dell'array. Ecco un esempio:
$struct = [{pippo => 1, pluto => 2}, "PIPPO", "PLUTO"];
$struct->{pippo}; # lo stesso che $struct->[1], cioe` "PIPPO"
$struct->{pluto}; # lo stesso che $struct->[2], cioe` "PLUTO"
keys %$struct; # restituira` ("pippo", "pluto") in qualche ordine
valores %$struct; # restituira` ("PIPPO", "PLUTO") nello stesso "qualche" ordine
while (my($k,$v) = each %$struct) {
print "$k => $v\n";
}
Perl solleverà un'eccezione qualora cercaste di accedere a dei campi che
non esistono. Per aggirare queste inconsistenze, usate sempre la
funzione fields::phash() fornita dalla direttiva fields .
use fields;
$pseudohash = fields::phash(pippo => "PIPPO", pluto => "PLUTO");
Per migliori prestazioni, Perl può fare anche la traduzione
dal nome del campo all'indice nell'array in fase di compilazione,
per riferimenti ad oggetti cui č stato assegnato un tipo. Si veda fields.
Ci sono due strade per controllare l'esistenza di una chiave in uno
pseudo-hash. La prima è utilizzare exists(). Questo metodo controlla
se un dato campo sia mai stato impostato. Essa agisce in questo modo
per controllare il comportamento di un hash ordinario. Per esempio:
use fields;
$phash = fields::phash([qw(pippo pluto pantaloni)], ['PIPPO']);
$phash->{pantaloni} = undef;
print exists $phash->{pippo}; # vero, 'pippo' era impostato nella dichiarazione
print exists $phash->{pluto}; # falso, 'pluto' non e` stato usato
print exists $phash->{pantaloni};# vero, i vostri 'pantaloni' sono stati toccati
La seconda è utilizzare exists() sul riferimento ad hash che si trova
nel primo elemento dell'array. In questo modo si controlla se la chiave
data è un campo valido nello pseudo-hash.
print exists $phash->[0]{pluto}; # vero, 'pluto' e` un campo valido
print exists $phash->[0]{scarpe}; # falso, 'scarpe' non puo` esser usato
delete() su un elemento di uno pseudo-hash cancella solamente il
valore corrispondente alla chiave, non la chiave stessa. Per cancellare
la chiave, dovrete cancellarla esplicitamente dal primo elemento
dell'hash.
print delete $phash->{pippo}; # stampa $phash->[1], "PIPPO"
print exists $phash->{pippo}; # falso
print exists $phash->[0]{pippo}; # vero, la chiave esiste ancora
print delete $phash->[0]{pippo}; # ora la chiave non c'e` piu`
print $phash->{pippo}; # eccezione a tempo di esecuzione
Come spiegato sopra, una funzione anonima con accesso alle variabili lessicali visibili quando
quella funzione è stata compilata, crea una chiusura. Essa mantiene l'accesso a quelle
variabili anche se non è in esecuzione successivamente, come in un signal handler o in una callback Tk.
Utilizzare una chiusura come un modello di funzione ci permette di
generare molte funzioni che si comportano in modo simile.
Supponiamo che vogliate delle funzioni con il nome dei colori, che
possano generare i cambiamenti di font necessari in HTML per ottenere
i colori stessi:
print "State ", rosso("attenti"), "con quella ", verde("luce");
Le funzioni rosso() e verde() dovrebbero esser simili. Per crearle,
assegneremo una chiusura ad un typeglob del nome della funzione che stiamo
cercando di andare a costruire.
@colori = qw(rosso blu verde giallo arancione porpora viola);
for my $nome (@colori) {
no strict 'refs'; # permette la manipolazione della tabella dei simboli
*$nome = *{uc $nome} = sub { "<FONT COLOR='$name'>@_</FONT>" };
}
Ora tutte queste differenti funzioni sembrano esistere indipendentemente.
Potete chiamare rosso(), ROSSO(), blu(), BLU(), verde(), ecc.
Questa tecnica fa risparmiare sia tempo di compilazione che utilizzo di memoria,
ed è meno incline ad errori, dato che i controlli sintattici vengono
fatti a tempo di compilazione. È di estrema importanza che qualsiasi
variabile nella subroutine anonima sia lessicale, in modo da creare la
chiusura in maniera appropriata. Questa è la ragione per il ``my'' nelle
variabili utilizzate nelle iterazioni.
Questo è uno dei pochi casi nei quali prototipizzare una chiusura ha un
qualche senso. Se aveste voluto imporre la valutazione dell'argomento
in contesto scalare (un'idea probabilmente non saggia per
questo particolare esempio), avreste potuto scriverlo cosė:
*$name = sub ($) { "<FONT COLOR='$nome'>$_[0]</FONT>" };
Comunque, dato che i controlli sui prototipi avvengono a tempo di
compilazione, l'assegnamento sopra avviene troppo tardi per poter essere
utilizzato. Potreste risolvere questo fatto mettendo l'intero ciclo di
assegnamenti tra un blocco BEGIN, forzandolo ad accadere durante la
compilazione.
Accedere a variabili lessicali che cambiano tipo--come quelle nel ciclo
for sopra--funziona solo con le chiusure, non nelle comuni
subroutine. Nel caso generale, quindi, le subroutine con nome non si
innestano correttamente, sebbene quelle con nome sì. Se siete
abituati ad utilizzare le subroutine innestate in altri linguaggi di
programmazione con le loro variabili private, in Perl dovrete lavorarci un po'
di più. Programmando intuitivamente questo tipo di cose
si incorre in misteriosi avvertimenti circa ``will not stay shared''
[letteralmente ``non saranno condivise'', NdT].
Per esempio, questo non funzionerà:
sub piu_esterno {
my $x = $_[0] + 35;
sub piu_interno { return $x * 19 } # SBAGLIATO
return $x + piu_interno();
}
una soluzione di ripiego è la seguente:
sub piu_esterno {
my $x = $_[0] + 35;
local *piu_interno = sub { return $x * 19 };
return $x + piu_interno();
}
Ora, piu_interno() può essere chiamata solamente dentro piu_esterno(), a causa
dell'assegnamento temporaneo della chiusura (subroutine anonima). Ma
quando questo viene fatto, la sub anonima ha normale accesso alla
variabile lessicale $x dallo scope di piu_esterno().
Questo ha l'interessante effetto di creare una funzione locale ad
un'altra funzione, qualcosa di solito non supportato in Perl.
Non è consigliabile utilizzare un riferimento come chiave in un hash.
Questo verrà infatti convertito in una stringa:
$x{ \$a } = $a;
Se provate a dereferenziare la chiave, essa non creerà un riferimento concreto,
e non si otterrà ciò che ci si aspetta. Potreste voler fare
qualcosa come
$r = \@a;
$x{ $r } = $r;
E poi, almeno potete utilizzare values(), che sarà un riferimento concreto,
invece di keys(), che non lo sarà.
Il modulo standard Tie::RefHash fornisce una soluzione conveniente a questo aspetto.
Accanto agli ovvi documenti, il codice sorgente può essere
istruttivo. Alcuni patologici esempi di utilizzo dei riferimenti
si possono trovare nel test di regressione t/op/ref.t nella
directory del codice sorgente Perl.
Consultate anche perldsc e perllol sul come utilizzare i riferimenti per
creare strutture dati complesse, e perltoot, perlobj, e perlbot sul
come utilizzarli per creare oggetti.
La versione su cui si basa questa traduzione è ottenibile con:
perl -MPOD2::IT -e print_pod perlref
Per maggiori informazioni sul progetto di traduzione in italiano si veda
http://pod2it.sourceforge.net/ .
Traduzione a cura di Daniele Ludovici.
Revisione a cura di dree.
Mon Jun 11 22:02:18 2012
|