perldsc - Il Ricettario delle strutture dati del Perl (Perl Data Structures Cookbook)
La mancanza più grave nel linguaggio di programmazione Perl prima della release
5.0 erano le strutture dati complesse.
Alcuni valorosi programmatori riuscirono ad emularle
anche senza il supporto diretto del linguaggio, ma
era un lavoro duro, e non certo per i deboli di cuore.
A volte ve la potevate cavare con la notazione $m{$AoA,$b} , presa in
prestito da awk, nella quale le chiavi sono in effetti più simili ad una
singola stringa contatenata "$AoA$b" , ma l'attraversamento e
l'ordinamento era difficile. I programmatori più disperati smanettarono
addirittura con le tabelle interne del Perl, una strategia che si dimostrò
ardua da sviluppare e mantenere -- per usare un eufemismo.
La release 5.0 di Perl ci permette di avere strutture dati più
complesse. Ora potete scrivere qualcosa del genere e avere un array
a tre dimensioni!
for $x (1 .. 10) {
for $y (1 .. 10) {
for $z (1 .. 10) {
$AoA[$x][$y][$z] =
$x ** $y + $z;
}
}
}
Ahimé, per quanto semplice possa sembrare, c'è più di quanto
si possa vedere a occhio nudo.
Come si fa a stampare? Perché non si può dire soltanto print @AoA ?
Come si fa ad ordinare? Come lo si può passare ad una funzione o come
lo si può ricevere da una funzione? È un oggetto? Lo si può salvare
su disco per leggerlo più tardi? Come si può accedere ad un'intera
colonna o riga della matrice? I valori devono essere tutti numerici?
Come potete vedere, è abbastanza facile confondersi.
Benché una piccola parte della colpa possa essere attribuita
all'implementazione basata su riferimenti, in realtà la colpa è
più che altro da dare alla mancanza di documentazione e di esempi
studiati per il principiante.
Questo documento è inteso come una disamina dettagliata ma
comprensibile dei differenti tipi di strutture dati che potreste voler
sviluppare. Dovrebbe inoltre servire come un ricettario di esempi.
In questo modo, quando avete bisogno di creare una di queste strutture
dati complesse, potete limitarvi a fare la spesa qui per un
esempio pronto.
Vediamo ognuno di questi costrutti in dettaglio. Ci sono sezioni
separate per ognuno dei seguenti:
Per adesso, vediamo le questioni generali, che sono comuni a tutti
i tipi di strutture dati.
La cosa più importante da capire a proposito delle strutture dati in
Perl -- compresi gli array a più dimensioni -- è che anche se possono
apparire diversamente, gli @ARRAY e gli %HASH sono tutti internamente
a una dimensione. Possono contenere soltanto valori scalari (vale a dire
una stringa, un numero o un riferimento). Non possono contenere direttamente
altri array o hash, ma riferimenti ad altri array o hash.
Non potete usare un riferimento ad un array o ad una hash nello stesso modo
in cui usereste un array o un hash. Per i programmatori C o C++ non
abituati a distinguere tra array e puntatori agli stessi, può risultare
fonte di confusione. Se è così, basta pensare alla differenza tra una
struttura e il puntatore alla struttura.
Potete (e dovreste) leggere ulteriori informazioni a proposito dei
riferimenti nella pagina di perlref(1). In sintesi, i riferimenti
sono come dei puntatori che sanno a cosa puntano. (Anche gli oggetti
sono un tipo di riferimenti, ma non ne avremo bisogno). Questo vuol dire
che quando avete qualcosa che vi sembra essere un accesso ad un array
o ad un hash a due o più dimensioni, ciò che accade in realtà
è che il tipo base è semplicemente una entità monodimensionale che
contiene riferimenti al livello successivo. Ma potete usarlo come se fosse un
array bi-dimensioniale. Questo è il modo in cui in C funzionano quasi tutti gli array
multidimensionali.
$array[7][12] # array di array
$array[7]{stringa} # array di hash
$hash{stringa}[7] # hash di array
$hash{stringa}{'altra stringa'} # hash di hash
Ora, poiché il livello principale contiene soltanto riferimenti,
se provate a stampare il vostro array con una semplice funzione print(),
otterrete qualcosa che non appare molto bello, come questo:
@AoA = ( [2, 3], [4, 5, 7], [0] );
print $AoA[1][2];
7
print @AoA;
ARRAY(0x83c38)ARRAY(0x8b194)ARRAY(0x8b1d0)
Questo perché il Perl non dereferenzia (mai) implicitamente le variabili.
Se volete ottenere ciò a cui punta il riferimento, allora dovete farlo
per conto vostro usando o il prefisso indicante il tipo, come
${$bla} , @{$bla} , @{$bla[$i]} , o altrimenti i puntatori
a freccia postfissi, come $a->[3] , $h->{fred} , o anche
$ob->metodo()->[3] .
I due errori più comuni nel costruire qualcosa come un array di array
sono o contare accidentalmente il numero di elementi oppure prendere ripetutamente
un riferimento alla stessa locazione di memoria.
Ecco il caso in cui avete il numero di elementi anziché l'array
nidificato:
for $i (1..10) {
@array = una_certa_funzione($i);
$AoA[$i] = @array; # SBAGLIATO!
}
Questo è proprio il semplice caso in cui si assegna un array ad uno scalare,
ottenendo soltanto il numero dei suoi elementi. Se questo è quello che
volete veramente, allora potreste considerare la possibilità di essere
più espliciti:
for $i (1..10) {
@array = una_certa_funzione($i);
$contatori[$i] = scalar @array;
}
Ecco il caso in cui si prende un riferimento alla stessa zona di memoria,
ancora e ancora:
for $i (1..10) {
@array = una_certa_funzione($i);
$AoA[$i] = \@array; # SBAGLIATO!
}
Dunque, qual è il grosso problema con tutto ciò? Sembra corretto, no?
Dopo tutto, ho soltanto detto che avete bisogno di un array di
riferimenti, quindi ne avete fatto uno!
Sfortunatamente, benché ciò sia vero, è ancora scorretto. Ciascun
riferimento in @AoA punta alla stessa posizione, e ognuno di conseguenza
conterrà l'ultimo elemento di @array! È simile al problema che si
riscontra nel seguente programma C:
#include <pwd.h>
main() {
struct passwd *getpwnam(), *rp, *dp;
rp = getpwnam("root");
dp = getpwnam("daemon");
printf("Il nome di daemon e` %s\nIl nome di root e` %s\n",
dp->pw_name, rp->pw_name);
}
Che stamperà
Il nome di daemon e` daemon
Il nome di root e` daemon
Il problema è che sia rp che dp sono puntatori alla stessa
locazione di memoria! In C, dovreste ricordarvi di allocare memoria
aggiuntiva con malloc(). In Perl, dovrete usare il costruttore di array []
oppure il costruttore di hash {} . Ecco la maniera giusta per
scrivere il frammento di codice errato di prima:
for $i (1..10) {
@array = una_certa_funzione($i);
$AoA[$i] = [ @array ];
}
Le parentesi quadre costruiscono un riferimento ad un nuovo array
che contiene una copia di quello che sta in @array al momento
dell'assegnamento. Questo è quello che volevate fare.
Va notato che il codice seguente produrrà qualcosa di simile, ma è
più difficile da leggere:
for $i (1..10) {
@array = 0 .. $i;
@{$AoA[$i]} = @array;
}
È la stessa cosa? Beh, forse sì e forse no. La sottile differenza
è che quando assegnate qualcosa che sta tra parentesi quadre,
sapete con sicurezza che è un riferimento nuovo con una copia dei
dati. Potrebbe accadere qualcosa di diverso in questo nuovo caso
con il dereferenziamento @{$AoA[$i]}} nella parte sinistra
dell'assegnamento. Dipende tutto dal fatto se @{$AoA[$i]}} è
indefinito oppure contiene già un riferimento. Se avete già
popolato @AoA con dei riferimenti, come in
$AoA[3] = \@un_altro_array;
Allora l'assegnamento con l'indirezione nel lato sinistro userebbe
il riferimento esistente che era già lì:
@{$AoA[3]} = @array;
Naturalmente, ciò avrebbe l'``interessante'' effetto di sovrascrivere
@un_altro_array. (Avete mai notato che quando un programmatore dice
``interessante'', in realtà, piuttosto che voler dire ``intrigante'',
hanno la tendenza ad intendere ``scocciante'' o ``difficile'', o entrambi? :-) ).
Quindi si ricordi di usare sempre i costruttori di array o hash [] e {},
e sarete a posto, benché non sempre sia la soluzione ottimale dal punto
di vista dell'efficienza.
Sorprendentemente, il seguente costrutto a prima vista pericoloso
funzionerà correttamente:
for $i (1..10) {
my @array = una_certa_funzione($i);
$AoA[$i] = \@array;
}
Questo perché my(), di per sé, è più un'istruzione a tempo di
esecuzione che una a tempo di compilazione. Questo significa che la variabile my() è
``rinfrescata'' tutte le volte che si cicla. Quindi, benché sembri che abbiate memorizzato
lo stesso riferimento ogni volta, in effetti non l'avete fatto! È una
distinzione sottile che può produrre codice più efficiente, ma con il rischio
di fuorviare coloro che non hanno molta esperienza. Per questo motivo
di solito si sconsiglia di insegnarla ai principianti. In effetti, a parte i
casi in cui si devono passare argomenti alle funzioni, di rado piacerebbe
vedere l'operatore dammi-un-riferimento (il backslash) usato spesso nel codice.
Piuttosto, si consiglia ai principianti (e a tutti) di provare ad usare i
costruttori [] e {}, più facili da capire, anziché lasciare che sia lo scope
lessicale (o dinamico) e il conteggio dei riferimenti ad agire dietro le quinte.
In sintesi:
$AoA[ $i ] = [ @array ]; # di solito il migliore
$AoA[ $i ] = \@array; # pericolo; quell'array era my() ?
@{ $AoA[ $i ] } = @array; # troppo complicato per la maggior parte dei programmatori
Quando si parla di cose come @{$AoA[$i]}, le due seguenti scritture
sono sostanzialmente la stessa cosa:
$aref->[2][2] # chiaro
$$aref[2][2] # puo` trarre in inganno
Ciò è dovuto al fatto che le regole di precedenza del Perl per i suoi
cinque prefissi di dereferenziazione (che assomigliano a qualcuno che sta
imprecando: $ @ * % & ) fanno sì che essi leghino in maniera più forte rispetto
alle parentesi postfisse, quadre o graffe, di indicizzazione! Questo sarà senza dubbio un grande shock
per il programmatore C o C++, che è abbastanza abituato ad usare *a[i] per
indicare ciò che è puntato dall'elemento i di a . In altre parole,
prima prendono l'elemento, e poi dereferenziano l'elemento.
Questo va bene in C, ma questo non è C.
Il costrutto apparentemente equivalente in Perl, $$aref[$i] , per prima
cosa dereferenzia $aref , facendo sì che tratti $aref come un
riferimento all'array per poi dereferenziarlo, e poi restituisce l'i-esimo
elemento dell'array puntato da $aref . Se aveste voluto il costrutto a-la C, avreste dovuto
scrivere ${$aref[$i]} per forzare la valutazione di $aref[$i] prima
della dereferenziazione $ iniziale.
Se comincia a sembrare più pauroso di quanto ne valga la pena,
rilassatevi. Il Perl ha alcune caratteristiche che vi aiutano ad
evitare gli errori più comuni. Il modo migliore per non confondersi
è cominciare ogni programma in questo modo:
#!/usr/bin/perl -w
use strict;
In questo modo, sarete obbligati a dichiarare tutte le variabili con
my() , inoltre disabilita i ``dereferenziamenti simbolici'' accidentali.
Quindi se avete scritto questo:
my $aref = [
[ "fred", "barney", "pebbles", "bambam", "dino", ],
[ "homer", "bart", "marge", "maggie", ],
[ "george", "jane", "elroy", "judy", ],
];
print $aref[2][2];
Il compilatore lo marcherebbe istantaneamente come un errore a
tempo di compilazione, perché stareste accedendo a @aref , una variabile
non dichiarata, e vi suggerirebbe di scrivere
print $aref->[2][2];
Prima della versione 5.002, il debugger standard di Perl non faceva
un gran lavoro nello stampare strutture dati complesse. Dalla versione
5.002 in poi, il debugger include diverse nuove caratteristiche, compresa
la possibilità di modificare i comandi già impartiti (da linea di comando),
così come il comando x per stampare il contenuto di strutture dati
complesse. Per esempio, dato l'assegnamento di prima a $aref , ecco
l'output del debugger.
DB<1> x $AoA
$AoA = ARRAY(0x13b5a0)
0 ARRAY(0x1f0a24)
0 'fred'
1 'barney'
2 'pebbles'
3 'bambam'
4 'dino'
1 ARRAY(0x13b558)
0 'homer'
1 'bart'
2 'marge'
3 'maggie'
2 ARRAY(0x13b540)
0 'george'
1 'jane'
2 'elroy'
3 'judy'
Ecco un po' di brevi esempi di codice, presentati con pochi commenti (un giorno
avranno la loro propria manpage), che illustrano l'accesso a vari tipi di strutture dati.
@AoA = (
[ "fred", "barney" ],
[ "george", "jane", "elroy" ],
[ "homer", "marge", "bart" ],
);
# lettura da file
while ( <> ) {
push @AoA, [ split ];
}
# chiamata di una funzione
for $i ( 1 .. 10 ) {
$AoA[$i] = [ somefunc($i) ];
}
# uso di variabili temporanee
for $i ( 1 .. 10 ) {
@tmp = somefunc($i);
$AoA[$i] = [ @tmp ];
}
# aggiunta ad una riga esistente
push @{ $AoA[0] }, "wilma", "betty";
# un elemento
$AoA[0][0] = "Fred";
# un altro elemento
$AoA[1][1] =~ s/(\w)/\u$1/;
# stampa tutto con i riferimenti
for $aref ( @AoA ) {
print "\t [ @$aref ],\n";
}
# stampa tutto con gli indici
for $i ( 0 .. $#AoA ) {
print "\t [ @{$AoA[$i]} ],\n";
}
# stampa tutto, un elemento alla volta
for $i ( 0 .. $#AoA ) {
for $j ( 0 .. $#{ $AoA[$i] } ) {
print "elt $i $j is $AoA[$i][$j]\n";
}
}
%HoA = (
flintstones => [ "fred", "barney" ],
jetsons => [ "george", "jane", "elroy" ],
simpsons => [ "homer", "marge", "bart" ],
);
# lettura dal file
# flintstones: fred barney wilma dino
while ( <> ) {
next unless s/^(.*?):\s*//;
$HoA{$1} = [ split ];
}
# lettura dal file; piu` variabili temporanee
# flintstones: fred barney wilma dino
while ( $riga = <> ) {
($chi, $resto) = split /:\s*/, $riga, 2;
@campi = split ' ', $resto;
$HoA{$chi} = [ @campi ];
}
# chiamata a funzione che restituisce una lista
for $gruppo ( "simpsons", "jetsons", "flintstones" ) {
$HoA{$gruppo} = [ prendi_famiglia($gruppo) ];
}
# stessa cosa, ma utilizzando variabili temporanee
for $gruppo ( "simpsons", "jetsons", "flintstones" ) {
@membri = prendi_famiglia($gruppo);
$HoA{$gruppo} = [ @membri ];
}
# aggiungi (in fondo) un nuovo membro ad una famiglia esistente
push @{ $HoA{"flintstones"} }, "wilma", "betty";
# un elemento
$HoA{flintstones}[0] = "Fred";
# un altro elemento
$HoA{simpsons}[1] =~ s/(\w)/\u$1/;
# stampa il tutto
foreach $famiglia ( keys %HoA ) {
print "$famiglia @{ $HoA{$famiglia} }\n"
}
# stampa il tutto con gli indici
foreach $famiglia ( keys %HoA ) {
print "famiglia ";
foreach $i ( 0 .. $#{ $HoA{$famiglia} } ) {
print " $i = $HoA{$famiglia}[$i]";
}
print "\n";
}
# stampa il tutto ordinato con il numero di membri
foreach $famiglia ( sort { @{$HoA{$b}} <=> @{$HoA{$a}} } keys %HoA ) {
print "$famiglia @{ $HoA{$famiglia} }\n"
}
# stampa il tutto ordinato per numero di membri e nome
foreach $famiglia ( sort {
@{$HoA{$b}} <=> @{$HoA{$a}}
||
$a cmp $b
} keys %HoA )
{
print "$famiglia: ", join(", ", sort @{ $HoA{$famiglia} }), "\n";
}
@AoH = (
{
Capo => "fred",
Amico => "barney",
},
{
Capo => "george",
Moglie => "jane",
Figlio => "elroy",
},
{
Capo => "homer",
Moglie => "marge",
Figlio => "bart",
}
);
# lettura da file
# formato: CAPO=fred AMICO=barney
while ( <> ) {
$rec = {};
for $campo ( split ) {
($chiave, $valore) = split /=/, $campo;
$rec->{$chiave} = $valore;
}
push @AoH, $rec;
}
# lettura da file
# formato: CAPO=fred AMICO=barney
# nessuna variabile temporanea
while ( <> ) {
push @AoH, { split /[\s+=]/ };
}
# chiamata a funzione che restituisce una lista di
# coppie chiave/valore, come:
# "capo","fred","figlia","pebbles"
while ( %campi = prendi_prossimo_insieme_coppie() ) {
push @AoH, { %campi };
}
# uguale, ma senza variabili temporanee
while (<>) {
push @AoH, { individua_coppie($_) };
}
# aggiungi una coppia chiave/valore ad un elemento
$AoH[0]{animale} = "dino";
$AoH[2]{animale} = "il piccolo aiutante di babbo natale";
# un elemento
$AoH[0]{capo} = "fred";
# un altro elemento
$AoH[1]{capo} =~ s/(\w)/\u$1/;
# stampa il tutto con i riferimenti
for $href ( @AoH ) {
print "{ ";
for $ruolo ( keys %$href ) {
print "$ruolo=$href->{$ruolo} ";
}
print "}\n";
}
# stampa il tutto con gli indici
for $i ( 0 .. $#AoH ) {
print "$i is { ";
for $ruolo ( keys %{ $AoH[$i] } ) {
print "$ruolo=$AoH[$i]{$ruolo} ";
}
print "}\n";
}
# stampa il tutto, un elemento alla volta
for $i ( 0 .. $#AoH ) {
for $ruolo ( keys %{ $AoH[$i] } ) {
print "elemento $i $ruolo e` $AoH[$i]{$ruolo}\n";???
}
}
%HoH = (
flintstones => {
capo => "fred",
amico => "barney",
},
jetsons => {
capo => "george",
moglie => "jane",
"il suo ragazzo" => "elroy",
},
simpsons => {
capo => "homer",
moglie => "marge",
bimbo => "bart",
},
);
# lettura da file
# flintstones: capo=fred amico=barney moglie=wilma animale=dino
while ( <> ) {
next unless s/^(.*?):\s*//;
$chi = $1;
for $campo ( split ) {
($chiave, $valore) = split /=/, $campo;
$HoH{$chi}{$chiave} = $valore;
}
# lettura da file; piu` variabili temporanee
while ( <> ) {
next unless s/^(.*?):\s*//;
$chi = $1;
$rec = {};
$HoH{$chi} = $rec;
for $campo ( split ) {
($chiave, $valore) = split /=/, $campo;
$rec->{$chiave} = $valore;
}
}
# chiamata a funzione che restituisce un hash di chiave,valore
for $gruppo ( "simpsons", "jetsons", "flintstones" ) {
$HoH{$gruppo} = { prendi_famiglia($gruppo) };
}
# uguale, ma utilizzando piu` variabili temporanee
for $gruppo ( "simpsons", "jetsons", "flintstones" ) {
%membri = prendi_famiglia($gruppo);
$HoH{$gruppo} = { %membri };
}
# aggiungi nuovi membri ad una famiglia esistente
%gente_nuova = (
moglie => "wilma",
animale => "dino",
);
for $cosa (keys %gente_nuova) {
$HoH{flintstones}{$cosa} = $gente_nuova{$cosa};
}
# un elemento
$HoH{flintstones}{moglie} = "wilma";
# un altro elemento
$HoH{simpsons}{capo} =~ s/(\w)/\u$1/;
# stampa il tutto
foreach $famiglia ( keys %HoH ) {
print "$famiglia: { ";
for $ruolo ( keys %{ $HoH{$famiglia} } ) {
print "$ruolo=$HoH{$famiglia}{$ruolo} ";
}
print "}\n";
}
# stampa il tutto, ordinato in qualche modo
foreach $famiglia ( sort keys %HoH ) {
print "$famiglia: { ";
for $ruolo ( sort keys %{ $HoH{$famiglia} } ) {
print "$ruolo=$HoH{$famiglia}{$ruolo} ";
}
print "}\n";
}
# stampa il tutto ordinato per numero di membri
foreach $famiglia ( sort { keys %{$HoH{$b}} <=> keys %{$HoH{$a}} } keys %HoH ) {
print "$famiglia: { ";
for $ruolo ( sort keys %{ $HoH{$famiglia} } ) {
print "$ruolo=$HoH{$famiglia}{$ruolo} ";
}
print "}\n";
}
# stabilisci un ordine (rango) di ordinamento per ciascun ruolo
$i = 0;
for ( qw(capo moglie figlio figlia amico animale) ) { $rango{$_} = ++$i }
# ora stampa il tutto ordinato per numero di membri
foreach $famiglia ( sort { keys %{ $HoH{$b} } <=> keys %{ $HoH{$a} } } keys %HoH ) {
print "$famiglia: { ";
# e stampa questi in accordo al rispettivo rango
for $ruolo ( sort { $rango{$a} <=> $rango{$b} } keys %{ $HoH{$famiglia} } ) {
print "$ruolo=$HoH{$famiglia}{$ruolo} ";
}
print "}\n";
}
Ecco un esempio che mostra come creare e usare un record i cui campi sono di
svariati tipi diversi:
$rec = {
TESTO => $stringa,
SEQUENZA => [ @vecchi_valori ],
RICERCA => { %una_qualche_tabella },
QUELCODICE => \&una_funzione,
QUESTOCODICE => sub { $_[0] ** $_[1] },
HANDLE => \*STDOUT,
};
print $rec->{TESTO};
print $rec->{SEQUENZA}[0];
$ultimo = pop @ { $rec->{SEQUENZA} };
print $rec->{RICERCA}{"chiave"};
($prima_chiave, $primo_valore) = each %{ $rec->{RICERCA} };
$risposta = $rec->{QUELCODICE}->($arg);
$risposta = $rec->{QUESTOCODICE}->($arg1, $arg2);
# attenzione alle graffe aggiuntive per i riferimenti a filehandle
print { $rec->{HANDLE} } "una stringa\n";
use FileHandle;
$rec->{HANDLE}->autoflush(1);
$rec->{HANDLE}->print(" una stringa\n");
%TV = (
flintstones => {
serie => "flintstones",
serate => [ qw(lunedi giovedi venerdi) ],
membri => [
{ nome => "fred", ruolo => "capo", anni => 36, },
{ nome => "wilma", ruolo => "moglie", anni => 31, },
{ nome => "pebbles", ruolo => "bimbo", anni => 4, },
],
},
jetsons => {
serie => "jetsons",
serate => [ qw(mercoledi sabato) ],
membri => [
{ nome => "george", ruolo => "capo", anni => 41, },
{ nome => "jane", ruolo => "moglie", anni => 39, },
{ nome => "elroy", ruolo => "bimbo", anni => 9, },
],
},
simpsons => {
serie => "simpsons",
serate => [ qw(lunedi) ],
membri => [
{ nome => "homer", ruolo => "capo", anni => 34, },
{ nome => "marge", ruolo => "moglie", anni => 37, },
{ nome => "bart", ruolo => "bimbo", anni => 11, },
],
},
);
# lettura da file
# si puo` fare piu` facilmente quando il file stesso e`
# nella forma grezza mostrata in precedenza. perl e` felice
# di interpretare strutture dati complesse se dichiarate come
# dati, per cui a volte e` piu` semplice fare cosi`.
# ecco una costruzione pezzo per pezzo
$rec = {};
$rec->{serie} = "flintstones";
$rec->{serate} = [ trova_giorni() ];
@membri = ();
# assumiamo che la sintassi di questo file sia campo=valore
while (<>) {
%campi = split /[\s=]+/;
push @membri, { %campi };
}
$rec->{membri} = [ @membri ];
# ora ricordiamoci di tutto
$TV{ $rec->{serie} } = $rec;
###########################################################
# ora, potreste voler aggiungere altri campi interessanti
# che includano puntatori verso la struttura dati stessa,
# in modo che i cambiamenti in un punto si riflettano negli
# altri. Ad esempio, se voleste un campo {bimbi} come
# riferimento ad un array dei record dei bimbi senza
# volerli duplicare e quindi avere problemi di
# aggiornamento.
###########################################################
foreach $famiglia (keys %TV) {
$rec = $TV{$famiglia}; # puntatore temporaneo
@bimbi = ();
for $persona ( @{ $rec->{membri} } ) {
if ($persona->{ruolo} =~ /bimbo|figlio|figlia/) {
push @bimbi, $persona;
}
}
# RICORDATE: $rec e $TV{$famiglia} puntano agli stessi dati!!!
$rec->{bimbi} = [ @bimbi ];
}
# avete copiato l'array, ma questo contiene puntatori ad
# oggetti non copiati. Cio` significa che se fate diventare
# bart piu` vecchio scrivendo
$TV{simpsons}{bimbi}[0]{anni}++;
# allora si avrebbero cambiamenti anche in
print $TV{simpsons}{membri}[2]{anni};
# perche' $TV{simpsons}{bimbi}[0] e $TV{simpsons}{membri}[2]
# puntano entrambi alla stessa tabella hash anonima sottostante
# stampa il tutto
foreach $famiglia ( keys %TV ) {
print "la $famiglia";
print " va in onda durante @{ $TV{$famiglia}{serate} }\n";
print "i suoi membri sono\n";
for $chi ( @{ $TV{$famiglia}{membri} } ) {
print " $chi->{nome} ($chi->{ruolo}), anni $chi->{anni}\n";
}
print "risulta che $TV{$famiglia}{capo} ha ";
print scalar ( @{ $TV{$famiglia}{bimbi} } ), " bimbi chiamati ";
print join (", ", map { $_->{nome} } @{ $TV{$famiglia}{bimbi} } );
print "\n";
}
Non si può legare facilmente una struttura dati a più
livelli (come un hash di hash) ad un file dbm. Il primo problema è
che tutti i db fatta eccezione per GDBM e Berkeley DB hanno limiti sulle
dimensioni, ma oltre a questo, potreste avere problemi riguardanti il
come i riferimenti vengono rappresentati su disco. Un modulo sperimentale
che tenta parzialmente di risolvere il problema è MLDBM.
Controllate il codice sorgente di MLDBM sul sito CPAN più vicino,
come descritto in perlmodlib.
perlref(1), perllol(1), perldata(1), perlobj(1)
Tom Christiansen <tchrist@perl.com>
La versione su cui si basa questa traduzione è ottenibile con:
perl -MPOD2::IT -e print_pod perldsc
Per maggiori informazioni sul progetto di traduzione in italiano si veda
http://pod2it.sourceforge.net/ .
Traduzione a cura di Stefano Rodighiero.
Revisione a cura di dree.
Mon Jun 11 22:02:14 2012
|