Flavio Poletti
Matrici in Perl
http://www.perl.it/documenti/articoli/2007/11/matrici-in-perl.html
© Perl Mongers Italia. Tutti i diritti riservati.

Se uno dà un'occhiata rapida al manuale (in particolare perlvar), scopre un'interessante verità circa i principali tipi di variabili in Perl:

  • scalari: possono contenere un valore singolo, di varia natura (come un numero, o una stringa);
  • array: sono delle collezioni ordinate di elementi - elementi scalari;
  • hash: sono delle associazioni fra stringhe (dette chiavi) e valori, i quali sono, nuovamente, degli scalari.

Così, di primo acchito, verrebbe da dire che non ci sia molto per sbizzarrirsi. O valori singoli, oppure insiemi che contengono valori singoli. Ma allora una matrice come la rappresentiamo?!? Come facciamo ad aggiungere dimensioni ad un semplice array?

Riferimenti alla riscossa

La chiave per la creazione di strutture multilivello in Perl sta nel comprendere un semplicissimo dato di fatto: un riferimento è uno scalare.

L'avete assorbita o siete svenuti? Vedo che alcuni hanno un bagliore intorno alla testa: o sono divenuti santi (cosa di cui dubito, vista la gentaccia che frequenta perl.it), oppure si è accesa la fatidica lampadina. Per altri, al contrario, l'unica cosa che illumina il buio è quel grosso, splendente punto interrogativo. Vediamo di mandarlo via.

Un riferimento è come un'etichetta che vi dice come trovare qualcosa. Una specie di nota che vi scrivete da una parte, e vi dice dove trovare l'oggetto cui ci si riferisce. Immaginate di avere tantissimi CD, ad esempio, e di volerne mantenere sempre il controllo. Va da sé: accendete il PC, lanciate un editor e cominciate a scrivere un programma in Perl. Nel programma, però, non potete infilare fisicamente il disco; usate allora qualcosa che rappresenta il disco, e che in qualche modo vi consente di ritrovarlo in mezzo a tutti gli altri.

