![]() |
| index | project | pods | responsabili |
NOMEperlref - I Riferimenti in Perl e le strutture dati annidate
NOTAQuesta è 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.
DESCRIZIONEPrima 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.
Creare RiferimentiI riferimenti possono esser creati in molti modi.
Utilizzare i RiferimentiQuesto è quanto per creare riferimenti. Ormai vi starete probabilmente chiedendo come utilizzare riferimenti per recuperare dati a ``lungo persi''. Ci sono molti metodi 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 L'operatore 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
print "Questo produce @{[$n + 5]} widget\n";
Riferimenti SimboliciAbbiamo 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
local $valore = 10;
$rif = "valore";
{
my $valore = 20;
print $$rif;
}
Questo stamperà ancora 10, non 20. Ricordate che
Riferimenti non così simboliciUna 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.
Pseudo-hash: Utilizzare un array come un hashAVVERTIMENTO: 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
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
print exists $phash->[0]{pluto}; # vero, 'pluto' e` un campo valido
print exists $phash->[0]{scarpe}; # falso, 'scarpe' non puo` esser usato
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 funzioneCome 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
@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
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, Questo ha l'interessante effetto di creare una funzione locale ad un'altra funzione, qualcosa di solito non supportato in Perl.
AVVERTIMENTINon è 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 ANCHEAccanto 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
VersioneLa 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/ .
TraduttoreTraduzione a cura di Daniele Ludovici.
RevisoreRevisione a cura di dree. Mon Jun 11 22:02:18 2012 |