La Torre di BaBasic - 3
La Torre di BaBasic - 3
Dopo aver esaminato nella scorsa puntata il concetto di variabile, passiamo ad esaminarne i tipi complessi. Per questo mese vedremo cosa propone Pascal, mentre nella prossima puntata sarà il turno di Ada.Se avete chiari in mente i quattro attributi del binding, le variabili non hanno più segreti per voi. Se i quattro attributi invece vi sono sconosciuti - vergogna! Non avete letto l'ultimo Bit.
Ricordiamo allora che una variabile, cioé quel posto dove stipiamo un valore per memorizzarlo in attesa di usarlo, è caratterizzata da valore, visibilità, vita e tipo: come vediamo nella figura 1.
Fig. 1
Una variabile è completamente caratterizzata dal valore che leassegnamo, la visibilità nel programma, il tempo di vita, iltipo di dati che contiene. Per esempio:
Nome variabile | Tipo variabile | Valore variabile |
---|---|---|
NomeAutore | Stringa | "Luca Accomazzi" |
LunghArtInPag | Intero | 4 |
AltezzaPagInCm | Reale | 28,25 |
Sinora abbiamo visto soltanto variabili con tipi semplici. I tipi semplici di Basic, per esempio, sono il tipo intero, reale e stringa. In Pascal esistono anche il tipo carattere (una sola lettera o cifra) e booleano. Una variabile booleana può valere "vero" oppure "falso".
Dobbiamo ricordare che nei programmi dobbiamo rappresentare oggetti del mondo reale: oggetti complessi, che difficilmente sono descrivibili con variabili così semplici.
Pertanto, molti linguaggi (tra i quali Pascal, C, Ada) ci permettono di affiancare ai tipi semplici predefiniti, come "numero intero" o "stringa di caratteri" dei tipi da noi definiti. Un primo uso di questa possibilità: rendere i listati più leggibili creando degli alias, come segue:
Type
Centimetri = integer;
A tutti gli effetti, abbiamo creato un nuovo tipo di variabile, che possiamo usare in modo perfettamente analogo a quanto facciamo con i tipi predefiniti. Nel momento in cui vogliamo creare delle nuove variabili possiamo scrivere:
Var
Altezza, Larghezza, Profondita: Centimetri;
UnNumeroQualunque: integer;
Anche se, nella realtà fisica al centro del computer, non c'è differenza tra la variabile UnNumeroQualunque e la variabile Altezza noi abbiamo introdotto un distinzione formale: si tratta di variabili che descrivono oggetti differenti.
Un uso un po' più raffinato della possibilità di creare nuovi tipi si rivela nell'uso dei subtype, o "sottotipi". Se abbiamo bisogno di una variabile per memorizzare un giorno del mese potremmo scrivere, con Pascal:
Giorno: integer;
Ma se facessimo una cosa del genere sarebbe legale scrivere nel programma:
Giorno := 5842;
il che non ha nessun senso, perchè i giorni del mese sono numerati da 1 a 31.
Ecco dove arriva il sottotipo: vediamo come, con la sintassi di Ada (leggermente più chiara di quella Pascal) risolviamo la questione:
Subtype GiornoDelMese is integer range 1..31;
Giorno: GiornoDelMese;
--a questo punto scrivere Giorno := 5842 non è consentito!
Un altro semplice costrutto che diviene però prezioso quando vogliamo scrivere programmi dai listati leggibili è il tipo enumerato: per esempio possiamo avere
Type Colore = (Rosso, Verde, Giallo, Blu, Bianco, Nero, ViolaConPoisGialli);
Var ColoreCamicia, ColoreCravatta: Colore;
e possiamo così scrivere nel nostro programma:
if (ColoreCamicia = Rosso) and (ColoreCravatta = Verde) then
WriteLn ('Pessimo gusto, capo...');
Pensiamo di voler creare un programma che cataloghi gli articoli apparsi su Bit in modo da poter rintracciare, in futuro, un articolo per consultarlo.
Il primo passo consiste nel creare una struttura dati che ci permetta di memorizzare più di un solo dato per volta. Infatti, se è necessario trattare qualche centinaio di articoli non possiamo certo pensare di utilizzare cento e più variabili semplici: quel che ci serve è il vettore (o array per gli anglofili). In Pascal scriveremmo:
Var
AnnataDiBit: Array [1..200] of Articolo;
{ Vedremo in seguito come si definisce il tipo "articolo" }
In Basic invece useremmo qualcosa come:
DIM AnnataDiBit$ (200)
Abbiamo così creato duecento variabili dello stesso tipo,chiamate AnnataDiBit [1], AnnataDiBit [2]...e così via sino a...AnnataDiBit [200]. I dati sul centotrentesimo articolo di Bit saranno memorizzati nella variabile chiamata AnnataDiBit [130].
Per accorgerci di una certa superiorità di Pascal su Basic facciamo un altro esempio: vogliamo ottenere dei dati statistici sull'altezza dei ragazzi di una scuola media, altezza espressa in centimetri. Useremo un vettore i cui elementi valgono tutti, inizialmente, zero.
Var
AltezzaScolari: Array [135..210] of Centimetri;
OgniAltezza: integer;
(...omissis...)
for OgniAltezza := 135 to 210 do
AltezzaScolari [OgniAltezza] := 0;
Misureremo poi l'altezza degli scolari uno alla volta; se lo scolaro in esame è alto un metro e sessanta aumenteremo di uno il valore della variabile corrispondente.
AltezzaScolari [160] := AltezzaScolari [160] + 1;
Questo si può fare in Pascal, che ci permette di creare un vettore la cui prima componente è numerata 135 (conoscete qualcuno più basso?) e l'ultima è numerata 210 (conosco qualcuno più alto di due metri e dieci, ma ai tempi della scuola media non ci arrivava).
In Basic, invece, possiamo solo scegliere quanti componenti debbono esserci nel vettore: saranno necessariamente numerati da zero in poi. La stessa cosa accade in linguaggio C. Ecco un problemino per i primi della classe: come possiamo risolvere il problema delle altezze obbedendo alla limitazione di Basic e C? Chi propone di usare un vettore con elementi numerati tra zero e 210, usandone solo alcuni, è uno sprecone e viene rimandato al posto con un brutto voto.
Possiamo pensare di migliorare ancora il vettore, rendendolo migliore di quanto avviene in Pascal? Certo! In Ada si è fatto di meglio, come i più fedeli lettori avranno certo sospettato.
Usiamo un altro esempio (e se i dettagli non vi sono chiari, accontentatevi di seguire il filo del discoso, perchè non vogliamo addentrarci nel campo della computer graphic): ho appena caricato dal dischetto una belissima videata grafica creata da qualche superbo artista (io so a malapena disegnare i fiorellini col gambo stilizzato) e voglio per curiosità contare quanti pixel ci sono per ciascuno dei colori.
Il programma per contare i colori non sarà molto diverso dal programma che contabilizza le altezze dei ragazzini: useremo un vettore con un elemento per ogni possibile colore: tutti gli elementi varranno inizialmente zero; passeremo in rassegna tutti i punti del disegno, ne scopriremo il colore e aumenteremo di una unità la variabile che corrisponde a quel colore.
Però il colore rosso è caratterizzato dal valore 3840, il verde dal numero 240, il blu dal 15, il giallo dal 4080 eccetera. I valori che noi leggiamo esaminando la videata sono, per motivi implementativi, numeri ben lontani tra di loro. In Ada possiamo comunque scrivere...
Type Colore is (Blu, Verde, Rosso, Giallo);
for Colore use (Blu => 15;
Verde => 240;
Rosso => 3840;
Giallo => 4080);
Type Pixel is array (Colore range Blu..Giallo) of integer;
Un commento sembra inutile, dato che Ada è abbastanza leggibile. Aggiungiamo che Ada consente altre possibilità ancora nel campo dei vettori, che vedremo nella prossima puntata, e che per una maggiore pulizia e leggibilità del listato siamo autorizzati a scrivere al posto dell'ultima riga il sinonimo:
Type Pixel is array (Colore range
Colore'First..Colore'Last) of integer;
Veniamo ora ai record. Si tratta di un altra struttura dati molto importante, al pari dell'array, e che non trova corrispettivo nel Basic. Il record ha diversi nomi nei diversi linguaggi - nel linguaggio C, ad esempio, si chiama struct - ma dato che il Pascal è un linguaggio particolarmente ben fatto per quanto riguarda l'architettura dei dati ci accoderemo all'uso generale ed useremo la sua terminologia, anche se per l'orecchio italiano il termine è riminiscente più di primati sportivi che di linguaggi di programmazione. Anche in Ada, del resto, si parla di record.
Per introdurre i record torneremo all'esempio iniziale del programma che deve memorizzare svariati dati sugli articoli dell'ultima annata di Bit. Immaginiamo che sia necessario conoscere, di ogni articolo, il titolo e il nome dell'autore; il numero della rivista su cui è apparso; e un singolo carattere di riferimento che varrà 'A' se l'articolo è apparso in Apple & Go, 'V' per la Vetrina, 'B' per il Bitest eccetera. In Pascal scriviamo:
Type
Articolo = record
Autore: string;
Titolo: string;
NumeroBit: integer;
Classificazione: char
end;
Var
AnnataDiBit: Array [1..200] of Articolo;
Quando usiamo un vettore usiamo una struttura di dati omogenei; abbiamo creato un vettore di numeri interi per contare quanti ragazzini sono alti 1.60, 1.61, 1.62 metri eccetera. Con il record vediamo invece una struttura di dati che raggruppa oggetti disomogenei come il titolo di un articolo e il numero della rivista che lo ha accolto.
Come abbiamo visto nell'ultimo esempio, nulla ci vieta di creare strutture molto complesse comnbinando questi costrutti: per trattare l'ultima annata di Bit abbiamo usato un vettore i cui singoli elementi erano dei record. Possiamo altrettanto facilmente usare dei record che contengono dei vettori (si dice in gergo che "uno dei campi del record è un vettore"). Per fare un esempio abbastanza complesso:
Type
Materie = (Italiano, Matematica, Inglese, Chimica);
Votazione = 0..10
Voti = array [Italiano..Chimica] ofVotazione;
In questo modo posso scrivere
Voti [Matematica] := 7
Studente = record
Nome: string;
Cognome: string;
Pagella: Voti
end;
Classe = array [1..20] of Studente;
Scuola = array [1..200] of Classe;
Per una trattazione completa dei record bisognerebbe ricordare anche quelli che in Pascal vengono chiamati record varianti; in linguaggio C hanno un nome, ancora una volta, più consono: union. Si tratta di record che contengono alternativamente l'una o l'altra tra due strutture dati.
Pensate, per chiarire le idee, alle necessità di una anagrafe che debba conservare nel caso di cittadini di sesso maschile tutti i dati relativi al servizio militare, e per le donne invece debba memorizzare l'eventuale cognome acquisito col matrimonio. Sarebbe possibile usare un rcord variante per risolvere la situazione (dato però che si tratta di una aggiunta relativamente insignificante, utilizzata perlopiù per implementare sporchi trucchi di programmazione, non approfondiremo ulteriormente la questione).