Se avete una qualsiasi variabile, potete prendere un riferimento ad essa in maniera molto rapida: basta mettere un carattere backslash (ossia il carattere "\") immediatamente prima della variabile, come ad esempio:

  my $rif_a_scalare = \$scalare;
  my $rif_a_array   = \@array;
  my $rif_a_hash    = \%hash;

Un esempio lo trovate nella figura AoA00-reference.png.

Se invece avete un riferimento, e volete ritornare all'oggetto puntato, occorrerà effettuare la cosiddetta dereferenziazione. Il modo universale per dereferenziare un qualsiasi riferimento derivante da un'espressione è mediante le parentesi graffe. Supponiamo ad esempio di avere:

  my $rif_a_array = \@array;

e di voler "tornare" all'array; il modo più generale per farlo è con:

  my @copia_di_array = @{ $rif_a_array };

Le graffe racchiudono l'espressione che restituisce il riferimento all'array; il sigillo che si trova immediatamente prima (ossia il carattere @ nel nostro caso) ci dice cosa ci stiamo aspettando dall'operazione di dereferenziazione - un array nel nostro caso.

Esiste anche una versione light che funziona quando il riferimento è contenuto in una variabile:

  my @copia_di_array = @$rif_a_array;

In pratica, in questi casi semplificati cadono le graffe.

Creare i riferimenti ad array

Come abbiamo visto, dato un array possiamo creare un riferimento ad esso utilizzando il backslash:

  my $rif_a_array = \@array;

Questo, però, ci costringe ad avere un array da cui partire! Se non vogliamo passare da un array esistente, ma costruire un array "al volo" e farci dare un riferimento ad esso, possiamo utilizzare il costruttore di array anonimi:

  my $rif_a_array_anonimo = [ 'ciao', 'a', 'tutti' ];

Una coppia di parentesi quadre ed il gioco è fatto! Ovviamente possiamo partire da un array vuoto:

  my $rif_a_array_vuoto = [ ];

oppure dalla copia di un altro array:

  my $rif_a_copia_di_array = [ @array ];

Insomma: tutto quello che si trova nelle parentesi quadre viene espanso come una lista e copiato nell'array anonimo. Il costruttore (ossia, la coppia di parentesi quadre) restituisce un riferimento a questo array, che in tal modo non ha necessità di avere un nome tutto per sé.

Usare i riferimenti ad array

Quando avete un riferimento ad array potete usarlo quasi come se aveste direttamente l'array stesso. Solamente che dovete ricordarvi di avere in mano un fogliettino, non il CD! Per questo motivo, se con un array in carne ed ossa scriveremmo:

  my $terzo_elemento = $array[2];

per accedere al terzo elemento, con un riferimento dobbiamo aggiungere una freccetta per indicare la dereferenziazione:

  my $terzo_elemento = $rif_a_array->[2];

Ovviamente avevamo già visto come dereferenziare, per cui potremmo scrivere anche:

  my $terzo_elemento = ${ $rif_a_array }[2];

ma che obbrobbrio! Converrete con me che è molto meglio la freccetta!

Ok, e le strutture multilivello?

In realtà, ora si tratta solo di mettere insieme i pezzi.

Abbiamo detto che un array, ad esempio, può contenere solamente scalari. Un array con altri array dentro, dunque, non è fattibile. Qui però i riferimenti ci vengono in aiuto: poiché questi sono degli scalari, infatti, possono andare tranquillamente ad occupare posizioni in un array. L'idea è che nell'array esterno (il contenitore) ci mettiamo dei riferimenti ai sotto-array invece dei sotto-array stessi.

La figura AoA01-bare.png mostra questa idea al lavoro mentre viene costruita una matrice a due dimensioni.

Prima di tutto, osserviamo che la struttura disegnata rispetta le regole poste; la figura AoA03-tutti-scalari.png, infatti, rende chiaro come tutti gli elementi dei vari array in gioco siano degli scalari.

L'array originale (figura AoA05-array-1.png), però, invece di contenere scalari "tradizionali" (come stringhe e numeri) contiene dei riferimenti ad altri array (quelli in figura AoA06-array-2.png).

La figura AoA07-dressed.png riassume quanto abbiamo detto: in verde sono riportati gli array, in blu gli scalari "normali" ed in rosso i riferimenti.

Proviamo allora a definire con il codice Perl quello che abbiamo rappresentato graficamente:

  my @array;
  for my $riga ( 0 .. 4 ) {
      my @sotto_array = 1 .. 6; # Qualche dato nella riga
      
      # Infiliamo il sotto-array in @array. Ovviamente non infiliamo
      # direttamente @sotto_array, ma un riferimento
      push @array, \@sotto_array;  # Notare il backslash "\"

  }
  my $rif_a_array = \@array;

Abbiamo che:

  • @array è quello di figura AoA05-array-1.png;
  • i vari @sotto_array creati durante il ciclo sono quelli indicati in figura AoA06-array-2. Da notare che ad ogni iterazione del ciclo viene creato un nuovo @sotto_array, poiché viene specificato con my all'interno del blocco di for;
  • la variabile $rif_a_array è quella indicata in figura AoA02-scalare.png.

Proviamo ad utilizzare la struttura, dunque:

  • il secondo elemento di @array (v. figura AoA08-secondo.png) è accessibile come $array[1] oppure, come abbiamo visto, come $rif_a_array->[1];
  • l'array puntato da quest'ultimo è accessibile nelle seguenti maniere:
      my @copia_secondo_array = @{ $array[1] };
      my @altra_copia_secondo = @{ $rif_a_array->[1] };
    e il terzo elemento della quarta riga (figura AoA09-43.png)? Facile:
  • il riferimento alla quarta riga, lo sappiamo già, è dato da $array[3] o, indifferentemente, da $rif_a_array->[3];
  • di conseguenza, per accedere al terzo elemento dell'array puntato da questo riferimento possiamo scrivere:
       my $elem_4_3     = $array[3]->[2]; # Oppure
       my $elem_4_3_bis = $rif_a_array->[3]->[2];

In Perl, ve ne sarete accorti, quando si hanno cose lunghe e noiose da scrivere di solito esiste una scorciatoia. Questo caso non fa eccezioni: per evitare la festa delle frecce, infatti, è possibile evitare di mettere le frecce quando queste andrebbero poste fra due gruppi di parentesi. A farla breve, dunque, possiamo scrivere:

   my $elem_4_3_ter    = $array[3][2]; # Oppure
   my $elem_4_3_quater = $rif_a_array->[3][2];

Da notare che, in $elem_4_3_quater, la prima freccia non è posta fra due gruppi di parentesi, per cui non possiamo ometterla.

Trovare le dimensioni

Trovare le dimensioni di una matrice in Perl è piuttosto semplice, basta ricordare che la valutazione di un array in contesto scalare restituisce il numero dei suoi elementi.

Eh?

Procediamo con un esempio, che è sempre meglio:

  my @primi = ( 2, 3, 5, 7, 11, 13 );
  my $conteggio = @primi;

Quanto varrà conteggio? Beh, deriva dalla valutazione di un array (@primi) in contesto scalare (per via dell'assegnazione allo scalare $conteggio stesso), per cui il valore è pari a 6. Ossia, il numero di elementi di @primi.

Se ora torniamo alla nostra matrice, notiamo che:

  1. il numero di righe è pari al numero di elementi di @array (v. AoA05-array-1.png);
  2. il numero di colonne è pari al numero di elementi di una qualsiasi delle righe, ad esempio $array[0] (v. AoA06-array-2.png). Questo presuppone, ovviamente, che si abbia a che fare con una matrice vera, in cui tutte le righe hanno la stessa lunghezza.

Non deve stupire allora che il numero di righe sia calcolabile piuttosto rapidamente:

  my $numero_righe             = @array;
  my $numero_righe_alternativo = @$rif_a_array;

e che anche il numero di colonne non riservi particolari sorprese:

  my $numero_colonne             = @{ $array[0] };
  my $numero_colonne_alternativo = @{ $rif_a_array->[0] };

Se proprio non vi piace utilizzare troppe parentesi, potete passare per una variabile intermedia:

  my $rif_prima_riga = $array[0]; # oppure:  = $rif_a_array->[0];
  my $numero_colonne_alt2 = @$rif_prima_riga;

In ogni caso, mettela un po' come vi pare: l'importante è che valutiate l'array giusto in contesto scalare!

Conclusioni

Buone notizie, dunque: non solo possiamo fare strutture multilivello (qui ci siamo fermati a due, ma lo stesso discorso si può ripetere al livello che si vuole), ma l'accesso ai vari elementi è anche piuttosto intuitivo, poiché richiede che gli indici delle varie dimensioni siano posti ciascuno dentro un paio di parentesi quadre.

Queste matrici in Perl hanno un nome particolare: AoA, ossia Array Of Array, array di array. Il nome deriva da questo modo un po' bizzarro con cui sono costruite, ma è formalmente impreciso perché ricordate: un array può contenere solamente scalari!

Come spesso succede con un tutorial, siete a poco più dell'inizio. Il che non vuol dire che siete messi male, al contrario: potete già sbizzarrirvi a costruire strutture multilivello ed a sbatterci contro la testa. Per non farvi troppo male... utilizzate il modulo Data::Dumper per stampare le strutture: potreste scoprire che non tutto è come vorreste che sia, ma almeno saprete dove mettere le mani!

Per proseguire si consiglia di guardare la seguente documentazione.

perldata

Doverosa una tappa. Spiega tutto quello che dovete sapere sui tipi di dato in Perl!

perlref

I riferimenti giocano un ruolo fondamentale nella costruzione delle strutture multilivello. Leggere perlref significa riempire la faretra con le frecce giuste (esatto! le frecce per la dereferenziazione!).

perllol

No, non sono un sacco di risate, ma un modo improprio di definire gli AoA in termini di List Of Lists. Il tutorial presuppone la conoscenza di riferimenti e simili, ma con quello che avete letto qui dovreste cavarvela.

perldsc

Meglio noto come Perl Data Structure Cookbook, ossia il ricettario Perl per le strutture dati. Oltre agli AoA vengono trattate altre strutture multilivello, in particolare quelle che coinvolgono le hash. Da non perdere!