index | project | pods | responsabili

NOME

perldsc - Il Ricettario delle strutture dati del Perl (Perl Data Structures Cookbook)


DESCRIZIONE

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.


RIFERIMENTI

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].


ERRORI COMUNI

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


AVVERTIMENTI SULLA PRECEDENZA

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.


PER QUALE MOTIVO DOVRESTE SEMPRE USARE use strict

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];


DEBUGGING

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'


ESEMPI DI CODICE

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.


ARRAY DI ARRAY

Dichiarazione di un ARRAY DI ARRAY

 @AoA = (
        [ "fred", "barney" ],
        [ "george", "jane", "elroy" ],
        [ "homer", "marge", "bart" ],
      );

Generazione di un ARRAY DI ARRAY

 # 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";

Accedere e stampare un ARRAY DI ARRAY

 # 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";
     }
 }


HASH DI ARRAY

Dichiarazione di un HASH DI ARRAY

 %HoA = (
        flintstones        => [ "fred", "barney" ],
        jetsons            => [ "george", "jane", "elroy" ],
        simpsons           => [ "homer", "marge", "bart" ],
      );

Generazione di un HASH DI ARRAY

 # 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";


Accedere e stampare un HASH DI ARRAY

 # 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";
 }


ARRAY DI HASH

Dichiarazione di un ARRAY DI HASH

 @AoH = (
        {
            Capo     => "fred",
            Amico    => "barney",
        },
        {
            Capo     => "george",
            Moglie   => "jane",
            Figlio   => "elroy",
        },
        {
            Capo     => "homer",
            Moglie   => "marge",
            Figlio   => "bart",
        }
  );

Generazione di un ARRAY DI HASH

 # 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";

Accedere e stampare un ARRAY DI HASH

 # 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";???
     }
 }


HASH DI HASH

Dichiarazione di un HASH DI HASH

   %HoH = (
      flintstones => {
         capo      => "fred",
         amico     => "barney",
      },
      jetsons     => {
         capo             => "george",
         moglie           => "jane",
         "il suo ragazzo" => "elroy",
      },
      simpsons    => {
         capo      => "homer",
         moglie    => "marge",
         bimbo     => "bart",
      },
   );

Generazione di un HASH DI HASH

 # 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};
 }


Accedere e stampare un HASH DI HASH

 # 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";
 }


RECORD PIÙ ELABORATI

Dichiarazione di RECORD PIÙ ELABORATI

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");

Dichiarazione di un HASH DI RECORD COMPLESSI

     %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, },
            ],
         },
      );

Generazione di un HASH DI RECORD COMPLESSI

     # 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";
     }


Legami con database

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.


SI VEDA ANCHE

perlref(1), perllol(1), perldata(1), perlobj(1)


AUTORE

Tom Christiansen <tchrist@perl.com>


TRADUZIONE

Versione

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/ .

Traduttore

Traduzione a cura di Stefano Rodighiero.

Revisore

Revisione a cura di dree.


Mon Jun 11 22:02:14 2012