Indice

Capitolo 4.
I tipi di dati






   1. Premessa: il concetto di variabile

In qualsiasi linguaggio di programmazione, una variabile è una posizione di memoria contrassegnata da un nome univoco all'interno della quale è possibile depositare un valore per poterlo successivamente recuperare o elaborare.

     In Perl è possibile utilizzare nomi composti di lettere, numeri e underscore ( _ ), incluse quelle combinazioni di caratteri che costituiscono parole riservate. Questo è dovuto al fatto che le variabili sono precedute da simboli speciali che le contraddistinguono come tali.


   
2. Bestiario

Perl è un linguaggio scarsamente tipizzato. Questo significa che non è prevista una rigida distinzione fra i vari tipi di dati che possono essere utilizzati. In Perl infatti uno numero intero coincide con la stringa testuale che lo rappresenta. Pertanto il valore 5 non differisce dalla stringa di testo "5". Perl all'occorrenza trasforma il numero in stringa e la stringa in numero in modo da poter utilizzare correttamente il dato nel contesto in cui lo si sta utilizzando. Se il contesto è una concatenazione di stringhe, Perl trasformerà il numero 5 nella stinga di testo "5", risparmiando un bel po' di lavoro al programmatore.

     Inoltre Perl utilizza tre caratteri riservati per marcare il tipo di dato. Il dollaro ($) indica gli scalari (il dollaro in fondo assomiglia alla S di Scalar); la a commerciale (@) per gli array (che appunto iniziano per A) e il simbolo di percentuale (%) che nella mente degli autori ricorda la H di Hash. Questo sistema è stato mutuato dai linguaggi di scripting preesistenti come sh e csh.

     Vediamo subito quali sono i tipi di dati accettati da Perl:


I tipi di dati accettati da Perl Fig. 1
 
    1:  #!/usr/bin/perl
    2: 
    3:  #
    4:  # Scalari (numeri interi, reali, stringhe di testo, reference, ... )
    5:  #
    6:  $int = 5;
    7:  $float = 5.2;
    8:  $string = "Corso di Perl";
    9: 
   10:  #
   11:  # Array ( liste ordinate monodimensionali di elementi )
   12:  #
   13:  @array = ( 1, 2, 3 );
   14:  $array[3] = 12;
   15: 
   16:  #
   17:  # Hash ( array accessibili per chiavi )
   18:  #
   19:  %hash = ( "auto", "ford", "modello", "fiesta" );
   20:  $hash{auto} = "opel";
   21:  $hash{modello} = "corsa";
 




   
3. Contesti

Perl non tipizza fortemente i dati; per contro riserva grande attenzione al contesto nel quale i dati sono elaborati. Il contesto può essere scalare o di lista. I tipi di Perl valutati in differenti contesti si comportano in maniera differente. Tracciamo un breve profilo dei differenti contesti e degli effetti che sortiscono sui differenti tipi di dati:


I contesti Fig. 2
 
    1:  #!/usr/bin/perl
    2: 
    3:  #
    4:  # Contesto scalare
    5:  #
    6:  $sc = @array; # Dimensione di @array
    7:  $sc = ( 'elem1', 'elem2', 'elem3' ); # L'elemento più a destra
    8: 
    9:  #
   10:  # Contesto di lista
   11:  #
   12:  # NOTA: localtime restituisce i seguenti valori: secondi, minuti, ore,
   13:  # giorno del mese, mese, anno, giorno della settimana, giorno dell'anno,
   14:  # anno bisestile in list context, il numero di secondi trascorsi dal
   15:  # 1 gennaio 1970 in scalar context (perché da quella data?
   16:  # mai sentito parlare di UNIX?)
   17:  #
   18:  ( $sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime();
   19:  $date = localtime();
 


Alla linea 6 'assegnamo' un array ad uno scalare. Il contesto quindi sarà scalare (il contesto è sempre dato dalla parte sinistra dell'assegnazione, dato che è qui che il valore in elaborazione viene memorizzato e quindi a questa parte deve conformarsi). Come si comporta il nostro array? Dentro $sc troveremo il numero di elementi dell'array.

     Diversamente si comporta una lista generica di elementi dati. Per quanto potrebbe sembrare in tutto analoga ad un array, una lista in contesto scalare ritorna l'ultimo elemento. In questo caso quindi, alla linea 7, stiamo assegnando alla variabile $sc il valore "elem3".

     Anche le funzioni ritornano valori differenti a seconda del contesto in cui sono utilizzate. Vediamo l'esempio della funzione localtime. Nella nota sono segnati i valori riportati in un contesto scalare. La notazione alla linea 18 assegna a ciascuna variabile il corrispettivo elemento ritornato dalla funzione. Questo esempio è utile anche per introdurre un'altro concetto: l'utilizzo delle liste come raggruppamento di variabili.

     Nell'ultimo caso (linea 19) localtime sa di agire in un contesto scalare e quindi ritorna un valore scalare pari al numero di secondi trascorsi dall' 1/1/1970.

     Facciamo un esempio notevole per capire la versatilità dei raggruppamenti di variabili in liste. Perl è uno dei pochi linguaggi (se non l'unico) in grado di scambiare il contenuto di due variabili senza ricorrere ad una variabile temporanea. Vediamo come:


Come scambiare il contenuto di due variabili Fig. 3
 
    1:  #!/usr/bin/perl
    2: 
    3:  $var1 = 15;
    4:  $var2 = 30;
    5: 
    6:  ($var1, $var2) = ($var2, $var1);
    7: 
    8:  print "var1 = $var1, var2 = $var2\n";
 


La linea 6 fa la magia. Il risultato dell'ottava linea di codice sarà: "var1 = 30, var2 = 15". Avremo comunque modo di approfondire il concetto in seguito.


   
4. Gli Scalari

Gli scalari sono il tipo base di informazione gestita da Perl. Comprendono: Escludendo per ora il concetto di reference, concentriamoci sui tipi più comuni. Vediamo alcuni esempi di valori scalari:


Esempi di valori scalari Fig. 4
 
    1:  #!/usr/bin/perl
    2: 
    3:  $int     = 56;
    4:  $float   = 12.786;
    5:  $science = 6.02e23;
    6:  $octal   = 0453;
    7:  $exa     = 0xa347d;
    8:  $binary  = 0b111; # Solo Perl versione 5.6 e successive
    9: 
   10:  $string1 = "Corso di Perl";
   11:  $string2 = "LOA HackLab Milano";
   12: 
   13:  print "$int, $float, $string2";
 


Il risultato di questo codice sarà la stringa di testo "56, 12.786, LOA HackLab Milano".


   
5. Gli Array e le Hash

Come si può notare, gli unici tipi di dato che hanno una loro tipizzazione in Perl sono gli array e le hash. Questo è dovuto al fatto che, più che tipi di dati, questi sono collezioni di dati, strutture di dati e non dati propriamente detti. Gli elementi di un array (semplificando per un momento) sono sempre e comunque degli scalari. Ed è per questo che si usa $ prima di un elemento di un array anziché @. Facendo riferimento ad un elemento di un array si stà indicando uno scalare e non un array!!!

     Il sistema di accesso agli elementi viene eseguito tramite subscripting. Ossia, indicata la struttura per nome, viene posto il numero dell'elemento desiderato fra parentesi quadre (nel caso di un array) oppure il nome della chiave desiderata fra parentesi graffe (nel caso di una hash). Nel conteggiare gli elementi di un array tenete sempre presente che il primo elemento è alla posizione 0 ( zero ).

     Gli array inoltre godono di una comoda convenzione: la notazione $#array ( dove array è ovviamente il nome dell'array ) è la posizione occupata dall'ultimo elemento dell'array. Quindi, il codice seguente:


Notazione $#array Fig. 5
 
    1:  #!/usr/bin/perl
    2: 
    3:  @days = (
    4:    'lunedi', 'martedi', 'mercoledi',
    5:    'giovedi', 'venerdi', 'sabato', 'domenica',
    6:  );
    7: 
    8:  print $#days;
 


ritornerà come valore "6" in quanto ( partendo da zero ) l'ultimo elemento di sette è il sesto.

     Intanto, giocando con una particolare notazione, ci siamo però accorti che Perl è un linguaggio molto flessibile. Abbiamo dichiarato l'array su 4 righe (dalla 3 alla 6), dividendo su due righe i valori dentro l'array e dopo l'ultimo elemento abbiamo lasciato una virgola (nonostante ad esso non faccia seguito alcun elemento). L'ultima virgola è esplicitamente consentita da Perl per evitare che un domani, aggiungendo nuovi elementi all'array, ci si dimentichi la virgola alla fine di questa linea e per così poco si rischi un esaurimento da debugging.

     A dimostrazione che Perl è sempre più flessibile di quello che vi aspettate, introduciamo ora una nuova notazione per creare hash. Anziché descriverla come una lista di elementi nella quale i dispari sono chiavi e i pari sono valori ( vedi
"I tipi di dati accettati da Perl" ), possiamo usare l'operatore =>, come in Fig. 6:

L'operatore => e la crezione di hash Fig. 6
 
    1:  #!/usr/bin/perl
    2: 
    3:  $hash = (
    4:    marca   => 'opel'
    5:    modello => 'corsa'
    6:    porte   => 3
    7:  );
 

Oltre ad aggiungere indubbiamente chiarezza al codice, questo operatore ha anche il vantaggio di eliminare la necessità di racchiudere tra virgolette la chiave (posto che questa non contenga caratteri speciali, come lo spazio).


   
6. Filehandle

Un filehandle è un simbolo attraverso il quale si ha accesso al contenuto di un file. Più in generale, il concetto si estende anche ad un pipe di comandi e ad una connessione in rete. In Perl un filehandle è considerato un tipo di dato. I filehandle usano il loro namespace privato e non rischiano il conflitto con le altre variabili. Esistono tre filehandle che ogni programma Perl trova automaticamente aperti all'inizio dell'esecuzione: STDIN, STDOUT e STDERR (rispettivamente il canale di input, output ed output di errore). Vediamo alcuni esempi di uso:


Filehandle Fig. 7
 
    1:  #!/usr/bin/perl
    2: 
    3:  print "Step 1\n";
    4:  print STDOUT "Step 2\n";
    5: 
    6:  open( IN, "source.txt" );
    7:  open( OUT, ">log.txt" );
    8: 
    9:  while ( $line = <IN> ) {
   10:    print OUT "Next line: $line\n";
   11:  }
   12: 
   13:  close( IN );
   14:  close( OUT );
 

NOTA: la funzione print accetta come primo elemento opzionale un filehandle al quale scrivere; di default questo filehandle è STDOUT.

Questo codice produce due linee a "video" ('Step 1' e 'Step 2'); notate come l'uso di STDIN sia ridondante nella seconda linea. Nella terza linea viene usata la funzione open per aprire un file ("source.txt") ed associargli il filehandle IN. Il file source.txt è ora aperto in sola lettura. Nella quarta linea open apre un filehandle in scrittura verso il file "log.txt". Il carattere > prima del nome del file indica che sul file si vuole scrivere e non leggere. Inoltre l'uso di un singolo > indica che il file deve essere aperto dal byte zero, quindi azzerato se preesistente; l'uso di un doppio >> indica invece che il contenuto inviato al filehandle andrà appeso alla fine del file, dopo l'ultimo byte (come del resto perfettamente noto a tutti gli utenti UNIX e programmatori di shellscript).

     Dalla quinta alla settima linea abbiamo un ciclo while che legge una linea alla volta il file source.txt e pone il contenuto del file (una linea alla volta) nella variabile $line. Per ciascuna linea letta scrive nel file "log.txt" la stringa "Next line: " seguita dalla linea appena letta. Terminato la lettura del file, i filehandle IN e OUT vengono chiusi.

     Abbiamo introdotto dei concetti anzitempo: le funzioni open, close, l'istruzione while e dall'inizio usiamo la funzione print. Se la cosa avesse dovuto generare confusione, non preoccupatevene: al momento non siete assolutamente tenuti a comprendere il significato preciso del codice che leggete. È sufficiente che ne comprendiate il meccanismo a grandi linee. Quello che conta è ora concentrarsi sui tipi e sulle variabili.


   
7. Reference

Tutti i linguaggi di programmazione hanno sviluppato un personale approccio all'uso della memoria. Molti includono il concetto di valore che si riferisce ad una locazione di memoria. C mette a disposizione i puntatori attraverso i quali si può interagire direttamente con la memoria del computer ed allocarne porzioni grandi a piacere per uno scopo. Java al contrario non consente di lavorare con la memoria in quanto ritiene la cosa troppo pericolosa rispetto i vantaggi che porta ( che RMS possa perdonarvi! ;-).

     Perl come al solito pensa per conto suo. Non consente di avere accesso diretto alla memoria anche perché non pone limite alcuno alla dimensione delle strutture dati e per conseguenza non richiede nemmeno di preoccuparsi esplicitamente dell'allocazione. In compenso consente di ottenere dei valori che rappresentano l'indirizzo di un dato simbolo per poterlo poi richiamare attraverso questo indirizzo. Per simbolo intendiamo qui una variabile di qualsiasi tipo, un filehandle e addirittura una subroutine.

     Per ottenere una reference ad un simbolo è sufficiente farlo precedere da un backslash (\). Il significato di questa scrittura è uno scalare che contiene informazioni sul tipo di dato referenziato e il suo indirizzo in memoria. Vediamo:


Creazione di reference Fig. 8
 
    1:  #!/usr/bin/perl
    2: 
    3:  $value = 10;
    4:  @array = ( 'elem1', 'elem2' );
    5: 
    6:  $ref_value = \$value;
    7:  $ref_array = \@array;
    8:  $ref_const = \3.14159;
 


Dopo aver assegnato due valori a due variabili, creiamo due variabili ($ref_value e $ref_array) che contengono una reference alle due variabili. Per avere un'idea di come siano conformate le reference possiamo stampare i valori contenuti in $ref_value e $ref_array (che non sono 10 e 'elem1', 'elem2', bensì: SCALAR(0x80d1fdc) e ARRAY(0x80d2048)). Notate come nella prima parte sia indicato il tipo di dato referenziato mentre la seconda parte è l'indirizzo vero e proprio di memoria al quale si trova il dato referenziato.

     In ultimo notate come sia possibile creare reference anche a costanti, stringhe di testo e dati generici (linea 8).

     Un'altra possibilità interessante viene da questa sintassi per la creazione di array e hash cosiddetti anonimi:


Creazione di array ed hash anonimi Fig. 9
 
    1:  #!/usr/bin/perl
    2: 
    3:  $array_ref = [ 'elem1', 'elem2', 'elemA', 'elemB' ];
    4:  $hash_ref = {
    5:    key1 => 'value1',
    6:    key2 => 'value2',
    7:  };
 


L'utilizzo delle parentesi quadre e graffe costruisce (rispettivamente) un'array o una hash senza associarle ad alcun nome di variabile, mentre ne ritorna semplicemente un reference. Sta al programmatore assegnare questo reference dentro una variabile.



Inizio Capitolo Indice