NOME
perlref - I Riferimenti in Perl e le strutture dati annidate
NOTA
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.
DESCRIZIONE
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 "Two-Phased Garbage Collection" in perlobj [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.
Creare Riferimenti
I riferimenti possono esser creati in molti modi.
- 1.
-
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.
- 2.
-
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). - 3.
-
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 unreturn
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. - 4.
-
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{}
oeval{}
. (Ad ogni modo, non importa quante volte eseguiate quella particolare linea (a meno che non siate in uneval("...")
), $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.
- 5.
-
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 è 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)
- 6.
-
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.
- 7.
-
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 ("open" in perlfunc), socket ("socket" in perlfunc e "socketpair" in perlfunc), e directory handle ("opendir" in perlfunc). 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 "Typeglobs and Filehandles" in perldata 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>; }
Utilizzare i Riferimenti
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;
2.
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.
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 è uno dei casi che avevamo menzionato prima nel quale i riferimenti potevano essere creati automaticamente [ossia quando sono utilizzati in un contesto 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 così fosse, esso è automaticamente definito con un riferimento ad hash così che possiamo fare un lookup di "{"pippo"}" in esso. Così come "$array[$x]->{"pippo"} sarà automaticamente definito con un riferimento ad array in maniera che possiamo fare lookup di "[0]" in esso. Questo processo è chiamato 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 realtà 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 sì.
4.
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 "ref" in perlfunc 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";
Riferimenti Simbolici
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.
Riferimenti non così simbolici
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 use warnings
o lo switch -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, perché la stringa è effettivamente posta fra virgolette.
Pseudo-hash: Utilizzare un array come un hash
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
Modelli di funzione
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.
AVVERTIMENTI
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.
SI VEDA ANCHE
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.
TRADUZIONE
Versione
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/ .
Traduttore
Traduzione a cura di Daniele Ludovici.
Revisore
Revisione a cura di dree.
4 POD Errors
The following errors were encountered while parsing the POD:
- Around line 351:
Expected '=item 2'
- Around line 389:
Expected '=item 3'
- Around line 433:
Expected '=item 4'
- Around line 646:
Non-ASCII character seen before =encoding in 'è'. Assuming CP1252