31.05.2013 Views

calcolatore, sistema di elaborazione elettronico ... - Studio SIP.

calcolatore, sistema di elaborazione elettronico ... - Studio SIP.

calcolatore, sistema di elaborazione elettronico ... - Studio SIP.

SHOW MORE
SHOW LESS

You also want an ePaper? Increase the reach of your titles

YUMPU automatically turns print PDFs into web optimized ePapers that Google loves.

INDICE<br />

Introduzione allo sviluppo del sw e manuale Turbo Pascal versione 1.0 Giugno 2005<br />

Alcune definizioni importanti: <strong>calcolatore</strong>, <strong>sistema</strong> <strong>di</strong> <strong>elaborazione</strong> <strong>elettronico</strong>,<br />

programmabilità, soluzioni hardware e soluzioni software.<br />

Pag. 1<br />

Logica cablata e logica programmata: soluzioni hardware e soluzioni software a<br />

confronto.<br />

Pag. 2<br />

Dal problema al programma: ciclo <strong>di</strong> sviluppo (semplificato) del software Pag. 3<br />

Stu<strong>di</strong>o della situazione reale, analisi dei requisiti Pag. 3<br />

Analisi dei dati Pag. 4<br />

Algoritmo risolutivo; definizione, caratteristiche; Pag. 5<br />

Definizione <strong>di</strong> programma, linguaggi <strong>di</strong> programmazione Pag. 6<br />

Fase <strong>di</strong> co<strong>di</strong>fica Pag. 6<br />

Figura dell’analista, <strong>di</strong>stinzione tra risolutore ed esecutore Pag. 7<br />

Fase <strong>di</strong> test (alpha, beta e gamma test) Pag. 7<br />

Fase <strong>di</strong> debug Pag. 8<br />

Release e manutenzione Pag. 8<br />

Dismissione Pag. 8<br />

Architettura hardware e software <strong>di</strong> un <strong>sistema</strong> <strong>di</strong> <strong>elaborazione</strong> (cpu, principali<br />

<strong>di</strong>spositivi interni quali la ALU, i registri, decoder)<br />

Pag. 9<br />

Ciclo <strong>di</strong> funzionamento <strong>di</strong> una CPU (fetch – decode – execute) Pag. 11<br />

Rappresentazione interna delle informazioni, giustificazione della scelta <strong>di</strong>gitale Pag. 14<br />

Rappresentazione delle istruzioni Pag. 15<br />

Evoluzione dei linguaggi <strong>di</strong> programmazione (co<strong>di</strong>ce macchina, assembly,<br />

assemblatori, cenni ai <strong>di</strong>agrammi <strong>di</strong> flusso)<br />

Pag. 16<br />

Linguaggi ad alto livello Pag. 18<br />

Differenza tra interpreti e compilatori Pag. 19<br />

La catena della programmazione (e<strong>di</strong>torIDE e programma sorgente) Pag. 20<br />

Compilazione e sue fasi: controllo lessicale, controllo sintattico, co<strong>di</strong>ce oggetto Pag. 21<br />

Il linker e le librerie Pag. 21<br />

L’eseguibile finale Pag. 22<br />

Struttura <strong>di</strong> un programma pascal, intestazione ed identificatori Pag. 23<br />

Sezione <strong>di</strong>chiarativa; constanti e vantaggi del loro uso Pag. 24<br />

Variabili Pag. 24<br />

Tipi <strong>di</strong> dati semplici e loro dominio, forma esponenziale dei floating point, co<strong>di</strong>ce<br />

ASCII<br />

Pag. 25<br />

Sezione esecutiva Pag. 26<br />

Assegnamento, compatibilità <strong>di</strong> tipo Pag. 26<br />

Uso delle parentesi Pag. 27<br />

Principali operatori e operatori relazionali utilizzabili sud<strong>di</strong>visi per tipo <strong>di</strong> dato, tipi <strong>di</strong><br />

errore (onverflow, underflow, operazione illegale)<br />

Pag. 27<br />

Tabella con l’or<strong>di</strong>ne <strong>di</strong> precedenza <strong>di</strong> tutti gli operatori visti Pag. 29<br />

Coman<strong>di</strong> <strong>di</strong> ingresso/uscita (readln e write/ln) Pag. 29<br />

Coman<strong>di</strong> per il controllo del flusso <strong>di</strong> esecuzione, programmazione strutturata Pag. 30<br />

Selezione ad una via (if … then) Pag. 31<br />

Selezione a due vie (if … then … else) Pag. 33<br />

Uso <strong>di</strong> con<strong>di</strong>zioni composte con i connettivi logici Pag. 33<br />

Selezione a molte vie (case … of) Pag. 34<br />

Esercizi riepilogativi sulla struttura selettiva Pag. 36<br />

Struttura iterativa enumerativa (for … do) Pag. 48<br />

Esercizi riepilogativi sul ciclo for Pag. 50<br />

Struttura iterativa indefinita repeat … until Pag. 61<br />

Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 1


Introduzione allo sviluppo del sw e manuale Turbo Pascal versione 1.0 Giugno 2005<br />

Struttura iterativa indefinita while … do Pag. 63<br />

Esercizi riepilogativi sul repeat e sul while Pag. 64<br />

Approfon<strong>di</strong>mento sui flow chart Pag. 68<br />

I sottoprogrammi (procedure e funzioni) Pag. 83<br />

Regole <strong>di</strong> visibilità e durata Pag. 108<br />

Gli array Pag. 110<br />

I record Pag. 126<br />

I files Pag. 131<br />

La ricorsione Pag. 152<br />

Limiti della memoria allocata staticamente Pag. 159<br />

Allocazione <strong>di</strong>namica della RAM (implementazione completa del tipo <strong>di</strong> dato astratto<br />

lista semplice)<br />

Pag. 160<br />

Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 2


Calcolatore? Non solo …<br />

Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 1<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

Nel nostro corso useremo spesso la parola computer (<strong>calcolatore</strong>). Ed anche se questa è entrata ormai a far parte del<br />

linguaggio comune, è assai riduttiva. Questo termine sarebbe infatti appropriato per quelle "macchinette" che tenete<br />

negli astucci e che sono in grado <strong>di</strong> svolgere giusto le quattro operazioni elementari, l'estrazione <strong>di</strong> ra<strong>di</strong>ce e poco più<br />

(sto volutamente ignorando le cosiddette calcolatrici programmabili che, <strong>di</strong> fatto, sono dei personal computer in<br />

miniatura, anche se con funzionalità limitate).<br />

Si dovrebbe infatti parlare <strong>di</strong> <strong>sistema</strong> <strong>di</strong> <strong>elaborazione</strong> <strong>elettronico</strong> delle informazioni programmabile.<br />

Soffermiamoci su ogni termine:<br />

• Sistema Insieme <strong>di</strong> componenti, ognuno con la sua specifica funzione, ma con uno scopo comune: elaborare<br />

(si parla <strong>di</strong> ‘processing’) dati forniti in ingresso (si parla <strong>di</strong> ‘input’) e fornire risultati (si parla <strong>di</strong> ‘output’)<br />

adeguatamente presentati.<br />

In un moderno <strong>sistema</strong> <strong>di</strong> <strong>elaborazione</strong> <strong>elettronico</strong> possiamo in<strong>di</strong>viduare tra i componenti: il microprocessore<br />

(ad esempio Intel Pentium, AMD Athlon), la memoria <strong>di</strong> lavoro RAM, il <strong>di</strong>sco fisso (hard <strong>di</strong>sk), il monitor, la<br />

stampante ecc.<br />

• Elaborazione Operazione (tra cui i calcoli) che trasforma uno o più dati/informazioni in altri dati/informazioni.<br />

Questi dati possono insomma essere sì tra loro sommati, sottratti ecc. (se sono numeri); ma possono anche<br />

essere confrontati tra loro, spostati o copiati da un punto all'altro della memoria, inviati ad un <strong>di</strong>spositivo per la<br />

loro visualizzazione (ad esempio il monitor) o stampa.<br />

NOTA: un dato è una misurazione <strong>di</strong> un aspetto della realtà e <strong>di</strong>venta informazione solo quando sappiamo<br />

dare un significato ad esso (ecco allora che un numero da anonimo <strong>di</strong>venta un peso, un'altezza, un punto <strong>di</strong><br />

un'immagine sul video ecc.<br />

• Elettronico Un’<strong>elaborazione</strong> può avvenire anche in modo manuale (come quando con carta e penna si mette in<br />

or<strong>di</strong>ne alfabetico un elenco <strong>di</strong> nomi).<br />

Se l'<strong>elaborazione</strong> avviene senza l'intervento umano si parla allora <strong>di</strong> <strong>elaborazione</strong> automatica ( ad esempio le<br />

macchine per lo smistamento della posta ).<br />

E se infine i <strong>di</strong>spositivi automatici non hanno parti meccaniche movimento ma sono costituiti da circuiti elettrici<br />

si parla <strong>di</strong> <strong>elaborazione</strong> elettronica (il microprocessore che somma due numeri).<br />

• Programmabile Con le macchinette calcolatrici non potrete fare altro che i calcoli previsti dal costruttore. Non<br />

c'è modo infatti <strong>di</strong> istruire quel piccolo congegno a svolgere calcoli <strong>di</strong>versi. Allo stesso modo in cui non potete<br />

ottenere altro da una lavatrice che le sequenze (programmi) <strong>di</strong> lavaggio previste dal costruttore! E così come<br />

con una lavatrice potrete lavare solo panni, allo stesso modo con una calcolatrice potrete usare solo numeri!<br />

Un computer, invece, è dotato <strong>di</strong> una memoria <strong>di</strong> lavoro elettronica (RAM, Random Access Memory, Memoria<br />

ad accesso casuale) in cui possono essere rappresentati numeri, lettere, immagini e suoni.<br />

Non solo: la memoria contiene anche la sequenza delle istruzioni che il microprocessore deve eseguire per<br />

svolgere un certo compito (il programma)! È sufficiente caricare (dall'hard <strong>di</strong>sk, dal CD, dal DVD ecc.) una<br />

<strong>di</strong>versa sequenza <strong>di</strong> istruzioni per avere una macchina elettronica in grado <strong>di</strong> svolgere un compito anche<br />

completamente <strong>di</strong>verso dal precedente.<br />

Tutto sommato, il ciclo <strong>di</strong> funzionamento <strong>di</strong> un <strong>sistema</strong> <strong>di</strong> <strong>elaborazione</strong> <strong>elettronico</strong> programmabile (ehm,<br />

computer d'ora in avanti e solo per como<strong>di</strong>tà) è assai semplice: prelievo dalla RAM dalla prossima istruzione da<br />

eseguire, interpretazione dell'istruzione (cosa deve essere fatto? Quali altri componenti devono essere attivati<br />

ed in che modo?) esecuzione (attivare nella giusta sequenza i componenti elettronici coinvolti). Si parla <strong>di</strong> ciclo<br />

<strong>di</strong> fetch (prelievo), decode (deco<strong>di</strong>fica) ed execute (esecuzione).<br />

NOTA: approfon<strong>di</strong>rete questi argomenti nel corso parallelo <strong>di</strong> ‘sistemi’


Logica cablata e logica programmata<br />

Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 2<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

Quando un <strong>di</strong>spositivo <strong>elettronico</strong> viene costruito in modo da poter funzionare senza un programma (i circuiti sono<br />

scelti e collegati per dare sempre la stessa gamma <strong>di</strong> risposte) viene definito in logica cablata (dalla parola inglese<br />

cable, cavo, filo).<br />

Un altro esempio (avevamo già visto quello della lavatrice) è rappresentato da un orologio <strong>di</strong>gitale <strong>di</strong> prima<br />

generazione (che <strong>di</strong>fferenza della lavatrice non presenta parti elettromeccaniche). In un orologio <strong>di</strong> questo tipo non è<br />

presente alcun microprocessore e nessun programma: i circuiti sono stati stampati per reagire in modo prefissato alla<br />

pressione dei tasti (reset, regolazione orario/data ecc.) e per incrementare l’orario/data.<br />

O ancora: una PlayStation costruita (per assurdo) in logica cablata vi consentirebbe <strong>di</strong> giocare a quel solo gioco<br />

corrispondente a quella particolare pre<strong>di</strong>sposizione <strong>di</strong> <strong>di</strong>spositivi elettronici e relativi collegamenti elettrici.<br />

Quando invece è presente un microprocessore che esegue istruzioni prelevate da una memoria si parla <strong>di</strong> logica<br />

programmata. Spesso la memoria può essere riscritta (come avviene in tutti i personal computer) e il programma è<br />

cambiato piacere. Ecco allora che il computer può <strong>di</strong> volta in volta <strong>di</strong>ventare l'equivalente <strong>elettronico</strong> <strong>di</strong> una macchina<br />

da scrivere (Word), <strong>di</strong> una super calcolatrice (Excel), simulatore <strong>di</strong> calcio (FIFA e simili!) ecc.<br />

E le soluzioni che sfruttano una logica cablata vengono dette soluzioni hardware (dove con questo termine si<br />

in<strong>di</strong>cano le parti fisiche <strong>di</strong> un <strong>sistema</strong> <strong>di</strong> <strong>elaborazione</strong>; in inglese il termine significa letteralmente ‘ ferraglia’). Quelle<br />

che sfruttano invece una logica programmata sono dette soluzioni software (i dati e le istruzioni memorizzate<br />

sottoforma <strong>di</strong> un segnale elettrico chiaramente impalpabile, morbido, soft (che significa appunto ‘ soffice’).<br />

La soluzione software è più flessibile: se viene trovato un errore è sufficiente cancellare qualche istruzione e la<br />

memoria e sostituirle con quelle corrette. Lo stesso accade nel caso si decida <strong>di</strong> apportare migliorie o adattare il<br />

programma a causa, per esempio, <strong>di</strong> una legge cambiata. In un circuito, invece, se viene trovato un errore questo<br />

può comportare lo scarto dell'intero circuito stesso e la realizzazione <strong>di</strong> un circuito completamente nuovo!<br />

La rigi<strong>di</strong>tà delle soluzioni hardware sembrano relegare queste ultime ad un ruolo <strong>di</strong> secondo piano rispetto alle<br />

soluzioni software, ma hanno almeno un grosso pregio che potrebbe essere determinante: la rapi<strong>di</strong>tà nel fornire la<br />

risposta! Mancando infatti il microprocessore e non essendoci co<strong>di</strong>ci d'istruzione da prelevare nella RAM né<br />

deco<strong>di</strong>fiche da effettuare, i dati in input sono trasformati in quelli <strong>di</strong> output ad una velocità molto superiore rispetto<br />

ad un programma che compie la stessa <strong>elaborazione</strong>.<br />

Nel nostro corso ci occuperemo <strong>di</strong> trovare soluzioni software ai problemi che affronteremo. E l'attività <strong>di</strong><br />

programmazione non è l'unica da mettere in gioco per risolvere un problema.


1<br />

Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 3<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

Dal problema al programma – ciclo <strong>di</strong> sviluppo (semplificato) del software<br />

La scrittura del programma è solo una delle fasi del processo <strong>di</strong> sviluppo <strong>di</strong> un'applicazione informatica. Tutto inizia<br />

con l'esigenza <strong>di</strong> risolvere un problema con un <strong>sistema</strong> informatico. La parola problema deve essere intesa in modo<br />

ampio: gestire la contabilità <strong>di</strong> un'azienda, usare il personal computer per scrivere documenti, per una simulazione <strong>di</strong><br />

guida, per svolgere calcoli complessi, controllare un processo industriale, pilotare un robot eccetera.<br />

Per quanto ci riguarda, i problemi saranno espressi in forma testuale. Ecco il testo dell'esame <strong>di</strong> Stato del 1998:<br />

Una galleria d'arte ha deciso <strong>di</strong> creare un <strong>sistema</strong> che consenta ai suoi clienti <strong>di</strong> consultare da<br />

casa il catalogo: dei quadri, tramite accesso a una pagina web che la galleria può creare<br />

presso un fornitore <strong>di</strong> servizi telematici.<br />

Per ogni quadro è compilata una scheda che riporta l'autore, il titolo, la tecnica (olio, tempera<br />

ecc.), le <strong>di</strong>mensioni, il prezzo. La consultazione del catalogo: può avvenire o semplicemente<br />

scorrendo avanti e in<strong>di</strong>etro le schede in or<strong>di</strong>ne alfabetico oppure cercando uno specifico<br />

autore.<br />

Il can<strong>di</strong>dato, fatte le ipotesi aggiuntive che ritiene necessarie,<br />

1) proponga una soluzione per la creazione del <strong>sistema</strong> illustrandone la struttura a blocchi e<br />

in<strong>di</strong>cando una soluzione <strong>di</strong> principio per ciascun blocco;<br />

2) proponga e illustri una struttura per il Data Base dei quadri,<br />

3) sviluppi in dettaglio la soluzione per almeno una delle seguenti funzioni, co<strong>di</strong>ficandone un<br />

segmento con uno strumento software <strong>di</strong> sua conoscenza:<br />

a) creazione del Data Base,<br />

b) creazione <strong>di</strong> una semplice pagina web della galleria,<br />

c) interfaccia per consentire al cliente la consultazione del catalogo: e la visione delle singole<br />

schede,<br />

4) facoltativamente proponga una soluzione <strong>di</strong> principio per realizzare un <strong>sistema</strong> che<br />

consenta <strong>di</strong> mostrare al cliente non solo la scheda <strong>di</strong> catalogo, ma anche una fotografia del<br />

quadro.<br />

Il testo va dapprima ha stu<strong>di</strong>ato per evidenziare parti poco chiare (sulle quali sarà necessario prendere delle<br />

decisioni) ed eventualmente fare ipotesi aggiuntive su aspetti per i quali il testo non <strong>di</strong>ce come comportarsi.<br />

Ad esempio, nel testo si parla <strong>di</strong> prezzo del quadro ma non viene in<strong>di</strong>cata la valuta da<br />

utilizzare. Una galleria d'arte spesso a clienti stranieri, per cui la soluzione <strong>di</strong> proporre il<br />

prezzo in lire (siamo nel ‘98... e l'euro non esisteva ancora e neppure l'obbligo <strong>di</strong> esporre il<br />

doppio prezzo in euro ed in lire) forse non è ottimale. Ecco allora la prima ipotesi<br />

aggiuntiva (o, se preferite, un primo chiarimento): i prezzi verranno in<strong>di</strong>cati sia in lire che<br />

in dollari.<br />

Questa fase viene chiamata stu<strong>di</strong>o della situazione reale. La letteratura informatica fa riferimento a questa fase<br />

anche con il nome <strong>di</strong> analisi (termine però non corretto da un punto <strong>di</strong> vista matematico). Naturalmente noi<br />

inizieremo con problemi e testi assai più semplici. Ad esempio: calcolare la spesa in euro per una settimana <strong>di</strong> viaggi<br />

andata e ritorno da casa a scuola. Le uniche cose da chiarire potrebbero essere: è necessario percorrere strade con<br />

pedaggi? Il numero <strong>di</strong> chilometri del percorso in andata è identico a quello del ritorno?


Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 4<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

Vengono anche presi in considerazione i requisiti (cosa deve fare il programma, con quali vincoli <strong>di</strong> velocità,<br />

occupazione <strong>di</strong> memoria, hardware e software a <strong>di</strong>sposizione, se deve funzionare in rete, se deve sapere interoperare<br />

con altri software magari su piattaforme hardware <strong>di</strong>verse, se deve funzionare in real time, se deve essere portabile<br />

in altri ambienti hardware/software, , che grado <strong>di</strong> robustezza, che grado <strong>di</strong> sicurezza, che tipo <strong>di</strong> periferiche deve<br />

supporatare ecc.)<br />

Devono poi essere in<strong>di</strong>viduate tutte le informazioni che è necessario gestire. Queste spesso verranno memorizzate in<br />

una banca dati (data base). Il risultato <strong>di</strong> questa fase viene <strong>di</strong> solito sintetizzato con uno schema che evidenzia i<br />

cosiddetti insiemi entità e le relazioni tra esse.<br />

Ad esempio, è possibile in<strong>di</strong>viduare l'insieme entità dei quadri e quello dei pittori:<br />

2<br />

Co<strong>di</strong>ce<br />

pittore<br />

nome<br />

Pittori<br />

Data <strong>di</strong><br />

nascita<br />

Per ogni insieme entità vengono anche in<strong>di</strong>cati gli attributi che descrivono un esemplare <strong>di</strong> quell’ insieme. Ad<br />

esempio, per l'insieme entità dei pittori si decide che ogni pittore verrà descritto tramite un co<strong>di</strong>ce, un nome ed una<br />

data <strong>di</strong> nascita. Ogni quadro verrà invece descritto da un co<strong>di</strong>ce e da un titolo. La freccia che da pittori e raggiunge<br />

quadri sta ad in<strong>di</strong>care che per ogni pittore esiste un certo numero <strong>di</strong> quadri. Nella banca dati verrà creata una tabella<br />

‘pittori’ che conterrà su ogni sua riga i dati <strong>di</strong> un pittore e similmente per i quadri. Ogni quadro potrebbe essere<br />

associato al suo pittore in<strong>di</strong>cando nel quadro il co<strong>di</strong>ce del pittore.<br />

Questa fase viene chiamata analisi dei dati.<br />

Co<strong>di</strong>ce<br />

quadro<br />

Quadri<br />

Titolo<br />

Anche per questa fase inizieremo con situazioni molto semplici. Proseguendo con l'esempio <strong>di</strong> problema presentato al<br />

punto uno, non è <strong>di</strong>fficile convincersi che i dati <strong>di</strong> cui abbiamo bisogno sono: costo <strong>di</strong> un litro <strong>di</strong> carburante, numero<br />

<strong>di</strong> chilometri tra la casa e la scuola, numero <strong>di</strong> chilometri che il mezzo utilizzato compie con un litro <strong>di</strong> carburante,<br />

costo <strong>di</strong> eventuali pedaggi.<br />

Per queste semplici situazioni uno schema come quello appena visto è esagerato. Ci accontenteremo <strong>di</strong> elencare i<br />

cosiddetti dati in ingresso (input); il programma li elabora (processing) per produrre i risultati, i dati in uscita<br />

(output).<br />

INPUT<br />

costo <strong>di</strong> un litro <strong>di</strong> carburante<br />

chilometri tra casa e scuola<br />

chilometri / litro del mezzo<br />

costo <strong>di</strong> eventuali pedaggi.<br />

ELABORAZIONE<br />

Elaborazione svolta dal<br />

programma<br />

OUTPUT<br />

costo <strong>di</strong> un litro <strong>di</strong> carburante<br />

chilometri tra casa e scuola<br />

chilometri / litro del mezzo<br />

costo <strong>di</strong> eventuali pedaggi.


Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 5<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

Deve essere molto chiara una cosa: per dati in input si intendono quelli in<strong>di</strong>spensabili, quelli cioè che il computer<br />

non può calcolare o derivare in altro modo. Facciamo un esempio: se ad un certo punto in un programma abbiamo<br />

<strong>di</strong>sposizione una quantità espressa in ore e per proseguire è necessario esprimerla i secon<strong>di</strong>, non ce<strong>di</strong>amo alla<br />

pigrizia chiedendo a chi sta usando il programma <strong>di</strong> inserire questo valore usando la tastiera, pretendendo che sia lui<br />

a fare la conversione! Sarà invece il programma a calcolare autonomamente il valore richiesto moltiplicando per 3600<br />

il numero delle ore...<br />

NOTA: non è raro il caso <strong>di</strong> alunni in <strong>di</strong>fficoltà al momento <strong>di</strong> in<strong>di</strong>viduare i dati in input. Intanto <strong>di</strong>ciamo che non è<br />

necessario avere la certezza <strong>di</strong> averli in<strong>di</strong>viduati proprio tutti per poter proseguire: è normale, dopo avere in<strong>di</strong>viduato<br />

i più importanti ed evidenti, iniziare la fase successiva (trovare un 'modo' <strong>di</strong> utilizzare i dati in input per giungere alla<br />

risultato); si ad un certo punto ci si accorge che manca qualche dato per poter proseguire lo si aggiungerà<br />

semplicemente ai dati <strong>di</strong> input.<br />

È arrivato il momento <strong>di</strong> descrivere il modo in cui i dati <strong>di</strong> input devono essere utilizzati per ottenere i risultati. Questa<br />

'descrizione' è chiamata algoritmo.<br />

3<br />

Da ‘Wikipe<strong>di</strong>a’, l’enciclope<strong>di</strong>a libera (http://it.wikipe<strong>di</strong>a.org), con qualche piccolo adattamento:<br />

Il termine (algoritmo) deriva dal nome del (grande) matematico arabo Al-Khwarizmi, che pubblicò,<br />

tra gli altri, il libro dal quale prende le origini la parola Algebra (ora sapete chi o<strong>di</strong>are). Nei suoi libri<br />

ne scrive anche i proce<strong>di</strong>menti per portare a termine alcuni tipi <strong>di</strong> calcolo: questi proce<strong>di</strong>menti<br />

presero il nome <strong>di</strong> algoritmi.<br />

Nella sua definizione più semplice ed intuitiva un algoritmo è una sequenza or<strong>di</strong>nata <strong>di</strong> passi<br />

semplici che hanno lo scopo <strong>di</strong> portare a termine un compito più complesso (una ricetta da cucina,<br />

ad esempio, può essere considerata come un algoritmo che partendo da un insieme <strong>di</strong> singoli<br />

alimenti <strong>di</strong> base ed eseguendo una sequenza <strong>di</strong> passi, produce come risultato un piatto composto).<br />

In un modo più formale, possiamo quin<strong>di</strong> definire l'algoritmo come una<br />

sequenza or<strong>di</strong>nata e finita <strong>di</strong> istruzioni che, dato uno od una serie <strong>di</strong><br />

elementi in input, produce uno od una serie <strong>di</strong> risultati in output .<br />

Sequenza or<strong>di</strong>nata significa che esiste un or<strong>di</strong>ne preciso in base al quale vengono eseguite le<br />

istruzioni (d'altronde sarebbe ben <strong>di</strong>fficile prima sbattere un uovo e poi rompere il guscio!).<br />

Sequenza finita significa che le istruzioni possono essere anche veramente tante, ma non in<br />

numero limitato; inoltre il numero <strong>di</strong> volte che globalmente queste istruzioni vengono eseguite non<br />

può essere illimitato.<br />

La sequenza delle operazioni deve essere chiara, mai ambigua, deve avere un or<strong>di</strong>ne ben preciso, e<br />

deve giungere a termine per ogni input. Tutte le istruzioni devono comportare delle azioni tra<br />

quelle che l’esecutore è in grado <strong>di</strong> svolgere. Il risultato <strong>di</strong> un algoritmo deve essere sempre uguale<br />

in<strong>di</strong>pendentemente da chi lo esegue.<br />

Se, come visto, una ricetta da cucina rappresenta un <strong>di</strong>screto esempio <strong>di</strong> algoritmo <strong>di</strong>rettamente<br />

eseguibile da un essere umano, l'istruzione "aggiungere sale quanto basta" <strong>di</strong>fficilmente sarà<br />

comprensibile per una macchina (ma anche tra gli umani stessi quel ‘ quanto basta’ verrebbe<br />

sicuramente interpretato in tanti mo<strong>di</strong> <strong>di</strong>versi!).<br />

Un passo <strong>di</strong> un algoritmo può essere definito anche tramite un altro algoritmo (chiamato in questo<br />

caso sottoalgoritmo), che sud<strong>di</strong>vide il compito in compiti ancora più elementari. Facciamo un<br />

esempio: l'algoritmo "va dal salotto alla cucina" si compone in realtà delle seguenti istruzioni:<br />

•esci dal salotto<br />

•curva a sinistra<br />

•prosegui per il corridoio fino all'ultima porta sulla sinistra<br />

•attraversa la porta a sinistra<br />

Questo è certamente fin troppo esplicito per un operatore umano (al quale già il problema originale<br />

"va dal salotto alla cucina" sembra probabilmente abbastanza elementare da non richiedere, in<br />

apparenza, sud<strong>di</strong>visioni), ma nel caso <strong>di</strong> un robot richiederebbe <strong>di</strong> specificare i passi con ben altra<br />

(minore) complessità.


L'algoritmo "attraversa la porta a sinistra" si compone <strong>di</strong>:<br />

•controlla se la porta è aperta<br />

•nel caso che la porta sia aperta salta il passo seguente<br />

•apri la porta<br />

•avanza <strong>di</strong> un metro<br />

L'algoritmo "apri la porta", compreso nel precedente, a sua volta si compone <strong>di</strong>:<br />

•proten<strong>di</strong> il braccio<br />

•afferra la maniglia<br />

•rotea la mano <strong>di</strong> 30 gra<strong>di</strong> in <strong>di</strong>rezione antioraria<br />

•applica una pressione alla maniglia <strong>di</strong>retta <strong>di</strong> fronte a te<br />

•...<br />

Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 6<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

Un modo dettagliato <strong>di</strong> rappresentare l'algoritmo "attraversa la porta a sinistra" è allora il seguente:<br />

•controlla se la porta è aperta<br />

•nel caso che la porta sia aperta salta il passo seguente<br />

•apri la porta<br />

•proten<strong>di</strong> il braccio<br />

•afferra la maniglia<br />

•rotea la mano <strong>di</strong> 30 gra<strong>di</strong> in <strong>di</strong>rezione antioraria<br />

•applica una pressione alla maniglia <strong>di</strong>retta <strong>di</strong> fronte a te<br />

•...<br />

•avanza <strong>di</strong> un metro<br />

Una breve analisi dell'esempio sopra, porta a delineare alcune caratteristiche essenziali <strong>di</strong> un<br />

algoritmo:<br />

•non ambiguo: le istruzioni devono essere univocamente interpretabili;<br />

•eseguibile: ogni istruzione deve terminare in tempo finito.<br />

•Inoltre, in informatica, si richiede generalmente che un algoritmo sia finito, ovvero termini per<br />

ogni insieme <strong>di</strong> dati <strong>di</strong> ingresso.<br />

Un algoritmo non è tale se risolve in un caso particolare <strong>di</strong> un problema: deve essere utile per la soluzione <strong>di</strong><br />

un'intera classe <strong>di</strong> problemi. Facciamo un esempio: il proce<strong>di</strong>mento che serve a calcolare l'area del triangolo<br />

la cui base misura 3 m e l'altezza 5 m, e solo l'area <strong>di</strong> questo triangolo, non può definirsi un algoritmo. Il<br />

proce<strong>di</strong>mento invece che descrive come calcolare l'area <strong>di</strong> un qualsiasi triangolo nota la misura della base e<br />

dell'altezza, risolve un'intera classe <strong>di</strong> problemi (è una soluzione generale) e può definirsi algoritmo.<br />

Un programma è la traduzione <strong>di</strong> un algoritmo in un blocco <strong>di</strong> istruzioni eseguibili automaticamente da un<br />

<strong>sistema</strong> <strong>di</strong> <strong>elaborazione</strong> <strong>elettronico</strong>.<br />

Arrivati a questo punto dobbiamo confrontarci con l'assoluta inadeguatezza del linguaggio parlato (il<br />

cosiddetto linguaggio naturale) per descrivere un algoritmo. Banalizzo con un classico esempio:<br />

la vecchia porta la sbarra<br />

Quale significato deve essere dato questa frase? Si tratta forse <strong>di</strong> un'anziana signora china sotto il peso <strong>di</strong><br />

una pesante sbarra? O si sta parlando <strong>di</strong> uscita sbarrata da una vecchia porta? Questa ambiguità è<br />

inaccettabile per un computer: esso deve sapere esattamente come comportarsi e deve produrre sempre gli<br />

stessi risultati se gli vengono sottoposti gli stessi dati in input.<br />

È necessario servirsi <strong>di</strong> linguaggi formali, cioè rigorosamente definiti. Questi tipi <strong>di</strong> linguaggio sono <strong>di</strong> solito<br />

molto meno ricchi <strong>di</strong> vocaboli e <strong>di</strong> regole sintattiche ma hanno il grosso pregio <strong>di</strong> non essere ambigui (ogni<br />

istruzione è chiara, ha un solo significato e produce sempre lo stesso risultato).<br />

4<br />

Questi linguaggi sono chiamati linguaggi <strong>di</strong> programmazione. La figura professionale che si occupa<br />

della scrittura dei programmi è il programmatore. la fase <strong>di</strong> scrittura <strong>di</strong> un programma è detta <strong>di</strong><br />

co<strong>di</strong>fica (il programmatore scrive il co<strong>di</strong>ce del programma)


5<br />

Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 7<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

Non è detto che sia il programmatore a stu<strong>di</strong>are il problema e ad ideare l'algoritmo risolutivo: la figura professionale<br />

specializzata in questi compiti preliminari e fondamentali è chiamato analista. E’ certamente vero che il ruolo<br />

dell'analista e del programmatore possano essere svolti dalla stessa persona. Per compiere una buona analisi è<br />

necessaria molta esperienza, ed è per questo che spesso si nasce 'semplici' programmatori per poi <strong>di</strong>ventare analisti<br />

o analisti-programmatori.<br />

il ruolo del programmatore ' puro' è quello allora <strong>di</strong> ricevere dall'analista la descrizione dell'algoritmo per provvedere<br />

alla co<strong>di</strong>fica (cioè scrittura) <strong>di</strong> quest'ultimo usando un linguaggio <strong>di</strong> programmazione.<br />

Torneremo presto sulla questione della descrizione degli algoritmi: è il<br />

tema portante <strong>di</strong> quest'anno scolastico!<br />

NOTA: è corretto fare <strong>di</strong>stinzione tra il risolutore <strong>di</strong> un problema (colui che ha ideato l’algoritmo che lo risolve) e<br />

l’esecutore materiale dei passi dell’algoritmo. Nel nostro caso il risolutore è sempre un uomo/donna e l’esecutore è<br />

il computer.<br />

Terminata la scrittura del programma inizia la fase <strong>di</strong> test. Sottoporre a test un programma significa provarlo<br />

con tutte le configurazioni <strong>di</strong> dati in input normali e particolari. Di nuovo, facciamo un semplice esempio<br />

immaginando <strong>di</strong> avere scritto un programma che, forniti due numeri in input, calcola che percentuale è il primo<br />

rispetto al secondo; ad esempio se il primo numero fosse 50 ed il secondo 150, il risultato fornito dovrebbe<br />

essere 33,3 % perio<strong>di</strong>co (50 è infatti un terzo <strong>di</strong> 150...). Non è <strong>di</strong>fficile convincersi che nel programma la formula<br />

usata è:<br />

(primo numero/secondo numero)*100<br />

Fare il test <strong>di</strong> questo programma con configurazioni <strong>di</strong> dati in input 'normali', significa provare il programma con<br />

coppie <strong>di</strong> numeri tipo (10,20) (50,150) eccetera. Poi ci si potrebbe domandare se il programma fornisce risultati<br />

corretti anche quando il primo numero è maggior dal secondo: (240,60); e scopriremo che la risposta è sì: otterremo<br />

come valore 400% (in effetti, 240 è il quadruplo <strong>di</strong> 60). E se usassimo numeri negativi? Nessun problema...<br />

Ok, è arrivato il momento <strong>di</strong> essere cattivi: e se usassimo numeri decimali? Tipo (10.2, 97.5) ? E se il primo numero<br />

fosse zero: (0, 34)? Anche con queste configurazioni <strong>di</strong> valori in input il programma continua a fornire risultati<br />

corretti. Giunti a questo punto, il programmatore inesperto (o pigro) potrebbe concludere che il programma funziona<br />

bene in tutti i casi possibili. Purtroppo, la matematica c'insegna che non è possibile <strong>di</strong>videre per zero: inserendo una<br />

configurazione <strong>di</strong> input con il secondo uguale a zero, come in (72,0), il programma andrebbe letteralmente tilt! Gli<br />

informatici in questi casi usano un'espressione assai colorita: il programma va in crash!<br />

Il caso dello zero come secondo numero è un cosiddetto caso limite: ogni programma dovrebbe essere testato in<br />

tutti i casi limite che potrebbero presentarsi, anche se con probabilità molto bassa!<br />

La fase <strong>di</strong> test viene <strong>di</strong> solito sud<strong>di</strong>visa a sua volta in:<br />

• alpha test: è quello svolto <strong>di</strong>rettamente dal programmatore che ha scritto il co<strong>di</strong>ce o comunque da personale<br />

interno alla <strong>di</strong>tta che commercializzerà il software; potremmo <strong>di</strong>re che in questa fase vengono trovati gli errori<br />

più grossolani;<br />

• beta test: quando il software viene ritenuto sufficientemente stabile viene <strong>di</strong>stribuito, <strong>di</strong> solito gratuitamente,<br />

ad un numero ristretto <strong>di</strong> utenti che, in cambio del beneficio <strong>di</strong> poter <strong>di</strong>sporre in anteprima del prodotto quasi<br />

finito, comunicheranno secondo protocolli stabiliti una descrizione degli errori che capitano durante l'utilizzo; se<br />

il prodotto è particolarmente complesso, il numero dei beta tester, può essere elevato: ad esempio, quando la<br />

Microsoft rilascia per il beta test una nuova versione <strong>di</strong> Windows lo fa anche a decine <strong>di</strong> migliaia <strong>di</strong> utenti!<br />

• gamma test: a volte viene definito un ulteriore livello che si <strong>di</strong>fferenzia dal precedente solo del fatto che<br />

dovrebbe essere quasi esente da errori


8<br />

6<br />

Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 8<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

La fase <strong>di</strong> test ha lo scopo <strong>di</strong> evidenziare gli errori durante il funzionamento del programma, il che non vuol <strong>di</strong>re<br />

però automaticamente sapere quale istruzione ha causato il problema.<br />

La fase <strong>di</strong> debug in<strong>di</strong>ca invece l'attività del programmatore volta ad in<strong>di</strong>viduare esattamente la porzione <strong>di</strong><br />

co<strong>di</strong>ce che contiene l'errore in modo da poter eliminare.<br />

Ogni volta che si scopre un errore è necessario ritornare alla fase <strong>di</strong> co<strong>di</strong>fica per mo<strong>di</strong>ficare il co<strong>di</strong>ce.<br />

ATTENZIONE!! Una delle cattive abitu<strong>di</strong>ni più comuni è quella <strong>di</strong> non testare nuovamente il programma dopo<br />

l'eliminazione <strong>di</strong> un errore. Purtroppo l'esperienza insegna che, non così raramente come si potrebbe pensare, le<br />

mo<strong>di</strong>fiche apportate per correggere un errore ne introducono altri!<br />

Finalmente il co<strong>di</strong>ce può essere immesso sul mercato! Si parla <strong>di</strong> rilascio (release). Ma non è finita qui! Anzi,<br />

rapportato a 100, il totale dell'impegno rappresentato da queste fasi viene stimato da molti come non superiore al<br />

40%<br />

Ed il resto ? Siate sinceri: quante volte vi è capitato <strong>di</strong> acquistare un video game senza essere costretti ad applicare<br />

una cosiddetta patch (letteralmente pezza, correzione). Quante volte come utenti siamo rimasti in attesa<br />

dell'ennesimo service pack (un kit che contiene molte patch ‘in un colpo solo’) <strong>di</strong> Windows o <strong>di</strong> Office o <strong>di</strong> altri<br />

software? Ma anche senza errori, ogni anno vengono immesse sul mercato nuove versioni (qualcuno riesce a contare<br />

quelle della serie FIFA? o <strong>di</strong> Final Fantasy?).<br />

Il 60% che manca <strong>di</strong> tutto il costo per lo sviluppo del software viene fagocitato dalla cosiddetta manutenzione. Ci<br />

sono tre tipi <strong>di</strong> manutenzione:<br />

7<br />

• correttiva: anche dopo aver superato tutte le fasi <strong>di</strong> test, è quasi impossibile che un software non contenga<br />

ancora almeno un errore; perio<strong>di</strong>camente, quin<strong>di</strong>, vengono rilasciate versioni che si spera siano <strong>di</strong> volta in volta<br />

meno affette da errori;<br />

• perfettiva o migliorativa: in<strong>di</strong>ca tutti quegli interventi che non servono a togliere degli errori ma a<br />

migliorare in qualche modo il prodotto; ad esempio a renderlo più veloce, a <strong>di</strong>minuire le sue esigenze <strong>di</strong><br />

spazio sul <strong>di</strong>sco, a renderlo capace <strong>di</strong> riconoscere nuove periferiche, a compiere funzioni prima non<br />

previste eccetera;<br />

• adattiva o adattativi: nessun errore da togliere, nessun miglioramento da portare; ma un cambiamento nel<br />

contesto in cui il software deve funzionare costringe ad apportare delle mo<strong>di</strong>fiche: il caso classico che si cita in<br />

questa situazione è la mo<strong>di</strong>fica <strong>di</strong> una legge che forza la software house a rispettarla, a costo <strong>di</strong> pesanti<br />

mo<strong>di</strong>fiche del co<strong>di</strong>ce!<br />

Infine, quando un'applicazione non è più utile (perché superata da altre, perché le mo<strong>di</strong>fiche richieste sono<br />

troppe, perché un evento imprevisto la rende obsoleta eccetera) se ne può anche decretare la morte, la<br />

<strong>di</strong>smissione.<br />

Riassumendo:<br />

1<br />

Stu<strong>di</strong>o della<br />

situazione reale<br />

2<br />

Analisi dei dati<br />

3<br />

Algoritmo/i risolutivo<br />

Questo tipo <strong>di</strong> modello è detto, per<br />

evidenti motivi, a cascata.<br />

E’ doveroso sottolineare che questo processo nel quale io ho in<strong>di</strong>viduato<br />

otto sta<strong>di</strong> non è l'unico modello riconosciuto per lo sviluppo del software<br />

(e a <strong>di</strong>re la verità l'ho semplificato rispetto quelli che trovate nella<br />

letteratura informatica). È però facile da capire e si adatta bene alla<br />

maggior parte delle stazioni che ci troveremo ad affrontare. Di tanto in<br />

tanto ritorneremo su uno degli sta<strong>di</strong> per aggiungere maggiore dettaglio.<br />

4<br />

Co<strong>di</strong>fica<br />

5<br />

test<br />

6<br />

debug<br />

7<br />

Manutenzione<br />

8<br />

Dismissione


Architettura hardware e software <strong>di</strong> un <strong>sistema</strong> <strong>di</strong> <strong>elaborazione</strong><br />

Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 9<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

Per capire cosa sia un linguaggio <strong>di</strong> programmazione e come utilizzarlo per scrivere programmi, è prima necessaria<br />

una panoramica che mostri a gran<strong>di</strong> linee il funzionamento <strong>di</strong> un <strong>sistema</strong> <strong>di</strong> <strong>elaborazione</strong>.<br />

Periferiche <strong>di</strong><br />

input (tastiera)<br />

Periferiche <strong>di</strong><br />

output (monitor)<br />

Periferiche <strong>di</strong><br />

input / output<br />

(modem)<br />

SCHEMA A BLOCCHI DI UN SISTEMA DI ELABORAZIONE<br />

Nota: tecnicamente parlando, i supporti <strong>di</strong> memorizzazione <strong>di</strong> massa<br />

sono ancora periferiche <strong>di</strong> input/output. La loro importanza è però<br />

tale da meritare un trattamento a parte.<br />

Il cuore del <strong>sistema</strong> è la CPU (Central Processing Unit, Unità centrale <strong>di</strong> processo). Nei personal computer la CPU è<br />

rappresentata da un singolo chip (il microprocessore): Pentium, Celeron, Athlon, Sempron, PowerPC, non vi <strong>di</strong>cono<br />

nulla questi nomi??<br />

La CPU è il ‘cervello’ del <strong>sistema</strong>. Il software <strong>di</strong> base (il BIOS che controlla <strong>di</strong>rettamente l’hardware, windows/linux<br />

cioè i sistemi operativi, gli strumenti <strong>di</strong> programmazione ed altre utilità <strong>di</strong> <strong>sistema</strong>) ed anche quello applicativo<br />

(programmi <strong>di</strong> video scrittura, contabilità, giochi ecc.) è formato da istruzioni, molte istruzioni. Per avere un idea della<br />

complessità, pensate che un software applicativo come Word è formato da milioni <strong>di</strong> istruzioni ... Queste in<strong>di</strong>cano ciò<br />

che va fatto per consentire all’utente <strong>di</strong> scrivere un documento usando il computer. Ecco, sinteticamente, come<br />

funziona l’intero meccanismo:<br />

Usando l’interfaccia che il <strong>sistema</strong> operativo mette a <strong>di</strong>sposizione, l’utente comanda il caricamento delle istruzioni (il<br />

programma) dai supporti <strong>di</strong> memorizzazione <strong>di</strong> massa: nel caso <strong>di</strong> Windows si fa doppio click, ad esempio, sull’icona<br />

<strong>di</strong> Word ...).<br />

Le istruzioni (se non tutte almeno quelle che servono in un primo momento) sono trasferite dai lenti supporti <strong>di</strong><br />

memorizzazione <strong>di</strong> massa nella velocissima memoria elettronica (RAM): da quest’ultima saranno a <strong>di</strong>sposizione della<br />

CPU in tempi molto brevi.<br />

La CPU preleva la prima istruzione del programma dalla RAM (Random Access Memory, memoria ad accesso<br />

casuale).<br />

La CPU ‘capisce’ cosa gli chiede l’istruzione.<br />

LA CPU invia i necessari coman<strong>di</strong> a tutti i <strong>di</strong>spositivi interessati da quell’istruzione, eseguendola.<br />

Se l’istruzione non è quella finale, si ritorna al punto 3 prelevando la successiva istruzione e così via ...<br />

CPU<br />

R<br />

A<br />

M<br />

Supporti <strong>di</strong> memorizzazione<br />

<strong>di</strong> massa (<strong>di</strong>schi, nastri, CD)


Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 10<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

Durante l’esecuzione delle istruzioni, la CPU può usare la parte della RAM rimasta libera per registrare dati interme<strong>di</strong>:<br />

è sempre per motivi <strong>di</strong> velocità che si preferisce la memoria elettronica a quella <strong>di</strong> massa (naturalmente si dovrà<br />

prima o poi provvedere anche alla registrazione dei dati sui supporti <strong>di</strong> massa, permanenti, pena la per<strong>di</strong>ta dei dati<br />

stessi allo spegnimento del computer). Ad esempio, i documenti creati con Word sono prima registrati nella RAM e<br />

solo con un comando esplicito <strong>di</strong> registrazione (File / Salva) sono memorizzati sui <strong>di</strong>schi.<br />

Alcune istruzioni chiederanno alla CPU <strong>di</strong> controllare l’uso <strong>di</strong> periferiche <strong>di</strong> input o <strong>di</strong> output. Sempre pensando a<br />

Word, non è <strong>di</strong>fficile convincersi che le sue istruzioni debbano fare in modo che la CPU rimanga in attesa fino a che<br />

chi sta usando il programma non preme un tasto qualsiasi o usa il mouse. Non appena l’utente preme un tasto, le<br />

istruzioni comandano la sua visualizzazione sul video; se con il mouse viene scelto un comando dal menu, le<br />

istruzioni chiederanno alla CPU <strong>di</strong> comportarsi in modo appropriato.<br />

In realtà tutto questo avviene con la collaborazione del <strong>sistema</strong> operativo (Windows o Lilnux, ad esempio):<br />

quest’ultimo mette a <strong>di</strong>sposizione <strong>di</strong> ogni programma una serie <strong>di</strong> servizi che possono essere invocati; ad esempio se<br />

un carattere deve essere fatto apparire sullo schermo, tutte le operazioni coinvolte non sono gestite al massimo<br />

dettaglio dal programmatore <strong>di</strong> word (per fortuna!); quello che accade è che il programmatore che ha scritto Word<br />

‘chiede’ a Windows <strong>di</strong> fare questo per lui. Sono tantissimi i servizi messi a <strong>di</strong>sposizione da un <strong>sistema</strong> operativo<br />

(gestione <strong>di</strong> tutte le periferiche, gestione della ram, del file system, della rete ecc.). Quando i sistemi operativi non<br />

esistevano ancora od erano molto primitivi, la vita del programmatore era molto più dura (doveva programmare<br />

TUTTI i dettagli). Approfon<strong>di</strong>rete questi aspetti nel corso <strong>di</strong> sistemi.<br />

Esaminiamo ora in dettaglio il funzionamento <strong>di</strong> una CPU. Ecco un ingran<strong>di</strong>mento:<br />

C<br />

P<br />

U<br />

bus dati esterno: prelevo un dato dalla RAM o lo registro dalla RAM<br />

ALU<br />

Aritmetic<br />

Logic<br />

Unit<br />

registri<br />

Deco<strong>di</strong>ficatore<br />

CU<br />

Control Unit<br />

In<strong>di</strong>rizzi per leggere<br />

o scrivere la RAM<br />

s<br />

Per comandare la<br />

lettura o la scrittura<br />

Non spaventatevi: è più semplice <strong>di</strong> quello che sembra a colpo d’occhio. Nella zona in grigio della RAM immaginiamo<br />

essere presenti un blocco <strong>di</strong> istruzioni da eseguire (potrebbero essere quelle <strong>di</strong> un programma come Word).<br />

La RAM: pensiamola come una sequenza <strong>di</strong> celle, chiamate byte. In ogni byte può essere memorizzato un<br />

dato, come un istruzione od un carattere (vedremo dopo come ed in che forma). Nel <strong>di</strong>segno qui sopra<br />

sono stati evidenziati in grigio quattro byte. Ogni cella (byte) viene in<strong>di</strong>cata con il suo in<strong>di</strong>rizzo, cioè la<br />

posizione a partire dall’inizio. Visto che si comincia a contare da zero, quest’ultimo è l’in<strong>di</strong>rizzo della prima<br />

cella in alto nella RAM (si parla anche <strong>di</strong> byte zero). La seconda cella dall’alto ha allora in<strong>di</strong>rizzo uno e così<br />

via... I byte in grigio iniziano all’in<strong>di</strong>rizzo cinque e terminano quattro byte più avanti, all’in<strong>di</strong>rizzo otto<br />

(considerando il cinque sono appunto quattro in<strong>di</strong>rizzi).<br />

R<br />

A<br />

M


Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 11<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

I registri: sono celle <strong>di</strong> memoria speciali, interne alla CPU, usate per memorizzare valori per <strong>di</strong>versi<br />

scopi: la CPU impiega pochissimo a trovare o scrivere un dato nei registri, perché sono molto vicini ad<br />

essa e molto veloci. E’ quin<strong>di</strong> assai conveniente tenere qui i risultati interme<strong>di</strong> delle operazioni e tutto<br />

ciò che si dovrebbe continuamente rileggere o scrivere nella più lenta RAM. Purtroppo la spazio<br />

all’interno della CPU è veramente minimo: c’è spazio solo per alcune decine <strong>di</strong> registri ... Chiaro che <strong>di</strong><br />

volta in volta si terrà, della RAM, solo ciò che serve alle istruzioni del momento, poi si dovrà sostituire<br />

il contenuto dei registri con un altra parte della RAM. Pur con queste limitazioni, lavorare con i registri dà grossi<br />

benefici rispetto all’uso della sola RAM.<br />

Alcuni registri hanno poi una funzione speciale: uno, chiamato contatore <strong>di</strong> programma (program counter) in<strong>di</strong>ca a<br />

quale in<strong>di</strong>rizzo della RAM si trova il dato che rappresenta la prossima istruzione da eseguire.<br />

Un altro è chiamato registro in<strong>di</strong>rizzi: è qui che va depositato un in<strong>di</strong>rizzo ogni volta che si vuole leggere o scrivere un<br />

byte della RAM (quando si vuole leggere il co<strong>di</strong>ce della prossima istruzione da eseguire, il contatore <strong>di</strong> programma<br />

viene infatti copiato qui).<br />

Un altro è chiamato registro istruzioni e contiene il co<strong>di</strong>ce numerico dell’istruzione letta nella RAM, che deve essere<br />

eseguita (infatti ogni possibile istruzione è rappresentata da un co<strong>di</strong>ce numerico).<br />

ALU: è l’Unità Aritmetico Logica. Essa svolge le operazioni aritmetiche (matematiche)<br />

elementari ed i confronti logici tra due dati (sa <strong>di</strong>re se sono uguali, se uno è più grande o<br />

più piccolo dell’altro ecc.).<br />

Control Unit: è l’unità <strong>di</strong> controllo. E’ lei che invia a tutti gli altri <strong>di</strong>spositivi segnali elettrici con<br />

cui comanda tutte le micro operazioni che possono essere svolte. Ad esempio può comandare<br />

l’invio del contenuto <strong>di</strong> un registro in un altro registro, oppure la lettura <strong>di</strong> un byte della RAM ed<br />

il trasferimento del suo contenuto in un registro (o il contrario), il trasferimento alla ALU <strong>di</strong> due<br />

dati da sommare o da sottrarre (ed il comando che fa eseguire la somma e la sottrazione). Può<br />

inviare segnali <strong>di</strong> comando anche a <strong>di</strong>spositivi esterni alla CPU: ad esempio può comandare allo<br />

scanner <strong>di</strong> inviare il prossimo blocco <strong>di</strong> dati del documento che sta leggendo. Insomma è il vero regista che regola il<br />

funzionamento <strong>di</strong> tutti gli altri <strong>di</strong>spositivi interni od esterni.<br />

Bus: si è parlato <strong>di</strong> invio <strong>di</strong> coman<strong>di</strong> dalla control unit, <strong>di</strong> trasportare byte dalla RAM ai registri e<br />

viceversa, <strong>di</strong> comunicare in<strong>di</strong>rizzi per usare la RAM. Come viaggiano tutte queste informazioni ?<br />

Semplice, usano il bus ! Battute a parte, il termine in<strong>di</strong>ca i canali fisici lungo i quali si spostano i segnali<br />

elettrici che rappresentano dati, in<strong>di</strong>rizzi e coman<strong>di</strong>. Ci sono tre tipi <strong>di</strong> bus:<br />

Bus dati: come suggerisce il nome, trasporta dati (il contenuto <strong>di</strong> una cella <strong>di</strong> RAM, <strong>di</strong> un registro o un byte da o<br />

verso una periferica. Esiste un bus dati interno, sul quale viaggiano i dati all’interno della CPU, ed uno esterno che<br />

permette lo scambio dei dati tra la CPU e <strong>di</strong>spositivi esterni (e viceversa) o tra <strong>di</strong>spositivi esterni stessi. Ho<br />

rappresentato i bus dati con una linea a tratti piccoli. Per non appesantire il <strong>di</strong>segno non ho collegato tutti i <strong>di</strong>spositivi<br />

che si possono scambiare dati grazie al bus interno. Le frecce in<strong>di</strong>cano naturalmente il senso <strong>di</strong> percorrenza.<br />

Nelle moderne architetture esistono bus specializzati per scambiare dati con periferiche che consumano questi ultimi<br />

ad un tasso elevatissimo. Un esempio è rappresentato dalla scheda grafica: essa sfrutta bus de<strong>di</strong>cati (connessione<br />

AGP, oggi in via <strong>di</strong> sostituzione con i collegamenti Serial Ata).<br />

Bus in<strong>di</strong>rizzi: ogni volta che la CPU legge o scrive un dato dalla RAM deve prima impostare il relativo in<strong>di</strong>rizzo ;<br />

pensate al bus in<strong>di</strong>rizzi come ad un bus dati specializzato nel trasportare in<strong>di</strong>rizzi. Ho in<strong>di</strong>cato il bus in<strong>di</strong>rizzi con una<br />

linea a tratti larghi.<br />

Bus controlli: è l’insieme delle linee su cui viaggiano i coman<strong>di</strong> (segnali elettrici) che la Control Unit invia a <strong>di</strong>spositivi<br />

esterni; serve anche ai <strong>di</strong>spositivi esterni per segnalare eventi particolari alla CPU: avete presente cosa accade<br />

quando finisce la carta della stampante ? O quando il <strong>di</strong>sco che tentate <strong>di</strong> scrivere è protetto ? O quando non c’è più<br />

spazio sul <strong>di</strong>sco ? L’ho in<strong>di</strong>cato nel <strong>di</strong>segno con le tre linee continue che puntano verso il basso. La control Unit può<br />

attivare o <strong>di</strong>sattivare anche i componenti interni grazie a delle linee <strong>di</strong> selezione: le ho in<strong>di</strong>cate con delle linee<br />

continue senza frecce: quando la Control Unit vuole attivare un <strong>di</strong>spositivo, invia lungo una linea <strong>di</strong> selezione un<br />

segnale apposito.


Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 12<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

Ora che sono stati descritti tutti i componenti, ve<strong>di</strong>amoli all’opera. Immaginiamo che le istruzioni da eseguire siano<br />

contenute nei byte in grigio:<br />

Il registro program counter, abbiamo detto, contiene l’in<strong>di</strong>rizzo dell’istruzione da eseguire (in questo caso l’in<strong>di</strong>rizzo<br />

del primo byte in grigio). Per poterla leggere dalla RAM è necessario che tale in<strong>di</strong>rizzo sia contenuto nel registro<br />

in<strong>di</strong>rizzi. L’unità <strong>di</strong> controllo comanda allora la copia del contenuto del program counter in questo registro.<br />

L’unità <strong>di</strong> controllo comanda quin<strong>di</strong> al <strong>di</strong>spositivo <strong>di</strong> lettura/scrittura della RAM (non evidenziato nel <strong>di</strong>segno per<br />

semplicità) <strong>di</strong> leggere il byte all’in<strong>di</strong>rizzo contenuto ora nel registro in<strong>di</strong>rizzi; il suo valore (il co<strong>di</strong>ce dell’istruzione)<br />

viene trasferito lungo il bus dati esterno e da lì sul bus dati interno, fino a depositarlo nel registro istruzioni.<br />

L’unità <strong>di</strong> controllo comanda a questo punto al deco<strong>di</strong>ficatore <strong>di</strong> leggere dal registro istruzioni il co<strong>di</strong>ce numerico<br />

dell’istruzione e <strong>di</strong> ‘capire’ che cosa è richiesto.<br />

Sulla base della ‘risposta’ del deco<strong>di</strong>ficatore l’unità <strong>di</strong> controllo attiva in sequenza i <strong>di</strong>spositivi interessati<br />

dall’istruzione; ad esempio (semplificando) se si dovesse calcolare A = B + C<br />

prelevare B dalla RAM (in<strong>di</strong>rizzo <strong>di</strong> B in registro in<strong>di</strong>rizzi e successiva lettura);<br />

prelevare C dalla RAM (in<strong>di</strong>rizzo <strong>di</strong> C in registro in<strong>di</strong>rizzi e successiva lettura);<br />

trasferire alla ALU gli operan<strong>di</strong> e comandare la somma<br />

copiare il risultato nella posizione dove in RAM si trova ‘C’ (in<strong>di</strong>rizzo <strong>di</strong> C in registro in<strong>di</strong>rizzi e scrittura)<br />

Nel frattempo il program counter è stato automaticamente incrementato e in<strong>di</strong>ca<br />

l’in<strong>di</strong>rizzo della prossima istruzione da eseguire. Si può allora ricominciare dal punto 1,<br />

fino a che ... il computer viene spento ! In realtà, anche dopo che il programma termina,<br />

ad esempio chiudendo Word, un altro rimane in funzione: quello che già funzionava<br />

prima <strong>di</strong> Word, Windows in persona. Infatti il <strong>sistema</strong> operativo, esso stesso un insieme<br />

<strong>di</strong> programmi, è all’opera dal momento dell’accensione del computer fino allo<br />

spegnimento. Quando l’utente comanda, usando l’interfaccia grafica, la partenza <strong>di</strong><br />

un’applicazione, le istruzioni <strong>di</strong> quest’ultima affiancano in RAM quelle del <strong>sistema</strong><br />

operativo ed il program counter viene fatto ‘puntare’ alla prima istruzione<br />

dell’applicazione, che inizia così ad essere eseguita. A programma terminato si riprende<br />

ad eseguire il <strong>sistema</strong> operativo. Durante il suo funzionamento l’applicazione può<br />

richiamare parti del <strong>sistema</strong> operativo (richiesta <strong>di</strong> servizi): il punto a cui si era arrivati con l’applicazione viene prima<br />

memorizzato, per poter riprendere, dopo che il <strong>sistema</strong> operativo ha esau<strong>di</strong>to la richiesta, il programma esattamente<br />

dall’istruzione a cui era stato interrotto.<br />

La fase in cui si provvede a prelevare il co<strong>di</strong>ce numerico dell’istruzione da eseguire è chiamata fase <strong>di</strong> fetch<br />

(prelievo).<br />

La fase in cui il deco<strong>di</strong>ficatore interpreta l’istruzione, determinando le microoperazioni che verranno poi eseguite<br />

dall’unità <strong>di</strong> controllo è chiamata fase <strong>di</strong> decode (deco<strong>di</strong>fica).<br />

La fase in cui le microoperazioni sono effettivamente portate a termine, attivando nella giusta sequenza i <strong>di</strong>spositivi<br />

interessati è chiamata fase <strong>di</strong> execute (esecuzione).<br />

Il ciclo fetch, decode ed execute viene ripetuto a velocità incre<strong>di</strong>bili: oggi miliar<strong>di</strong><br />

<strong>di</strong> milioni <strong>di</strong> volte al secondo (1 volta al secondo = 1 Hertz = 1Hz; un milione <strong>di</strong><br />

volte al secondo = 1 Mega Hertz = 1Mhz; 1Ghz= un miliardo). Le fantastiche<br />

capacità <strong>di</strong> un <strong>sistema</strong> <strong>di</strong> <strong>elaborazione</strong> sono tutte qui: una velocità pazzesca ed<br />

una memoria perfetta nell’eseguire istruzioni semplicissime (sposta un byte <strong>di</strong> qua<br />

e mettilo <strong>di</strong> là ...) ! E’ come se un muratore usasse mattoni gran<strong>di</strong> come pezzi del<br />

lego (le istruzioni semplicissime) ma lo facesse ad una velocità incre<strong>di</strong>bile, costruendo palazzi meravigliosi, ma solo se<br />

qualcuno lo guida passo passo (il programma) ... Qualcuno ha infatti definito il computer uno stupido molto veloce !


RAPPRESENTAZIONE INTERNA DELLE INFORMAZIONI<br />

Rimane da sciogliere ancora un nodo importante: come sono rappresentate le informazioni<br />

nella RAM e nei registri ? E le istruzioni ? Essenzialmente devono essere trattati numeri e<br />

caratteri. Le prime macchine calcolatrici (attenzione: non sto ancora parlando <strong>di</strong> computer<br />

elettronici!) erano gran<strong>di</strong> grovigli <strong>di</strong> ingranaggi: pur costruite con una precisione<br />

ammirabile, gli attriti ed i tempi <strong>di</strong> funzionamento <strong>di</strong> tutte queste parti meccaniche<br />

costituivano un limite serio per la velocità <strong>di</strong> <strong>elaborazione</strong>.<br />

Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 13<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

Mentre un ingranaggio impiega un certo tempo per funzionare, un segnale elettrico va alla velocità della luce<br />

(300.000 Km al secondo) ! Come <strong>di</strong>re, più <strong>di</strong> sette volte il giro della terra in un secondo ! Sono stati ideati dei<br />

<strong>di</strong>spositivi (circuiti elettronici) in grado sfruttare i segnali elettrici per rappresentare le informazioni ed elaborarle. Si è<br />

anche capito che il miglior modo <strong>di</strong> procedere sarebbe stato quello <strong>di</strong> rappresentare tutto sotto forma <strong>di</strong> numeri<br />

binari. Cerchiamo <strong>di</strong> arrivare a questa conclusione per gra<strong>di</strong>.<br />

I segnali elettrici non sono perfetti: un segnale elettrico, mentre viaggia su un filo, è soggetto a<br />

<strong>di</strong>sturbi ed attenuazioni <strong>di</strong> vario tipo che tendono a <strong>di</strong>storcerlo: in partenza, cioè, possiede certe<br />

caratteristiche (forma, potenza ecc.) ; all’arrivo non sono più esattamente le stesse. Per cui se<br />

scelgo due segnali abbastanza simili tra loro e li trasmetto, potrebbe capitare che chi li riceve<br />

scambi l’uno per l’altro (errore). Questa possibilità cresce con il numero dei tipi <strong>di</strong> segnale usati.<br />

Questo spiega perché i prototipi <strong>di</strong> <strong>calcolatore</strong> che cercavano <strong>di</strong> usare tanti segnali <strong>di</strong>versi per<br />

rappresentare le cifre numeriche ed i caratteri dell’alfabeto non ebbero molto successo:<br />

semplicemente la probabilità <strong>di</strong> errore era troppo alta ! Certo se i tipi <strong>di</strong> segnale fossero <strong>di</strong> meno,<br />

<strong>di</strong>minuirebbe la probabilità <strong>di</strong> confonderli ... Fin dove possiamo arrivare ? Un solo segnale non<br />

basta: sarebbe sempre uguale a se stesso e non porterebbe informazione, ma due ... Due è proprio<br />

risultato il numero ottimale: usando solo due segnali il più possibile <strong>di</strong>versi tra loro, è ben <strong>di</strong>fficile che un segnale<br />

venga <strong>di</strong>storto al punto tale da confonderlo per l’altro. E’ nata la logica <strong>di</strong>gitale (binaria), basata cioè sull’uso <strong>di</strong> due<br />

soli simboli (che possiamo far corrispondere alle cifre 0 ed 1). Tutte le informazioni devono essere co<strong>di</strong>ficate<br />

(rappresentate) con combinazioni <strong>di</strong>verse <strong>di</strong> 0 ed 1. Queste cifre, dette binarie, sono anche chiamate bit. Un bit può<br />

essere 0 od 1.<br />

I circuiti <strong>di</strong>gitali sono facili da costruire: <strong>di</strong>gitali, così sono chiamati i circuiti in grado <strong>di</strong> trattare segnali <strong>di</strong> due tipi, per<br />

confrontarli e combinarli. Purtroppo non è possibile neanche accennare alla realizzazione dei circuiti <strong>di</strong>gitali. Vi basti<br />

sapere che è relativamente semplice costruire circuiti per memorizzare, sommare, sottrarre, moltiplicare, <strong>di</strong>videre,<br />

confrontare ecc. due numeri binari. Questo perché le regole dell’aritmetica binaria (come si fanno le somme,<br />

sottrazioni, moltiplicazioni ecc.) sono ad<strong>di</strong>rittura più semplici <strong>di</strong> quelle che usiamo noi con la nostra aritmetica<br />

decimale (le cifre dallo 0 al 9) ! Beh, avete un intero corso <strong>di</strong> elettronica per togliervi certe voglie!<br />

Il <strong>sistema</strong> binario è equivalente a quello decimale: come <strong>di</strong>re che ogni numero espresso con la nostra<br />

notazione decimale è esprimibile anche in binario ed in modo biunivoco. Il termine significa che, dato<br />

un qualsiasi numero espresso in decimale, lo si può convertire ottenendo un numero in forma binaria;<br />

viceversa se si parte da questo numero binario e lo si converte in decimale, si ritorna al numero <strong>di</strong><br />

partenza. Questo è molto importante: significa poter continuare per noi a fornire dati in decimale<br />

all’elaboratore. Questi saranno convertiti in binario ed elaborati. Il risultato sarà <strong>di</strong> nuovo, per noi,<br />

convertito in binario, con la certezza <strong>di</strong> non commettere equivoci. Equivalentemente, significa che<br />

tutte le operazioni matematiche possibili in decimale lo sono anche in binario e che se una certa<br />

operazione in decimale dà un certo risultato, lo stesso verrà fornito in binario dall’operazione corrispondente.<br />

Potremmo <strong>di</strong>re, volgarmente, che il <strong>sistema</strong> binario è ‘potente’ quanto quello decimale.<br />

Anche le informazioni non numeriche sono comunque rappresentabili sotto forma <strong>di</strong> numeri. Consideriamo caso per<br />

caso:<br />

Alfanumeriche: le singole cifre numeriche, le lettere dell’alfabeto ed altri caratteri come la punteggiatura: basta far<br />

corrispondere ad ogni carattere un ben preciso co<strong>di</strong>ce numerico. Uno tra i co<strong>di</strong>ci più <strong>di</strong>ffusi è l’ASCII: secondo<br />

questo co<strong>di</strong>ce, ad esempio la lettera ‘A’ è co<strong>di</strong>ficata con il numero 65, la ‘B’ con 66 e così via (i numeri precedenti il<br />

65 sono usati per altri caratteri). Naturalmente l’elaboratore saprà quando interpretare un numero come tale o come<br />

la rappresentazione <strong>di</strong> un carattere. Nota: in fondo al libro troverai un appen<strong>di</strong>ce con l’intera tabella ASCII.


Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 14<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

Grafiche: se il <strong>di</strong>segno è <strong>di</strong> tipo tecnico memorizzo le coor<strong>di</strong>nate numeriche <strong>di</strong> ogni elemento geometrico ed altre<br />

caratteristiche (coor<strong>di</strong>nate del centro <strong>di</strong> una circonferenza e la misura del raggio, le coor<strong>di</strong>nate degli angoli <strong>di</strong> un<br />

rettangolo o degli estremi <strong>di</strong> una retta ecc.). Se il <strong>di</strong>segno è pittorico, cioè fatto per punti (pixel), si memorizza per<br />

ogni pixel il valore dell’intensità <strong>di</strong> rosso, verde e blu che lo caratterizza.<br />

Animazioni e filmati: è un caso particolare del precedente. Infatti ogni sequenza <strong>di</strong> animazione o filmato è formata<br />

da tanti fotogrammi, ognuno dei quali può essere considerato un <strong>di</strong>segno statico, ricadendo nel caso precedente. Un<br />

a tecnica molto usata per risparmiare memoria è quella <strong>di</strong> memorizzare solo alcuni fotogrammi chiave (key frame) e<br />

<strong>di</strong>re per ogni fotogramma interme<strong>di</strong>o cosa cambia a livello <strong>di</strong> pixel (compressioni mpeg / <strong>di</strong>vx).<br />

Suoni: ogni suono può essere scomposto, determinando le caratteristiche delle onde sonore che lo costituiscono.<br />

Questi caratteristiche sono rappresentate da numeri ... Esistono dei formati compressi anche per l’au<strong>di</strong>o (MP3, WMA).<br />

RAPPRESENTAZIONE DELLE ISTRUZIONI:<br />

Si adotta anche in questo caso una rappresentazione numerica. Ecco qui sotto il formato <strong>di</strong> una generica istruzione:<br />

co<strong>di</strong>ce operazione operando 1 (o suo in<strong>di</strong>rizzo) operando 2 (o suo in<strong>di</strong>rizzo) in<strong>di</strong>rizzo risultato<br />

Ogni possibile istruzione è in<strong>di</strong>cata da un co<strong>di</strong>ce numerico. Ad esempio la somma potrebbe essere in<strong>di</strong>cata con 001<br />

(ricor<strong>di</strong>amo che i numeri sono binari, ed usano solo lo 0 e l’1), la <strong>di</strong>fferenza con 002 ecc. Dopo aver specificato il tipo<br />

<strong>di</strong> istruzione è necessario in<strong>di</strong>care gli operan<strong>di</strong> (ad esempio i numeri da sommare). Qualche volta nell’operazione si<br />

mettono <strong>di</strong>rettamente i valori da usare, altre volte si specifica l’in<strong>di</strong>rizzo dove trovare in RAM i valori che servono.<br />

Così si potrebbe chiedere <strong>di</strong> sommare <strong>di</strong>rettamente 0010 (2 in binario) e 0100 (4 in binario) oppure intendere che<br />

0010 è l’in<strong>di</strong>rizzo in RAM in cui trovare il primo numero da sommare e che 0100 è l’in<strong>di</strong>rizzo dove trovare in RAM il<br />

secondo numero da sommare. A seconda del co<strong>di</strong>ce dell’istruzione la CPU sa come comportarsi. Se l’istruzione<br />

produce un risultato è anche possibile in<strong>di</strong>care a quale in<strong>di</strong>rizzo in RAM depositare il risultato. Ecco come potrebbe<br />

apparire un’istruzione completa:<br />

0001 0010 0100 1100<br />

Noterete che sono necessari 16 bit, cioè due byte, per rappresentarla. Infatti, anche se le celle della RAM contengono<br />

un byte, spesso sono considerati a gruppi. Il deco<strong>di</strong>ficatore riceve proprio la sequenza completa <strong>di</strong> bit, la scompone<br />

in co<strong>di</strong>ce operativo, operan<strong>di</strong> e in<strong>di</strong>rizzo risultato e comunica alla Control Unit le sequenze (microistruzioni) <strong>di</strong><br />

coman<strong>di</strong> da attivare. Alcune istruzioni, semplici, occupano un solo byte, altre molti. La CPU non fa in questo caso<br />

confusione a causa delle <strong>di</strong>verse lunghezze: il co<strong>di</strong>ce operativo (il primo ad essere letto nella RAM) chiarisce subito <strong>di</strong><br />

che tipo <strong>di</strong> istruzione si tratta e <strong>di</strong> quanti byte necessita, per ogni sua parte. E’ per questo che il program counter può<br />

essere automaticamente aumentato del giusto numero <strong>di</strong> byte per ‘puntare’ alla posizione in RAM in cui si trova la<br />

prossima istruzione.<br />

OSSERVAZIONE IMPORTANTE. Sembrerebbe che qualsiasi aspetto della realtà sia perfettamente rappresentabile e<br />

trattabile da un elaboratore ... ma non è esattamente così ! Dobbiamo convincerci che ciò che viene costruito nelle<br />

memorie <strong>di</strong> un elaboratore è solo un modello (una rappresentazione semplificata, parziale, mai perfetta) della realtà.<br />

Certo, quando scriviamo una lettera con Word è esattamente quello che si voleva fare: in questo caso il modello è<br />

praticamente perfetto. Ma in altri, per motivi pratici, sono stati imposti dei limiti. Pensiamo ai numeri interi. Più sono<br />

gran<strong>di</strong> e più bit sono necessari per rappresentarli, questo è intuitivo. Per motivi <strong>di</strong> efficienza e praticità si è deciso <strong>di</strong><br />

de<strong>di</strong>care sempre lo stesso numero <strong>di</strong> bit per rappresentare un intero: sono usati cioè gli stessi bit per un numero<br />

grande che per uno piccolo. E’ un po’ come scrivere 00128 che è ancora 128: stessa cosa in binario (i bit <strong>di</strong> più ed<br />

inutili sono messi a 0). Già, ma quanti bit ? Se ne vengono usati pochi allora non sarà possibile rappresentare numeri<br />

gran<strong>di</strong>. Se ne vengono usati tanti, tutte le volte che si rappresenta un numero piccolo ne vengono ‘sprecati’ un certo<br />

numero ... Qualunque sia la scelta, la conclusione è sempre la stessa: fissato il numero <strong>di</strong> bit da usare,<br />

automaticamente è fissato anche il più grande ed il più piccolo numero intero (positivo e negativo) che si può<br />

rappresentare. Stessa cosa per i numeri reali (quelli ‘con la virgola’): in questo caso, oltre ai limiti esiste anche un<br />

problema <strong>di</strong> precisione. Infatti un numero può anche essere piccolo ma con un numero infinito <strong>di</strong> cifre decimali


Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 15<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

(pensate al pi greco): se si fissa il numero <strong>di</strong> bit, più <strong>di</strong> tante cifre decimali non potranno essere rappresentate. Ecco<br />

allora che un numero come 3,145672943456 sarà invece memorizzato come 3,145673, arrotondando la sesta cifra.<br />

Ai fini pratici, per la stragrande maggioranza delle applicazioni, la questione è praticamente ininfluente, ma è giusto<br />

sapere che questi limiti esistono e che ciò che viene rappresentato all’interno <strong>di</strong> un <strong>sistema</strong> <strong>di</strong> <strong>elaborazione</strong> può<br />

essere soggetto ad approssimazioni. Attenzione: poiché ogni informazione è rappresentata in forma numerica,<br />

questo significa che ogni tipo <strong>di</strong> informazione è soggetto ad approssimazioni (scegliendo, ad esempio, <strong>di</strong> usare pochi<br />

bit per le intensità dei colori <strong>di</strong> un immagine, la gamma dei colori risultanti sarà povera e <strong>di</strong>scorde da quella reale).<br />

Evoluzione dei linguaggi <strong>di</strong> programmazione<br />

Ok, tutto questo <strong>di</strong>scorso per avere perfettamente chiaro questo concetto: nella sua forma più primitiva un<br />

programma è una sequenza <strong>di</strong> valori numerici memorizzati in altrettanti byte della RAM; il formato <strong>di</strong> memorizzazione<br />

<strong>di</strong> questi valori è quello binario. Un programma in questa forma è imme<strong>di</strong>atamente comprensibile al deco<strong>di</strong>ficatore e<br />

<strong>di</strong> linguaggio in cui è espresso è chiamato linguaggio macchina. Si parla anche <strong>di</strong> linguaggio basso livello<br />

perché è vicino alla macchina e lontano dal nostro linguaggio naturale.<br />

I primi programmatori non avevano<br />

molta scelta: non esisteva la tastiera,<br />

non esistevano i supporti <strong>di</strong><br />

memorizzazione <strong>di</strong> massa ... Ogni<br />

istruzione ed ogni dato veniva<br />

comunicato al computer usando le<br />

cosiddette schede perforate (ormai<br />

reperti archeologici !). Sono tessere <strong>di</strong><br />

carta, dalle <strong>di</strong>mensioni <strong>di</strong> una grossa<br />

banconota, su cui i numeri binari sono<br />

rappresentati bucando o no piccoli<br />

rettangoli <strong>di</strong>sposti in file verticali: un<br />

buco all’interno del rettangolino<br />

significa 1, niente buco 0. Una fila<br />

verticale <strong>di</strong> rettangolini corrisponde ad un byte: quin<strong>di</strong>, l’uno <strong>di</strong> fianco all’altro, sulla stessa scheda possono stare<br />

anche più byte (file <strong>di</strong> rettangolini). Il programmatore, usando un <strong>di</strong>spositivo chiamato perforatrice, preparava pacchi<br />

<strong>di</strong> schede, corrispondenti ai programmi che voleva far eseguire. Le schede perforate venivano poi lette da un lettore<br />

meccanico: questo scan<strong>di</strong>va ogni scheda rilevando i fori e comunicando 1 o 0 al computer, che a sua volta<br />

memorizzava i byte nella RAM.<br />

A parte la scomo<strong>di</strong>tà fisica <strong>di</strong> questo meccanismo (era facilissimo spiegazzare una scheda, con conseguente<br />

inceppamento del perforatore o del lettore, oppure invertire la posizione <strong>di</strong> due schede ecc.), la realizzazione e la<br />

manutenzione <strong>di</strong> un programma non era pane per i denti <strong>di</strong> tutti:<br />

• bisognava <strong>di</strong>ventare esperti (in materia <strong>di</strong> programmazione non sono ammesse mezze misure) <strong>di</strong> quel<br />

particolare linguaggio riconosciuto dalla CPU usata, fatto <strong>di</strong> intricate sequenze <strong>di</strong> zeri e <strong>di</strong> uno: provate ad<br />

immaginare la fatica <strong>di</strong> trovare un piccolo errore in una lista <strong>di</strong> centinaia <strong>di</strong> migliaia <strong>di</strong> co<strong>di</strong>ci binari ...<br />

• bisognava essere esperti <strong>di</strong> aritmetica binaria, visto che tutto è espresso in quella forma;<br />

• dato che le istruzioni sono così a basso livello, vicino all’hardware, era necessario conoscere alla perfezione<br />

anche quest’ultimo; se cambia l’hardware i programmi dovevano essere pesantemente mo<strong>di</strong>ficati; se cambiava<br />

il microprocessore (la CPU), e con esso l’insieme delle istruzioni accettate, l’intero programma andava riscritto;<br />

Per tutti questi motivi era <strong>di</strong>fficile trovare buoni programmatori e lo sviluppo del software era costoso e lento.<br />

Nonostante queste <strong>di</strong>fficoltà i primi abbozzi <strong>di</strong> sistemi operativi e le prime applicazioni sono stati scritti proprio così ...<br />

<strong>di</strong>rettamente linguaggio macchina!<br />

Si parla <strong>di</strong> linguaggio perché come ogni linguaggio parlato ha un suo vocabolario <strong>di</strong> ‘parole’ (l’insieme dei co<strong>di</strong>ci delle<br />

istruzioni che si possono usare) ed una sua sintassi, cioè le regole con cui si possono formare le ‘frasi’ (le istruzioni e<br />

le sequenze <strong>di</strong> istruzioni). Ogni frase ha poi il suo ‘significato’ (l’effetto che produce).<br />

Un primo miglioramento (oltre alla <strong>di</strong>ffusione <strong>di</strong> <strong>di</strong>spositivi come le tastiere ed i <strong>di</strong>schi) è stato introdotto con l’uso <strong>di</strong><br />

co<strong>di</strong>ci mnemonici al posto delle sequenze <strong>di</strong> 1 e <strong>di</strong> 0. Il concetto è assai intuitivo: è molto più semplice ricordare


falso<br />

Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 16<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

qualche cosa del tipo ‘SUM 15 21’ che non ‘100100101011’, per in<strong>di</strong>care che vanno sommati 15 e 21. Il<br />

programmatore può ora scrivere il programma usando un linguaggio un po’ più lontano da quello macchina ma più<br />

vicino al suo modo <strong>di</strong> esprimersi. Questo linguaggio è chiamato assembly. Naturalmente, prima <strong>di</strong> sottoporre il<br />

programma al computer è necessario un processo <strong>di</strong> traduzione: i co<strong>di</strong>ci mnemonici ed i valori espressi in decimale<br />

non sono comprensibile dalla CPU ! Un programma apposito chiamato assemblatore (assembler) , scritto<br />

ovviamente in linguaggio macchina, traduce ogni co<strong>di</strong>ce mnemonico e valore decimale nella corrispondente sequenza<br />

<strong>di</strong> 1 e <strong>di</strong> 0. I linguaggi assembly devono essere considerati, come linguaggio macchina, <strong>di</strong> basso livello.<br />

Qui a lato, un esempio <strong>di</strong> programma<br />

scritto in un linguaggio assembly.<br />

Esso calcola la somma <strong>di</strong> una<br />

sequenza <strong>di</strong> numeri letti (istruzione<br />

IN 1) dalla periferica 1 (che potrebbe<br />

essere la tastiera) fino a che non<br />

viene specificato lo zero come<br />

numero da sommare. Quando viene<br />

inserito lo zero si termina il ciclo <strong>di</strong><br />

lettura e si invia il risultato (istruzione<br />

OUT 2) alla periferica 2 (che potrebbe<br />

essere il video).<br />

NOTA: non sforzatevi <strong>di</strong> comprendere<br />

i dettagli (è ancora troppo presto!)<br />

Beh, certamente meglio <strong>di</strong> una sfilza <strong>di</strong> bit, ma ancora piuttosto <strong>di</strong>fficile da leggere! Inoltre questo stesso programma<br />

è inservibile con un'altra CPU... Non solo: un programmatore abituato a programmare per un'altra CPU (e quin<strong>di</strong> con<br />

un altro linguaggio assembly) potrebbe non capirci nulla!<br />

inizio<br />

totale


Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 17<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

I linguaggi assembly sono, infatti, ancora troppo scomo<strong>di</strong> e soffrono, in fondo, ancora <strong>di</strong> tutti i problemi elencati per<br />

il linguaggio macchina: hanno reso solo un poco più lieve la vita al programmatore, ma sono ancora molto legati alla<br />

CPU usata. Il passaggio successivo è stato lo sviluppo <strong>di</strong> linguaggi ancora più lontani dal linguaggio macchina e, <strong>di</strong><br />

conseguenza, più vicini al nostro modo <strong>di</strong> esprimerci. Sono nati i linguaggi ad alto livello. Questi sono<br />

caratterizzati da istruzioni molto potenti, con descrizioni facili da ricordare, che corrispondono anche a centinaia <strong>di</strong><br />

semplici istruzioni in linguaggio macchina o assembly.<br />

Program somma;<br />

var totale, dato: integer;<br />

begin<br />

totale :=0;<br />

repeat<br />

read(dato);<br />

totale := totale + dato<br />

until dato=0;<br />

write(totale).<br />

end.<br />

Ed ecco qui a lato la versione dello stesso algoritmo co<strong>di</strong>ficata con il<br />

linguaggio <strong>di</strong> programmazione Pascal (per l'esattezza il suo ‘ <strong>di</strong>aletto’ Turbo<br />

Pascal).<br />

È molto leggibile, compatto. Dopo un'intestazione che assegna un nome al<br />

programma, il programmatore <strong>di</strong>chiara i simboli che vorrà utilizzare<br />

in<strong>di</strong>cando anche che sono dei numeri interi (integer).<br />

Il ‘begin’ in<strong>di</strong>ca appunto l'inizio del programma. Come prima istruzione si<br />

azzera il totale e poi si ripetono (repeat) due istruzioni fino a che (until)<br />

non viene introdotto come dato il valore zero: la lettura (read) del dato e la<br />

sua aggiunta al totale. Terminato il ciclo si provvede alla scrittura del totale<br />

calcolato.<br />

Siamo chiaramente ad un livello più astratto: il programmatore non deve conoscere i meccanismi hardware della<br />

stampante o della CPU: ad esempio, deve solo ricordare che il comando <strong>di</strong> stampa è ‘write’ e che deve specificare tra<br />

parentesi ciò che vuole sia stampato. Cambia la stampante ? Il comando rimane sempre questo ! Cambia la CPU ? Il<br />

comando rimane sempre questo ... Cambia ad<strong>di</strong>rittura il tipo <strong>di</strong> computer? Non devo mo<strong>di</strong>ficare o riscrivere tutti i<br />

programmi: il Pascal rimane Pascal...<br />

Ricor<strong>di</strong>amo qualche nome <strong>di</strong> linguaggio ad alto livello tra i più famosi: Fortran (<strong>di</strong>ffusissimo in ambito scientifico e<br />

storicamente il primo linguaggio ad alto livello), il ‘C’ (<strong>di</strong>ffusissimo in molti ambiti; la sua capacità <strong>di</strong> interagire ancora<br />

facilmente con l’hardware della macchina gli vale la definizione in effetti <strong>di</strong> linguaggio <strong>di</strong> me<strong>di</strong>o livello; molti sistemi<br />

operativi, anche per l’efficienza dei programmi che i compilatori ‘C’ sono in grado <strong>di</strong> produrre, sono per questo motivo<br />

in gran parte sviluppati con questo linguaggio), Cobol (<strong>di</strong>ffusissimo in ambito gestionale e commerciale), il mitico<br />

Basic (il linguaggio che ha aperto, per la sua semplicità, la porta della programmazione a milioni <strong>di</strong> utenti anche non<br />

esperti <strong>di</strong> informatica), il Pascal (usatissimo negli ambienti scolastici), il Lisp ed il Prolog (usati per sviluppare<br />

programmi <strong>di</strong> intelligenza artificiale), il C++ (il successore del C). Conclu<strong>di</strong>amo con un linguaggio che sta<br />

rapi<strong>di</strong>ssimamente conquistandosi i favori dei programmatori perché molto adatto a sviluppare programmi funzionanti<br />

su Internet: Java (che molto ha in comune con il C++).<br />

Ad ognuno il suo. Ma se questa è solo una scelta dei principali linguaggi, quanti sono in tutto ? Il numero preciso non<br />

è noto, ma è certo che superi qualche centinaio ... Come mai ? Per lo stesso motivo per cui esistono in fondo tanti<br />

linguaggi anche all’interno del nostro: esiste un linguaggio matematico (osereste negarlo, pensando all’algebra, alle<br />

formule matematiche ecc. ?), un linguaggio filosofico, uno giuri<strong>di</strong>co ecc. A seconda dell’ambito applicativo può<br />

convenire usare un linguaggio specializzato. Il Fortran deve il suo nome al termine ‘Formula Translator’, cioè<br />

traduttore <strong>di</strong> formule: il suo <strong>di</strong>zionario è ricco <strong>di</strong> coman<strong>di</strong> per calcoli matematici complessi. Il Cobol l’opposto: è ricco<br />

però <strong>di</strong> coman<strong>di</strong> per gestire efficientemente archivi sui <strong>di</strong>schi, che è proprio ciò che occorre per sviluppare programmi<br />

<strong>di</strong> gestione aziendale. Attenzione: non è che con un linguaggio si possano fare certe cose ma non altre, solo ... più<br />

semplicemente. Provate a spiegare le equazioni senza usare termini matematici ... E’ possibile, ma che fatica ... Per<br />

lo stesso motivo sarebbe anche possibile scrivere in Cobol un programma per il calcolo dell’orbita <strong>di</strong> un satellite, ma<br />

che fatica ... Certo, alcuni linguaggi sono più specializzati <strong>di</strong> altri. Alcuni sono invece ad uso generale: il Basic ed il ‘C’<br />

od il C++ non eccellono in nessun campo in particolare ma sono più che adeguati per ogni compito. E’ il mercato che<br />

decide la fortuna <strong>di</strong> un linguaggio.<br />

Ovviamente anche per i linguaggi a me<strong>di</strong>o/alto livello è necessaria una traduzione in linguaggio macchina prima <strong>di</strong><br />

poterli mandare in esecuzione!<br />

NOTA: tutti i linguaggi <strong>di</strong>versi dal co<strong>di</strong>ce macchina sono chiamati simbolici perché hanno sostituito dei simboli<br />

0.mnemonici alle sequenze <strong>di</strong> 1 e


Esistono due tipi <strong>di</strong> traduttori per linguaggi a me<strong>di</strong>o/alto livello:<br />

Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 18<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

1. Interpreti. Il programmatore scrive le istruzioni usando un programma <strong>di</strong> video scrittura (e<strong>di</strong>tor). Quando si<br />

comanda la partenza del programma, l’interprete legge le istruzioni una alla volta, non opera una vera traduzione ma<br />

si limita a riconoscere ciò che le istruzioni vogliono ed a compiere l’azione corrispondente; otteniamo quin<strong>di</strong> subito,<br />

senza attese apprezzabili, il risultato dell'esecuzione <strong>di</strong> quell'istruzione ma non viene prodotto in modo permanente<br />

nessun co<strong>di</strong>ce binario: se questa istruzione deve essere eseguita <strong>di</strong> nuovo, anche per 1000 volte, deve essere<br />

reinterpretata.<br />

2 Compilatori: questi sono dei veri traduttori. Come prima, il programmatore scrive le istruzioni usando un<br />

programma <strong>di</strong> video scrittura(e<strong>di</strong>tor). Ma, ogni volta che mo<strong>di</strong>fica anche un solo carattere del testo del programma<br />

e ne richiede l'esecuzione, il programmatore deve comandare ad uno specifico programma (compilatore) la<br />

traduzione in linguaggio macchina <strong>di</strong> tutte le istruzioni che compongono il programma. Come approfon<strong>di</strong>remo meglio<br />

in seguito, il risultato della compilazione prima <strong>di</strong> poter essere mandato in esecuzione deve essere elaborato anche<br />

da un altro programma (linker) che, per così <strong>di</strong>re, completa l'opera <strong>di</strong> traduzione iniziata dal compilatore.<br />

Interpreti e compilatori a confronto<br />

Interpreti Compilatori<br />

Minori tempi <strong>di</strong> attesa durante lo sviluppo: per vedere il<br />

risultato in esecuzione <strong>di</strong> una mo<strong>di</strong>fica ad un'istruzione o<br />

della giunta <strong>di</strong> una nuova istruzione, si deve attendere<br />

solo l'interpretazione delle istruzioni che portano a quella<br />

che interessa; tutto il resto del programma è come se<br />

venisse ignorato.<br />

Maggiori tempi totali nel fornire il risultato: l'esecuzione<br />

è rallentata dal continuo bisogno <strong>di</strong> interpretare le righe<br />

del programma, anche se sono già state interpretate<br />

molte volte. In certe situazioni il tempo <strong>di</strong> esecuzione <strong>di</strong><br />

un programma interpretato può essere anche 100 volte<br />

superiore a quello del corrispondente programma<br />

compilato …<br />

Dipendenza del programma dall’interprete: l’interprete<br />

deve sempre essere già pronto e <strong>di</strong>sponibile in memoria.<br />

Relativamente semplici da scrivere.<br />

Possibili lunghi tempi <strong>di</strong> attesa durante lo sviluppo:<br />

prima <strong>di</strong> potere e stare un'istruzione qualsiasi <strong>di</strong> un<br />

programma, è necessario che tutto il programma venga<br />

tradotto in linguaggio macchina; se le istruzioni sono<br />

poche (qualche centinaio) le potenze elaborative e la<br />

velocità degli o<strong>di</strong>erni hard <strong>di</strong>sk (che a volte non vengono<br />

ad<strong>di</strong>rittura utilizzati perché sono sufficienti le ormai assai<br />

capienti e molto più veloci RAM) la velocità della<br />

compilazione è tale da sembrare che si stia utilizzando<br />

un interprete! Se il progetto consta invece <strong>di</strong> migliaia,<br />

centinaia <strong>di</strong> migliaia, <strong>di</strong> milioni <strong>di</strong> istruzioni (30 milioni<br />

per Windows 98!) prima <strong>di</strong> veder partire il programma in<br />

seguito ad una mo<strong>di</strong>fica potrebbero rendersi necessari<br />

tempi <strong>di</strong> attesa anche <strong>di</strong> parecchi minuti...<br />

Esecuzione veloce: tempo per la compilazione a parte, il<br />

co<strong>di</strong>ce binario prodotto dalla compilazione+link può<br />

essere eseguito a grande velocità senza bisogno <strong>di</strong><br />

ulteriori traduzioni.<br />

In<strong>di</strong>pendenza del programma dal compilatore: una volta<br />

tradotto in binario il programma, il compilatore non<br />

serve più.<br />

Più complessi da realizzare rispetto agli interpreti.<br />

Volendo sintetizzare vantaggi e svantaggi, <strong>di</strong>ciamo che con un interprete risulta velocizzata la fase <strong>di</strong> sviluppo: non ci<br />

sono i tempi <strong>di</strong> compilazione e <strong>di</strong> link che costringono a ritradurre l’intero programma anche se si sbaglia,<br />

letteralmente, una virgola. Un compilatore produce un programma autonomo che garantisce il massimo della velocità<br />

in esecuzione (anche decine <strong>di</strong> volte più veloce): mentre un interprete è perfetto per lo sviluppo del programma, il<br />

compilatore serve a generare prodotto finale. Per la verità, queste considerazioni erano molto più vere qualche anno<br />

fa: i computer erano molto più lenti nel far funzionare i compilatori (CPU meno potenti e <strong>di</strong>schi molto più lenti): era<br />

allora molto conveniente usare un interprete nelle fasi iniziali (in cui si commettono molti errori) ed il compilatore per<br />

i ritocchi finali. Oggi i tempi <strong>di</strong> compilazione e link sono rapi<strong>di</strong>ssimi e l’interprete come strumento è molto meno<br />

appetibile.<br />

Sviluppare software, oggi sempre più complicato, è molto costoso. Essere produttivi è in<strong>di</strong>spensabile per avere<br />

successo come sviluppatori <strong>di</strong> software. Gli o<strong>di</strong>erni compilatori e linker sono integrati in ambienti <strong>di</strong> sviluppo<br />

sofisticatissimi, dove alcune parti standard <strong>di</strong> un programma sono scritte automaticamente ! Ad esempio, mentre un<br />

tempo, per gestire una finestra come quelle <strong>di</strong> Windows, era necessario scrivere decine <strong>di</strong> istruzioni, oggi il<br />

programmatore <strong>di</strong>segna la finestra con il mouse (come fareste in Paintbrush per un rettangolo) e le istruzioni<br />

corrispondenti sono generate in automatico. Lo stesso per molte finestre <strong>di</strong> <strong>di</strong>alogo, i bottoni, i menu, le caselle con


Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 19<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

gli elenchi, le schede <strong>di</strong> inserimento dati, i pannelli <strong>di</strong> ricerca <strong>di</strong> un file per l’apertura o la registrazione <strong>di</strong> un archivio<br />

ecc. E’ sempre <strong>di</strong>sponibile anche un potente strumento per la ricerca degli errori (debugger). Questi sono solo alcuni<br />

dei moltissimi bonus che un moderno ambiente per lo sviluppo dei programmi è in grado <strong>di</strong> offire ...<br />

La catena della programmazione<br />

Il programmatore inizia con lo scrivere le istruzioni (nel linguaggio <strong>di</strong> programmazione scelto) con un programma<br />

chiamato e<strong>di</strong>tor. Questo termine inglese significa redattore cioè colui che cura la scrittura <strong>di</strong> un testo, ed ha quin<strong>di</strong><br />

un significato abbastanza generico. Molto semplicemente, pensate all'e<strong>di</strong>tor come ad un programma <strong>di</strong> videoscrittura<br />

specializzato per la stesura delle istruzioni. Il file scritto con l'e<strong>di</strong>tor prende il nome <strong>di</strong> co<strong>di</strong>ce sorgente. (source<br />

code). I primi e<strong>di</strong>tor erano molto semplici e permettevano solo <strong>di</strong> scrivere il co<strong>di</strong>ce (il testo corrispondente alle<br />

istruzioni) e <strong>di</strong> salvarlo su <strong>di</strong>sco: spettava poi al programmatore mandare in esecuzione, manualmente, il compilatore<br />

ed il linker.<br />

Oggi gli e<strong>di</strong>tor sono <strong>di</strong>ventati ‘ intelligenti’:<br />

• colorano, o evidenziano automaticamente in altro modo (usando il grassetto, il corsivo, la sottolineatura ecc) le<br />

<strong>di</strong>verse parti <strong>di</strong> un'istruzione per facilitare la lettura (si parla <strong>di</strong> sintax highlightining, cioè evidenziazione della<br />

sintassi; e così anche decisamente più <strong>di</strong>fficile commettere certi errori (tipo <strong>di</strong>menticarsi <strong>di</strong> chiudere delle<br />

parentesi aperte);<br />

• sono in grado <strong>di</strong> completare automaticamente la scrittura delle istruzioni (code completion): il programmatore<br />

inizia a scrivere un comando e l'e<strong>di</strong>tor può proporre un elenco <strong>di</strong> tutti i coman<strong>di</strong> che iniziano con le stesse<br />

lettere; a questo punto programmatore può inserire rapidamente il comando che voleva scrivere senza <strong>di</strong>gitare<br />

il resto dei caratteri; può anche usare delle abbreviazioni <strong>di</strong> pochissime lettere per far inserire un'istruzione<br />

composta anche da molte righe che poi verrà completata;<br />

un'altra caratteristica utilissima è rappresentata dai cosiddetti tooltip (suggerimenti): mentre si sta completando<br />

la scrittura <strong>di</strong> una istruzione complessa sopra il punto <strong>di</strong> inserimento dei caratteri appare un piccolo riquadro<br />

che contiene informazioni sull'istruzione stessa; questo meccanismo consente <strong>di</strong> risparmiare tantissimo tempo<br />

che una volta venire impiegato per andare a consultare un manuale;<br />

• sono integrati con un <strong>sistema</strong> <strong>di</strong> aiuto (help in linea) ipertestuale (cioè navigabile facendo clic sui collegamenti<br />

che portano ad approfon<strong>di</strong>menti, argomenti correlati, esempi); a volte l'help è ad<strong>di</strong>rittura iperme<strong>di</strong>ale (si<br />

possono richiamare filmati, animazioni, commenti vocali ecc.).<br />

• permettono, ad<strong>di</strong>rittura, <strong>di</strong> ‘<strong>di</strong>segnare’ parte del programma invece <strong>di</strong> scriverlo: usando il mouse il<br />

programmatore <strong>di</strong>spone componenti preconfezionati su quella che <strong>di</strong>venterà la finestra del programma in<br />

esecuzione: caselle in cui inserire del testo, bottoni su cui fare clic, menù, tavolozze che appariranno quando si<br />

vorrà far scegliere un colore all'utente, finestre <strong>di</strong> <strong>di</strong>alogo che permetteranno <strong>di</strong> cercare un file sul <strong>di</strong>sco quando<br />

l'utente comanderà il caricamento <strong>di</strong> un documento o il suo salvataggio eccetera; per ogni componente scelto<br />

l'e<strong>di</strong>tor inserirà automaticamente il co<strong>di</strong>ce corrispondente! Quando un e<strong>di</strong>tor è in grado <strong>di</strong> funzionare in questo<br />

modo è detto visuale.<br />

• sono in grado <strong>di</strong> richiamare automaticamente il compilatore ed il linker e se uno <strong>di</strong> questi due programmi trova<br />

degli errori mostrare a video i relativi messaggi spostando il punto <strong>di</strong> inserimento del testo sulla riga del primo<br />

errore; se invece il programma non contiene errori può anche essere mandato automaticamente in esecuzione<br />

per provarlo: al termine dell'esecuzione si verrà riportati nell'ambiente <strong>di</strong> lavoro dell'e<strong>di</strong>tto. In questo modo il<br />

programmatore è in grado <strong>di</strong> controllare tutti i passaggi senza mai abbandonare le e<strong>di</strong>tor. È per queste<br />

caratteristiche che oggi, più che <strong>di</strong> semplici e<strong>di</strong>tor, si preferisce parlare <strong>di</strong> IDE, Integrated Development<br />

Envinronment (ambienti integrati <strong>di</strong> sviluppo).<br />

I file dei co<strong>di</strong>ci sorgenti vengono registrati sul <strong>di</strong>sco con un'estensione (la parte del nome del file dopo il punto) che<br />

aiuta a riconoscere il linguaggio <strong>di</strong> programmazione utilizzato. Ecco le principali estensioni: .pas (Pascal), .c (C), .cpp<br />

(c ++), .asm (assembly), .asp (ASP), .bas (Basic).


Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 20<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

Il compilatore trasforma il co<strong>di</strong>ce sorgente nel cosiddetto co<strong>di</strong>ce oggetto (object code); ma questo processo <strong>di</strong><br />

traduzione potrebbe interrompersi per vari motivi:<br />

• viene scoperto un simbolo che non appartiene al cosiddetto <strong>di</strong>zionario delle parole chiave (keyword) proprie<br />

del linguaggio e neppure all’insieme dei simboli aggiunti dal programmatore: ad esempio se il linguaggio<br />

prevede un comando write per scrivere qualche cosa sullo schermo, il programmatore potrebbe sbagliare e<br />

scrivere wrote; questa parola viene non verrebbe riconosciuta come appartenente al linguaggio; viene allora<br />

cercata nell’insieme degli simboli (si parla più correttamente <strong>di</strong> identificatori) introdotti dal programmatore;<br />

quest’ultimo potrebbe aver <strong>di</strong>chiarato <strong>di</strong> voler usare un simbolo chiamato area per memorizzare un valore con<br />

la virgola in cui far calcolare la superficie del cerchio:<br />

area := raggio * raggio * 3.14; dai al simbolo area il valore della formula in<strong>di</strong>cata (* significa moltiplicato)<br />

naturalmente raggio è un altro simbolo introdotto dal programmatore per memorizzare il dato corrispondente<br />

alla misura del raggio …<br />

se un identificatore non viene trovato neppure nell’insieme definito dal programmatore, allora il compilatore<br />

decide che ha trovato un errore lessicale; questa fase viene infatti chiamata analisi lessicale e quella parte<br />

del compilatore che la svolge analizzatore lessicale.<br />

• Errori sintattici ed analizzatore sintattico. Anche se viene superata la fase <strong>di</strong> analisi lessicale ci<br />

potrebbero essere ancora errori <strong>di</strong> tipo sintattico, cioè che violano una regola sintattica <strong>di</strong> quel linguaggio; le<br />

regole sintattiche servono a costruire frasi corrette, al <strong>di</strong> là del fatto <strong>di</strong> aver usato singoli termini riconosciuti.<br />

Nulla <strong>di</strong> sorprendente: anche con la lingua italiana dovete rispettare delle regole (soggetto, verbo,<br />

complemento oggetto ecc. ricordate?), solo che quelle <strong>di</strong> un linguaggio <strong>di</strong> programmazione sono decisamente<br />

più semplici! Ed anche in italiano, pur usando tutte parole presenti sul <strong>di</strong>zionario, è facilissimo scrivere frasi<br />

senza senso!<br />

Facciamo un esempio: molti linguaggi hanno la regola che pretende che ogni istruzione sia terminata con un<br />

punto e virgola:<br />

write(‘Questa istruzione contiene un errore!’)<br />

Mentre dal punto <strong>di</strong> vista dell’analizzatore lessicale non ci sono errori, quello sintattico si accorge della<br />

mancanza della virgola alla fine dell’istruzione.<br />

Controllo lessicale e sintattico non esauriscono il compito <strong>di</strong> un compilatore: esiste anche un controllo chiamato<br />

semantico che non è però il caso <strong>di</strong> approfon<strong>di</strong>re in questa sede.<br />

Nota: i co<strong>di</strong>ce oggetto hanno <strong>di</strong> solito come estensione .obj<br />

Il co<strong>di</strong>ce oggetto, risultato della compilazione, non è ancora in una forma del tutto eseguibile dalla CPU: alcune parti<br />

del co<strong>di</strong>ce sorgente non possono essere tradotte dal compilatore perché rappresentano dei riferimenti ( link) a dei<br />

coman<strong>di</strong> esterni che non fanno parte <strong>di</strong>rettamente del linguaggio <strong>di</strong> programmazione in uso ma sono resi <strong>di</strong>sponibili<br />

in raccolte chiamate librerie. (library).<br />

Queste ultime sono un preziosissimo strumento per la programmazione: una volta che un problema è stato risolto il<br />

co<strong>di</strong>ce corrispondente (già tradotto in linguaggio macchina) può essere depositato in una raccolta (la libreria) dalla<br />

quale potrà essere estratto per essere incorporato in altri programmi che ne hanno bisogno.<br />

Ad esempio: immaginiamo <strong>di</strong> avere scritto un comando che <strong>di</strong>segna sullo schermo una circonferenza note le sue<br />

coor<strong>di</strong>nate sullo schermo del centro e la misura del raggio; il co<strong>di</strong>ce sorgente viene compilato e tradotto in co<strong>di</strong>ce<br />

oggetto (da notare che anche in questo caso ci potrebbero essere della parti ancora non tradotte…) ed aggiunto con<br />

un programma apposito ad una libreria (immaginiamo che si chiami grafica.lib, dove .lib è naturalmente l’estensione<br />

con cui sono <strong>di</strong> solito in<strong>di</strong>viduate su <strong>di</strong>sco le librerie). Deci<strong>di</strong>amo <strong>di</strong> chiamare crf (abbreviazione <strong>di</strong> circonferenza) il<br />

comando in questione; le coor<strong>di</strong>nate del centro e la misura del raggio della circonferenza dovranno essere in<strong>di</strong>cate<br />

(come in quasi tutti i linguaggi <strong>di</strong> programmazione) tra parentesi tonde al momento dell’uso del comando stesso,


Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 21<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

Immaginiamo ora <strong>di</strong> scrivere un programma <strong>di</strong> geometria e <strong>di</strong> avere la necessità <strong>di</strong> <strong>di</strong>segnare alcune circonferenze.<br />

Ecco come richiameremmo il comando:<br />

programma geometria;<br />

USES grafica;<br />

…<br />

crf( 12 , 61 , 30);<br />

…<br />

crf( 7, 49 , 22);<br />

Il programmatore utilizza in questo esempio due volte il comando per <strong>di</strong>segnare<br />

una circonferenza; il centro della prima ha come coor<strong>di</strong>nate 12, 61 ed un raggio<br />

pari a 30; similmente per la seconda.<br />

E’ molto importante che il programmatore specifichi dove è possibile trovare il<br />

co<strong>di</strong>ce oggetto che corrisponde al comando crf! Questa informazione viene<br />

fornita con ‘USES grafica’.<br />

Il compilatore non è in grado <strong>di</strong> tradurre il comando crf (non appartiene a quelli standard del pascal) ed in mancanza<br />

<strong>di</strong> altre in<strong>di</strong>cazioni dovrebbe interrompersi ed emettere un messaggio <strong>di</strong> errore (tipo ‘simbolo non trovato’ in<br />

corrispondenza delle righe in cui si richiama il comando crf).<br />

Trovando però l’in<strong>di</strong>cazione <strong>di</strong> utilizzo della libreria grafica, lascia nel co<strong>di</strong>ce oggetto un cosiddetto collegamento (link)<br />

non risolto, cioè una specie <strong>di</strong> segnalibro per ricordare che in quel punto c’è da completare la traduzione.<br />

E’ compito del programma linker analizzare tutti i collegamenti irrisolti dei programmi oggetto che gli vengono<br />

sottoposti al fine <strong>di</strong> produrre un unico eseguibile (estensione .exe) e cercare nelle librerie in<strong>di</strong>cate il co<strong>di</strong>ce mancante;<br />

quest'ultimo verrà estratto in copia ed incorporato nei punti richiesti. Ecco uno schema che esemplifica il<br />

meccanismo:<br />

Co<strong>di</strong>ce Sorgente<br />

(Geometria.pas)<br />

Program geometria;<br />

USES grafica;<br />

…<br />

crf( 12 , 61 , 30);<br />

…<br />

crf( 7, 49 , 22);<br />

Geometria.obj<br />

compilatore Co<strong>di</strong>ce<br />

oggetto<br />

Il co<strong>di</strong>ce della crf<br />

viene estratto dalla<br />

libreria ed<br />

incorporato nel<br />

programma finale.<br />

La libreria <strong>di</strong><br />

solito contiene il<br />

co<strong>di</strong>ce <strong>di</strong> molti<br />

coman<strong>di</strong><br />

crf<br />

quadrato<br />

linker<br />

Co<strong>di</strong>ce binario<br />

+ del comando crf =<br />

Programma eseguibile finale<br />

traduzione in linguaggio macchina<br />

delle istruzioni scritte dal<br />

programmatore.<br />

Abbiamo ora tutte le nozioni necessarie per esaminare in dettaglio il proce<strong>di</strong>mento <strong>di</strong> scrittura <strong>di</strong> un programma.<br />

….<br />

….<br />

….<br />

….<br />

….<br />

Ecc.<br />

LIBRERIA GRAFICA.LIB<br />

Geometria.exe<br />

+<br />

co<strong>di</strong>ce della crf prelevato dalla<br />

libreria


Struttura <strong>di</strong> un programma Pascal<br />

Ogni programma Pascal è costituito da tre sezioni:<br />

Intestazione<br />

Program ;<br />

Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 22<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

E’ una riga che inizia con la parola riservata (keyword) program seguita dal nome che si vuol dare al programma.<br />

Le parole riservate sono alcune decine ed il programmatore non può usare identificatori con lo stesso nome <strong>di</strong> una <strong>di</strong><br />

esse. Ad esempio, non possiamo chiamare un programma proprio program. Il nome da dare al programma è invece<br />

un esempio <strong>di</strong> identificatore, un nome scelto dal programmatore.<br />

Ci sono alcune regole da rispettare quando scegliamo un identificatore:<br />

• il suo primo carattere non può essere una cifra numerica (il compilatore penserebbe che in quel punto sta<br />

iniziando un numero); ad esempio 1arrivato non va bene; arrivato1 invece sì;<br />

• non può contenere spazi; totale iva non va bene; totaleIva o TotaleIva o totale_iva invece sì; notate in<br />

particolare l'uso del carattere <strong>di</strong> sottolineatura _ che consente <strong>di</strong> ‘staccare’ le parole come se avessimo inserito<br />

uno spazio;<br />

• non può contenere caratteri strani: ad esempio !#?&%$” eccetera; come regola pratica potreste ricordare che<br />

possono essere usate solo lettere (maiuscole e minuscole), cifre numeriche ed il carattere <strong>di</strong> sottolineatura;<br />

alcuni linguaggi fanno <strong>di</strong>fferenza tra minuscole e maiuscole (e sono detti case sensitive) per cui gli identificatori<br />

totale e Totale sono considerati <strong>di</strong>versi; altri linguaggi, tra cui Pascal, non <strong>di</strong>stinguono invece minuscole<br />

maiuscole (e sono detti case insensitive);<br />

• hanno una lunghezza massima, <strong>di</strong> solito piuttosto ampia (quasi sempre almeno 32 caratteri); attenzione<br />

perché è superata la lunghezza massima non è detto che il compilatore segnali errore ma potrebbe<br />

semplicemente considerare uguali due identificatori che iniziano con la stessa sequenza <strong>di</strong> caratteri pari a<br />

lunghezza massima; se questa fosse otto, ad esempio, i due identificatori costoTotale e costoTot potrebbero<br />

essere considerati lo stesso identificatore, causando situazioni d'errore <strong>di</strong>fficili da scoprire;<br />

Dopo il nome del programma deve essere messo un punto e virgola. Il punto e virgola è molto usato in Pascal (ed in<br />

genere da molti linguaggi <strong>di</strong> programmazione): serve a separare due istruzioni; questo significa che se dopo<br />

un'istruzione dev'essere scritta una parola riservata che <strong>di</strong> per sé non viene considerata un’ istruzione il punto e<br />

virgola può anche essere omesso. In ogni caso, almeno all'inizio, potreste ricordare come regola semplificata quella<br />

<strong>di</strong> mettere un punto e virgola alla fine <strong>di</strong> un'istruzione. Ecco allora qualche esempio <strong>di</strong> intestazione corretta:<br />

program prova; program primoProgramma; program primo_programma;<br />

program capitolo1;


Sezione <strong>di</strong>chiarativa<br />

Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 23<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

Qui il programmatore assegna un nome (identificatore) ai dati che devono essere memorizzati durante<br />

l'<strong>elaborazione</strong>.<br />

Costanti<br />

Se un identificatore viene descritto nella sezione introdotta dalla parola riservata const (abbreviazione <strong>di</strong> constant,<br />

costante) deve anche essere in<strong>di</strong>cato dopo il simbolo dell'uguaglianza (=) il suo valore che non potrà mai variare<br />

durante l'esecuzione del programma. Ad esempio il valore del pi greco può essere introdotto in un programma in<br />

questo modo:<br />

Program geometria;<br />

const<br />

PI_GRECO = 3.14159265;<br />

In un programma possono essere introdotte tutte le costanti che si vogliono. Il tipo del dato (un numero reale, real in<br />

Pascal) per una costante viene dedotto dal valore posto dopo il simbolo dell'uguaglianza. Ecco alcuni altri esempi <strong>di</strong><br />

costanti:<br />

GIORNI_SETTIMANA = 7; esempio <strong>di</strong> costante <strong>di</strong> tipo intero (integer in Pascal)<br />

RISPOSTA_AFFERMATIVA = ‘S’; esempio <strong>di</strong> costante <strong>di</strong> tipo carattere; gli apici sono obbligatori!<br />

MESSAGGIO_ERRORE = ‘Sbagliato !!’ ; esempio <strong>di</strong> costante <strong>di</strong> tipo stringa (sequenza <strong>di</strong> caratteri racchiusi tra apici,<br />

string in Pascal)<br />

Vantaggi dell'uso delle costanti:<br />

• aumentano la leggibilità <strong>di</strong> un programma: è più facile riconoscere nomi simbolici che numeri complessi;<br />

• aumentano la flessibilità <strong>di</strong> un programma: se vi è la necessità <strong>di</strong> cambiare il valore associato ad una costante<br />

sarà sufficiente farlo solamente nella sezione const ed automaticamente il nuovo valore verrà utilizzato in ogni<br />

punto del programma in cui è stata usata la costante:<br />

• migliorano la sicurezza: il programmatore non potrà neppure per sbaglio cambiare il valore della costante<br />

perché il compilatore glielo impe<strong>di</strong>rà emettendo un messaggio <strong>di</strong> errore<br />

NOTA: la sezione delle costanti può anche non essere presente.<br />

Variabili<br />

Quando invece il valore <strong>di</strong> un dato può cambiare durante l'esecuzione deve essere memorizzato in una cosiddetta<br />

variabile. Le variabili devono essere <strong>di</strong>chiarate in una sezione, in<strong>di</strong>viduata dalla parola chiave var, posta dopo la<br />

sezione const:<br />

Program geometria;<br />

const<br />

PI_GRECO = 3.14159265;<br />

var<br />

raggio : integer;<br />

Nel resto del programma invece <strong>di</strong> essere costretti a ricordare questa scomoda<br />

sequenza <strong>di</strong> cifre decimali sarà possibile utilizzare la costante PI_GRECO.<br />

Nota: è abitu<strong>di</strong>ne dei programmatori usare le maiuscole per gli identificatori che<br />

rappresentano una costante.<br />

In Pascal le variabili non possono ricevere un valore <strong>di</strong> partenza <strong>di</strong>rettamente nella sezione <strong>di</strong>chiarativa ma dovrà<br />

essere usato un apposito comando (assegnamento) nella sezione esecutiva. Come certamente immaginate questo<br />

stesso comando non può essere invece utilizzato con le costanti. Dopo l’identificatore del nome <strong>di</strong> una variabile deve<br />

essere messo il simbolo ‘:’ (due punti) e poi l'identificatore <strong>di</strong> un tipo, ed infine il punto e virgola.


Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 24<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

Più variabili dello stesso tipo possono essere <strong>di</strong>chiarate insieme separando gli identificatori con una virgola:<br />

var<br />

base, altezza: integer;<br />

Il tipo in<strong>di</strong>vidua l’insieme dei possibili valori che possono essere assunti da una costante o da una variabile (si parla<br />

anche <strong>di</strong> dominio). Ogni tipo ha infatti i suoi limiti . Ecco la tabella che riassume la situazione per i principali tipi del<br />

Pascal:<br />

INTERI<br />

Tipo da a<br />

Byte 0 255<br />

Shortint -128 127<br />

Integer -32768 32767<br />

Word 0 65535<br />

Longint -2147483648 2147483647<br />

REALI<br />

Tipo da a<br />

Real 2.9x10 -38 1.7x10 38<br />

Single 1.5x10 -45 3.4x10 38<br />

Double 5.0x10 -324 1.7x10 308<br />

extended 1.9x10 -4951 1.1x10 4932<br />

Esempi <strong>di</strong> numeri reali: 3.14 4.761E-3;<br />

Il secondo numero è espresso in forma esponenziale: la lettera E seguita da un numero<br />

equivale a moltiplicare (o <strong>di</strong>videre se il numero dopo la E è negativo) per quella potenza del <strong>di</strong>eci.<br />

Quin<strong>di</strong> 4.761E-3 = 4.76 x 10 -3 = 4.76 / 1000 = 0.00476; 2.45E+4 = 2.45 x 10 4 = 24500<br />

N OTA: ci si riferisce a questo tipo <strong>di</strong> dato anche con la <strong>di</strong>citura floating point, a virgola mobile.<br />

TIPO CARATTERE (char). Il tipo char si usa per il singolo carattere (lettera alfabetica, cifra numerica da 0 a 9 o<br />

qualsiasi altro simbolo rappresentabile con il P.C. come la punteggiatura, ma anche le lettere dell’alfabeto greco,<br />

alcuni caratteri semigrafici tipo cuoricini o faccine, lettere straniere come âåë o simboli matematici). L’elenco<br />

completo forma quella che viene chiamata la tabella ASCII (American Standard Code form Information<br />

Interchange) che esiste in forma orginale (128 simboli) o estesa (256 simboli). I caratteri vanno racchiusi tra due<br />

apici semplici come in ‘a’, ‘A’, ‘]’,’4’.<br />

Attenzione a non confondere una cifra numerica con il carattere corrispondente: 1 è <strong>di</strong>verso da ‘1’ ! Anche se in un<br />

documento <strong>di</strong> testo i numeri vengono memorizzati con la sequenza dei co<strong>di</strong>ci ascii corrispondenti, questo formato<br />

risulta molto inefficiente per svolgere calcoli numerici. Numeri interi e reali vengono rappresentati in modo assai<br />

<strong>di</strong>verso ma questi argomenti verranno affrontati in parallelo nel corso <strong>di</strong> sistemi.


Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 25<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

TIPO BOOLEANO (boolean). I dati boolean possono assumere solo due valori: vero (true) e falso (false).<br />

TIPO STRINGA (string) Un dato string è un insieme <strong>di</strong> caratteri racchiusi tra apici. La lunghezza massima è 255<br />

caratteri.<br />

Esempi <strong>di</strong> string: ‘Marco’, ‘12/03/1998’;<br />

Questi appena visti sono dati semplici o non strutturati: non sono ulteriormente scomponibili. Più avanti nella<br />

<strong>di</strong>spensa verrà introdotta la possibilità <strong>di</strong> usare dati complessi o strutturati. Vedremo anche come sia possibile per il<br />

programmatore aggiungere nuovi dati (user defined type, tipi definiti dall’utente) basandosi su quelli nativi del pascal<br />

(standard type, tipi standard).<br />

NOTA: la sezione delle costanti può anche non essere presente.<br />

Procedure e Funzioni<br />

Dopo la sezione delle variabili può essere presente quella dei cosiddetti sottoprogrammi. Si rimanda la <strong>di</strong>scussione <strong>di</strong><br />

questo argomento ad un momento più appropriato, molto più avanti nella <strong>di</strong>spensa… Per il momento possiamo<br />

comportarci come se questa sezione non esistesse.<br />

Sezione esecutiva (corpo principale del programma)<br />

E’ la parte veramente operativa del programma! Corrisponde in pratica alla traduzione dell’algoritmo. La parte<br />

<strong>di</strong>chiarativa è infatti solo preparatoria e serve al compilatore per essere più efficiente (ad esempio se conosce in<br />

anticipo i tipi delle variabili può organizzarle più efficientemente nella RAM) e controllare il resto del programma<br />

per alcuni tipi <strong>di</strong> errore (ad esempio tentare <strong>di</strong> mo<strong>di</strong>ficare una costante: come potrebbe se non avessimo <strong>di</strong>chiarato in<br />

anticipo quali identificatori considerare associati a valori immutabili?).<br />

Questa sezione inizia con la parola riservata begin e termina con la parola riservata end seguita da un punto:<br />

program Esempio; Intestazione<br />

const<br />

…<br />

Sezione <strong>di</strong>chiarativa<br />

var<br />

…<br />

begin<br />

<br />

Sezione esecutiva<br />

end;<br />

TIPI DI ISTRUZIONE CHE SI POSSONO METTERE NELLA SEZIONE ESECUTIVA<br />

assegnamento: memorizza in una variabile una costante oppure il valore <strong>di</strong> un'altra variabile oppure il risultato <strong>di</strong><br />

un calcolo (espressione); in Pascal il simbolo dell'assegnamento è questo: :=<br />

X := 8; costante X := Y; variabile X := Y*2 +4; espressione<br />

program EsempiDiAssegnamento;<br />

var<br />

n1, n2: integer; x: real; s: string;<br />

begin<br />

n1 := 100; n1 := n2; n1 := 3*(34 –<br />

n2);<br />

x := 2; x := 3.14; x := n1; s := ‘ciao<br />

come stai?’;<br />

end.<br />

program AssegnamentiCompatibili;<br />

var<br />

c: char; x: real; s: string;<br />

begin<br />

x := 3 + 7 (* equivale a 10.0 *);<br />

c := ‘A’;<br />

s := c; (* un carattere ‘sta’ in una<br />

Il valore che viene assegnato ad una variabile deve essere <strong>di</strong> tipo compatibile.. Questo<br />

non significa per forza identico ma che (almeno) il risultato dell'espressione alla destra<br />

del simbolo <strong>di</strong> assegnamento sia convertibile nel tipo della variabile in<strong>di</strong>cata alla<br />

sinistra.<br />

Come regola pratica ricordate questa: un tipo più grande può accoglierne uno più<br />

‘piccolo’ ma non il viceversa.<br />

Ad esempio, se X è una variabile reale: x := 7 corrisponde a x := 7.0 e x:=3+7<br />

corrisponde a x := 10.0 (gli interi 7 e l'intero risultato dell'espressione 3+7 possono<br />

essere convertiti nel corrispondente numero reale senza problemi). Ma se x fosse una<br />

variabile intera, l'assegnamento x := 1.5, ammesso che fosse accettato, dovrebbe<br />

trasformare il numero reale 1.5 o nel numero intero uno o nel numero intero due con<br />

una per<strong>di</strong>ta <strong>di</strong> precisione inaccettabile e tale da richiedere un comando esplicito ( x :=<br />

trunc(1.5) ad esempio tronca il numero reale restituendo un intero.


stringa *)<br />

end.<br />

Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 26<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004


Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 27<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

E’ anche possibile calcolare espressioni facenti uso <strong>di</strong> parentesi. Un computer non ha però necessità <strong>di</strong> facilitarsi la<br />

lettura <strong>di</strong> un'espressione utilizzando <strong>di</strong>versi tipi <strong>di</strong> parentesi: quadre e graffe non devono essere usate! Bisogna usare<br />

invece più livelli <strong>di</strong> parentesi tonde:<br />

Come siamo abituati noi Come dev'essere scritto con un linguaggio <strong>di</strong> programmazione<br />

(5 + 4) (5 + 4)<br />

(4 -2) * (23 +9) (4 -2) * (23 +9)<br />

[ (4 + 6) / 3] * (23 + 7) ( (4 + 6) / 3 ) * (23 + 7)<br />

{ [ (4 + 6) / 3] * (23 + 7) } / (67 – 9) ( ( (4 + 6) / 3) * (23 + 7) ) / (67 – 9)<br />

Principali operatori predefiniti ed operatori relazionali utilizzabili per i principali tipi <strong>di</strong> dato<br />

CHAR<br />

Ord( carattere ) Co<strong>di</strong>ce ascii corrispondente: ord(‘A’) 65; ord(‘a’) 97; ord(‘1’) 49<br />

Chr( co<strong>di</strong>ce ascii) Carattere corrispondente: chr(65) ‘A’; chr(97) ‘a’<br />

Upcase( carattere) Trasforma il carattere in maiuscolo: upcase(‘a’) ‘A’; upcase(‘A’) ‘A’<br />

Confronti (pre<strong>di</strong>cati): =, < (minore), > (maggiore), (<strong>di</strong>verso), = (maggiore od uguale).<br />

NOTA: sono tutti da intendersi in senso alfabetico.<br />

INTERI<br />

+ - * Somma, sottrazione, moltiplicazione<br />

a DIV b Quoziente intero tra a e b: 12 <strong>di</strong>v 4 3; 14 <strong>di</strong>v 4 3;<br />

a MOD b Resto intero della <strong>di</strong>visione tra a e b<br />

Succ( numero ) Successore del numero in<strong>di</strong>cato: succ(4) 5<br />

Pred( numero ) Predecessore del numero in<strong>di</strong>cato: pred(5) 4<br />

Dec( numero ) Toglie uno a numero: dec(7) 6<br />

Dec(numero, quantita) Toglie quantità a numero: dec(12,5) 7<br />

Inc( numero ) Aggiunge uno a numero: inc(7) 8<br />

Inc(numero, quantita) Aggiunge quantità a numero: inc(12,5) 17<br />

Abs( numero ) Valore assoluto (o modulo) <strong>di</strong> numero<br />

Sqr(x) Quadrato<br />

-x Opposto <strong>di</strong> x<br />

Possibili errori: overflow e <strong>di</strong>visione per zero (<strong>di</strong>vision by zero).<br />

Confronti (pre<strong>di</strong>cati): =, < (minore), > (maggiore), (<strong>di</strong>verso), = (maggiore od uguale).


Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 28<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

REALI - ricordare che sono rappresentati nella forma x = mantissa * 10 caratteristica come in 5.28 * 10 6<br />

+ - * / Somma, sottrazione, moltiplicazione, <strong>di</strong>visione<br />

Sqrt (x) Ra<strong>di</strong>ce quadrata<br />

Sqr(x) Quadrato<br />

Sin(x), cos(x), tan(x), Seno, coseno, tangente, arcotangente <strong>di</strong> x<br />

arctan(x)<br />

Abs( x ) Valore assoluto (o modulo) <strong>di</strong> x<br />

-x Opposto <strong>di</strong> x<br />

Trunc(x) Tronca la parte frazionare <strong>di</strong> x<br />

Round(x) Arrotondamento <strong>di</strong> x<br />

Possibili errori: overflow (caratteristica troppo grande per essere rappresentata), underflow (caratteristica troppo<br />

piccola per essere rappresentata), <strong>di</strong>visione per zero, argomento illegale (illegal argument, come in sqrt(-10)).<br />

Problemi <strong>di</strong> precisione: è limitata dal numero <strong>di</strong> bit de<strong>di</strong>cati alla mantissa<br />

Problemi <strong>di</strong> approssimazione: sono dovuti alla conversione tra decimale e binario; ad esempio 0.1 (in decimale) può<br />

essere rappresentati in binario solo come numero perio<strong>di</strong>co (0.0001 0001 0001…) che una volta convertito in<br />

decimale può dare origine a nome approssimati come 0.0999999 o 9.99999E-2<br />

Confronti (pre<strong>di</strong>cati): =, < (minore), > (maggiore), (<strong>di</strong>verso), = (maggiore od uguale).<br />

STRINGHE<br />

+ Concatenazione: ‘ciao ‘ + ‘a tutti’ ‘ciao a tutti’<br />

Copy(s, inizio,<br />

quanti)<br />

Restituisce della stringa s la sotto stringa che inizia alla posizione inizio e continua per quanti caratteri<br />

Confronti (pre<strong>di</strong>cati): =, < (minore), > (maggiore), (<strong>di</strong>verso), = (maggiore od uguale).<br />

NOTA: sono tutti da intendersi in senso alfabetico.<br />

ALTRI COMANDI UTILI<br />

Length(s) Restituisce alla lunghezza (numero <strong>di</strong> caratteri) della stringa s<br />

Pos(s1, s2) Restituisce la posizione della stringa s1 all'interno della stringa s2; se s1 non viene trovata restituisce zero<br />

Val(s, x, errore) Se possibile trasforma la stringa s nel numero corrispondente e memorizza il risultato nella variabile X; s<br />

la conversione riesce nella variabile intera errore e ha messo il valore zero; se la conversione non riesce i<br />

errore viene emesso un valore <strong>di</strong>verso da zero<br />

Str(x, s) Converte il numero x nella stringa corrispondente memorizzandola nella variabile stringa s<br />

Incremento/decremento del valore <strong>di</strong> una variabile.<br />

x := x + 1; x assume il valore attuale incrementato <strong>di</strong> uno (x <strong>di</strong>venta x incrementato <strong>di</strong> uno)<br />

ATTENZIONE: non confondetelo con la scrittura x = y + 1 (test <strong>di</strong> confronto tra il valore contenuto nella variabile x<br />

e quello della variabile y incrementato <strong>di</strong> uno.<br />

x := x + 5.2; x viene incrementata <strong>di</strong> 5.2 (evidentemente il tipo <strong>di</strong> x è real)<br />

x := x -10; x viene incrementata <strong>di</strong> 10


not<br />

Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 29<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

PRINCIPALI OPERATORI IN ORDINE DECRESCENTE DI PRECEDENZA<br />

Negazione logica (booleana); esempio: not x=y (espressione vera se il valore della variabile x è<br />

<strong>di</strong>verso da quello <strong>di</strong> y);<br />

Negazione bit a bit (bitwise): not 10010 01101<br />

* Moltiplicazione aritmetica<br />

/ Divisione (tra numeri reali)<br />

<strong>di</strong>v Divisione intera<br />

mod Modulo (il resto <strong>di</strong> una <strong>di</strong>visione intera) 5 mod 2 1 18 mod 5 3<br />

and<br />

And booleano: (x>4) and (x Test maggiore <strong>di</strong><br />

Or booleano: (x12) (espressione vera se x ha un valore esterno all’intervallo (4,12)<br />

Or bitwise: 1001 and 1011 1011<br />

Test <strong>di</strong> non uguaglianza (<strong>di</strong>verso da); x y (espressione vera se il valore della variabile x è <strong>di</strong>verso<br />

da quello <strong>di</strong> y)<br />

= Test maggiore o uguale <strong>di</strong><br />

coman<strong>di</strong> <strong>di</strong> ingresso/uscita: servono per acquisire dati dalla tastiera o per visualizzare risultati numerici, messaggi<br />

<strong>di</strong> testo, caratteri semigrafici, primitive geometriche (rette, circonferenze), suoni, stampare ecc.<br />

uscita (scrittura)<br />

write(espressione1, espressione2, ): mostra sullo schermo la sequenza dei valori delle espressioni in<strong>di</strong>cate tra<br />

parentesi; espressione1/2ecc. può essere praticamente qualsiasi cosa (una costante numerica,una costante stringa,<br />

una variabile <strong>di</strong> tutti i principali tipi, un'espressione che verrebbe prima calcolata ed il cui valore sarebbe quin<strong>di</strong><br />

visualizzato; nota: non va a capo automaticamente; per cui il successivo comando <strong>di</strong> visualizzazione su video<br />

continuerà sulla stessa linea dello schermo (a meno che si sia già arrivati all'ultima posizione, nel qual caso si va<br />

comunque a capo);<br />

writeln(espressione1, espressione2, ): come il comando write ed in più si posiziona automaticamente sulla<br />

prossima linea dello schermo.<br />

Esempi: write(12); write(‘a’); write(‘ciao mondo’); write(‘Gianni ha ‘, eta, ‘ anni’);<br />

writeln(‘In un secolo ci sono ‘, 60*60*24*365*100.0, ‘secon<strong>di</strong>’);<br />

NOTA: se non moltiplicassimo per 100.0 (ci avevate badato?) otterremmo un messaggio <strong>di</strong> errore perché il risultato<br />

supererebbe il più grande numero intero (longint) rappresentabile con il turbo Pascal. In<strong>di</strong>cando invece un numero<br />

reale il calcolo viene effettuato nel dominio dei numeri reali.


Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 30<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

Il formato del risultato usa la notazione esponenziale: 3.1536000000E+09. E’ però possibile in<strong>di</strong>care degli<br />

in<strong>di</strong>catori <strong>di</strong> ampiezza: detto in altre parole possiamo scegliere quante posizioni sul video utilizzare per le cifre<br />

prima e dopo il punto decimale:<br />

writeln(60*60*24*365*100.0:10:0) 3153600000 :10:0 significa 10 posizioni in tutto, <strong>di</strong> cui<br />

0 (nessuna) per le cifre dopo il punto decimale<br />

writeln(60*60*24*365*100.0:13:2) 3153600000.00 :13:2 significa 13 posizioni in tutto, <strong>di</strong> cui 2 per le<br />

cifre dopo il punto decimale (bisogna contare<br />

anche quest'ultimo)<br />

ingresso (lettura)<br />

readln( variabile): il programma si mette in attesa <strong>di</strong> un valore che deve essere <strong>di</strong>gitato usando la tastiera; il<br />

tipo del dati inserito deve corrispondere a quello della variabile; un quadratino od un trattino lampeggiante<br />

in<strong>di</strong>cano lo stato <strong>di</strong> attesa; il dato si intende <strong>di</strong>gitato completamente quando viene premuto il tasto invio (enter<br />

sulle tastiere americane); il valore inserito viene memorizzato nella variabile in<strong>di</strong>cata: da questo momento in<br />

avanti <strong>di</strong>venta <strong>di</strong>sponibile nel programma.<br />

Coman<strong>di</strong> per il controllo del flusso <strong>di</strong> esecuzione<br />

Tutti i coman<strong>di</strong> visti fino ad ora sono del tutto insufficienti per implementare (realizzare sul computer) algoritmi<br />

anche molto semplici. Immaginiamo ad esempio un programma che chiede a chi lo sta usando <strong>di</strong> inserire l'età; il<br />

programma dovrebbe rispondere con un messaggio appropriato a seconda che ci si trovi <strong>di</strong> fronte ad un maggiorenne<br />

piuttosto che a un minorenne.<br />

Non c'è modo scrivendo semplicemente un'istruzione dopo l'altra <strong>di</strong> riuscire in questo intento! Per ora, infatti, siamo<br />

solo in grado <strong>di</strong> in<strong>di</strong>care in sequenza una serie <strong>di</strong> coman<strong>di</strong>. Questo è certamente utile ed in<strong>di</strong>vidua la prima delle<br />

cosiddette strutture fondamentali della programmazione. Ne mancano due: la selezione e l'iterazione. La<br />

selezione corrisponde la possibilità <strong>di</strong> far eseguire un blocco <strong>di</strong> istruzioni piuttosto che un altro a seconda del<br />

risultato <strong>di</strong> un confronto. L'iterazione corrisponde invece la possibilità <strong>di</strong> far ripetere un blocco <strong>di</strong> istruzioni fino al<br />

verificarsi <strong>di</strong> una certa con<strong>di</strong>zione.<br />

Due stu<strong>di</strong>osi hanno <strong>di</strong>mostrato che con sequenza, selezione e l'iterazione possono essere scritti tutti i possibili<br />

programmi che un microprocessore è in grado <strong>di</strong> eseguire. Si parla anche <strong>di</strong> programmazione strutturata.<br />

NOTA: siete caldamente invitati a consultare l'approfon<strong>di</strong>mento sui <strong>di</strong>agrammi <strong>di</strong> flusso per vedere come sono<br />

rappresentate le strutture fondamentali della programmazione.<br />

Ci sono tre tipi <strong>di</strong> struttura selettiva: ad una via, a due vie (detta anche binaria), a molte vie.


SELEZIONE AD UNA VIA<br />

Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 31<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

Fa eseguire una singola od un blocco <strong>di</strong> istruzioni (delimitate in questo caso dalla coppia <strong>di</strong> parole chiave begin<br />

end) solo se la con<strong>di</strong>zione in<strong>di</strong>cata e vera (true). Se la con<strong>di</strong>zione è falsa l’istruzione (o il blocco <strong>di</strong><br />

istruzioni) viene semplicemente ignorato. Ecco la sintassi (a sinistra il caso <strong>di</strong> singola istruzione ed a destra<br />

quello con più <strong>di</strong> una istruzione:<br />

if con<strong>di</strong>zione then<br />

istruzione;<br />

ma non sarebbe sbagliato:<br />

if con<strong>di</strong>zione then<br />

begin<br />

istruzioneSingola<br />

end<br />

if con<strong>di</strong>zione then<br />

begin<br />

istruzione1;<br />

istruzione2;<br />

…<br />

istruzioneN<br />

end;<br />

Dopo istruzioneN non è stato <strong>di</strong>menticato un punto e virgola: prima <strong>di</strong> un end può essere omessa in quanto il ;<br />

serve a separare due istruzioni e l’end non può essere considerato un’istruzione ma un delimitatore.<br />

con<strong>di</strong>zione è un espressione che una volta valutata deve generare un valore booleano (vero/falso); le con<strong>di</strong>zioni<br />

si esprimono con gli operatori relazionali:<br />

OPERATORE ESEMPI<br />

< minore X < 4 x+y < z a < b+c Ord(c) < 67 (1) Parola1 < Parola2 (2)<br />

= b+c X >= sqrt(z) (3) Parola1 >= Parola2<br />

> maggiore Somma>100 x+y > z a > b+c X > sqr(z) Length(cognome)>12 (4)<br />

<strong>di</strong>verso x y x+y z a > b+c X sqr(z) Cognome1 Cognome2<br />

(1) c è un carattere; la funzione ord restituisce il valore del suo co<strong>di</strong>ce ASCII che viene confrontato con il<br />

valore 67.<br />

(2) Parola1 e Parola2 sono due stringhe; il confronto deve essere inteso in senso lessicografico, cioè<br />

considerando la posizione che ciascuna parola occuperebbe sul vocabolario; per cui ‘cane’ < ‘ca in anni<br />

libri, e mi sa’; il tutto si applica anche quando la stringa contiene più parole come in ‘bel cane’ < ‘bel gatto’<br />

(3) Il valore della variabile X viene confrontato con quello della ra<strong>di</strong>ce quadrata del valore contenuto nella<br />

variabile z<br />

(4) Si controlla se la lunghezza in caratteri della stringa cognome supera 12<br />

Immaginiamo <strong>di</strong> voler applicare uno sconto al prezzo <strong>di</strong> un prodotto solo ai clienti abituali...<br />

program ApplicaSconto;<br />

var<br />

costo: real; tipoCliente: string; percentualeSconto:<br />

integer;<br />

begin<br />

writeln(Inserisci costo prodotto: ); readln( costo );<br />

writeln(Inserisci eventuale % <strong>di</strong> sconto: );<br />

readln(percentualeSconto );<br />

writeln(Inserisci il tipo <strong>di</strong> cliente: ); readln(<br />

tipoCliente );<br />

if tipoCliente=abituale then<br />

costo := costo (costo/100) * percentualeSconto;<br />

NOTA: in molti degli esempi che verranno proposti non ci si<br />

preoccuperà <strong>di</strong> controllare la correttezza dei dati inseriti<br />

tramite la tastiera perché il co<strong>di</strong>ce si appesantirebbe e<br />

<strong>di</strong>venterebbe molto più <strong>di</strong>fficile da seguire.<br />

In riferimento al programma qui a lato, ad esempio, l'utente<br />

potrebbe inserire un costo pari a zero o negativo, una<br />

percentuale <strong>di</strong> sconto senza senso ed anche una descrizione<br />

per il tipo del cliente che potrebbe ‘ mettere in crisi’ il test<br />

fatto con l’if: ‘Abituale’ con la A maiuscola non verrebbe<br />

riconosciuto invalido per l'applicazione dello sconto.<br />

writeln(Questo cliente paga :, costo):<br />

readln; (* un readln senza variabile attende fino a che non viene premuto il tasto INVIO)


end.<br />

Altri esempi.<br />

program selezione;<br />

var<br />

a,b,c: integer;<br />

s1,s2: string;<br />

c1,c2: char;<br />

begin<br />

a:=1; b:=2; c:=1;<br />

s1:='rossi'; s2:='mario';<br />

c1:='a';<br />

c2:='b';<br />

if a=b then<br />

writeln('a uguale b');<br />

if a>3 then<br />

writeln('a maggiore 3');<br />

if b=4 then<br />

writeln('I n. maggiore uguale II');<br />

(* il seguente confronto segue l'or<strong>di</strong>ne alfabetico (a2 then<br />

Begin<br />

Istruzione;<br />

Istruzione;<br />

Istruzione<br />

End;<br />

Istruzione;<br />

If x>2 then<br />

begin<br />

Istruzione1;<br />

Istruzione2;<br />

Istruzione3<br />

end;<br />

Istruzione<br />

If x>2 then<br />

Begin<br />

Istruzione;<br />

Istruzione;<br />

Istruzione<br />

End;<br />

Istruzione;


SELEZIONE A DUE VIE<br />

Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 33<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

Rispetto a quella ad una via viene aggiunta la parte da eseguire quando la con<strong>di</strong>zione falsa:<br />

if con<strong>di</strong>zione then<br />

istruzioneSeVera<br />

else<br />

istruzioneSeFalsa;<br />

Qui sopra sono stati esemplificati tutti i possibili casi che si possono presentare a seconda che sia presente una<br />

sola istruzione da eseguire (nella parte then o nella parte else) piuttosto che un blocco <strong>di</strong> due o più istruzioni che<br />

vanno quin<strong>di</strong> racchiuse tra un begin ed un end. Nel primo esempio c'è una sola istruzione da eseguire sia che la<br />

con<strong>di</strong>zione sia vera sia che sia falsa. Nel secondo esempio ci sono N istruzioni da eseguire quando la con<strong>di</strong>zione<br />

vera ed una sola quando la con<strong>di</strong>zione falsa. Nel terzo esempio c'è una sola istruzione da eseguire quando la<br />

con<strong>di</strong>zione vera ed N istruzioni quando la con<strong>di</strong>zione falsa. Nell'ultimo esempio più <strong>di</strong> un'istruzione da eseguire<br />

sia nel caso la con<strong>di</strong>zione sia vera sia nel caso sia falsa.<br />

Se la con<strong>di</strong>zione è vera viene eseguita l'istruzione (o il blocco <strong>di</strong> istruzioni tra begin end) dopo il then e viene<br />

invece ignorata la parte else. Viceversa, se la con<strong>di</strong>zione è falsa viene ignorata l'istruzione (o il blocco <strong>di</strong><br />

istruzioni tra begin end) dopo il then e viene eseguita la parte else.<br />

Con<strong>di</strong>zioni composte<br />

if con<strong>di</strong>zione then<br />

begin<br />

Istruzione1SeVera;<br />

Istruzione2SeVera;<br />

…<br />

IstruzioneNSeVera;<br />

end<br />

else<br />

istruzioneSeFalsa;<br />

Le con<strong>di</strong>zioni usate fino ad ora sono definite semplici; usando i connettivi logici and, or e not possiamo<br />

comporne <strong>di</strong> più complesse (composte).<br />

La con<strong>di</strong>zione composta che usa and è vera quando lo sono CONTEMPORANEAMENTE tutte le con<strong>di</strong>zioni<br />

coinvolte; se ANCHE UNA SOLA <strong>di</strong> esse e' falsa, la con<strong>di</strong>zione intera risulterà falsa. Ogni con<strong>di</strong>zione deve<br />

essere racchiusa tra parentesi tonde.<br />

if (a>=1) and (a=18) then<br />

writeln('rossi e'' maggiorenne, maschio o femmina');<br />

if con<strong>di</strong>zione then<br />

IstruzioneSeVera<br />

else<br />

begin<br />

Istruzione1SeFalsa;<br />

Istruzione2SeFalsa;<br />

…<br />

IstruzioneNSeFalsa;<br />

end;<br />

ERRORE COMUNE: mettere il ; prima dellelse. In pascal l’istruzione if then else<br />

è considerata in<strong>di</strong>visibile e, lo sapete, il ; serve a separare due istruzioni.<br />

(* le con<strong>di</strong>zioni possono anche essere piu'' <strong>di</strong> 2 ... devono sempre essere vere<br />

CONTEMPORANEAMENTE *)<br />

if (s1='rossi') and (eta>18) and (s2='femmina') then<br />

writeln('rossi e'' maggiorenne, femmina');<br />

if con<strong>di</strong>zione then<br />

begin<br />

Istruzione1SeVera;<br />

Istruzione2SeVera;<br />

…<br />

IstruzioneNSeVera;<br />

end<br />

else<br />

begin<br />

Istruzione1SeFalsa;<br />

Istruzione2SeFalsa;<br />

…<br />

IstruzioneNSeFalsa;<br />

end;


Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 34<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

La con<strong>di</strong>zione composta che usa or è vera quando lo è ALMENO UNA delle con<strong>di</strong>zioni coinvolte.; è falsa<br />

quando le con<strong>di</strong>zioni sono false TUTTE CONTEMPORANEAMENTE.<br />

If (mese=Gennaio) or (mese=Marzo) or (mese=Luglio) then<br />

Numero_giorni := 31;<br />

Prestate particolare attenzione al seguente esempio; va letto così: se oppure allora ..<br />

osservate le parentesi, OBBLIGATORIE: if ( ) or ( ( ) and ( ) )<br />

if (s1='bella') or ( (eta>60) and (s2='ricca') ) then<br />

writeln('la sposo in ogni caso ...');<br />

Variante sul tema … (più <strong>di</strong> una istruzione da eseguire)<br />

if (s1='bella') or ( (eta>60) and (s2='ricca') ) then<br />

begin<br />

writeln('la sposo in ogni caso ...');<br />

writeln('poi forse mi pentiro!')<br />

end<br />

else<br />

begin<br />

writeln('in questo caso niente dubbi ...');<br />

writeln('... meglio scapoli !!')<br />

end;<br />

IF ni<strong>di</strong>ficati: gli if possono contenere altri if; si parla <strong>di</strong> if ni<strong>di</strong>ficati:<br />

(* troviamo il massimo tra a b c *)<br />

if a>=b then<br />

if a>=c then<br />

writeln('il max e'' a')<br />

else<br />

writeln('il max e'' c')<br />

else<br />

if b>=c then<br />

writeln('il max e'' b')<br />

else<br />

writeln('il max e'' c');<br />

SELEZIONE A MOLTE VIE<br />

Cominciamo con il <strong>di</strong>re e non è una struttura in<strong>di</strong>spensabile ma solo una como<strong>di</strong>tà. È stata introdotta per sostituire la<br />

struttura degli if in cascata che rischia <strong>di</strong> <strong>di</strong>ventare poco leggibile. Ecco qui sotto le due strutture a confronto nel<br />

compito <strong>di</strong> determinare quanti giorni ci sono in un certo mese dell'anno: la variabile mese contiene un numero da 1 a<br />

12; la variabile anno il numero che corrisponde all'anno <strong>di</strong> riferimento.<br />

If mese=2 then<br />

If anno mod 4 = 0 then<br />

Giorni:=29 (* bisestile *)<br />

Else<br />

Giorni:=28<br />

Else<br />

If (mese=1) or (mese=3) or (mese=5)<br />

or (mese=7) or (mese=8) or (mese=10)<br />

or (mese=12) then<br />

Giorni:=31<br />

Else<br />

Giorni:=30<br />

IF in cascata:<br />

Case eta of<br />

1..11: writeln(‘ sei un poppante!’);<br />

12..15:<br />

begin<br />

writeln(‘OK, <strong>di</strong>mmi come ti chiami’);<br />

readln( nome );<br />

end;<br />

else<br />

begin<br />

writeln(‘Matusa non ammessi …’);<br />

writeln(‘Premi invio per continuare’);<br />

readln<br />

end<br />

end;<br />

if con<strong>di</strong>zione1 then<br />

…<br />

else<br />

if con<strong>di</strong>zione2 then<br />

…<br />

else<br />

if con<strong>di</strong>zione3 then<br />

….<br />

Else


Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 35<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

La struttura <strong>di</strong> destra ‘si legge’ così: nel caso il valore <strong>di</strong> mese sia (case mese of …) 2 fai il controllo per il bisestile,<br />

nel caso invece il valore sia uno tra 1,3,5,7,8,10,12 i giorni sono 31, altrimenti sono 30. In pratica si elencano uno<br />

dopo l'altro i casi possibili (i valori assunti dalla variabile che si sta controllando) e per ciascun caso si in<strong>di</strong>cano le<br />

istruzioni da eseguire. La struttura che ne risulta è certamente più lineare e leggibile <strong>di</strong> quella ottenibile con una<br />

serie <strong>di</strong> if then else annidati e/o in cascata: e più sono i casi da considerare e più conviene usare questa<br />

nuova struttura. Ma ve<strong>di</strong>amone in dettaglio la sintassi:<br />

Case mese of<br />

2: If anno mod 4 = 0 then<br />

Giorni:=29 (* bisestile *)<br />

Else<br />

Giorni:=28;<br />

1,3,5,7,8,10,12: Giorni:=31;<br />

else<br />

Giorni:=30;<br />

end;<br />

Dopo la parola riservata case deve essere specificata la variabile da controllare: possiamo usare solo variabili <strong>di</strong> tipo<br />

integer e char (in realtà tutte le variabili <strong>di</strong> tipo or<strong>di</strong>nale).<br />

Per ogni caso che si presenta possiamo elencare:<br />

• un singolo valore (come il numero 2 che rappresenta febbraio nell'esempio)<br />

• più valori separati da virgola con l'idea che la variabile da controllare può assumere uno qualsiasi <strong>di</strong> questi<br />

valori per ricadere in questo caso<br />

• un intervallo <strong>di</strong> valori: 1..12 significa da 1a 12, ‘a’..’z’ significa dalla ‘a’ alla ‘z’<br />

• una situazione mista tra le due precedenti come in 1,3,5-12 che verrebbe interpretato come o il valore 1, o il<br />

valore 3, o un valore tra 5 e 12<br />

Dopo l'elenco dei valori che in<strong>di</strong>viduano un certo caso si deve mettere simbolo : (due punti) e poi o l'unica<br />

istruzione da eseguire o il blocco delle istruzioni da eseguire racchiuse tra begin e end. Ogni caso deve essere<br />

separato dal successivo con un punto e virgola.<br />

È possibile, ma non obbligatorio, inserire una parte else begin end con l'idea <strong>di</strong> in<strong>di</strong>care le istruzioni da eseguire<br />

nel caso il valore della variabile <strong>di</strong> controllo non trovi riscontro in nessuno dei casi primi elencati. Ad esempio nel<br />

controllo del valore della variabile mese se siamo certi che il suo valore è nell'intervallo 1..12, dopo aver controllato<br />

che non sia due e tutti i valori che corrispondono a mesi con 31 giorni, la parte else non può che corrispondere a casi<br />

in cui il mese è uno <strong>di</strong> quelli con 30 giorni. Naturalmente avremmo anche potuto usare la forma estesa senza la parte<br />

else:<br />

Case mese of<br />

2: If anno mod 4 = 0 then<br />

Giorni:=29 (* bisestile *)<br />

Else<br />

Giorni:=28;<br />

1,3,5,7,8,10,12: Giorni:=31;<br />

4,6,9,11: Giorni:=30;<br />

end;<br />

Case eta of<br />

1..11: writeln(‘ sei un poppante!’);<br />

12..15:<br />

begin<br />

writeln(‘OK, <strong>di</strong>mmi come ti chiami’);<br />

readln( nome );<br />

end;<br />

else<br />

begin<br />

Non è un errore!<br />

writeln(‘Matusa non ammessi …’);<br />

writeln(‘Premi invio per continuare’);<br />

readln<br />

end<br />

end;<br />

Il case va sempre concluso con un end. Un’ultima particolarità: prima dell’else <strong>di</strong> un case<br />

deve essere messo un punto e virgola perché altrimenti potrebbe essere confuso con la parte<br />

else <strong>di</strong> un if then else inserito nell'ultimo caso… Infatti nell'esempio qui sulla destra senza il<br />

punto e virgola dopo il 20, else che dovrebbe essere eseguito quando il soggetto ha più <strong>di</strong> 18<br />

anni (caso ’pigliatutto’ del case) verrebbe invece erroneamente associato con l’if che controlla<br />

le multe.<br />

Case eta of<br />

1..13: paghetta:=5;<br />

14-18: if multe=0 then<br />

paghetta:=20;<br />

else<br />

writeln(‘Vai a lavorare!’)<br />

end;


ESERCIZI E RIEPILOGATIVI SULLA SELEZIONE<br />

Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 36<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

SEL1. <strong>di</strong>fficoltà: bassa Inserita un’età <strong>di</strong>re se siamo in presenza <strong>di</strong> un minorenne o maggiorenne.<br />

Program maggiorenni1;<br />

uses<br />

newdelay, crt;<br />

var<br />

eta: integer; (* variabile in cui verra' memorizzata l'eta' *)<br />

(* NOTA: non usare mai le lettere accentate per i<br />

nomi delle variabili ! *)<br />

begin<br />

clrscr; (* cancello lo schermo *)<br />

write('Quanti hanni hai? -> ');<br />

readln(eta);<br />

if eta>=18 then<br />

writeln('OK, vedo che sei maggiorenne ...')<br />

else<br />

writeln('Sei ancora un poppante, torna tra ', 18 - eta, 'anni !!');<br />

writeln('Programma terminato. Premere INVIO per continuare...');<br />

readln; (* per dare il tempo <strong>di</strong> leggere il messaggio *)<br />

(* problemi aperti: cosa succede se chi usa il programma sbaglia ed inserisce<br />

un eta' negativa o troppo grande per essere vera ?<br />

mo<strong>di</strong>fica il programma per controllare queste possibilita' !!<br />

trovi la soluzione nel sorgente 'maggior2.pas' *)<br />

end.<br />

SEL2. <strong>di</strong>fficoltà: bassa Inserito un numero, <strong>di</strong>re se e' pari o <strong>di</strong>spari<br />

program pari<strong>di</strong>spari1;<br />

uses newdelay, crt;<br />

var<br />

numero: integer; (* variabile in cui verra' memorizzata il numero *)<br />

begin<br />

clrscr; (* cancello lo schermo *)<br />

write('Inserisci un numero e ti <strong>di</strong>ro'' se e'' pari o <strong>di</strong>spari -> ');<br />

readln(numero);<br />

if numero mod 2 = 0 then<br />

writeln('e'' pari')<br />

else<br />

writeln('e'' <strong>di</strong>spari');<br />

writeln('Programma terminato. Premere INVIO per continuare...');<br />

readln; (* per dare il tempo <strong>di</strong> leggere il messaggio *)<br />

end.


SEL3. <strong>di</strong>fficoltà: bassa Inserito un carattere, <strong>di</strong>re se e' una vocale od una consonante.<br />

program consonanteVocale;<br />

uses<br />

newdelay, crt;<br />

var<br />

c: char; (* variabile in cui verra' memorizzata il singolo carattere *)<br />

(* commento: il tipo char può memorizzare un solo carattere; occupa la<br />

meta' dello spazio <strong>di</strong> una stringa lunga un solo carattere perché con le<br />

stringhe un byte supplementare serve a memorizzare la lunghezza della<br />

stringa stessa; questo non e' invece ovviamente necessario per una<br />

variabile carattere, la cui lunghezza e'' sempre 1 *)<br />

begin<br />

clrscr; (* cancello lo schermo *)<br />

write('Inserisci un carattere -> ');<br />

readln(c);<br />

(* PRIMA SOLUZIONE *)<br />

writeln('PRIMA SOLUZIONE');<br />

if ( c='a' ) or ( c='e' ) or ( c='i' ) or ( c='o' ) or ( c='u' ) then<br />

writeln('e'' una vocale')<br />

else<br />

writeln('e'' una consonante');<br />

writeln('--------------------------');<br />

writeln;<br />

(* commento: questa soluzione non tiene conto delle vocali in maiuscolo *)<br />

(* SECONDA SOLUZIONE *)<br />

writeln('SECONDA SOLUZIONE');<br />

if ( c='a' ) or ( c='e' ) or ( c='i' ) or ( c='o' ) or ( c='u' ) or<br />

( c='A' ) or ( c='E' ) or ( c='I' ) or ( c='O' ) or ( c='U' )<br />

then<br />

writeln('e'' una vocale')<br />

else<br />

writeln('e'' una consonante');<br />

writeln('--------------------------');<br />

writeln;<br />

(* commento: provate con la A: la prima soluzione non fornisce un risultato<br />

corretto, la seconda si'; pero'' ci sono molti test ... ecco una soluzione<br />

piu'' efficiente *)<br />

(* TERZA SOLUZIONE *)<br />

writeln('TERZA SOLUZIONE');<br />

(* prima trasformiamo il carattere in maiuscolo e poi facciamo i test<br />

solo con le maiuscole; upcase e' una funzione che restituisce il<br />

maiuscolo del carattere fornito come parametro tra parentesi;<br />

se il carattere era gia' in maiuscolo, rimane in maiuscolo *)<br />

c := upcase( c );<br />

if ( c='A' ) or ( c='E' ) or ( c='I' ) or ( c='O' ) or ( c='U' ) then<br />

writeln('e'' una vocale')<br />

else<br />

writeln('e'' una consonante');<br />

Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 37<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004


writeln('--------------------------');<br />

writeln;<br />

(* QUARTA SOLUZIONE *)<br />

writeln('QUARTA SOLUZIONE');<br />

(* soluzione con if in cascata, poco leggibile e lunga *)<br />

c := upcase( c );<br />

if ( c'A' ) then<br />

if ( c'E' ) then<br />

if ( c'I' ) then<br />

if ( c'O' ) then<br />

if ( c'U' ) then<br />

writeln('e'' una consonante')<br />

else<br />

writeln('e'' una vocale (U)')<br />

else<br />

writeln('e'' una vocale (O)')<br />

else<br />

writeln('e'' una vocale (I)')<br />

else<br />

writeln('e'' una vocale (E)')<br />

else<br />

writeln('e'' una vocale (A)');<br />

(* commenti: questa soluzione, piuttosto pesante, consente pero' <strong>di</strong><br />

intraprendere azioni <strong>di</strong>fferenziate a seconda delle vocali incontrate;<br />

i blocchi ELSE sono tutti necessari! <strong>di</strong>versamente inserendo quella vocale<br />

non verrebbe stampato nulla! Prova a togliere l'ultimo blocco ed inserisci<br />

la A ... *)<br />

writeln('--------------------------');<br />

writeln;<br />

(* QUINTA SOLUZIONE *)<br />

writeln('QUINTA SOLUZIONE');<br />

(* la piu' efficiente, sfruttando una delle funzioni <strong>di</strong>sponibili<br />

per le stringhe: la funzione pos restituisce la prima posizione <strong>di</strong> una<br />

stringa all'interno <strong>di</strong> un altra; se non la trova restituisce zero;<br />

ad esempio pos('po','topolino') restituisce 3 perche' la stringa 'po'<br />

e' stata trovata a partire dalla posizione 3 nella stringa 'topolino' *)<br />

if pos(c,'aAeEiIoOuU') 0 then (* c trovata da qualche parte *)<br />

writeln('e'' una vocale')<br />

else<br />

writeln('e'' una consonante');<br />

writeln('--------------------------');<br />

writeln;<br />

writeln('Programma terminato. Premere INVIO per continuare...');<br />

readln; (* per dare il tempo <strong>di</strong> leggere il messaggio *)<br />

end.<br />

Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 38<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004


Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 39<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

SEL4. <strong>di</strong>fficoltà: bassa Inseriti A, B e C <strong>di</strong>re se B e' compreso tra A e C; in pratica si controlla se B appartiene<br />

all'intervallo [A,C]<br />

program intervallo;<br />

uses<br />

newdelay, crt;<br />

var<br />

a, b, c: real;<br />

(* variabili in cui verranno memorizzati i numeri *)<br />

begin<br />

clrscr; (* cancello lo schermo *)<br />

write('Inserisci l''estremo sinistro dell''intervallo -> ');<br />

readln(a);<br />

write('Inserisci l''estremo destro dell''intervallo -> ');<br />

readln(c);<br />

(* se i numeri sono stati inseriti nell'or<strong>di</strong>ne sbagliato, stop *)<br />

if a>c then<br />

writeln('Intervallo impossibile.')<br />

else<br />

begin<br />

write('Inserisci un numero e ti <strong>di</strong>ro'' se appartiene all''intervallo -> ');<br />

readln(b);<br />

if ( b>=a ) and ( b=a ) or ( b=5 ) or ( 13=5 anche<br />

se e' falso che 13


Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 40<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

SEL5. <strong>di</strong>fficoltà: bassa Inserite due misure, una in pollici e l'altra in centimetri, <strong>di</strong>re qual è la maggiore<br />

program centimetriPollici;<br />

uses<br />

newdelay, crt;<br />

var<br />

cm: real; (* variabile in cui verra' memorizzate la misura in centimetri *)<br />

po: real; (* variabile in cui verra' memorizzate la misura in pollici *)<br />

begin<br />

clrscr; (* cancello lo schermo *)<br />

write('Inserisci la misura in centimetri -> ');<br />

readln(cm);<br />

write('Inserisci la misura in pollici -> ');<br />

readln(po);<br />

(* ricordando che 1 pollice=2.54 cm ... *)<br />

if po * 2.54 > cm then<br />

writeln('La misura in pollici e'' maggiore (', po*2.54:4:2, 'cm)')<br />

else<br />

if po * 2.54 < cm then<br />

writeln('La misura in centimetri e'' maggiore')<br />

else<br />

writeln('Le due misure sono equivalenti');<br />

writeln('Programma terminato. Premere INVIO per continuare...');<br />

readln; (* per dare il tempo <strong>di</strong> leggere il messaggio *)<br />

end.<br />

SEL 6. <strong>di</strong>fficoltà: bassa Inserite le misure dei lati <strong>di</strong> 2 rettangoli <strong>di</strong>re quale dei due ha la superficie maggiore<br />

program confrontaAree;<br />

uses<br />

newdelay, crt;<br />

var<br />

b1, b2: real; (* variabile in cui verranno memorizzate le due basi *)<br />

h1, h2: real; (* variabile in cui verranno memorizzate le due altezze *)<br />

a1, a2: real; (* variabile in cui verranno memorizzate le due aree *)<br />

begin<br />

clrscr; (* cancello lo schermo *)<br />

write('Inserisci la base del primo rettangolo -> ');<br />

readln(b1);<br />

write('Inserisci l''altezza del primo rettangolo -> ');<br />

readln(h1);<br />

write('Inserisci la base del secondo rettangolo -> ');<br />

readln(b2);


write('Inserisci l''altezza del secondo rettangolo -> ');<br />

readln(h2);<br />

(* PRIMA SOLUZIONE *)<br />

writeln('PRIMA SOLUZIONE');<br />

(* se non interessa memorizzare i valori delle due aree si puo' procedere<br />

al confronto <strong>di</strong>retto delle rispettive formule <strong>di</strong> calcolo *)<br />

if b1*h1 > b2*h2 then<br />

writeln('Il primo rettangolo ha la superficie maggiore')<br />

else<br />

if b1*h1 < b2*h2 then<br />

writeln('Il secondo rettangolo ha la superficie maggiore')<br />

else<br />

writeln('I due rettangoli hanno la stessa superficie');<br />

writeln('--------------------------');<br />

writeln;<br />

(* SECONDA SOLUZIONE (migliore) *)<br />

writeln('SECONDA SOLUZIONE');<br />

(* calcoliamo prima le due superfici e salviamo il risultato in a1 e a2 *)<br />

a1 := b1*h1;<br />

a2 := b2*h2;<br />

(* poi usiamo a1 e a2 per i confronti *)<br />

if a1>a2 then<br />

writeln('Il primo rettangolo ha la superficie maggiore')<br />

else<br />

if a1


Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 42<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

SEL7. <strong>di</strong>fficoltà: me<strong>di</strong>a Inserita un'età', <strong>di</strong>re se siamo in presenza <strong>di</strong> un maggiorenne o <strong>di</strong> un<br />

minorenne; controllare anche eventuali errori <strong>di</strong> inserimento da parte dell'utente<br />

program maggiorenni2;<br />

uses newdelay, crt;<br />

var<br />

eta: integer; (* variabile in cui verra' memorizzata l'eta' NOTA: non usare mai le lettere accentate per i<br />

nomi delle variabili ! *)<br />

begin<br />

clrscr; (* cancello lo schermo *)<br />

(* rispetto alla soluzione dell'esercizio maggior.pas in<strong>di</strong>chiamo nella richiesta dell'eta' anche l'intervallo accettato: da<br />

0 anni (neonato) a 120 *)<br />

write('Quanti hanni hai (0-120) ? -> ');<br />

readln(eta);<br />

(* SOLUZIONE 1 *)<br />

(* controlliamo per errori: eta' negative o superiori a 120 *)<br />

writeln('SOLUZIONE 1');<br />

if ( eta120 ) then<br />

writeln('ERRORE: l''eta'' deve essere compresa tra 0 e 120 !')<br />

else<br />

if eta>=18 then<br />

writeln('OK, vedo che sei maggiorenne ...')<br />

else<br />

writeln('Sei ancora un poppante, torna tra ', 18 - eta, 'anni !!');<br />

(* commenti: notate le parentesi OBBLIGATORIE quando in un if c'e' più <strong>di</strong> una con<strong>di</strong>zione; notate come sia<br />

possibile iniziare una parte ELSE subito con un altro test; questo tipo <strong>di</strong> struttura<br />

if ... then<br />

...<br />

else<br />

if ... then<br />

...<br />

else<br />

if ... then<br />

ecc. ecc.<br />

e' detta con if in cascata e permette <strong>di</strong> verificare in successione una sequenza <strong>di</strong> con<strong>di</strong>zioni<br />

La soluzione precedente non consente, in caso <strong>di</strong> errore, <strong>di</strong> capire se lo sbaglio e' relativo ad un'età' minore <strong>di</strong> zero<br />

o ad un'età' troppo grande perchè le due con<strong>di</strong>zioni sono testate insieme. Ve<strong>di</strong>amo come ottenere un controllo ancora<br />

piu' accurato rispondendo con messaggi <strong>di</strong> errore <strong>di</strong>fferenziati e piu' accurati*)<br />

(* SOLUZIONE 2 *)<br />

writeln('------------------------------');<br />

writeln('SOLUZIONE 2');<br />

if ( eta120 ) then<br />

writeln('ERRORE: l''eta'' non puo''essere maggiore <strong>di</strong> 120!')<br />

else<br />

if eta>=18 then<br />

writeln('OK, vedo che sei maggiorenne ...')<br />

else<br />

writeln('Sei ancora un poppante, torna tra ', 18 - eta, ' anni !!');<br />

writeln('Programma terminato. Premere INVIO per continuare...');<br />

readln; (* per dare il tempo <strong>di</strong> leggere il messaggio *)<br />

end.


Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 43<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

SEL 8. <strong>di</strong>fficoltà: me<strong>di</strong>a Letto un carattere, <strong>di</strong>re se corrisponde ad una lettera maiuscola e se sì<br />

<strong>di</strong>re se e' una consonante od una vocale.<br />

program maiuscola;<br />

var<br />

c: char;<br />

begin<br />

write('Inserire un carattere -> ');<br />

readln(c);<br />

(* PRIMA SOLUZIONE: SENZA CASE *)<br />

(* se il co<strong>di</strong>ce ASCII del carattere letto e' compreso tra quelli <strong>di</strong> A e Z<br />

allora si tratta <strong>di</strong> una maiuscola ... *)<br />

writeln('SENZA ''CASE'' ...');<br />

if ( ord(c)>=ord('A') ) and ( ord(c)


Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 44<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

SEL 9. <strong>di</strong>fficoltà: me<strong>di</strong>a Disegna il <strong>di</strong>agramma <strong>di</strong> flusso e scrivi un programma che, letti tre numeri, li metta in<br />

or<strong>di</strong>ne crescente<br />

program or<strong>di</strong>na_tre;<br />

var a,b,c,temp: integer;<br />

begin<br />

write('Inserire il primo numero -> ');<br />

readln(a);<br />

write('Inserire il secondo numero -> ');<br />

readln(b);<br />

write('Inserire il terzo numero -> ');<br />

readln(c);<br />

(* e' necessario assicurarsi che tre numeri siano in or<strong>di</strong>ne *)<br />

(* prima or<strong>di</strong>na a e b tra loro *)<br />

if b


Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 45<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

SEL 10. <strong>di</strong>fficoltà: me<strong>di</strong>a Letti tre numeri da tastiera, determinare se possono appartenere ad una progressione<br />

aritmetica. TRACCIA: in una progressione aritmetica la <strong>di</strong>fferenza tra due numeri<br />

consecutivi e' costante. Ad esempio: 1 4 7 10 13 16 ecc. è la progressione in cui ogni numero si ottiene<br />

sommando 3 al precedente. La <strong>di</strong>fferenza tra due numeri consecutivi qualsiasi è pari a 3.<br />

program progressione;<br />

var a,b,c,temp: integer;<br />

begin<br />

write('Inserire il primo numero -> ');<br />

readln(a);<br />

write('Inserire il secondo numero -> ');<br />

readln(b);<br />

write('Inserire il terzo numero -> ');<br />

readln(c);<br />

(* e' necessario prima assicurarsi che i tre numeri siano in or<strong>di</strong>ne *)<br />

(* prima or<strong>di</strong>na a e b tra loro *)<br />

if b


SEL 11. <strong>di</strong>fficoltà: alta Verificare se una data inserita da tastiera e' corretta<br />

program dataCorretta;<br />

var gg,mm,aa: integer; (* gg=giorno, mm=mese, aa=anno *)<br />

ok_data: boolean;<br />

begin<br />

write('Inserire il giorno: ');readln(gg);<br />

write('Inserire il mese: ');readln(mm);<br />

write('Inserire l''anno: ');readln(aa);<br />

(* PRIMA SOLUZIONE SENZA CASE *)<br />

ok_data:=false; (* se data supera controlli ok_data verra' messo a true *)<br />

if (gg>0) and (gg0) and (mm0) then<br />

if mm=2 then (* febbraio *)<br />

begin<br />

if aa mod 4 = 0 then (* per semplicita': bisestili se multipli <strong>di</strong> 4 *)<br />

begin<br />

if gg


(* POI SOLUZIONE CON CASE *)<br />

ok_data:=false;<br />

if (gg>0) and (gg0) and (mm0) then<br />

case mm of<br />

2: if aa mod 4 = 0 then<br />

begin<br />

if gg


STRUTTURE ITERATIVE<br />

Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 48<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

Esamineremo ora i tre tipi classici <strong>di</strong> struttura iterativa enumerativa (ciclo for do), indefinita con controllo in<br />

coda/uscita per vero (ciclo repeat until) e della indefinita con controllo in testa/uscita per falso (ciclo while).<br />

Premessa: in realtà potremo scrivere un qualsiasi programma utilizzando solo una delle due forme <strong>di</strong> struttura<br />

iterativa indefinita. Infatti la struttura iterativa enumerativa può essere ‘simulata’ con una delle altre due, ed è<br />

sempre possibile riscrivere un segmento <strong>di</strong> co<strong>di</strong>ce che utilizza una delle due forme <strong>di</strong> iterazione indefinita usando<br />

l'altra. Detto in altre parole tutte le nostre esigenze potrebbero essere sod<strong>di</strong>sfatte o utilizzando solo il ciclo repeat <br />

until o usando solo il ciclo while.<br />

Come ho avuto già modo <strong>di</strong> sottolineare per i costrutto case l'uso <strong>di</strong> forme <strong>di</strong>verse facilita la programmazione in<br />

quei casi in cui una delle forme <strong>di</strong> iterazione esistenti è particolarmente in<strong>di</strong>cata.<br />

Struttura iterativa enumerativa<br />

Si chiama così perché può essere usata solo se il numero <strong>di</strong> volte che l'istruzione od il blocco <strong>di</strong> istruzioni deve<br />

essere ripetuto è noto nel momento in cui il ciclo inizia, ed è per cui possibile far contare (cioè enumerare) al<br />

programma il numero <strong>di</strong> volte che deve ripetere le istruzioni. Ecco la sintassi:<br />

Oppure:<br />

For variabile_<strong>di</strong>_controllo := valore_inizio to valore_fine do<br />

Istruzione;<br />

For variabile_<strong>di</strong>_controllo := valore_inizio to valore_fine do<br />

begin<br />

Istruzione1;<br />

Istruzione2;<br />

<br />

IstruzioneN;<br />

end;<br />

La variabile <strong>di</strong> controllo deve essere <strong>di</strong> tipo or<strong>di</strong>nale (integer, longint o char anche se quest'ultimo caso è assai raro).<br />

Ad essa viene assegnato come valore iniziale quello in<strong>di</strong>cato dopo l'operatore <strong>di</strong> assegnamento. Se il valore iniziale<br />

è inferiore a quello in<strong>di</strong>cato come finale dopo la parola chiave to le istruzioni non sono ripetute neppure una volta.<br />

Diversamente il ciclo inizia e le istruzioni sono eseguite una prima volta; dopo che è stata eseguita l'ultima<br />

istruzione la variabile <strong>di</strong> controllo viene incrementata <strong>di</strong> una unità; se il valore è inferiore o uguale a quello in<strong>di</strong>cato<br />

come finale il ciclo viene ripetuto. Il ciclo termina quando il valore della variabile <strong>di</strong> controllo supera quello in<strong>di</strong>cato<br />

come valore finale.<br />

L'esempio che segue<br />

stampa per cinque<br />

volte il messaggio<br />

‘ciao!’:<br />

program cicli;<br />

var i: integer;<br />

begin<br />

for i:=1 to 5 do<br />

writeln(‘ciao!’);<br />

readln;<br />

end.<br />

In questo caso il ciclo<br />

viene ripetuto una<br />

volta<br />

program cicli;<br />

var i: integer;<br />

begin<br />

for i:=1 to 1 do<br />

writeln(‘ciao!’);<br />

readln;<br />

end.<br />

In questo caso il ciclo<br />

non viene ripetuto<br />

neanche una volta<br />

program cicli;<br />

var i: integer;<br />

begin<br />

for i:=1 to 0 do<br />

writeln(‘ciao!’);<br />

readln;<br />

end.<br />

Il valore d'inizio non deve essere per<br />

forza 1. Il ciclo che segue stamperà il<br />

messaggio sette volte (non<br />

<strong>di</strong>mentichiamo che deve considerare<br />

anche il valore 0 nella sequenza che qui<br />

riporto per intero -3 -2 -1 0 1 2 3 )<br />

program cicli;<br />

var i: integer;<br />

begin<br />

for i:= -3 to 3 do<br />

writeln(‘ciao!’);<br />

readln;<br />

end.


program cicli;<br />

var i, base, altezza, quanti: integer;<br />

begin<br />

writeln(‘Quanti triangoli vuoi esaminare?’);<br />

readln( quanti );<br />

for i:= 1 to quanti do<br />

begin<br />

writeln(‘Inserisci la misura della base del<br />

triangolo’);<br />

readln( base );<br />

writeln(‘Inserisci la misura dell’’ altezza<br />

corrispondente’);<br />

readln( altezza );<br />

writeln(‘La misura della superficie <strong>di</strong> questo triangolo è: ‘, base*altezza/2);<br />

readln;<br />

end; (* del for *)<br />

end.<br />

Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 49<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

Contare come i gamberi: esiste una forma leggermente mo<strong>di</strong>ficata del costrutto for che consente <strong>di</strong> considerare i<br />

valori della variabile <strong>di</strong> controllo in modo decrescente contando, per così <strong>di</strong>re, all'in<strong>di</strong>etro. L'esempio seguente<br />

stampa sul video i numeri da 10 a 1:<br />

program cicli;<br />

var i: integer;<br />

begin<br />

for i:= 10<br />

downto 1 do<br />

writeln( i );<br />

readln;<br />

end.<br />

Questo esempio chiarisce come non sia necessario conoscere l'esatto<br />

valore del numero delle volte che sarà ripetuto il ciclo al momento della<br />

scrittura del co<strong>di</strong>ce.<br />

È invece necessario che questo valore sia noto al momento in cui il ciclo<br />

dovrà essere eseguito: noto non significa sapere quale sarà<br />

effettivamente questo valore (è l'utente programma che decide quanti<br />

saranno i triangoli esaminati <strong>di</strong>gitando questo un valore quando il<br />

programma glielo chiede); significa invece sapere che questo valore è<br />

contenuto in una certa variabile (quanti) ed è quest'ultima che può essere<br />

utilizzata come valore terminale della variabile <strong>di</strong> controllo.<br />

Come vedete, è sufficiente in<strong>di</strong>care invece <strong>di</strong> to la parola chiave downto e come punto <strong>di</strong> inizio un valore<br />

più grande <strong>di</strong> quello in<strong>di</strong>cato per il punto <strong>di</strong> fine.<br />

Questo esempio è molto interessante anche per un altro motivo: <strong>di</strong>mostra come sia possibile utilizzare<br />

nelle istruzioni del ciclo il valore assunto in quel momento dalla variabile <strong>di</strong> controllo.


ESERCIZI E RIEPILOGATIVI SUL CICLO FOR<br />

ITE1. <strong>di</strong>fficoltà: bassa stampa dei primi N numeri naturali, con N letto da tastiera<br />

program numeri;<br />

uses ewdelay, crt;<br />

Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 50<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

var<br />

i: integer; (* contatore ciclo *) n: integer; (* qui viene memorizzato il numero letto da tastiera *)<br />

begin<br />

clrscr; (* cancello lo schermo *)<br />

write('Fino a che numero devo arrivare? -> '); readln( n );<br />

for i := 1 to n do<br />

writeln( i );<br />

(* commenti: e' ancora materia <strong>di</strong> <strong>di</strong>sputa tra matematici se considerare lo<br />

zero un naturale; se lo si vuole considerare tale il co<strong>di</strong>ce <strong>di</strong>venta:<br />

for i := 0 to n-1 do<br />

writeln( i );<br />

infatti se chiedessimo i primi 5 naturali dovremmo ottenere: 0,1,2,3,4<br />

e sarebbe quin<strong>di</strong> necessario fermare il ciclo a 4, cioe' n-1<br />

*)<br />

writeln('Programma terminato. Premere INVIO per continuare...');<br />

readln; (* per dare il tempo <strong>di</strong> leggere il messaggio *)<br />

end.<br />

ITE2. <strong>di</strong>fficoltà: bassa stampa dei primi N numeri naturali, con N letto da tastiera; la stampa deve avvenire dal<br />

numero piu' grande al piu' piccolo<br />

program numeri;<br />

uses newdelay, crt;<br />

var<br />

i: integer; (* contatore ciclo *) n: integer; (* qui viene memorizzato il numero letto da tastiera *)<br />

begin<br />

clrscr; (* cancello lo schermo *)<br />

write('Fino a che numero devo arrivare? -> '); readln( n );<br />

for i := n downto 1 do (* NOTATE IL DOWNTO INVECE DI TO !!!! *)<br />

writeln( i );<br />

writeln('Programma terminato. Premere INVIO per continuare...');<br />

readln; (* per dare il tempo <strong>di</strong> leggere il messaggio *)<br />

end.


Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 51<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

ITE3. <strong>di</strong>fficoltà: bassa stampa dei primi N numeri naturali, con N letto da tastiera; a fianco <strong>di</strong> ciascun numero<br />

in<strong>di</strong>care se e' pari o <strong>di</strong>spari<br />

program numeri;<br />

uses newdelay, crt;<br />

var<br />

i: integer; (* contatore ciclo *) n: integer; (* qui viene memorizzato il numero letto da tastiera *)<br />

begin<br />

clrscr; (* cancello lo schermo *)<br />

write('Fino a che numero devo arrivare? -> '); readln( n );<br />

(* ricordo che l'operatore MOD calcola il resto della <strong>di</strong>visione tra il numero che viene messo alla sua sinistra e<br />

quello che viene messo alla sua destra; ad esempio 7 mod 3 calcola il resto della <strong>di</strong>visione tra 7 e 3, quin<strong>di</strong> 1; 13<br />

mod 8 -> 5; 19 mod 5 -> 4; quando il primo numero e' multiplo del secondo il resto e' zero: 4 mod 2 -> 0;<br />

15 mod 3 -> 0; 15 mod 5 -> 0; *)<br />

for i := 1 to n do<br />

begin<br />

write( i );<br />

(* i numeri pari <strong>di</strong>visi per due danno resto 0 *)<br />

if i mod 2 = 0 then<br />

writeln(' numero pari')<br />

else<br />

writeln(' numero <strong>di</strong>spari');<br />

end;<br />

writeln('Programma terminato. Premere INVIO per continuare...');<br />

readln; (* per dare il tempo <strong>di</strong> leggere il messaggio *)<br />

end.<br />

ITE4. <strong>di</strong>fficoltà: bassa Stampa dei numeri <strong>di</strong>spari minori o uguali a N, con N letto da tastiera<br />

program <strong>di</strong>spari;<br />

uses newdelay, crt;<br />

var<br />

i: integer; (* contatore ciclo *) n: integer; (* qui viene memorizzato il numero letto da tastiera *)<br />

begin<br />

clrscr; (* cancello lo schermo *)<br />

write('Fino a che numero devo arrivare? -> '); readln( n );<br />

for i := 1 to n do<br />

begin<br />

if i mod 2 = 1 then<br />

writeln( i );<br />

end;<br />

writeln('Programma terminato. Premere INVIO per continuare...');<br />

readln; (* per dare il tempo <strong>di</strong> leggere il messaggio *)<br />

end.


Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 52<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

ITE5. <strong>di</strong>fficoltà: bassa Stampa dei primi N numeri naturali (N letto da tastiera) con a fianco<br />

i rispettivi quadrati e cubi<br />

program potenze;<br />

uses<br />

newdelay, crt;<br />

var<br />

i: integer; (* contatore ciclo *)<br />

n: integer; (* qui viene memorizzato il numero letto da tastiera *)<br />

begin<br />

clrscr; (* cancello lo schermo *)<br />

write('Fino a che numero devo arrivare? -> ');<br />

readln( n );<br />

(* PRIMA SOLUZIONE *)<br />

writeln('PRIMA SOLUZIONE');<br />

for i := 1 to n do<br />

writeln( i, ' ', i*i, ' ', i*i*i );<br />

writeln('--------------------------');<br />

writeln;<br />

(* SECONDA SOLUZIONE (sfrutta l'operatore sqr che calcola il quadrato *)<br />

writeln('SECONDA SOLUZIONE');<br />

for i := 1 to n do<br />

writeln( i, ' ', sqr(i), ' ', sqr(i)*i );<br />

writeln('--------------------------');<br />

writeln;<br />

(* TERZA SOLUZIONE<br />

allinea i numeri usando gotoxy(colonna, riga) per posizionare il cursore alla colonna/riga dello schermo<br />

desiderata; whereY e' una funzione che restituisce il numero <strong>di</strong> riga dove si trova il cursore; quin<strong>di</strong> gotoxy(7,<br />

whereY) significa colonna 7, non muoverti dalla riga su cui ti trovi ...*)<br />

writeln('TERZA SOLUZIONE');<br />

for i := 1 to n do<br />

begin<br />

write( i );<br />

gotoxy(7,whereY); write( sqr(i) );<br />

gotoxy(14,whereY); writeln( sqr(i)*i )<br />

end;<br />

writeln('--------------------------');<br />

writeln;<br />

writeln('Programma terminato. Premere INVIO per continuare...');<br />

readln; (* per dare il tempo <strong>di</strong> leggere il messaggio *)<br />

end.


Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 53<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

ITE6. <strong>di</strong>fficoltà: bassa Stampa dei numeri interi relativi da -N a +N, con N letto da tastiera<br />

program numeri;<br />

uses<br />

newdelay, crt;<br />

var<br />

i: integer; (* contatore ciclo *)<br />

n: integer; (* qui viene memorizzato il numero letto da tastiera *)<br />

begin<br />

clrscr; (* cancello lo schermo *)<br />

write('Fino a che numero devo arrivare? -> ');<br />

readln( n );<br />

for i := -n to n do<br />

writeln( i );<br />

writeln('Programma terminato. Premere INVIO per continuare...');<br />

readln; (* per dare il tempo <strong>di</strong> leggere il messaggio *)<br />

end.<br />

ITE7. <strong>di</strong>fficoltà: bassa Stampa dei numeri da INIZIO a FINE, con INIZIO e FINE letti da tastiera.<br />

Controllare che INIZIO sia ');<br />

readln( inizio );<br />

write('Numero finale -> ');<br />

readln( fine );<br />

(* PRIMA SOLUZIONE *)<br />

writeln('PRIMA SOLUZIONE');<br />

if iniziofine) si esegue un ciclo con gli estremi invertiti; *)


(* SECONDA SOLUZIONE *)<br />

writeln('SECONDA SOLUZIONE');<br />

(* se inizio e fine sono invertiti, li scambio *)<br />

if inizio>fine then<br />

begin<br />

temp:=inizio;<br />

inizio:=fine;<br />

fine:=temp<br />

end;<br />

(* ora inizio e fine sono senza dubbio nell'or<strong>di</strong>ne giusto ... *)<br />

for i := inizio to fine do<br />

writeln( i );<br />

writeln('--------------------------');<br />

writeln;<br />

writeln('Programma terminato. Premere INVIO per continuare...');<br />

readln; (* per dare il tempo <strong>di</strong> leggere il messaggio *)<br />

end.<br />

ITE8. <strong>di</strong>fficoltà: bassa generazione <strong>di</strong> numeri casuali<br />

(* random.pas<br />

Con questo esercizio imparerete:<br />

*)<br />

- l'utilizzo della funzione random per generare numeri in modo casuale<br />

(utile per simulazioni, giochi ecc.<br />

- l'utilizzo della funzione randomize per avere sequenze <strong>di</strong> numeri casuali<br />

sempre <strong>di</strong>verse<br />

program prova_random;<br />

uses newdelay crt;<br />

var x,i,num: integer; (* non hanno un significato particolare *)<br />

begin<br />

clrscr;<br />

(* ESPERIENZA 1 *)<br />

(* random(N) restituisce un numero casuale compreso tra 0 e N-1 *)<br />

x:=random(5); (* x <strong>di</strong>venta un numero compreso tra 0 e 4 *)<br />

writeln('Esperienza 1: ', x);<br />

(* possiamo usare random <strong>di</strong>rettamente, senza memorizzare il risultato in x *)<br />

writeln('Esperienza 1: ',random(9));<br />

readln;<br />

Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 54<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004


(* ESPERIENZA 2 *)<br />

(* generiamo una sequenza <strong>di</strong> 10 numeri compresi tra 0 e 15 *)<br />

for i:=1 to 10 do<br />

writeln('Esperienza 2: ', random(16));<br />

readln;<br />

(* NOTA BENE: tutte le volte che si fa ripartire il programma, random genera<br />

la stessa sequenza <strong>di</strong> numeri (osservatelo con l'esperienza n. 2 facendo<br />

partire il programma piu' volte).<br />

Se si vuole iniziare ogni volta con una sequenza <strong>di</strong>versa, usare il comando<br />

randomize: esso cambia la sequenza basandosi sul tempo scan<strong>di</strong>to<br />

dall'orologio interno del computer *)<br />

(* ESPERIENZA 3 *)<br />

(* riscrivete il ciclo dell'esperienza due facendolo precedere dal comando<br />

randomize; fate ripartire piu' volte il programma e notate come la sequenza<br />

dell'esperienza 3 cambi tutte le volte *)<br />

randomize;<br />

for i:=1 to 4 do<br />

writeln('Esperienza 3: ', random(16));<br />

readln;<br />

(* ESPERIENZA 4 *)<br />

(* Invece <strong>di</strong> un numero possiamo usare come parametro per la random una<br />

variabile intera *)<br />

write ('Dimmi un numero: ');<br />

readln(num);<br />

writeln('Ora genero una sequenza <strong>di</strong> 10 numeri tra 0 e ',num - 1);<br />

for i:=1 to 10 do<br />

writeln('Esperienza 4: ', random(num));<br />

readln;<br />

(* PROVA DA SOLO ...<br />

1. Cosa accade in<strong>di</strong>cando 1 come parametro della random ?<br />

2. Cosa accade in<strong>di</strong>cando 0 come parametro della random ?<br />

3. Cosa accade in<strong>di</strong>cando un numero negativo come parametro della random ?<br />

4. Cosa accade in<strong>di</strong>cando un numero reale come parametro della random ?<br />

5. Cosa accade in<strong>di</strong>cando una variabile integer che contiene un valore<br />

negativo come parametro della random?<br />

*)<br />

Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 55<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

(* ESERCIZI SUGGERITI<br />

ES. 1: trovate un modo per generare numeri tra 1 e N;<br />

---------------------------------------------------------------------------<br />

ES. 2: sfruttando quanto scoperto nell'esercizio n. 1, generate una colonna<br />

<strong>di</strong> risultati <strong>di</strong> una sche<strong>di</strong>na del totocalcio;<br />

---------------------------------------------------------------------------<br />

ES. 3: generate in modo casuale la sequenza dei nomi per le interrogazioni <strong>di</strong> una classe con solo quattro alunni che<br />

si chiamano Primo, Secondo, Terzo, e Quarto;<br />

SUGGERIMENTI: il problema sara' non estrarre piu' volte lo stesso nome; fate corrispondere ogni nome ad un<br />

numero; quando viene estratto quel numero, stampate il nome corrispondente e poi 'cancellate' quel nome<br />

copiando nella variabile che lo memorizza una 'X' al posto del nome; se generate un numero casuale che corrisponde<br />

ad una X (nome gia' estratto) dovete provare con un altro numero; per sapere quando fermarsi gestite<br />

un contatore che aumenterete solo quando non troverete una 'X'<br />

----------------------------------------------------------------------------- *) end.


ITE9. <strong>di</strong>fficoltà: bassa utilizza la random per estrarre numeri da 1 a N<br />

program estrai_1_N;<br />

uses newdelay, crt;<br />

var i,n: integer;<br />

begin<br />

clrscr;<br />

(* dato che random(N) estrae numeri tra 0 ed N-1, e' sufficiente aggiungere<br />

1 al risultato per avere numeri tra 0+1=1 e N-1+1=N<br />

*)<br />

write('Che N vuoi usare? -> ');<br />

readln(n);<br />

(* estraiamo 20 numeri da 1 a n *)<br />

for i:=1 to 20 do<br />

writeln( random(n)+1 );<br />

(* CONCLUSIONI<br />

La formula che genera un numero casuale tra 1 e N e':<br />

RANDOM(N) + 1<br />

*)<br />

readln;<br />

end.<br />

ITE10. <strong>di</strong>fficoltà: bassa utilizza la random per estrarre numeri tra A e B<br />

program estrai_AB;<br />

uses newdelay, crt;<br />

var i,a,b: integer;<br />

begin<br />

clrscr;<br />

(* random(N) estrae numeri tra 0 ed N-1; immaginiamo <strong>di</strong> voler estrarre numeri<br />

tra 5 e 12: e' sufficiente aggiungere a 5 un numero casuale tra 0 e 7<br />

intuitivamente, infatti, 5+0=5 e 5+7=12; in generale dovremo aggiungere<br />

al primo estremo (A) un numero casuale tra 0 e B-A<br />

*)<br />

write('Che A vuoi usare? -> ');<br />

readln(a);<br />

write('Che B vuoi usare? -> ');<br />

readln(b);<br />

(* estraiamo 20 numeri da A a B *)<br />

(* NOTA: attenzione! random(B-A) estrarrebbe numeri tra 0 e (B-A) - 1<br />

e' quin<strong>di</strong> necessario in<strong>di</strong>care random (B-A+1) *)<br />

for i:=1 to 20 do<br />

writeln( A + random(B-A+1) );<br />

readln;<br />

end.<br />

Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 56<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004


ITE11. <strong>di</strong>fficoltà: bassa generazione <strong>di</strong> una colonna <strong>di</strong> una sche<strong>di</strong>na del totocalcio<br />

program colonna_totocalcio;<br />

uses newdelay, crt;<br />

var i: integer; risultato: integer;<br />

begin<br />

clrscr;<br />

randomize;<br />

(* non potendo estrarre la X, useremo un numero al suo posto: il 3; generiamo<br />

allora 13 numeri da 1 a 3 ... *)<br />

for i:=1 to 13 do<br />

begin<br />

risultato:= random(3)+1;<br />

if risultato = 3 then<br />

writeln('X')<br />

else<br />

writeln(risultato) (* 1 o 2 *)<br />

end;<br />

readln;<br />

Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 57<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

(* proviamo con una sche<strong>di</strong>na piu' realistica che privilegia gli 1 rispetto alle X e le X rispetto ai 2; si estrae un n. da<br />

1 a 13 e se e' tra 1 e 6 e' come se fosse uscito l'1, tra 7 e 10 l'X e tra 11 e 13 il 2 *)<br />

for i:=1 to 13 do<br />

begin<br />

risultato:= random(13)+1;<br />

if risultato


Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 58<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

ITE13 <strong>di</strong>fficoltà: bassa far scrivere sullo schermo 5 volte la frase 'come programma mi sento un po' stupido ...';<br />

in<strong>di</strong>care all'inizio <strong>di</strong> ogni riga il suo numero progressivo (1, 2, 3 ...)<br />

program messaggi;<br />

uses<br />

newdelay, crt;<br />

const<br />

var<br />

i: integer; (* contatore ciclo *)<br />

begin<br />

clrscr; (* cancello lo schermo *)<br />

(* PRIMA SOLUZIONE *)<br />

writeln('PRIMA SOLUZIONE');<br />

for i := 1 to QUANTE_VOLTE do<br />

writeln('N. riga: ', i ,' come programma mi sento un po'' stupido ...');<br />

writeln('--------------------------');<br />

writeln;<br />

(* spesso nelle istruzioni <strong>di</strong> un ciclo for e' utile usare la variabile <strong>di</strong> controllo del ciclo stesso; in questo esempio, al<br />

momento <strong>di</strong> scrivere ogni riga la variabile <strong>di</strong> controllo i ha proprio il valore che serve stampare come numero<br />

progressivo <strong>di</strong> riga; infatti quando si stampa per la prima volta il messaggio il suo valore e' uno, quando si stampa<br />

il messaggio per la seconda volta il suo valore e' due ecc. *)<br />

(* SECONDA SOLUZIONE *)<br />

writeln('SECONDA SOLUZIONE');<br />

for i := 1 to QUANTE_VOLTE do<br />

begin<br />

write('N. riga: ');(* non va a capo ... *)<br />

write(i); (* non va a capo ... *)<br />

writeln(' come programma mi sento un po'' stupido ...');<br />

end;<br />

writeln('--------------------------');<br />

writeln;<br />

(* commenti: la seconda soluzione e' equivalente alla prima; notate come sia INDISPENSABILE aggiungere un<br />

begin ed un end per racchiudere ed in<strong>di</strong>care in modo preciso l'inizio e la fine del blocco <strong>di</strong> istruzioni che deve<br />

essere ripetuto; se infatti avessimo scritto:<br />

for i := 1 to QUANTE_VOLTE do<br />

write('N. riga: ');<br />

write(i);<br />

writeln(' come programma mi sento un po'' stupido ...');<br />

writeln('--------------------------');<br />

il compilatore non potrebbe in alcun modo capire quali sono le istruzioni da ripetere (per quale motivo fermarsi al<br />

primo write piuttosto che al secondo?); l'aver allineato i coman<strong>di</strong> write/writeln serve solo a rendere piu' leggibile il<br />

co<strong>di</strong>ce per noi ... ma per il compilatore significa comprendere nel ciclo for SOLO LA RIGA SUCCESSIVA:<br />

for i := 1 to QUANTE_VOLTE do<br />

write('N. riga: ');<br />

write(i);<br />

writeln(' come programma mi sento un po'' stupido ...');<br />

writeln('--------------------------');<br />

prova a togliere il begin/end e sperimenta personalmente che il ciclo


stamperebbe per <strong>di</strong>eci volte la scritta 'N. riga:' e solo una volta il<br />

resto !!<br />

Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 59<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

A COSA SERVE ALLORA AVER COMPLICATO IL CODICE ??<br />

RISPOSTA: AD AVER MAGGIOR CONTROLLO, COME MOSTRATO NELLA 'TERZA SOLUZIONE' *)<br />

(* TERZA SOLUZIONE<br />

questa volta si 'pretende' che il messaggio che precede il numero <strong>di</strong> riga<br />

sia scritto in rosso, il numero <strong>di</strong> riga in giallo e la scritta in verde<br />

*)<br />

writeln('TERZA SOLUZIONE');<br />

for i := 1 to QUANTE_VOLTE do<br />

begin<br />

textcolor(RED); write('N. riga: ');(* non va a capo ... *)<br />

textcolor(YELLOW); write(i); (* non va a capo ... *)<br />

textcolor(GREEN); writeln(' come programma mi sento un po'' stupido ...');<br />

end;<br />

writeln('--------------------------');<br />

writeln;<br />

(* commenti: solo costruendo il messaggio una porzione alla volta usando il write e' possibile intervallare i coman<strong>di</strong><br />

per la selezione del colore *)<br />

writeln('Programma terminato. Premere INVIO per continuare...');<br />

readln; (* per dare il tempo <strong>di</strong> leggere il messaggio *)<br />

end.


STRUTTURA ITERATIVA INDEFINITA – il ciclo repeat until<br />

Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 60<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

Serve a ripetere una o più istruzioni fino al momento in cui la con<strong>di</strong>zione specificata dopo la parola chiave until<br />

<strong>di</strong>venta vera. Questo momento non è preve<strong>di</strong>bile da cui la denominazione ‘indefinita’ per questo tipo <strong>di</strong> ciclo.<br />

Ovviamente il ciclo terminerà solo se le istruzioni che vengano ripetute avranno come effetto il raggiungimento<br />

della con<strong>di</strong>zione <strong>di</strong> uscita. Se questo non dovesse avvenire il ciclo si ripeterebbe all'infinito (in gergo si <strong>di</strong>rebbe che<br />

il programma è ‘andato in loop infinito’ o, semplicemente,’ andato in loop’)<br />

Nell'esempio che segue si chiede all'utente <strong>di</strong> inserire un valore maggiore o uguale 10. Se l'utente sbaglia (inserendo<br />

un valore minore <strong>di</strong> 10) riceve un avviso ed il dato viene richiesto. Ecco come viene programmata questa<br />

operatività:<br />

program prova;<br />

var valore: integer;<br />

begin<br />

repeat<br />

writeln(‘Inserire un numero maggiore od uguale 10: ‘);<br />

readln (valore );<br />

if valore=10;<br />

… resto del programma …<br />

end.<br />

Caratteristiche del ciclo repeat:<br />

• il controllo che decide se ripetere o meno il blocco delle istruzioni si trova in fondo al ciclo ed è per questo<br />

che il ciclo repeat viene detto con controllo in coda; questo comporta che almeno una volta le istruzioni del<br />

ciclo vengono eseguite; qualche volta questo è un vantaggio ma altre volte no (quando è necessario controllare<br />

prima una con<strong>di</strong>zione senza la quale potrebbe essere ad<strong>di</strong>rittura deleterio eseguire le istruzioni); se c'è questa<br />

necessità probabilmente è meglio utilizzare l'altro tipo <strong>di</strong> struttura iterativa indefinita: il ciclo while (ve<strong>di</strong> più<br />

avanti)<br />

• il ciclo termina quando la con<strong>di</strong>zione <strong>di</strong> uscita è vera: per questo motivo si parla anche <strong>di</strong> ciclo con uscita per<br />

vero<br />

Uscita dal ciclo repeat con contatore.<br />

Fondamentalmente si utilizza una variabile per contare quante volte è stato eseguito il ciclo. In pratica possiamo<br />

emulare il comportamento <strong>di</strong> un ciclo for ma con tanta flessibilità in più: non siamo ad esempio costretti a contare <strong>di</strong><br />

uno in uno ma possiamo scegliere <strong>di</strong> quanto aumentare la variabile <strong>di</strong> controllo ad ogni esecuzione del ciclo; non<br />

siamo neppure costretti ad incrementarla <strong>di</strong> valori interi: potremmo ad esempio optare per incrementi <strong>di</strong> millesimi <strong>di</strong><br />

unità per calcoli <strong>di</strong> tipo scientifico. Ve<strong>di</strong>amo qualche esempio.<br />

i:=0;<br />

repeat<br />

i := i+1;<br />

writeln( i );<br />

until i=10;<br />

Sarebbe impossibile programmare la stessa operatività<br />

utilizzando il ciclo for.<br />

Chiaramente è impossibile sapere quante volte sarà necessario<br />

ripetere le operazioni a causa <strong>di</strong> ripetuti errori da parte<br />

dell'utente!<br />

Questo ciclo stampa sul video i numeri da uno a 10. Tutti d'accordo sul fatto che in situazioni <strong>di</strong> questo tipo sia meglio<br />

utilizzare il ciclo for: con quest'ultimo non è il programmatore a doversi ricordare che è necessario inizializzare la<br />

variabile <strong>di</strong> controllo (i:=0), incrementarla ad ogni ciclo (i:=i+1) e verificare se è arrivato il momento <strong>di</strong> interrompere il<br />

ciclo, ma è tutto svolto in automatico secondo le in<strong>di</strong>cazioni date dal programmatore sulla prima riga della struttura<br />

fordo.


i:=0;<br />

repeat<br />

i := i+3;<br />

writeln( i );<br />

until i=21;<br />

Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 61<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

Ma ecco una prima applicazione interessante: vengono stampati tutti i multipli <strong>di</strong> 3 fino al 21. Non è necessario utilizzare<br />

l'operatore MOD per testare la <strong>di</strong>visibilità per tre <strong>di</strong> ogni singolo valore della variabile <strong>di</strong> controllo come avremmo fatto<br />

con un ciclo for: invece <strong>di</strong> incrementare la variabile <strong>di</strong> una unità alla volta, si aggiunge tre in<strong>di</strong>viduando ogni volta a colpo<br />

sicuro un nuovo multiplo!


Uscita dal ciclo repeat con domanda all'operatore.<br />

Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 62<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

Da usare in quei casi in cui è necessario far inserire da tastiera una sequenza <strong>di</strong> dati ed è l'operatore che deve<br />

decidere quando sono finiti. Immaginiamo un programma funzionante all'ingresso <strong>di</strong> una mostra che deve servire a<br />

richiedere i dati a grafici <strong>di</strong> ogni visitatore ed a stampare per ciascuno un cartellino <strong>di</strong> riconoscimento. Non è<br />

ovviamente possibile utilizzare un ciclo for: impossibile, infatti, sapere a priori quanti saranno i visitatori… Per lo<br />

stesso motivo non è possibile utilizzare un ciclo repeat con uscita controllata da un contatore. È invece sufficiente<br />

organizzare un ciclo repeat in cui l'uscita venga decisa dalla risposta data dall'operatore ad una domanda posta dal<br />

computer come ultimo per azione del ciclo:<br />

repeat<br />

writeln(‘Inserire il nominativo del visitatore: ‘);<br />

readln(nome_cognome);<br />

writeln(‘Inserire la data <strong>di</strong> nascita del visitatore: ‘);<br />

readln(data_nascita);<br />

<br />

writeln(Sono terminati i visitatori?);<br />

readln( risp );<br />

until risp=;<br />

Uscita dal ciclo repeat con inserimento <strong>di</strong> un valore convenzionale.<br />

È un miglioramento della tecnica precedente. Quest'ultima, infatti, costringe l'operatore a rispondere anche molte<br />

volte alla stessa domanda per <strong>di</strong>re quando sono terminati i dati. Il ‘trucco’ consiste nello stabilire che il ciclo deve<br />

terminare quando alla richiesta <strong>di</strong> uno dei dati previsti viene inserito un valore speciale. Riprendendo l'esempio della<br />

mostra potremmo decidere che il ciclo termina quando come nominativo del visitatore viene inserita la parola<br />

NESSUNO. Ecco come potrebbe apparire il co<strong>di</strong>ce:<br />

repeat<br />

writeln(‘Inserire il nominativo del visitatore (NESSUNO per terminare): ‘);<br />

readln(nome_cognome);<br />

if nome_cognomeNESSUNO then<br />

begin<br />

writeln(‘Inserire la data <strong>di</strong> nascita del visitatore: ‘);<br />

readln(data_nascita);<br />

<br />

end;<br />

until nome_cognome=NESSUNO;<br />

NOTA: la con<strong>di</strong>zione <strong>di</strong> uscita da un ciclo repeat può essere composta, tipo quelle già viste per la struttura selettiva.<br />

L'esempio seguente richiede all'operatore un valore compreso nell'intervallo 1-100:<br />

repeat<br />

writeln(‘Inserire un valore compreso tra 1 e 100: ‘);<br />

readln(valore);<br />

until (valore>=1) and (valore


STRUTTURA ITERATIVA INDEFINITA – il ciclo while<br />

Si <strong>di</strong>stingue dal repeat per queste caratteristiche:<br />

Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 63<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

• il controllo che decide la terminazione ciclo è fatto prima delle istruzioni del ciclo stesso: per questo motivo<br />

viene anche in<strong>di</strong>cato come ciclo con controllo in testa; se la con<strong>di</strong>zione non è sod<strong>di</strong>sfatta fin dall'inizio il ciclo<br />

potrebbe non cominciare neppure, cioè le sue istruzioni non essere eseguite neanche una volta;<br />

• il ciclo termina quando la con<strong>di</strong>zione è falsa: si parla infatti <strong>di</strong> ciclo con uscita per falso;<br />

Ecco la sintassi:<br />

while con<strong>di</strong>zione do oppure while con<strong>di</strong>zione do<br />

istruzioneSingola begin<br />

istruzione1;<br />

istruzione1;<br />

…<br />

istruzioneN;<br />

end<br />

In generale potremmo <strong>di</strong>re che le tecniche per decidere la terminazione del ciclo viste per il ciclo repeat vanno bene<br />

anche per questo ciclo con i dovuti adattamenti resi necessari dalla <strong>di</strong>fferente logica:<br />

Stampa dei numeri da 1 a 10. Quando stampa il 10 la con<strong>di</strong>zione <strong>di</strong>venta falsa (10 non è minore <strong>di</strong> 10 ma uguale!)<br />

i:=0;<br />

while i


end;<br />

end (* del while *)<br />

Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 64<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004


Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 65<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

ESERCIZI RIEPILOGATIVI SUI CICLI REPEAT E WHILE<br />

ITE14 <strong>di</strong>fficoltà: me<strong>di</strong>a Calcolo delle prime N potenze <strong>di</strong> 2; deve essere 0


ITE15. <strong>di</strong>fficoltà: bassa Calcolo della me<strong>di</strong>a <strong>di</strong> N numeri inseriti dall'utente<br />

program me<strong>di</strong>aNnumeri;<br />

var<br />

n: integer; (* quanti numeri inserire *)<br />

numero: integer;<br />

somma: real ; (* la somma può superare 32767 ... *)<br />

me<strong>di</strong>a: real;<br />

i: integer; (* per i cicli *)<br />

begin<br />

writeln('CALCOLO MEDIA');<br />

writeln;<br />

(* continuo a chiedere un N finche' sod<strong>di</strong>sfa ... *)<br />

repeat<br />

writeln('quanti numeri vuoi inserire? (N>0): ');<br />

readln(n);<br />

if n0;<br />

(* VEDIAMO PRIMA LA SOLUZIONE CON IL REPEAT ...*)<br />

somma:=0; i:=1;<br />

writeln('SOLUZIONE CON IL REPEAT');<br />

repeat<br />

write('Inserire un numero (',i,') -> ' );<br />

readln(numero);<br />

somma:=somma+numero;<br />

i:=i+1;<br />

until i>n;<br />

me<strong>di</strong>a:=somma/n;<br />

writeln('me<strong>di</strong>a: ',me<strong>di</strong>a:6:2);<br />

readln;<br />

(* VEDIAMO POI LA SOLUZIONE CON IL WHILE ...*)<br />

somma:=0; i:=1;<br />

writeln('SOLUZIONE CON IL WHILE');<br />

while i


(* VEDIAMO INFINE LA SOLUZIONE CON IL FOR ...*)<br />

writeln('SOLUZIONE CON IL FOR');<br />

somma:=0;<br />

for i:=1 to n do<br />

begin<br />

write('Inserire un numero (',i,') -> ' );<br />

readln(numero);<br />

somma:=somma+numero;<br />

end;<br />

me<strong>di</strong>a:=somma/n;<br />

writeln('me<strong>di</strong>a: ',me<strong>di</strong>a:6:2);<br />

readln;<br />

end.<br />

Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 67<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

ITE16. <strong>di</strong>fficoltà: me<strong>di</strong>a Dato in input un numero intero N, sommare i primi N numeri <strong>di</strong>spari e<br />

verificare che tale somma e' uguale al quadrato <strong>di</strong> N.<br />

program som_<strong>di</strong>sp;<br />

uses newdelay, crt;<br />

var<br />

i,quanti: integer; numero,somma: real;<br />

begin<br />

clrscr;<br />

(* il controllo che segue*sembra* efficace: provate con 35000;<br />

e poi con 70000: l'errore non viene rilevato; perche' ?? *)<br />

repeat<br />

writeln('Quanti numeri <strong>di</strong>spari vuoi considerare? (da 0 a ',MAXINT,')');<br />

readln(quanti);<br />

if (quantiMAXINT) then<br />

writeln('Errato, ripetere l''inserimento')<br />

until (quanti>=0) and (quanti


Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 68<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

ITE17. <strong>di</strong>fficoltà: me<strong>di</strong>a Un giro turistico e' fatto <strong>di</strong> N tappe, delle quali si introducono da tastiera<br />

il nome della citta' <strong>di</strong> arrivo e i km percorsi. Calcolare il percorso totale e il percorso me<strong>di</strong>o delle tappe<br />

program tappe;<br />

uses newdelay, crt; var<br />

i,quante_tappe: integer;<br />

km_tappa,somma_km: real;<br />

citta: string;<br />

begin<br />

clrscr;<br />

repeat<br />

writeln('Quante sono le tappe? (da 0 a ',MAXINT,')');<br />

readln(quante_tappe);<br />

if (quante_tappeMAXINT) then<br />

writeln('Errato, ripetere l''inserimento')<br />

until (quante_tappe>=0) and (quante_tappe=0) and (km_tappa


COSA SONO I FLOW CHART<br />

Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 69<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

I flow chart sono schemi che descrivono visivamente come procede l’esecuzione <strong>di</strong> un programma. Essi non sono<br />

legati ad uno specifico linguaggio: dato un flow chart, il programmatore può poi usare un qualsiasi linguaggio <strong>di</strong><br />

programmazione (si tratta, per così <strong>di</strong>re, <strong>di</strong> un linguaggio visuale comprensibile a tutti i programmatori). Il flow<br />

chart aiuta anche il programmatore a descrivere correttamente un algoritmo (il proce<strong>di</strong>mento risolutivo <strong>di</strong> un<br />

problema).<br />

Ogni tipo <strong>di</strong> istruzione che si può inserire in un programma ha un suo simbolo ed ognuna delle tre strutture<br />

fondamentali della programmazione (sequenza, selezione ed iterazione) può essere rappresentata. Esistono anche<br />

simboli speciali (inizio programma, fine programma ecc.) che non rappresentano istruzioni vere e proprie ma che<br />

sono utili per la costruzione del flow chart.<br />

I PRINCIPALI SIMBOLI<br />

Inizio programma (i simboli sono equivalenti) Fine programma (i simboli sono<br />

equivalenti)<br />

puoi ricordare solo un simbolo a scelta … puoi ricordare solo un simbolo a<br />

scelta …<br />

inizio start S I<br />

fine end F<br />

‘S’ sta per Start, ‘i’ sta per Inizio ‘F’ sta per fine, ‘E’ sta per End<br />

Assegnamento o altre istruzioni generiche Scrittura <strong>di</strong> un valore sul video (i simboli<br />

sono equivalenti)<br />

(esempio: dai ad x il valore 8) puoi ricordare solo un simbolo a<br />

scelta …<br />

x 8 Scrivi ‘ciao’ S ‘ciao’<br />

(esempio <strong>di</strong> istruzione generica)<br />

attiva allarme<br />

‘S’ sta per scrivi, ‘W’ sta per Write<br />

Lettura <strong>di</strong> un valore scritto con la tastiera e memorizzato nella variabile in<strong>di</strong>cata<br />

(i simboli che seguono sono equivalenti: ricordane uno a tua scelta ..)<br />

Esempio: leggere dove abita una persona e memorizzare l’informazione nella variabile in<strong>di</strong>rizzo<br />

Leggi in<strong>di</strong>rizzo L in<strong>di</strong>rizzo R in<strong>di</strong>rizzo<br />

‘L’ sta per Leggi, ‘R’ sta per Read<br />

E<br />

W ‘ciao’


ALTRI ESEMPI<br />

x y x y - 1<br />

dai ad x lo stesso valore <strong>di</strong> y dai ad x il valore <strong>di</strong> y <strong>di</strong>minuito <strong>di</strong> 1<br />

x (5 – 2) * (4 + 3)<br />

dai ad x il valore dell’espressione (5 – 2) * (4 + 3)<br />

Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 70<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

I simboli visti fino ad ora servono per istruzioni singole. Ve<strong>di</strong>amo ora come si rappresentano le strutture<br />

fondamentali della programmazione (sequenza, selezione, iterazione).<br />

Sequenza<br />

S x, y<br />

scrivi sul video prima il valore della<br />

variabile x e poi quello della variabile y<br />

Per in<strong>di</strong>care che due istruzioni vanno eseguite una dopo l’altra si mettono i loro simboli uno sotto l’altro<br />

collegandoli con una freccia. Ecco un esempio.<br />

Calcoliamo quante settimane ci sono in un anno <strong>di</strong>videndo il<br />

numero dei giorni per 7.<br />

Memorizziamo prima il risultato della <strong>di</strong>visione nella<br />

variabile quante.<br />

Poi scriviamo un messaggio sul video che ‘annuncia’ il<br />

risultato<br />

Infine scriviamo il risultato, cioè il valore attuale della<br />

variabile quante.<br />

Il senso della freccia in<strong>di</strong>ca il flusso <strong>di</strong> esecuzione.<br />

L cognome, nome<br />

accetta dalla tastiera un primo valore che<br />

memorizzerai nella variabile ‘cognome’ e poi un<br />

secondo valore che memorizzerai nella variabile<br />

‘nome’<br />

quante


Selezione (decisione, scelta, alternativa)<br />

Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 71<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

Il solo fatto <strong>di</strong> poter in<strong>di</strong>care un’istruzione dopo l’altra (sequenza) non ci consentirebbe <strong>di</strong> sviluppare programma<br />

interessanti. Ci sono innumerevoli situazioni in cui è necessario fare un ‘controllo’ ed agire <strong>di</strong> conseguenza.<br />

Esempi<br />

Se l’anno è bisestile allora considera febbraio con 29 giorni, altrimenti consideralo con 28.<br />

Se l’età è minore <strong>di</strong> 18 allora applica sconto, altrimenti applica prezzo pieno.<br />

Se la temperatura supera 37 C allora fai suonare l’allarme.<br />

Come avrete osservato, si controlla una con<strong>di</strong>zione (anno bisestile ?, età minore <strong>di</strong> 18 ?, temperatura supera 37 ?)<br />

che può risultare vera o falsa. In qualche caso (primi due esempi) ci sono operazioni (<strong>di</strong>verse) sia nel caso la<br />

con<strong>di</strong>zione risulti vera sia nel caso risulti falsa. E’ però possibile (terzo esempio) che nel caso la con<strong>di</strong>zione sia falsa<br />

non ci sia nulla da fare.<br />

Ecco come si rappresenta in un flow chart la struttura selettiva:<br />

Se l’anno è bisestile allora<br />

considera febbraio con 29 giorni<br />

altrimenti<br />

consideralo con 28<br />

questo tipo <strong>di</strong> selezione<br />

è detto a 2 vie<br />

La con<strong>di</strong>zione deve essere scritta all’interno del rombo. Il flusso<br />

<strong>di</strong> esecuzione si <strong>di</strong>vide a seconda del risultato del controllo<br />

per Vero e F sta per Falso) e solo la strada ‘a destra’ o ‘a<br />

sinistra’ viene percorsa.<br />

F<br />

(V sta<br />

Le istruzioni nella sezione ‘vera’ (o ‘falsa’) possono essere anche molte e non una sola come nel nostro esempio.<br />

Quando le istruzioni da eseguire a con<strong>di</strong>zione vera (o falsa) sono terminate il flusso si ricongiunge usando il simbolo<br />

chiamato connettore.<br />

Attenzione: nel rombo potete mettere solo con<strong>di</strong>zioni, non NO assegnamenti! !!<br />

Sarebbe quin<strong>di</strong> un errore grave il seguente:<br />

x


Se l’età è minore <strong>di</strong> 18 allora<br />

applica sconto<br />

altrimenti<br />

applica prezzo pieno.<br />

Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 72<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

Nota: nel flow chart immaginiamo che<br />

le variabili eta e prezzo abbiano già un<br />

valore valido e che i calcoli in<strong>di</strong>cati siano quin<strong>di</strong> fattibili. Lo sconto (10%) viene calcolato <strong>di</strong>videndo il prezzo pieno<br />

per 100 (calcolando così l’1%) e moltiplicando il risultato per 10 (ottenendo il 10%).<br />

Se la temperatura supera 37 C allora<br />

fai suonare l’allarme.<br />

Come potete vedere se non c’è nulla che deve essere fatto quando la<br />

con<strong>di</strong>zione è falsa, si congiunge la parte ‘falso’ <strong>di</strong>rettamente con il<br />

connettore.<br />

V F<br />

eta < 18<br />

sconto 37<br />

V<br />

attiva allarme<br />

F<br />

questo tipo <strong>di</strong> selezione<br />

è detto a una via<br />

NO !!<br />

ERRORE !!!<br />

V<br />

temp > 37<br />

V<br />

attiva allarme<br />

S prezzo<br />

F<br />

F


Selezione a molte vie<br />

Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 73<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

Esiste anche un’utile variante che prevede molte vie a seconda del valore <strong>di</strong> una variabile. Immaginiamo che la<br />

variabile mese contenga un valore da 1 a 12 che rappresenta uno dei mesi dell’anno. Di nuovo, vorremmo sapere<br />

quanti giorni considerare. Vi ricordo che i mesi con 31 giorni sono 1 (gennaio), 3 (marzo), 5 (ecc.), 7, 8, 10, 12;<br />

quelli con 30 giorni sono 4 (aprile), 6, 9, 11; immaginiamo per semplicità che febbraio (2) ne abbia sempre 28.<br />

giorni


SEL2. Inserita un'età', <strong>di</strong>re se siamo in<br />

presenza <strong>di</strong> un maggiorenne o <strong>di</strong> un<br />

minorenne; controllare anche eventuali<br />

errori <strong>di</strong> inserimento da parte dell'utente<br />

Prima soluzione: ipotizzando un età<br />

massima <strong>di</strong> 120 anni, controlliamo che<br />

l’età inserita non sia al <strong>di</strong> fuori<br />

dell’intervallo 0 (neonato) – 120.<br />

NOTA: siate coerenti con l’utilizzo delle<br />

etichette ‘V’ e ‘F’: io ho scelto <strong>di</strong><br />

proseguire per vero (V) sempre a destra e<br />

per falso (F) sempre a sinistra.<br />

S 'minorenne'<br />

Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 74<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

eta>=18<br />

S 'maggiorenne'<br />

NOTA: è importante <strong>di</strong>fferenziare con un<br />

pallino-connettore separato le uscite dalle<br />

due strutture selettive. Il seguente flow<br />

chart è quin<strong>di</strong> sbagliato. Non riusciremmo<br />

infatti a tradurlo nel corrispondente<br />

programma Pascal (si accavallerebbero gli<br />

‘end’ dei blocchi ‘begin … end’ delle<br />

parti ‘then’ ed ‘else’ dei due ‘if’ !!<br />

S 'minorenne'<br />

L eta<br />

F V<br />

eta120<br />

eta>=18<br />

F<br />

F<br />

S 'maggiorenne'<br />

NO !!<br />

L eta<br />

F V<br />

eta120<br />

V<br />

V<br />

S 'errore'<br />

S 'errore'


Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 75<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

SEL2 bis. Con l’algoritmo precedente, quando l’utente sbaglia ad inserire un valore per l’età non si sa se per un<br />

valore negativo o maggiore <strong>di</strong> 120: si sa solo che ha commesso un errore ma non quale. Il seguente flow chart<br />

rappresenta un algoritmo che tiene conto anche <strong>di</strong> questo particolare, <strong>di</strong>fferenziando i controlli sui possibili errori:<br />

Struttura a ‘cascata’<br />

S 'minorenne'<br />

F<br />

eta>=18<br />

S 'maggiorenne'<br />

SEL3bis. Inserito un numero, <strong>di</strong>re se e' pari o <strong>di</strong>spari<br />

NOTA: si sta immaginando che in ogni linguaggio <strong>di</strong><br />

programmazione esista un operatore per il calcolo del<br />

modulo: MOD.<br />

E’ comunque accettabile (mi sto rivolgendo ai miei<br />

alunni) usare gli operatori del Pascal.<br />

V<br />

F<br />

eta>120<br />

V<br />

F<br />

S 'non puoi avere più!'<br />

'<strong>di</strong> 120 anni !!<br />

F<br />

S '<strong>di</strong>spari'<br />

L eta<br />

eta


SEL4. Inserito un carattere, <strong>di</strong>re se e' una vocale od una consonante.<br />

Soluzione 1 (non si fa <strong>di</strong>stinzione tra le vocali)<br />

F<br />

S 'consonante'<br />

L c<br />

c


Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 77<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

SEL5. Inseriti A, B e C <strong>di</strong>re se B e' compreso tra A e C; in pratica si controlla se B appartiene all'intervallo [A,C]<br />

(ripetizione, cicli)<br />

Iterazione<br />

Pur avendo a <strong>di</strong>sposizione sequenza e<br />

iterazione rimangono molte le situazioni ‘intrattabili’ con questi strumenti. Consideriamo infatti questo problema<br />

all’apparenza molto semplice: stampare i numeri da 1 a 10000. Certamente la soluzione <strong>di</strong> usare per 10000 volte<br />

l’istruzione <strong>di</strong> scrittura per stampare ogni numero non è molto praticabile …<br />

I<br />

S 1<br />

S 2<br />

…<br />

S 10000<br />

F<br />

F<br />

S 'intervallo'<br />

'impossibioe'<br />

L a, c<br />

a =a and b


Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 78<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

Diversamente procederà all’infinito (come potrebbe servire per il controllo <strong>di</strong> un processo industriale).<br />

Questo tipo <strong>di</strong> ciclo è detto ‘con ripetizione per falso’, o a ‘uscita per vero’ ed ancora ‘con controllo in coda’ (cioè<br />

alla fine del ciclo).<br />

Notiamo ancora: con questo tipo <strong>di</strong> ciclo le istruzioni vengono eseguite almeno una volta.<br />

Ve<strong>di</strong>amo il flow chart completo per la stampa dei numeri da 1 a 10000<br />

F<br />

I<br />

Num


Esercizi risolti sulla struttura iterativa<br />

ITE1: stampa dei primi N numeri naturali,<br />

con N letto da tastiera.<br />

L n<br />

i


ITE3. Stampa dei primi N numeri naturali,<br />

con N letto da tastiera; a fianco <strong>di</strong> ciascun<br />

numero in<strong>di</strong>care se e' pari o <strong>di</strong>spari<br />

F<br />

S '<strong>di</strong>spari'<br />

L n<br />

i


ITE5. Far inserire una sequenza <strong>di</strong> numeri da tastiera.<br />

La sequenza si intende terminata quando viene<br />

inserito il numero 0. Si deve calcolare e visualizzare<br />

la somma <strong>di</strong> tutti i numeri inseriti.<br />

F<br />

totale


Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 82<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

1. Chiedere da tastiera l’inserimento <strong>di</strong> una sequenza <strong>di</strong> date. Ad ogni data, espressa nella forma giorno mese ed<br />

anno (‘g’, ‘m’ ed ‘a’ nel flow chart), aggiungere un giorno e stampare la data risultante. E’ l’utente del programma<br />

che <strong>di</strong>ce quando sono finite le date (rispondendo ad una opportuna domanda posta dal programma). Ipotesi<br />

semplificativa: considerare Febbraio sempre da 28 giorni. Traccia: è necessario prestare attenzione alle date che<br />

corrispondono a fine mese o anno: nella data risultante cambierà sicuramente il mese e forse anche l’anno …<br />

g>31<br />

V<br />

g


2. Stampa dei numeri da B ad A con B>A.<br />

Controllare che B sia maggiore <strong>di</strong> A<br />

prima <strong>di</strong> iniziare.<br />

L A<br />

L B<br />

B > A<br />

V<br />

s B<br />

B


I SOTTOPROGRAMMI - riutilizzare per sopravvivere !<br />

Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 84<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

Se un programmatore professionista dovesse sempre ricominciare da zero nello scrivere i programmi, <strong>di</strong>venterebbe<br />

presto un ... accattone professionista !K. Ad esempio, se tutte le volte che fosse necessario lo stesso calcolo<br />

complesso dovessimo riscrivere le sue istruzioni, i programmi <strong>di</strong>venterebbero più lunghi del necessario, più <strong>di</strong>fficili da<br />

leggere, sprecheremmo più tempo e rischieremmo ogni volta <strong>di</strong> commettere errori <strong>di</strong>versi (e sempre per risolvere lo<br />

stesso problema).<br />

Una pericolosa illusione ...<br />

L’utilizzo del ‘copia/incolla’ per ricopiare le righe farebbe risparmiare solo fatica ma ci esporrebbe a gravi rischi ... Per<br />

convincercene, immaginiamo che le istruzioni siano una cinquantina e <strong>di</strong> averle copiate/incollate più volte in <strong>di</strong>versi<br />

programmi. Cosa accadrebbe se scoprissimo un errore nelle righe copiate così tante volte? Dovremmo mo<strong>di</strong>ficare<br />

tutte le copie delle cinquanta istruzioni incriminate … e guai a <strong>di</strong>menticarcene una !<br />

La soluzione: i sottoprogrammi<br />

Per fortuna una soluzione c’è e la sapete già usare. Lo avete fatto tutte le volte che avete usato coman<strong>di</strong> come<br />

sqrt(9) o clrscr. Questi coman<strong>di</strong> richiamano un programma secondario (si parla infatti <strong>di</strong> sottoprogrammi) che<br />

svolgono il compito previsto. Nel caso della sqrt il compito è il calcolo della ra<strong>di</strong>ce quadrata <strong>di</strong> un numero (quello che<br />

deve essere specificato tra parentesi dopo il nome del comando); nel caso <strong>di</strong> clrscr non deve essere calcolato nulla<br />

ma semplicemente cancellato il video. Dopo l’esecuzione del comando il programma principale continua la sua<br />

<strong>elaborazione</strong>.<br />

ESEMPIO (calcolo della ra<strong>di</strong>ce quadrata <strong>di</strong> un numero)<br />

Program prova;<br />

var<br />

x: real; (* il numero <strong>di</strong> cui si vuole la ra<strong>di</strong>ce quadrata *)<br />

ra<strong>di</strong>ce_quadrata: real; (* il risultato da calcolare *)<br />

begin<br />

writeln(‘Inserisci un numero ed io calcolero’’ la sua ra<strong>di</strong>ce quadrata: ’);<br />

readln(x);<br />

ra<strong>di</strong>ce_quadrata:=sqrt(x);<br />

writeln(‘risultato: ‘, ra<strong>di</strong>ce_quadrata );<br />

readln;<br />

end.<br />

per sod<strong>di</strong>sfare la richiesta della riga precedente il programma principale si ‘ferma’ e<br />

richiama il programma secondario sqrt; quando si riceve il risultato, il programma<br />

principale continua con la riga qui sotto stampando il valore calcolato *)<br />

Tipi <strong>di</strong> sottoprogrammi: procedure e funzioni<br />

Quando un sottoprogramma restituisce al programma principale un valore (come sqrt con la ra<strong>di</strong>ce quadrata) si<br />

chiama funzione (function). Quando non restituisce nulla si chiama procedura (procedure). Un esempio <strong>di</strong> procedure<br />

è rappresentata dal comando clrscr: non restituisce nulla programma principale ma si limita a cancellare il video.<br />

I parametri<br />

Un sottoprogramma può aver bisogno <strong>di</strong> uno o più dati per portare a termine il suo compito. Ad esempio sqrt ha<br />

bisogno del numero <strong>di</strong> cui si vuole calcolare la ra<strong>di</strong>ce. Clrscr non ha invece bisogno <strong>di</strong> alcun dato. Questi dati si<br />

chiamano parametri e vengono in<strong>di</strong>cati tra parentesi dopo il nome del sottoprogramma.<br />

Nel comando sqrt( 9 ) il parametro è costituito dalla costante numerica 9. Nel comando sqrt (12+3) il parametro è<br />

costituito dal risultato dell’espressione 12+3. Nel comando sqrt(X) il parametro è il valore contenuto nella variabile X<br />

al momento dell’uso del comando.<br />

Se i parametri sono più d’uno vanno separati con la virgola. Ad esempio il comando gotoxy(10, 5) sposta il punto <strong>di</strong><br />

scrittura sul video alla colonna 10, riga 5. Il primo parametro è il numero della colonna, il secondo il numero della<br />

riga. NOTA: per provare il comando gotoxy dovete in<strong>di</strong>care uses crt come spiegato alla pagina successiva.


Abbiamo due possibilità:<br />

Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 85<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

Interessante… ma quali e quanti sottoprogrammi abbiamo a <strong>di</strong>sposizione ?<br />

Usare un sottoprogramma scritto da altri. I sottoprogrammi riutilizzabili sono spesso raccolti in librerie. Una libreria è<br />

un file (archivio) normalmente presente sul <strong>di</strong>sco e contiene blocchi <strong>di</strong> co<strong>di</strong>ce già tradotto in linguaggio macchina che<br />

possono essere a comando incorporati in un programma. Alcune librerie sono sempre fornite insieme all’ambiente <strong>di</strong><br />

sviluppo che si decide <strong>di</strong> usare (sia che si tratti <strong>di</strong> un prodotto commerciale sia che si tratti <strong>di</strong> un prodotto gratuito<br />

come accade per l’open source).<br />

Ad esempio con il Turbo Pascal sono <strong>di</strong>sponibili un buon numero <strong>di</strong> librerie insieme al prodotto acquistato. Il Turbo<br />

Pascal chiama le librerie unit (unità <strong>di</strong> programmazione). NOTA: per un elenco completo <strong>di</strong> queste librerie consultate<br />

l'help in linea del turbo Pascal (lo potete attivare con la combinazione SHIFT-F1, cercando poi sotto la lettera S il<br />

paragrafo Standard Unit).<br />

La funzione sqrt ed altri sottoprogrammi <strong>di</strong> uso assai comune sono memorizzate in una libreria (la System) per la<br />

quale non è neppure necessario in<strong>di</strong>carne l’uso: viene automaticamente inclusa con il resto del programma.<br />

Il comando clrscr e gotoxy() invece sono due dei sottoprogrammi <strong>di</strong>sponibili nella unit crt. Per questa ed altre unit<br />

(ed in particolare per tutte quelle create ed aggiunte dai programmatori) è invece necessario in<strong>di</strong>care il loro nome<br />

nella sezione uses del programma. Esistono unit con sottoprogrammi per fare grafica (graph) e TANTE altre cose che<br />

scopriremo insieme un poco alla voltaJ.<br />

program prova;<br />

begin<br />

writeln( sqrt(9) )<br />

end.<br />

Il co<strong>di</strong>ce della sqrt<br />

viene estratto dalla<br />

libreria ed<br />

incorporato nel<br />

programma finale<br />

Sqrt<br />

random<br />

+ =<br />

Programma eseguibile finale<br />

traduzione in linguaggio macchina<br />

delle istruzioni scritte dal<br />

programmatore<br />

In commercio sono poi <strong>di</strong>sponibili parecchie librerie per i compiti più svariati. Se una libreria è fatta bene vale mille<br />

volte i sol<strong>di</strong> che costa: sviluppare software costa MOLTO! Su Internet è facile trovare ed acquistare queste librerie.<br />

Su internet, soprattutto sui siti de<strong>di</strong>cati alla programmazione si trovano anche MOLTE librerie gratuite.<br />

….<br />

….<br />

….<br />

….<br />

….<br />

Ecc.<br />

Scriversi i sottoprogrammi da soli raggruppandoli poi in librerie (che possono essere scambiate, rese <strong>di</strong>sponibili<br />

gratuitamente su Internet o vendute). Un sottoprogramma viene scritto <strong>di</strong> solito prima all’interno <strong>di</strong> un programma<br />

normale e solo in un secondo tempo viene aggiunto ad una libreria.<br />

+<br />

co<strong>di</strong>ce della sqrt prelevato dalla<br />

libreria


Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 86<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

Come si fa in Turbo Pascal ad usare un sottoprogramma <strong>di</strong> una libreria?<br />

Prima della sezione VAR del programma si mette una sezione intitolata USES e si elencano, separati da virgole, i nomi<br />

delle unit che contengono i sottoprogrammi che si vogliono utilizzare. Come <strong>di</strong>cevo prima, i sottoprogrammi clrscr e<br />

gotoxy() sono contenuti nella unit crt e quin<strong>di</strong> per usarli:<br />

program prova;<br />

uses crt;<br />

begin<br />

clrscr;<br />

gotoxy(10,5);<br />

writeln(‘sto scrivendo alla colonna 10, quinta riga dello schermo …);<br />

readln;<br />

end.<br />

Prima <strong>di</strong> esaminare in dettaglio come si scrivono i sottoprogrammi con il turbo Pascal dovete essere convinti della<br />

loro utilità.<br />

Riassunto dei benefici dell’uso dei sottoprogrammi:<br />

Risparmio <strong>di</strong> tempo e <strong>di</strong> spazio: le istruzioni che corrispondono al sottoprogramma sono scritte una volta sola,<br />

tradotte ed inserite in una libreria; il programmatore non le deve riscrivere (o copiare/incollare) ma può richiamarle<br />

semplicemente scrivendo il nome del sottoprogramma ed in<strong>di</strong>cando tra parentesi le costanti o le variabili che<br />

contengono i valori necessari al funzionamento del sottoprogramma (i parametri).<br />

Non incorre nei rischi del copia/incolla:<br />

se viene scoperto un errore nel sottoprogramma: si mo<strong>di</strong>ficano solo le istruzioni <strong>di</strong> quest'ultimo e si aggiorna poi la<br />

libreria con la nuova versione; è poi sufficiente ricompilare/linkare i programmi che ne facevano uso (questi ultimi<br />

non sono stati mo<strong>di</strong>ficati!);<br />

se scritto in modo corretto il sottoprogramma non interferisce con il resto del programma: un sottoprogramma può<br />

avere le sue variabili per i cicli e per tutto il resto; ha, in definitiva, un suo spazio <strong>di</strong> lavoro che lo rende in<strong>di</strong>pendente;<br />

riceverete maggiori dettagli più avanti;<br />

Rende il programma più comprensibile: è palese come il co<strong>di</strong>ce sulla destra, a <strong>di</strong>fferenza <strong>di</strong> quello a sinistra,<br />

comunichi al lettore imme<strong>di</strong>atamente lo scopo per cui è stato scritto:<br />

const<br />

pi_greco=3.14;<br />

var<br />

x: real;<br />

begin<br />

writeln( 4/3 * pi_greco * 12 * 12 * 12)<br />

end.<br />

writeln( VolumeSfera(12) )<br />

Infatti pochi sanno riconoscere nella formula a sinistra il calcolo del volume <strong>di</strong> una sfera <strong>di</strong> raggio 12 ... Nel riquadro<br />

a destra è invece palese che si sta invocando una funzione dal nome molto chiaro ed è altrettanto evidente che il<br />

valore comunicato tra parentesi è il raggio della sfera. In ogni caso le modalità d'uso <strong>di</strong> ogni sottoprogramma sono<br />

documentate nel manuale tecnico che accompagna sempre una libreria.


Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 87<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

Diminuisce la complessità della scrittura dei programmi: un programma può essere assemblato a partire da<br />

sottoprogrammi più semplici da trattare. E’ anche più facile trovare gli errori concentrandosi solo sulle righe <strong>di</strong> un<br />

sottoprogramma per volta. Inoltre rende possibile sviluppare uno stesso programma insieme ad altri programmatori<br />

senza che questi interferiscano tra loro: ad ogni programmatore viene assegnato lo sviluppo <strong>di</strong> un certo numero <strong>di</strong><br />

sottoprogrammi che vengono poi fusi assieme.<br />

Nessun team <strong>di</strong> sviluppo si sognerebbe oggi <strong>di</strong> sviluppare un progetto software senza adottare questo tipo <strong>di</strong><br />

programmazione detto modulare. Questi concetti sono stati poi ampliati nella programmazione ad oggetti (OOP)<br />

che sarà materia <strong>di</strong> stu<strong>di</strong>o in quarta/quinta.<br />

Mentre vengono sviluppati e provati i sottoprogrammi vengono <strong>di</strong> solito scritti <strong>di</strong>rettamente all'interno <strong>di</strong> un<br />

programma che li usa. È solo in un secondo momento, quando il sottoprogramma è stato perfettamente testato, <strong>di</strong><br />

quest'ultimo viene inserito in una libreria.<br />

Qui a lato ho evidenziato come in un programma i sottoprogrammi<br />

vengono scritti dopo la sezione VAR e devono terminare prima del begin<br />

<strong>di</strong> inizio programma. Non c'è limite al numero <strong>di</strong> sottoprogrammi che si<br />

possono scrivere (se non quelli imposti dall'ambiente <strong>di</strong> sviluppo per la<br />

scrittura del programma principale).<br />

Come <strong>di</strong>cevo, questo facilita la scrittura dei sottoprogrammi ma ne riduce<br />

la facilità <strong>di</strong> riutilizzo (dovremmo ricorrere ad un copia/incolla per usarli in<br />

altri programmi), pur mantenendo inalterati tutti gli altri benefici. Una<br />

volta che il sottoprogramma è stato ben testato lo si può togliere dal<br />

programma principale ed includerlo in una libreria, recuperando *tutti* i<br />

benefici <strong>di</strong>scussi prima.<br />

Anche noi procederemo così, visto che l’obiettivo è imparare a scrivere i<br />

sottoprogrammi.<br />

NOTA: nel programma principale possiamo sfruttare un qualsiasi<br />

sottoprogramma in più punti, tutte le volte che ciò si rende necessario!<br />

program prova;<br />

const ...<br />

var ...<br />

sottoprogramma 1<br />

sottoprogramma 2<br />

Begin<br />

... uso sottoprogramma1<br />

... uso sottoprogramma1<br />

... uso sottoprogramma2<br />

....<br />

End.


Come si scrive un sottoprogramma ?<br />

Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 88<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

Iniziano con la parola procedure o function seguita dal nome che il programmatore vuole dare al sottoprogramma. Se<br />

non sono previsti parametri si mette il punto e virgola subito dopo il nome del sottoprogramma (tutti i nostri esempi<br />

iniziali non avranno parametri per semplicità …). Poi tra begin e end si mettono le istruzioni che si vogliono far<br />

eseguire quando si richiama il sottoprogramma. Ad esempio, ecco come si scrive una procedure per stampare sullo<br />

schermo un rettangolo fatto da asterischi.<br />

program esempio;<br />

uses crt;<br />

procedure<br />

<strong>di</strong>segnaQuadrato;<br />

begin<br />

writeln('****');<br />

writeln('****');<br />

writeln('****');<br />

writeln('****');<br />

end;<br />

begin<br />

<strong>di</strong>segnaQuadrato;<br />

writeln;<br />

writeln;<br />

<strong>di</strong>segnaQuadrato;<br />

writeln;<br />

writeln;<br />

readln<br />

end.<br />

Nel prossimo esempio scriveremo un sottoprogramma che restituisce un numero scelto a caso per il gioco del lotto<br />

(un numero da 1 a 90). Poiché restituisce un valore dobbiamo usare una function, che si <strong>di</strong>fferenzia leggermente da<br />

una procedure, ma non <strong>di</strong> molto.<br />

program esempio;<br />

function estraiLotto : integer;<br />

begin<br />

randomize;<br />

estraiLotto := random(90) + 1<br />

end;<br />

begin<br />

writeln (‘primo numero lotto estratto: ‘);<br />

writeln ( estraiLotto );<br />

writeln (‘secondo numero lotto estratto: ‘);<br />

writeln ( estraiLotto );<br />

end.<br />

Il sottoprogramma viene <strong>di</strong>chiarato una volta sola e<br />

richiamato due volte nel programma principale.<br />

Fosse necessario stampare anche mille rettangoli, le<br />

istruzioni del sottoprogramma sono state scritte una<br />

volta sola.<br />

Se si vogliono aumentare o <strong>di</strong>minuire il numero <strong>di</strong><br />

righe o colonne degli asterischi è sufficiente farlo<br />

solo nel sottoprogramma ed il nuovo funzionamento<br />

si ripercuoterà automaticamente in TUTTO il<br />

programma.<br />

Ho evidenziato le 2 cose che cambiano rispetto ad<br />

una procedure.<br />

1. Dopo il nome del sottoprogramma (e dopo la<br />

parentesi chiusa dell’eventuale lista dei parametri<br />

che nell’esempio manca) si mettono i due punti ed il<br />

tipo del valore che viene restituito.<br />

2. La function DEVE terminare con l’assegnazione<br />

al suo nome del valore da restituire a chi usa la<br />

function.


Sottoprogrammi con uso <strong>di</strong> parametri<br />

Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 89<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

La funzione estraiLotto non può essere utilizzata per altre simulazioni; ad esempio per un gioco <strong>di</strong> da<strong>di</strong> (dove,<br />

ovviamente, l'intervallo <strong>di</strong> valori e quello che va da uno a sei). Possiamo però riscrivere la funzione in modo da poter<br />

in<strong>di</strong>care al momento della chiamata il massimo numero da estrarre. Cambiamo anche il nome della funzione in<br />

quanto sarebbe fuorviante lasciare il riferimento al lotto.<br />

program esempio;<br />

function estrai(Massimo: integer) : integer;<br />

begin<br />

randomize;<br />

estraiLotto := random(massimo) + 1<br />

end;<br />

begin<br />

writeln (‘primo numero lotto estratto: ‘);<br />

writeln ( estrai(90) );<br />

writeln (‘tiro il dado … è uscito: ‘);<br />

writeln ( estrai(6) );<br />

end.<br />

program esempio;<br />

var numero: integer;<br />

procedure asterischi(quanteRighe: integer);<br />

var i: integer;<br />

begin<br />

for i:=1 to quanteRighe do<br />

writeln(‘***************’);<br />

end;<br />

begin<br />

writeln (‘quante righe <strong>di</strong> * devo stampare? ‘);<br />

readln(numero);<br />

asterischi( numero );<br />

readln;<br />

end.<br />

I parametri vengano specificati tra parentesi prima<br />

del nome del sottoprogramma. Per ciascuno deve<br />

essere specificato il tipo. Notate come sia ancora<br />

necessario, se si tratta <strong>di</strong> una funzione, in<strong>di</strong>care il<br />

tipo del valore restituito dal sottoprogramma.<br />

Il nome che scegliamo per il parametro serve al<br />

sottoprogramma per sapere come riferirsi al valore<br />

che riceve quando viene chiamato.<br />

Anche le procedure possono ricevere valori sotto<br />

forma <strong>di</strong> parametri. Nell'esempio qui a lato la<br />

procedura riceve il numero <strong>di</strong> righe <strong>di</strong> asterischi che<br />

deve stampare (il parametro chiamato quanteRighe).<br />

Essendo una procedura, dopo la parentesi determina<br />

l'elenco dei parametri non deve essere specificato un<br />

tipo come abbiamo visto per le funzioni.<br />

In questo esempio c'è un altro particolare molto<br />

interessante da notare: dopo l'intestazione con il<br />

nome della procedura/funzione è possibile iniziare<br />

una sezione VAR in cui <strong>di</strong>chiarare le variabili ad<br />

uso esclusivo del sottoprogramma. Nel caso in<br />

questione si tratta della variabile <strong>di</strong> controllo del<br />

ciclo for.<br />

Torneremo presto su questo argomento.


Parametri per valore (by value)<br />

program esempio;<br />

var numero: integer; simboli: string;<br />

Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 90<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

procedure asterischi(quanteRighe: integer; riga: string);<br />

Qui a lato trovate una versione potenziata della<br />

var i: integer;<br />

procedura esaminata in precedenza. Chi la usa non è<br />

begin<br />

più limitato ad una riga <strong>di</strong> asterischi ma può<br />

for i:=1 to quanteRighe do<br />

scegliere, specificandolo come secondo parametro,<br />

writeln(riga);<br />

end;<br />

la riga <strong>di</strong> caratteri da usare.<br />

begin<br />

writeln (‘quante righe <strong>di</strong> * devo stampare? ‘);<br />

readln( numero );<br />

writeln (‘che simboli uso per la stampa? ‘);<br />

readln( simboli);<br />

asterischi( numero, simboli );<br />

readln;<br />

end.<br />

program esempio;<br />

var num1, num2: integer;<br />

function max(a,b: integer) : integer;<br />

begin<br />

if a>=b then<br />

max:=a<br />

else<br />

max:=b<br />

end;<br />

begin<br />

writeln (‘inserisci un numero‘);<br />

readln ( num 1 );<br />

writeln (‘inserisci un altro numero‘);<br />

readln ( num 2 );<br />

writeln (‘il maggiore dei numeri inseriti è: ‘);<br />

writeln ( max(num1, num2) );<br />

readln;<br />

end.<br />

Quando ce n'è più d'uno, i parametri devono essere<br />

separati con un punto e virgola e per ciascuno deve<br />

essere specificato il tipo corrispondente.<br />

NOTATE: mentre quando scriviamo il testo del<br />

sottoprogramma gli eventuali parametri devono<br />

essere separati con un punto e virgola, quando<br />

invece il sottoprogramma viene usato i valori o le<br />

variabili che corrispondono ai parametri previsti<br />

vengono invece separati con una virgola.<br />

La funzione qui a lato restituisce il più grande dei<br />

due valori che riceve come parametri. Essendo<br />

questi ultimi dello stesso tipo, è possibile<br />

<strong>di</strong>chiararne il tipo insieme (separando però i loro<br />

nomi con una virgola)<br />

IMPORTANTE: i nomi delle variabili che<br />

eventualmente vengono utilizzate al momento del<br />

richiamo del sottoprogramma non devono chiamarsi<br />

per forza come i parametri. Detto in altre parole: per<br />

il fatto <strong>di</strong> aver chiamato a e b i parametri della<br />

funzione non siamo assolutamente obbligati a<br />

chiamare allo stesso modo le variabili del<br />

programma principale usate per chiamare il<br />

sottoprogramma; nell'esempio, le variabili <strong>di</strong>chiarate<br />

nel programma principale si chiamano infatti num1<br />

e num2: quello che accade è che il valore contenuto<br />

in num1 viene copiato nel parametro a e che il<br />

valore contenuto in num2 viene copiato nel<br />

parametro b. Si parla infatti <strong>di</strong> passaggio dei<br />

parametri per valore (by value).<br />

NOTA. Se vi state domandando come si faccia a <strong>di</strong>stinguere la modalità <strong>di</strong> passaggio dei parametri by value dall'altra<br />

che esamineremo tra poco, considerate questa semplice ‘regola’: quando nella specifica del parametro appare solo il<br />

suo nome ed il suo tipo il passaggio è by value. Dovremo infatti aggiungere qualche cosa nella <strong>di</strong>chiarazione per<br />

scegliere l'altra modalità.


Caratteristiche del passaggio dei parametri per valore<br />

Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 91<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

Il passaggio per valore ha una caratteristica decisamente interessante: è ‘sicuro’ in quanto non consente al<br />

sottoprogramma <strong>di</strong> mo<strong>di</strong>ficare, tramite un parametro, una variabile del programma principale. Cerchiamo <strong>di</strong> capire il<br />

perché con un esempio.<br />

program esempio;<br />

var numero: integer; simboli: string;<br />

procedure asterischi(quanteRighe: integer; riga: string);<br />

begin<br />

repeat<br />

writeln(riga);<br />

quanteRighe := quanteRighe – 1<br />

until quanteRighe=0;<br />

end;<br />

begin<br />

numero:=5;<br />

simboli:=’******************’;<br />

asterischi( numero, simboli );<br />

writeln( numero ); (* che valore viene stampato ? *)<br />

readln;<br />

end.<br />

I parametri in<strong>di</strong>cati nella procedura sono chiamati parametri formali; quelle in<strong>di</strong>cate nel programma al momento<br />

dell'utilizzo del sottoprogramma sono invece chiamati parametri attuali.<br />

program esempio;<br />

var numero: integer; simboli: string;<br />

Parametri formali<br />

procedure asterischi(quanteRighe: integer; riga: string);<br />

var i: integer;<br />

begin<br />

for i:=1 to quanteRighe do<br />

writeln(riga);<br />

end;<br />

begin<br />

writeln (‘quante righe <strong>di</strong> * devo stampare? ‘);<br />

readln( numero );<br />

writeln (‘che simboli uso per la stampa? ‘);<br />

readln( simboli);<br />

Parametri attuali<br />

asterischi( numero, simboli );<br />

readln;<br />

end.<br />

Questa volta nel sottoprogramma il ciclo è<br />

realizzato con la struttura repeat until. Ma<br />

attenzione: quest’ultimo usa come contatore il<br />

parametro quanteRighe stesso, <strong>di</strong>minuendolo <strong>di</strong><br />

uno ad ogni ciclo.<br />

La domanda da un milione <strong>di</strong> euro è: se la<br />

variabile numero prima <strong>di</strong> chiamare la procedura<br />

vale 5, dopo aver chiamato la procedura (che<br />

apporta mo<strong>di</strong>fiche al suo primo parametro) quale<br />

sarà il suo valore?<br />

Risposta: lo stesso che aveva prima della<br />

chiamata; detto in altre parole, il passaggio dei<br />

parametri by value non consente ad un<br />

sottoprogramma <strong>di</strong> mo<strong>di</strong>ficare il valore <strong>di</strong> una<br />

variabile esterna utilizzata nel programma<br />

principale per richiamare la procedura stessa.<br />

Il passaggio è by value: il sottoprogramma<br />

non può mo<strong>di</strong>ficare il valore dei parametri<br />

attuali numero e simboli.<br />

Il valore che la variabile del programma<br />

numero ha al momento dell'uso del<br />

sottoprogramma viene assegnato alla<br />

variabile parametro formale<br />

corrispondente (quanteRighe). La stessa<br />

cosa avviene per il secondo parametro. Di<br />

fatto il sottoprogramma opera su una<br />

copia dei parametri attuali e della copia<br />

può <strong>di</strong>sporre come preferisce senza<br />

alterare realmente il valore delle variabili<br />

Se la procedura potesse invece mo<strong>di</strong>ficare (senza che il suo utilizzatore ne fosse consapevole) le variabili che<br />

corrispondono ai parametri attuali si correrebbe il rischio <strong>di</strong> incappare in errori molto <strong>di</strong>fficili da scoprire.


Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 92<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

Immaginiamo infatti la situazione in cui la procedura asterischi, una volta sperimentata e considerata ‘perfetta’,<br />

venga tolta come co<strong>di</strong>ce dal programma visto prima ed inserita in una libreria chiamata stampe. Il programma<br />

apparirebbe allora così strutturato:<br />

program esempio;<br />

In<strong>di</strong>co l’uso della libreria<br />

uses stampa;<br />

var numero: integer; simboli: string;<br />

begin<br />

numero:=5;<br />

simboli:=’******************’;<br />

asterischi( numero, simboli );<br />

Come vedete, le istruzioni della procedura<br />

asterischi non sono più visibili.<br />

Il co<strong>di</strong>ce corrispondente, già compilato in<br />

linguaggio macchina, viene incorporato<br />

nell'eseguibile finale dal linker.<br />

Nel programma rimangono solo le righe<br />

end.<br />

che invocano il sottoprogramma.<br />

Il programmatore non ha modo <strong>di</strong> capire se la procedura asterischi mo<strong>di</strong>ficherà o meno il valore <strong>di</strong> uno dei suoi<br />

parametri formali! Ed anche se il co<strong>di</strong>ce dal sottoprogramma fosse ancora inserito <strong>di</strong>rettamente nel programma<br />

saremo sempre costretti ad un alto livello <strong>di</strong> attenzione perdendo molto tempo nel controllare le istruzioni <strong>di</strong> ogni<br />

sottoprogramma per capire se esiste questa possibilità.<br />

Insomma, sarebbe un bel guaio se il sottoprogramma potesse mo<strong>di</strong>ficare (senza che questo fosse <strong>di</strong>chiarato in<br />

qualche modo) a piacimento il valore <strong>di</strong> uno dei parametri attuali (numero, simboli)!<br />

Invece, grazie ai vincoli imposti dal passaggio dei parametri per valore (by value) il programmatore è<br />

sicuro che un sottoprogramma neanche per errore potrà mo<strong>di</strong>ficare il valore <strong>di</strong> un parametro attuale.<br />

Esistono comunque dei vincoli tra i parametri in<strong>di</strong>cati ne la scrittura del co<strong>di</strong>ce <strong>di</strong> un sottoprogramma (chiamati<br />

parametri formali) e quelli che il programmatore utilizza nel programma principale quando intende usare il<br />

sottoprogramma (chiamati parametri attuali):<br />

per ogni parametro formale deve essere in<strong>di</strong>cato al momento dell'utilizzo del sottoprogramma un corrispondente<br />

parametro attuale; riferendoci all'esempio della procedura asterischi, non è ammesso il suo utilizzo specificando solo<br />

il numero delle righe o solo i singoli da utilizzare; non ha ovviamente senso anche cercare <strong>di</strong> usare più parametri<br />

attuali <strong>di</strong> quelli previsti;<br />

il parametro attuale dev'essere dello stesso tipo del parametro formale o almeno compatibile; il parametro formale<br />

quanteRighe della procedura asterischi è <strong>di</strong> tipo integer, per cui, richiamandola, come primo parametro attuale<br />

potremo solo in<strong>di</strong>care o una costante integer o una variabile integer o una qualsivoglia espressione che restituisca un<br />

valore integer;<br />

se un parametro formale fosse <strong>di</strong> tipo real allora il corrispondente parametro attuale potrebbe essere al limite anche<br />

un integer visto che quest'ultimo tipo <strong>di</strong> dato è compatibile con il real (ad esempio il 5, integer, verrebbe convertito<br />

nel real 5.0); il contrario non sarebbe invece possibile, in quanto il numero <strong>di</strong> byte necessario a rappresentare un real<br />

è maggiore <strong>di</strong> quello necessario a rappresentare un integer; ad esempio, il numero real 12.67 non viene<br />

automaticamente troncato al valore integer 12 a causa <strong>di</strong> una per<strong>di</strong>ta <strong>di</strong> precisione che potrebbe risultare<br />

inaccettabile; il programmatore può però in<strong>di</strong>care come parametro attuale l'espressione trunc(12.67) che <strong>di</strong>etro<br />

esplicito comando ‘tronca’ un valore real in un integer (in realtà in un longint, compatibile a sua volta con un integer<br />

a patto che il valore cada nell'intervallo previsto per gli integer);<br />

i parametri attuali devono essere forniti nello stesso or<strong>di</strong>ne logico previsto per quelli formali; la procedura asterischi<br />

si aspetta il numero delle righe come primo parametro e la stringa dei singoli da usare come secondo; tentare <strong>di</strong><br />

invertire l'or<strong>di</strong>ne non ha gravi conseguenze, in questo particolare caso, perché i tipi dei due parametri sono <strong>di</strong>versi ed<br />

il compilatore si accorgerebbe subito del problema e bloccherebbe la compilazione con un messaggio d'errore; ben<br />

<strong>di</strong>verso è il caso <strong>di</strong> parametri dello stesso tipo: il compilatore non s'accorgerebbe <strong>di</strong> nulla dal momento che dal suo<br />

punto <strong>di</strong> vista si tratterebbe comunque <strong>di</strong> due interi o <strong>di</strong> due stringhe eccetera<br />

Viva l’in<strong>di</strong>pendenza!<br />

Quella che sto per enunciare non è una regola sintattica ma una dettata dall'esperienza. Idealmente un<br />

sottoprogramma dovrebbe essere strutturato in modo da essere il più in<strong>di</strong>pendente possibile dal programma che lo<br />

utilizzerà o da altri sottoprogrammi.<br />

Questo obiettivo viene raggiunto soprattutto con l'utilizzo dei parametri, evitando <strong>di</strong> usare <strong>di</strong>rettamente<br />

eventuali variabili del programma principale. Consideriamo infatti questo esempio (ho ripreso la forma più<br />

semplice della procedura asterischi):


program esempio;<br />

var i: integer;<br />

procedure asterischi(quanteRighe: integer);<br />

begin<br />

for i:=1 to quanteRighe do<br />

writeln(‘***************’);<br />

end;<br />

begin<br />

writeln (‘quante righe <strong>di</strong> * devo stampare? ‘);<br />

readln(numero);<br />

asterischi( numero );<br />

readln;<br />

end.<br />

Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 93<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

Se proviamo a mandare in esecuzione il programma tutto sembra funzionare e, in effetti, funziona veramente... Ma la<br />

procedura è in realtà meno leggibile, meno riutilizzabile e meno sicura.<br />

Meno leggibile: non è sufficiente passare in rassegna tutte le istruzioni della procedura per capire tutto della<br />

procedura; infatti per capire cosa sia la i usata dal ciclo for dobbiamo cercare nel programma principale la sua<br />

<strong>di</strong>chiarazione; e se qualcuno tra voi sta pensando che realtà è ovvio che la i sia una variabile <strong>di</strong> tipo integer, vi invito<br />

a provare il seguente programma!<br />

program prova;<br />

var i: char;<br />

begin<br />

for i:=’A’ to ‘Z’ do<br />

write( i );<br />

readln;<br />

end.<br />

La mo<strong>di</strong>fica che ho apportato è piccola ma<br />

sostanziale: ho spostato la <strong>di</strong>chiarazione della<br />

variabile i utilizzata per il ciclo for dall'interno della<br />

procedura alla sezione var del programma<br />

principale.<br />

Non è più così ovvio che la variabile usata per un ciclo for sia<br />

per forza <strong>di</strong> tipo integer, vero ???<br />

Meno riutilizzabile: se spostiamo la procedura in un altro programma o la inclu<strong>di</strong>amo in una libreria non è detto che<br />

in quel programma o in quella libreria sia presente, come nelle programma originale, una variabile <strong>di</strong> tipo integer<br />

chiamata i; il ciclo for della procedura non potrebbe quin<strong>di</strong> funzionare. E per quanto vi possa sembrare strano questo<br />

è il caso più fortunato tra quelli che possono capitare: infatti il compilatore ci avverte dell'assenza della variabile ed il<br />

programmatore può intervenire e soprattutto si accorge che c'è un problema.<br />

Meno sicura: provate a pensare se nel programma in cui viene copiata la procedura esiste già una variabile con lo<br />

stesso nome e dello stesso tipo: il compilatore non farebbe una grinza perché la procedura pretende una certa<br />

variabile nel programma principale e questa viene trovata. Il problema è che se in questo nuovo programma quella<br />

stessa variabile serve per altri scopi, non appena si invoca la procedura quest'ultima mo<strong>di</strong>fica in modo inaspettato il<br />

suo valore interferendo con il resto del programma.<br />

Ne <strong>di</strong>scende un'altra regola: tutte le variabili <strong>di</strong> lavoro <strong>di</strong> un sottoprogramma dovrebbero essere definite all'interno <strong>di</strong><br />

quest'ultimo. Eventuali valori/variabili esterne verranno comunicati tramite un parametro.


Passaggio <strong>di</strong> parametri per in<strong>di</strong>rizzo (by reference)<br />

Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 94<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

Come abbiamo visto, il passaggio <strong>di</strong> parametri per valore non consente la mo<strong>di</strong>fica <strong>di</strong> variabili esterne. Qualche volta<br />

però questo è proprio ciò <strong>di</strong> cui abbiamo bisogno... Immaginiamo <strong>di</strong> volere scrivere un sottoprogramma per il calcolo<br />

delle due ra<strong>di</strong>ci reali (se esistono) <strong>di</strong> un’equazione <strong>di</strong> secondo grado:<br />

function equazione2grado(a, b, c: real) : real;<br />

begin<br />

…. Calcoli …<br />

end;<br />

Gli unici valori <strong>di</strong> cui questa funzione ha bisogno sono i coefficienti del termine <strong>di</strong> secondo grado, del termine <strong>di</strong><br />

primo grado e del termine noto (per applicare la notissima formula<br />

x<br />

12<br />

− b μ<br />

=<br />

2<br />

b − 4ac<br />

2a<br />

). Ecco quin<strong>di</strong><br />

giustificata la presenza dei tre parametri chiamati a, b e c. Purtroppo, però, una funzione può restituire un solo<br />

valore: impossibile, quin<strong>di</strong>, far restituire con i meccanismi noti i due valori corrispondenti alle due soluzioni richieste.<br />

La situazione parrebbe senza vie d'uscita: o si fa restituire la funzione la prima delle due ra<strong>di</strong>ci (X1) oppure si fa<br />

restituire la seconda (X2).<br />

Qualcuno potrebbe essere tentato dalla seguente strada (ma, come appena visto, assolutamente da evitare):<br />

program equazioni;<br />

var x1,x2: real;<br />

function equazione2grado(a, b, c: real) : real;<br />

begin<br />

…. Calcoli …<br />

x1:= …; x2:= …;<br />

end;<br />

Quello che occorre è un modo ‘sicuro’ per consentire ad un sottoprogramma la mo<strong>di</strong>fica <strong>di</strong> una variabile esterna;<br />

sicuro significa che un programmatore che sta per utilizzare un sottoprogramma è in grado, consultando la<br />

documentazione, <strong>di</strong> capire che la variabile che sta in<strong>di</strong>cando come parametro attuale può essere mo<strong>di</strong>ficata dal<br />

sottoprogramma ed in che modo.<br />

Tutto questo accade con l'utilizzo dei parametri passati per in<strong>di</strong>rizzo (by reference). A livello sintattico la<br />

mo<strong>di</strong>fica è minima: basta aggiungere prima del nome <strong>di</strong> un parametro formale la parola var:<br />

program equazioni;<br />

var soluzione1, soluzione2: real;<br />

procedure equazione2grado(a, b, c: real; var x1,x2: real);<br />

begin<br />

…. Calcoli …<br />

x1:= …; x2:= …;<br />

end;<br />

begin<br />

equazione2grado(3,4,5, soluzione1, soluzione2);<br />

writeln(‘Le soluzione sono ‘, soluzione1, soluzione2);<br />

readln;<br />

end.<br />

Questa soluzione utilizza <strong>di</strong>rettamente due<br />

variabili del programma principale. Si tratta <strong>di</strong> una<br />

pratica assolutamente da evitare per tutti i motivi<br />

visti in precedenza.<br />

Quando un parametro formale è<br />

specificato per in<strong>di</strong>rizzo una mo<strong>di</strong>fica ad<br />

esso si ripercuote in modo permanente<br />

sulla variabile utilizzata come parametro<br />

attuale al momento della chiamata.<br />

Detto in altre parole: nella sezione delle<br />

variabili del programma principale sono<br />

state <strong>di</strong>chiarate due variabili (soluzione1 e<br />

soluzione2) per memorizzare i risultati che<br />

verranno calcolati dalla procedura.<br />

La procedura viene poi richiamata<br />

in<strong>di</strong>cando proprio queste due variabili<br />

come ultimi due parametri attuali.<br />

Poiché soluzione1 corrisponde al<br />

parametro formale X1 e poiché X1 è stato<br />

<strong>di</strong>chiarato per in<strong>di</strong>rizzo, ogni mo<strong>di</strong>fica fatta<br />

ad X1 si ripercuoterà anche su soluzione1;


ESERCIZI SVOLTI – primo blocco<br />

Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 95<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

SOT 1. <strong>di</strong>fficoltà: bassa Che percentuale rappresenta un numero A rispetto ad un altro numero B? Esempio: 100<br />

rispetto a 150 = 75 (100 è il 75% <strong>di</strong> 150).<br />

program percentuale;<br />

uses newdelay,crt;<br />

(* che percentuale è A <strong>di</strong> B? *)<br />

function perc(A, B: real): real;<br />

begin<br />

perc := (A/B) * 100<br />

end;<br />

begin<br />

clrscr;<br />

(* esempi d'uso ... *)<br />

writeln( perc(1, 1):3:2, '%' ); (* 100% *)<br />

writeln( perc(17, 34):3:2, '%' ); (* 50% *)<br />

writeln( perc(8, 24):3:2, '%' ); (* 33.33333333% ...*)<br />

writeln( perc(234.78, 4356.87):3:2, '%' ); (* e chi lo sa ?? *)<br />

(* viene calcolato un valore corretto anche quando A < B *)<br />

writeln( perc(150, 100):3:2, '%' ); (* 150% *)<br />

writeln('INVIO per continuare ...');<br />

readln;<br />

end.<br />

SOT 2. <strong>di</strong>fficoltà: bassa Calcolare una certa percentuale <strong>di</strong> un numero.<br />

program provaPercentuale;<br />

uses newdelay, crt;<br />

function percentuale(perc: real; numero: real): real;<br />

begin<br />

percentuale := (numero/100) * perc;<br />

end;<br />

begin<br />

clrscr;<br />

writeln( percentuale(20, 100):2:2); (* 20% <strong>di</strong> 100 = 20 *)<br />

writeln( percentuale(20, 35):2:2); (* 20% <strong>di</strong> 35 = 7 *)<br />

writeln( percentuale(0, 47):2:2); (* 0% <strong>di</strong> qualunque numero = 0 *)<br />

writeln( percentuale(20, 0):2:2); (* 20% <strong>di</strong> 0 = 0 *)<br />

readln;<br />

end.


SOT 3. <strong>di</strong>fficoltà: bassa Calcolare un prezzo comprensivo d’IVA.<br />

program provaPercentuale;<br />

uses newdelay, crt;<br />

var prezzo, iva: real;<br />

(* sfruttiamo la funzione scritta prima … *)<br />

function percentuale(perc: real; numero: real): real;<br />

begin<br />

percentuale := (numero/100) * perc;<br />

end;<br />

function prezzoConIVA(prezzoSenzaIVA: real; percentualeIVA: real): real;<br />

begin<br />

prezzoConIVA := prezzoSenzaIVA + percentuale(prezzoSenzaIVA, percentualeIVA);<br />

end;<br />

begin<br />

clrscr;<br />

writeln('Inserire prezzo senza IVA');<br />

readln(prezzo);<br />

writeln('Inserire pecentuale IVA da applicare');<br />

readln(iva);<br />

writeln('Ecco il totale IVA compresa: ', prezzoConIVA(prezzo, iva):6:2);<br />

readln;<br />

end.<br />

SOT 4. <strong>di</strong>fficoltà: bassa Conversione da metri a chilometri e viceversa.<br />

program conversioni;<br />

uses newdelay, crt;<br />

var metri, km: real;<br />

(* da m a km *)<br />

function m_km(quantiMetri: real): real;<br />

begin<br />

m_km := quantiMetri / 1000;<br />

end;<br />

(* da km a m *)<br />

function km_m(quantiKm: real): real;<br />

begin<br />

km_m := quantiKm * 1000;<br />

end;<br />

begin<br />

clrscr;<br />

writeln('Inserire metri');<br />

readln(metri);<br />

writeln(metri:5:2,' metri corrispondono a ',m_km(metri):5:2,' chilometri');<br />

writeln('Inserire ora i chilometri');<br />

readln(km);<br />

writeln(km:5:2,' chilometri corrispondono a ',km_m(km):5:2,' metri');<br />

readln;<br />

end.<br />

Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 96<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004


Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 97<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

SOT 5. <strong>di</strong>fficoltà: bassa Convertire un certo numero <strong>di</strong> secon<strong>di</strong> nei minuti corrispondenti. Si vogliono anche<br />

sapere i secon<strong>di</strong> che avanzano.<br />

program conversioni;<br />

uses newdelay, crt;<br />

var ss, avanzano: integer;<br />

(* da secon<strong>di</strong> a minuti; viene calcolato anche l'avanzo *)<br />

function ss_mm(quantiSecon<strong>di</strong>: integer; var avanzo: integer): integer;<br />

begin<br />

avanzo := quantiSecon<strong>di</strong> mod 60;<br />

ss_mm := quantiSecon<strong>di</strong> <strong>di</strong>v 60;<br />

end;<br />

begin<br />

clrscr;<br />

writeln('Inserire secon<strong>di</strong>');<br />

readln(ss);<br />

writeln(ss,' secon<strong>di</strong> corrispondono a ',ss_mm(ss,avanzano),' minuti e ', avanzano, ' secon<strong>di</strong>');<br />

readln;<br />

end.<br />

SOT 6. <strong>di</strong>fficoltà: bassa Convertire un certo numero <strong>di</strong> minuti nelle ore corrispondenti. Si vogliono anche sapere i<br />

minuti che avanzano.<br />

program conversioni;<br />

uses newdelay, crt;<br />

var mm, avanzano: integer;<br />

(* da secon<strong>di</strong> a minuti; viene calcolato anche l'avanzo *)<br />

function ss_mm(quantiSecon<strong>di</strong>: integer; var avanzo: integer): integer;<br />

begin<br />

avanzo := quantiSecon<strong>di</strong> mod 60;<br />

ss_mm := quantiSecon<strong>di</strong> <strong>di</strong>v 60;<br />

end;<br />

(* da minuti a ore; viene calcolato anche l'avanzo *)<br />

function mm_hh(quantiMinuti: integer; var avanzo: integer): integer;<br />

begin<br />

(* sfrutto la precedente: il calcolo da fare e' infatti lo stesso ! *)<br />

mm_hh := ss_mm(quantiMinuti, avanzo);<br />

end;<br />

begin<br />

clrscr;<br />

writeln('Inserire minuti');<br />

readln(mm);<br />

writeln(mm,' minuti corrispondono a ',mm_hh(mm,avanzano),' ore e ', avanzano, ' minuti');<br />

readln;<br />

end.


Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 98<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

SOT 7. <strong>di</strong>fficoltà: bassa Convertire un certo numero <strong>di</strong> secon<strong>di</strong> nelle ore corrispondenti. Si vogliono anche sapere<br />

i minuti ed i secon<strong>di</strong> che che avanzano.<br />

program conversioni;<br />

uses newdelay, crt;<br />

var ss, avanzano, avanzano_mm: integer;<br />

(* da secon<strong>di</strong> a minuti; viene calcolato anche l'avanzo *)<br />

function ss_mm(quantiSecon<strong>di</strong>: integer; var avanzo: integer): integer;<br />

begin<br />

avanzo := quantiSecon<strong>di</strong> mod 60;<br />

ss_mm := quantiSecon<strong>di</strong> <strong>di</strong>v 60;<br />

end;<br />

(* da minuti a ore; viene calcolato anche l'avanzo *)<br />

function mm_hh(quantiMinuti: integer; var avanzo: integer): integer;<br />

begin<br />

(* sfrutto la precedente: il calcolo da fare e' infatti lo stesso ! *)<br />

mm_hh := ss_mm(quantiMinuti, avanzo);<br />

end;<br />

(* da secon<strong>di</strong> a ore; viene calcolato anche l'avanzo *)<br />

function ss_hh(quantiSecon<strong>di</strong>: integer; var avanzo_ss, avanzo_mm: integer): integer;<br />

var quantiMinuti: integer;<br />

begin<br />

quantiMinuti := ss_mm(quantiSecon<strong>di</strong>, avanzo_ss);<br />

ss_hh := mm_hh(quantiMinuti, avanzo_mm)<br />

end;<br />

begin<br />

clrscr;<br />

writeln('Inserire i secon<strong>di</strong> per convertirli in ore ...'); readln(ss);<br />

write(ss,' secon<strong>di</strong> corrispondono a ',ss_hh(ss,avanzano, avanzano_mm),' ore, ');<br />

writeln(avanzano_mm, ' minuti e ', avanzano,' secon<strong>di</strong>');<br />

readln;<br />

end.<br />

SOT 8. <strong>di</strong>fficoltà: bassa Estrarre la parte decimale <strong>di</strong> un numero reale X (quella 'dopo la virgola'); in pratica simula<br />

la funzione standard frac... Esempio: decimale(13,75) - > 0,75.<br />

program parteDecimale;<br />

uses newdelay,crt;<br />

(* NOTA. Con il Pascal la parte decimale e' separata da quella intera da un punto, non dalla virgola;<br />

NOTA. Viene sfruttata la funzione predefinita TRUNC (che elimina la parte decimale da un reale.<br />

TRUNC(10.7) -> 10 *)<br />

function decimale(x: real): real;<br />

begin<br />

decimale := x - trunc(x);<br />

end;<br />

begin<br />

clrscr;<br />

(* esempi d'uso ... *)<br />

writeln( decimale(0.8917):4:4 ); (* 0.8917 *)<br />

writeln( decimale(4.8917):4:4 ); (* 0.8917 *)<br />

writeln( decimale(368.8917):4:4 ); (* 0.8917 *)<br />

writeln('INVIO per continuare ...'); readln; end.


SOT 9. <strong>di</strong>fficoltà: bassa Dato un carattere <strong>di</strong>re se rappresenta una lettera maiuscola.<br />

program controllaMaiuscolo;<br />

uses newdelay,crt;<br />

Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 99<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

(* restituisce true solo se il carattere e’ una lettera maiuscola cioe' solo se il suo co<strong>di</strong>ce ascii e' compreso tra quello<br />

della A e quello della Z *)<br />

function isUpCase(c: char) : boolean;<br />

begin<br />

isUpCase := ( ord(c) >= ord('A') ) and ( ord(c)


Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 100<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

(* trasformo in minuscolo: al co<strong>di</strong>ce ascii del carattere sommo la sfasatura con il set delle minuscole, e ritrasformo il<br />

risultato nel carattere corrispondente *)<br />

lowCase := chr( ord(c) + <strong>di</strong>stanza )<br />

end<br />

else<br />

lowCase:=c<br />

end;<br />

begin<br />

clrscr;<br />

(* esempi d'uso ... *)<br />

writeln( lowCase('A') );<br />

writeln( lowCase('a') );<br />

writeln( lowCase('Z') );<br />

writeln( lowCase('z') );<br />

writeln( lowCase('!') ); (* inalterato ... *)<br />

writeln('INVIO per continuare ...');<br />

readln;<br />

end.<br />

SOT 11. <strong>di</strong>fficoltà: alta Data una stringa convertirla in minuscolo o maiuscolo a seconda del valore <strong>di</strong> un<br />

parametro come (se come='m' converte in minuscolo, se come='M' converte in maiuscolo; se come non ha un<br />

valore valido la stringa viene restituita immo<strong>di</strong>ficata). Sfruttare alcune funzioni precedenti (SOT9 e SOT10)<br />

program MinuscoloMaiuscolo;<br />

uses newdelay, crt;<br />

(* per un commento ve<strong>di</strong> SOT9 *)<br />

function isUpCase(c: char) : boolean;<br />

begin isUpCase := ( ord(c) >= ord('A') ) and ( ord(c)


is:='';<br />

for i:=1 to length(s) do<br />

ris:=ris + upcase(s[i]); (* sfrutta la funzione predefinita upcase … *)<br />

maiuscolo:=ris;<br />

end;<br />

Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 101<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

(* converte un maiuscolo a seconda del parametro 'come'; se come='m' converte in minuscolo, se come='M'<br />

converte in maiuscolo; se come non ha un valore valido la stringa viene restituita identica *)<br />

function MiniMaiu(s: string; come: char) : string;<br />

begin<br />

case come of<br />

'm': MiniMaiu := minuscolo(s);<br />

'M': MiniMaiu := maiuscolo(s);<br />

else<br />

MiniMaiu := s;<br />

end;<br />

end;<br />

begin<br />

clrscr;<br />

(* esempi d'uso ... *)<br />

writeln( MiniMaiu('AaZz12!','m') );<br />

writeln( MiniMaiu('AaZz12!','M') );<br />

writeln( MiniMaiu('AaZz12!','t') );<br />

writeln('INVIO per continuare ...');<br />

readln;<br />

end.<br />

Sfruttando i sottoprogrammi già esistenti, la soluzione del<br />

problema originale <strong>di</strong>venta banale.


Metodologie Top Down e Bottom Up<br />

Problema originale<br />

Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 102<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

L’esercizio precedente introduce in modo naturale una tecnica <strong>di</strong> sviluppo utilizzata per problemi <strong>di</strong> me<strong>di</strong>a/alta<br />

complessità. Invece <strong>di</strong> tentare <strong>di</strong> risolvere il problema come un tutt’uno, compito <strong>di</strong> solito molto <strong>di</strong>fficile, si procede<br />

ad una sud<strong>di</strong>visione in sottoproblemi. E’ intuitivo che ogni sottoproblema sia più semplice <strong>di</strong> quello originale. Per ogni<br />

sottoproblema che si ritiene ancora troppo complesso si procede ad ulteriore sud<strong>di</strong>visione. Il proce<strong>di</strong>mento viene<br />

ripetuto fino ad ottenere un certo numero <strong>di</strong> sottoproblemi sufficientemente semplici da essere risolti e co<strong>di</strong>ficati con<br />

una certa facilità. A livello pratico questo coincide ad in<strong>di</strong>viduare tutta una serie <strong>di</strong> sottoprogrammi che a loro volta<br />

ne richiamano altri. Questa metodologia viene chiamata TOP DOWN (dall’alto al basso), perché graficamente<br />

possiamo rappresentarla come una struttura a piramide in cui in cima (top) si mette il problema originale e via via<br />

che si scende <strong>di</strong> livello sottoproblemi sempre più semplici, fino ad arrivare alla base (bottom) della piramide in cui<br />

troviamo i sottoproblemi più semplici.<br />

Ve<strong>di</strong>amo <strong>di</strong> riconoscere questo proce<strong>di</strong>mento nell’esercizio precedente. Si trattava <strong>di</strong> realizzare un comando per<br />

trasformare una stringa in minuscolo o in maiuscolo. Questo problema iniziale viene sud<strong>di</strong>viso in modo molto naturale<br />

nei due sottoproblemi 1) convertire una stringa in minuscolo e 2) convertire una stringa in maiuscolo:<br />

Convertire una<br />

Stringa in Minuscolo<br />

Convertire una Stringa<br />

in Minuscolo/Maiuscolo<br />

Convertire una<br />

Stringa in Maiuscolo<br />

sottoproblemi<br />

Il sottoproblema ‘convertire una stringa in minuscolo’ può essere ulteriormente scomposto in due altri sotto problemi:<br />

1) decidere se un carattere è minuscolo e 2) convertire un singolo carattere in minuscolo (più semplice rispetto alla<br />

conversione <strong>di</strong> un'intera stringa). Anche per l'altro sotto problema viene in<strong>di</strong>viduato il sotto problema corrispondente<br />

alla conversione in maiuscolo <strong>di</strong> un singolo carattere.<br />

Convertire una<br />

Stringa in Minuscolo<br />

Convertire una Stringa<br />

in Minuscolo/Maiuscolo<br />

Convertire una<br />

Stringa in Maiuscolo<br />

Il carattere è minuscolo ? Converti un singolo carattere in minuscolo Converti un singolo carattere in maiuscolo<br />

NOTA: questa tecnica viene in<strong>di</strong>cata anche come ‘<strong>di</strong>vide et impera’ (<strong>di</strong>vi<strong>di</strong> e mantieni sotto controllo) da un detto<br />

degli antichi romani: per mantenere sotto controllo un vasto territorio appena conquistato vaste fette della popolazione<br />

venivano deportate in aree geografiche lontane. In questo modo quel popolo perdeva la sua unità e la sua forza ed era<br />

più facile dominarlo.


Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 103<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

Oltre alla riduzione della complessità la metodologia top-down offre un'altra caratteristica: consente <strong>di</strong> allestire la<br />

struttura portante dell'intero programma senza aver completato i singoli componenti, così da poterlo provare nei suoi<br />

aspetti macroscopici. Per fare un paragone è come se avessimo a <strong>di</strong>sposizione il prototipo <strong>di</strong> un'automobile con il<br />

telaio (ma non i vetri e le rifiniture), il volante e le ruote (ma un finto servosterzo e niente copertoni), il cruscotto con<br />

i controlli ma senza un vero impianto elettrico, pedali e cambio ma niente <strong>sistema</strong> <strong>di</strong> frenatura e solo un abbozzo del<br />

motore. Certamente tutto questo non ci permetterebbe <strong>di</strong> fare un giro <strong>di</strong> pista a Monza ma consentirebbe agli<br />

ingegneri ed ai meccanici <strong>di</strong> salire veramente in macchina e saggiare dal vivo almeno le caratteristiche e economiche<br />

(visibilità, como<strong>di</strong>tà del posto guida, spazi interni, corretta <strong>di</strong>sposizione dei controlli eccetera); insomma non è come<br />

avere singoli pezzi, pur funzionanti ma che non consentono <strong>di</strong> trovare il tutto almeno in alcuni dei suoi aspetti.<br />

Di nuovo, cerchiamo <strong>di</strong> ritrovare questi concetti nell'esempio precedente. Dopo aver in<strong>di</strong>viduato i sottoprogrammi da<br />

sviluppare il programmatore può rapidamente scrivere solo l'intelaiatura <strong>di</strong> ciascuno <strong>di</strong> essi ma già collegando il tutto<br />

a formare un programma funzionante:<br />

program MinuscoloMaiuscolo;<br />

uses newdelay, crt;<br />

function isUpCase(c: char) : boolean;<br />

begin<br />

isUpCase := true;<br />

end;<br />

function lowCase(c: char) : char;<br />

begin<br />

lowCase := ‘a’;<br />

end;<br />

function minuscolo(s: string): string;<br />

begin<br />

minuscolo:=’stringa <strong>di</strong> prova’;<br />

end;<br />

function maiuscolo(s: string): string;<br />

maiuscolo:=’STRINGA DI PROVA’;<br />

end;<br />

function MiniMaiu(s: string; come: char) :<br />

string;<br />

begin<br />

case come of<br />

'm': MiniMaiu := minuscolo(s);<br />

'M': MiniMaiu := maiuscolo(s);<br />

else<br />

MiniMaiu := s;<br />

end;<br />

end;<br />

begin<br />

clrscr;<br />

(* esempi d'uso ... *)<br />

writeln( MiniMaiu('AaZz12!','m') );<br />

writeln( MiniMaiu('AaZz12!','M') );<br />

writeln( MiniMaiu('AaZz12!','t') );<br />

writeln('INVIO per continuare ...');<br />

readln;<br />

end.<br />

Ecco il ‘trucco’: pur avendo stabilito l'interfaccia <strong>di</strong> ogni<br />

sottoprogramma (il nome ed i parametri che riceverà) il co<strong>di</strong>ce<br />

<strong>di</strong> ciascuno <strong>di</strong> essi è ridotto all'osso, il minimo in<strong>di</strong>spensabile<br />

per poterli richiamare i poter provare il programma principale.<br />

Ogni funzione non calcola veramente il suo risultato ma<br />

restituisce un valore fasullo (però del tipo giusto): ad esempio<br />

la funzione maiuscolo restituisce come risultato la stringa fissa<br />

‘ STRINGA DI PROVA’.<br />

Questo permette comunque a programmatore <strong>di</strong> scrivere nei<br />

minimi dettagli la funzione MiniMaiu che, per così <strong>di</strong>re, non si<br />

accorge <strong>di</strong> richiamare delle funzioni incomplete (anche se,<br />

naturalmente, i valori che si vede restituire sono sempre quelli e<br />

fasulli.<br />

Il programmatore può anche organizzare il corpo principale del<br />

programma verificando che tutti i sottoprogrammi si richiamino<br />

correttamente tra loro. Fatto questo può procedere a completare<br />

veramente ciascun sottoprogramma oppure, e questo è un altro<br />

vantaggio dell'aver costruito rapidamente lo ‘scheletro’ del<br />

programma, può fidare il completamento a più programmatori<br />

in contemporanea e da ciascuno consegnare una copia <strong>di</strong> questo<br />

scheletro in modo da poter testare il co<strong>di</strong>ce senza avere a<br />

<strong>di</strong>sposizione la parte che viene sviluppata dagli altri.<br />

Il completamento può avvenire anche per affinamenti<br />

successivi: si iniziano con l'introdurre versioni non perfette ma<br />

via via sempre più funzionanti dei vari sottoprogrammi. Questo<br />

aiuta a concentrarsi sugli aspetti fondamentali del problema: ad<br />

esempio non è importante all'inizio preoccuparsi <strong>di</strong> tutti i<br />

possibili errori <strong>di</strong> inserimento dati che potrebbe commettere<br />

l'utente; la gestione degli errori è senz'altro un aspetto che può<br />

essere introdotto in un secondo momento.<br />

Naturalmente dopo che ciascun programmatore avrà<br />

completato il test sulla sua parte sarà necessaria una fase <strong>di</strong> test<br />

conclusiva con tutto il co<strong>di</strong>ce al suo posto!


Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 104<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

La tecnica del top-down presuppone, però, che il programmatore abbia un'idea chiara, almeno a gran<strong>di</strong> linee, del<br />

programma nella sua versione completa. Solo in questo modo potrà infatti prefigurarsi i sottoprogrammi che<br />

dovranno essere sviluppati e come questi dovranno interagire tra loro. Diversamente non sarebbe infatti in grado <strong>di</strong><br />

abbozzare lo scheletro dell'intero programma.<br />

Bottom-Up<br />

Quando questo non è possibile (per l'estrema complessità del progetto e/o per l'alto grado <strong>di</strong> innovazione dello<br />

stesso che non consente <strong>di</strong> avere le idee chiare su tutto fin dall'inizio) può essere d'aiuto procedere in un altro modo:<br />

cominciare a sviluppare piccole porzioni sulle quali si hanno comunque idee chiare senza sapere ancora come si<br />

incastreranno tra loro (almeno nei dettagli). Sarà solo in un secondo momento che ci si preoccuperà <strong>di</strong> assemblare il<br />

programma con questi componenti. Per fare un paragone, è un po' come se si volesse realizzare un veicolo<br />

innovativo per spostarsi sulla sabbia: il progettista potrebbe non aver ancora le idee chiare sulla forma e le<br />

caratteristiche definitive del veicolo. Potrebbe però decidere <strong>di</strong> cominciare a realizzare delle ruote speciali, per poi<br />

passare al motore, poi alle singole parti del telaio e così via. Infine deciderà come assemblare insieme queste parti:<br />

certamente potrebbe essere costretto a rifare dei pezzi o a scartarne alcuni o ad adattarne degli altri (è il prezzo per<br />

questa modalità <strong>di</strong> sviluppo in cui non è possibile progettare nei dettagli fin dall'inizio); inoltre, <strong>di</strong> nuovo non avendo<br />

progettato fin dall'inizio le interconnessioni tra le varie parti, sarà più <strong>di</strong>fficile affidarne lo sviluppo a <strong>di</strong>versi<br />

programmatori senza essere costretti a dell'ulteriore lavoro in fase <strong>di</strong> assemblaggio (un programmatore potrebbe<br />

aver progettato l'interfaccia <strong>di</strong> un suo sottoprogramma in modo non ottimale per un altro).<br />

Questa tecnica è chiamata BOTTOM-UP (dal basso verso l'alto): si parte dai dettagli e dai componenti più semplici<br />

per assemblare via via i componenti <strong>di</strong> più alto livello, quelli più complessi.<br />

Non si può <strong>di</strong>re che la tecnica sia superiore all'altra, tant'è che non è infrequente il caso in cui vengano in realtà<br />

utilizzate entrambe: il progetto viene fatto magari in modo top-down ma lo sviluppo parte in modo dettagliato dei<br />

livelli più bassi. La tecnica bottom-up si rivela particolarmente efficace quando un progetto viene sviluppato seguendo<br />

la filosofia della OOP (Object Oriented Programmino) che vede programmatore sviluppare componenti autonomi con<br />

l'obiettivo <strong>di</strong> poterli facilmente riutilizzare in progetti <strong>di</strong>versi. L’OOP verrà affrontata in quarta.


ESERCIZI SVOLTI – secondo blocco<br />

SOT 12. <strong>di</strong>fficoltà: bassa Dati due numeri determinare il maggiore.<br />

program massimo;<br />

var n1,n2,x: real;<br />

(* restituisce il massimo tra due valori comunicati *)<br />

function max(a,b: real): real;<br />

var risultato: real;<br />

begin<br />

if a>=b then<br />

risultato:=a<br />

else<br />

risultato:=b;<br />

max:=risultato<br />

end;<br />

(* prove <strong>di</strong> utilizzo della funzione massimo *)<br />

begin<br />

(* specificando come parametri delle costanti numeriche ... *)<br />

writeln ( max(3,46):2:0 );<br />

writeln ( max(46,3):2:0 );<br />

writeln ( max(3,3):2:0 );<br />

writeln ( max(-43,1) :2:0);<br />

(* specificando come parametri delle variabili esterne *)<br />

writeln('Inserisci due numeri e ti <strong>di</strong>ro'' qual''e'' il piu'' grande');<br />

write('Dimmi il primo -> '); readln(n1);<br />

write('Dimmi il secondo -> '); readln(n2);<br />

writeln ( max(n1,n2) :2:0);<br />

writeln ( max(n1,46) :2:0);<br />

writeln ( max(46,n2) :2:0);<br />

(* una funzione puo' essere usata <strong>di</strong>rettamente nel calcolo <strong>di</strong> un'espressione *)<br />

x:=13 * ( max(n1,6) - 18);<br />

writeln('X: ',x:2:0);<br />

(* max puo' essere usata nella con<strong>di</strong>zione <strong>di</strong> un if *)<br />

if max(n1,n2) > 100 then<br />

writeln('Il massimo tra i due valori supera 100');<br />

Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 105<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

(* o <strong>di</strong> un repeat ... *)<br />

(* ad una centralina <strong>di</strong> controllo arrivano i dati sull'inquinamento letti da due sonde poste in punti strategici della<br />

citta'; leggere questi dati fino a quando una delle due sonde comunica un valore maggiore <strong>di</strong> 50 (microgrammi/mc)<br />

*)<br />

repeat<br />

write('Inserire valore sonda n. 1 -> ');<br />

readln(n1);<br />

write('Inserire valore sonda n. 2 -> ');<br />

readln(n2)<br />

until max(n1,n2)>50; (* grazie alla funzione max il controllo può essere effettuato in contemporanea *)<br />

readln;<br />

end.


SOT 13. <strong>di</strong>fficoltà: bassa Determinare il numero <strong>di</strong> vocali presenti in una stringa.<br />

program prova;<br />

uses newdelay,crt;<br />

(* conta quante vocali ci sono in una stringa, prima tecnica *)<br />

function contaVocali(s: string) : integer;<br />

var nv,i: integer;<br />

begin<br />

nv:=0; (* numero vocali *)<br />

for i:=1 to length(s) do<br />

case s[i] of<br />

'a','A','e','E','i','I','o','O','u','U': nv:=nv+1;<br />

end;<br />

contaVocali:=nv<br />

end;<br />

(* conta quante vocali ci sono in una stringa, secondo tecnica *)<br />

(* usa la funzione POS invece del case *)<br />

function contaVocali2(s: string) : integer;<br />

var nv,i: integer;<br />

begin<br />

nv:=0; (* numero vocali *)<br />

for i:=1 to length(s) do<br />

if pos(s[i], 'aAeEiIoOuU')0 then<br />

nv:=nv+1;<br />

contaVocali2:=nv<br />

end;<br />

begin<br />

writeln(contaVocali('casa dolce casa'));<br />

readln<br />

end.<br />

Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 106<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

SOT 14. <strong>di</strong>fficoltà: bassa Scrivere un comando che invita l'operatore a premere un tasto per continuare.<br />

(* ha bisogno <strong>di</strong> commenti ? *)<br />

procedure atten<strong>di</strong>;<br />

begin<br />

writeln('.............. PREMI INVIO PER CONTINUARE ...............');<br />

readln<br />

end;


Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 107<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

SOT 15. <strong>di</strong>fficoltà: bassa Scrivere un comando per la visualizzazione <strong>di</strong> un messaggio qualsiasi tra due cornicette<br />

<strong>di</strong> asterischi; l'esecuzione del programma deve anche interrompersi invitando l'utente a per un tasto per continuare<br />

(sfruttare il comando precedente).<br />

Program prova;<br />

procedure atten<strong>di</strong>;<br />

begin<br />

writeln('.............. PREMI INVIO PER CONTINUARE ...............');<br />

readln<br />

end;<br />

(* visualizza la stringa ricevuta ed attende la pressione <strong>di</strong> INVIO *)<br />

procedure messaggio(mes: string);<br />

begin<br />

writeln('---------------------------------------------');<br />

writeln(mes);<br />

writeln('---------------------------------------------');<br />

atten<strong>di</strong>;<br />

end;<br />

begin<br />

clrscr;<br />

messaggio(‘Ciao, come va?’);<br />

end.<br />

SOT 16. <strong>di</strong>fficoltà: bassa Scrivere un sottoprogramma per il calcolo <strong>di</strong> x y con X e Y interi positivi.<br />

program potenze;<br />

uses newdelay, crt;<br />

var unaBase,unEsponente: integer;<br />

function eleva(base,esponente: integer):real;<br />

var risultato: real; i: integer;<br />

begin<br />

risultato:=1;<br />

for i:=1 to esponente do<br />

risultato:=risultato*base;<br />

eleva:=risultato<br />

end;<br />

begin<br />

clrscr;<br />

write('Dimmi la base '); readln(unaBase);<br />

write('Dimmi l''esponente '); readln(unEsponente);<br />

writeln('base: ',unaBase,' - esponente: ',unEsponente);<br />

writeln('il risultato e'': ', eleva(unaBase,unEsponente):6:0 );<br />

readln<br />

end.


Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 108<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

SOT 17. <strong>di</strong>fficoltà: me<strong>di</strong>a Togliere da una stringa eventuali spazi inutili all'inizio o alla fine della stringa stessa.<br />

program elimina_spazi;<br />

uses crt;<br />

var stringa: string;<br />

function togli_spazi(s: string): string;<br />

var i: integer;<br />

inizio,fine: integer;<br />

spazio: boolean;<br />

risultato: string;<br />

begin<br />

inizio:=1; fine:=length(s);<br />

(* cerco il primo carattere <strong>di</strong>verso da spazio a sinistra *)<br />

spazio:= (s[inizio]=' ');<br />

while ( inizio=inizio ) and spazio do<br />

begin<br />

dec(fine);<br />

spazio:=(s[fine]=' ')<br />

end;<br />

risultato:='';<br />

for i:=inizio to fine do<br />

risultato:=risultato+s[i];<br />

togli_spazi:=risultato<br />

end;<br />

begin<br />

clrscr;<br />

writeln( '#',togli_spazi(''),'#' );<br />

writeln( '#',togli_spazi('a'),'#' );<br />

writeln( '#',togli_spazi(' a'),'#' );<br />

writeln( '#',togli_spazi('a '),'#' );<br />

writeln( '#',togli_spazi('ab'),'#' );<br />

writeln( '#',togli_spazi(' a'),'#' );<br />

writeln( '#',togli_spazi('a '),'#' );<br />

writeln( '#',togli_spazi(' ab'),'#' );<br />

writeln( '#',togli_spazi('ab '),'#' );<br />

writeln( '#',togli_spazi(' ab '),'#');<br />

writeln( '#',togli_spazi(' questa e’’ una frase '),'#' );<br />

readln<br />

end.


Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 109<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

REGOLE DI VISIBILITA (SCOPING RULES) E DURATA DELLE VARIABILI<br />

Scrivendo programmi <strong>di</strong> una certa complessità è importante capire per ogni entità (variabili, costanti,<br />

sottoprogrammi, tipi ecc.) dove può essere usata e la sua durata (che non è detto che si estenda dal momento in cui il<br />

programma viene fatto partire a quello in cui viene terminato).<br />

Quando un'applicazione è costituita da un unico file sorgente e non si fa uso <strong>di</strong> sottoprogrammi il problema non<br />

sussiste: in un qualsiasi punto del co<strong>di</strong>ce tra il begin <strong>di</strong> inizio e l’end finale possiamo far riferimento ad una variabile<br />

qualsiasi, ad una costante qualsiasi ecc; queste stesse variabili esistono dal momento in cui il programma inizia la<br />

sua esecuzione fino al momento in cui il programma viene terminato.<br />

La situazione si complica quando in un programma si <strong>di</strong>chiarano dei sottoprogrammi (procedure o funzioni): infatti<br />

possiamo avere parametri con lo stesso nome <strong>di</strong> variabili o costanti del programma principale, oppure variabili o<br />

costanti <strong>di</strong>chiarate nelle rispettive sezioni del sottoprogramma e che hanno lo stesso nome <strong>di</strong> variabili e costanti del<br />

programma principale. Cerchiamo <strong>di</strong> mettere a fuoco la situazione con un esempio:<br />

program principale;<br />

var A: integer; S: string;<br />

X: real;<br />

procedure P(A: integer);<br />

var S: real;<br />

begin<br />

write(A);<br />

S := 3.14*X;<br />

end;<br />

begin<br />

A := 4;<br />

S := ciao;<br />

end.<br />

In quali punti del co<strong>di</strong>ce è visibile la variabile A <strong>di</strong>chiarata nella sezione var<br />

del programma principale?<br />

Quando ci troviamo all'interno del blocco begin … end. del programma<br />

principale e tentiamo <strong>di</strong> assegnare un valore alla variabile A, viene usata<br />

quella <strong>di</strong>chiarata nella sezione var del programma principale oppure quella<br />

<strong>di</strong>chiarata come parametro per la procedura P?<br />

E quando ci troviamo all'interno del blocco begin … end. del programma<br />

principale e tentiamo <strong>di</strong> assegnare un valore alla variabile S, viene usata<br />

quella <strong>di</strong>chiarata nella sezione var del programma principale oppure quella<br />

<strong>di</strong>chiarata come variabile locale nella procedura P?<br />

La variabile A usata con l'istruzione write nella procedura è il suo parametro<br />

o la variabile A <strong>di</strong>chiarata nella sezione var del programma principale?<br />

La variabile S usata con l’assegnamento nella procedura è la sua variabile<br />

locale o, <strong>di</strong> nuovo, quella della sezione var del programma principale?<br />

La variabile A usata nel corpo principale del programma è quella <strong>di</strong>chiarata<br />

nella sezione var del programma principale o il parametro della procedura?<br />

E infine: la variabile S usata nel corpo principale del programma è quella<br />

<strong>di</strong>chiarata nella sezione var del programma principale o la variabile locale<br />

con lo stesso nome <strong>di</strong>chiarata nella procedura?<br />

Come potete ben capire è essenziale stabilire delle regole, pena il caos totale!<br />

• Le entità <strong>di</strong>chiarate nella sezione var del programma principale sono visibili:<br />

o in un punto qualsiasi tra il begin e l’end. del programma principale<br />

o in un sottoprogramma qualsiasi a patto che nessun parametro o variabile locale abbiano lo stesso<br />

nome: in questo ultimo caso tra il begin e l'end hanno il sopravvento i parametri delle variabili<br />

locali (potremmo <strong>di</strong>re che si fa sempre riferimento alla <strong>di</strong>chiarazione ‘più vicina’ al punto in cui<br />

stiamo scrivendo);<br />

NOTA: abbiamo comunque più volte riba<strong>di</strong>to che un sottoprogramma non dovrebbe mai tentare <strong>di</strong><br />

utilizzare <strong>di</strong>rettamente una variabile esterna (sempre e solo attraverso un parametro!)<br />

• Le entità <strong>di</strong>chiarate nella sezione var del programma principale esistono dal momento in cui inizia il<br />

programma fino al momento in cui termina.


Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 110<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

Le entità <strong>di</strong>chiarate a livello <strong>di</strong> sottoprogramma (parametri e variabili locali) sono visibili:<br />

o tra il begin e l'end <strong>di</strong> quel sottoprogramma; parametri e variabili locali non possono quin<strong>di</strong> nessun<br />

modo essere utilizzati all'esterno del sottoprogramma in cui sono <strong>di</strong>chiarati;<br />

o in altri sottoprogrammi ma solo se <strong>di</strong>chiarati all'interno del primo (e a patto che questi ultimi a<br />

loro volta non utilizzino parametri o loro variabili locali con lo stesso nome);<br />

• Le entità <strong>di</strong>chiarate a livello <strong>di</strong> sottoprogramma (parametri e variabili locali) vengono create nel momenti in<br />

cui inizia l'esecuzione del sottoprogramma e vengono <strong>di</strong>strutte nel momenti in cui l'esecuzione del<br />

sottoprogramma termina; ne deriva che quando un sottoprogramma viene chiamato più volte i valori assunti in<br />

precedenza dalle variabili locali non sono più <strong>di</strong>sponibili.<br />

Applicando queste regole all'esempio precedente: la variabile A del programma può essere usata nel corpo<br />

principale ma non nella procedura (il parametro A la oscura); stessa situazione per la stringa S del programma (non<br />

può essere utilizzata nella procedura perché oscurata dalla variabile locale S; la variabile X può essere utilizzata<br />

invece dappertutto, nel programma e nella procedura (pratica, quest'ultima, da sconsigliare); il parametro A e la<br />

variabile locale S della procedura possono essere utilizzati solo in quest'ultima.<br />

La letteratura informatica in<strong>di</strong>ca anche con la <strong>di</strong>citura ambiente locale l’insieme delle entità <strong>di</strong>chiarate all'interno <strong>di</strong><br />

una stessa entità sintattica (programma, sottoprogramma); ad esempio l'ambiente locale per una procedura è<br />

costituito dai suoi parametri e dalle sue variabili locali. Ciò che è invece <strong>di</strong>chiarato all'esterno <strong>di</strong> una certa unità<br />

sintattica viene invece in<strong>di</strong>cato come ambiente non locale. Ad esempio, rispetto ad un sottoprogramma le variabili<br />

globali fanno parte dell'ambiente non locale. Attenzione però a non far corrispondere l'ambiente non locale con le<br />

variabili globali: se consideriamo infatti il caso <strong>di</strong> un sottoprogramma <strong>di</strong>chiarato all'interno <strong>di</strong> un altro per<br />

quest'ultimo l'ambiente non locale è costituito certamente delle variabili globali ma anche dalle variabili locali alla<br />

sottoprogramma che lo contiene:<br />

program prova;<br />

var x: real;<br />

procedure P1;<br />

var y: real;<br />

procedure P2;<br />

var z: real;<br />

begin<br />

end;<br />

begin<br />

end;<br />

begin end.<br />

La procedura P2 è <strong>di</strong>chiarata all'interno <strong>di</strong> P1. Il senso <strong>di</strong> ciò sta nel fatto che P2<br />

svolge un compito molto particolare che a senso solo nell'ambito <strong>di</strong> P1. Dichiarata<br />

in questo modo, solo P1 può invocare P2 evitando usi maldestri.<br />

L'ambiente non locale <strong>di</strong> P2 è costituito non solo dalla variabile x del programma<br />

principale ma anche dalla variabile y della procedura P1.<br />

Quando l'applicazione è il risultato della compilazione e link <strong>di</strong> più sorgenti la situazione si complica: in generale<br />

bisogna decidere cosa concedere in utilizzo <strong>di</strong> un certo sorgente quando stiamo scrivendo il co<strong>di</strong>ce <strong>di</strong> un altro. I<br />

linguaggi si <strong>di</strong>fferenziano per possibilità e notazioni sintattiche anche molto <strong>di</strong>verse. Di solito è comunque possibile<br />

in<strong>di</strong>care un’entità <strong>di</strong>chiarata nella sezione var principale come globale rispetto agli altri file sorgenti; ad esempio,<br />

dare la possibilità dal file sorgente<br />

‘unit1’ <strong>di</strong> usare una variabile globale<br />

File principale<br />

della ‘Unit2’ o <strong>di</strong> richiamare un<br />

sottoprogramma <strong>di</strong> una qualsiasi altra<br />

Unit. In alcuni linguaggi, turbo Pascal<br />

Unit1 Unit2 Unit3 UnitN<br />

compreso, per accedere all'entità <strong>di</strong><br />

un altro file si deve rendere palese la<br />

volontà <strong>di</strong> usare quest'ultimo con una<br />

<strong>di</strong>rettiva al compilatore (uses crt non<br />

vi <strong>di</strong>ce nulla?).<br />

.EXE


TIPO DI DATO ARRAY<br />

Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 111<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

Voglio presentarvi alcune situazioni ‘problematiche’ per evidenziare l’inadeguatezza degli strumenti da<br />

programmatore che sono attualmente in vostro possesso.<br />

Caso <strong>di</strong> stu<strong>di</strong>o 1: a proposito <strong>di</strong> me<strong>di</strong>e …<br />

Avete fatto carriera e vi ritrovate prof. Informatica! Avete anche appena terminato <strong>di</strong> correggere il primo pacco <strong>di</strong><br />

compiti (sigh) e vorreste contare quanti alunni hanno preso un voto superiore alla me<strong>di</strong>a della classe: che<br />

informatici sareste se non pensaste <strong>di</strong> utilizzare un programmino per calcolare una volta per tutte questo valore ?<br />

Program me<strong>di</strong>e;<br />

var<br />

i, voto: integer; (* serve per i cicli, voto per leggere da tastiera il voto *)<br />

somma_voti, num_alunni: integer; (* num_alunni: numero degli alunni *)<br />

quanti_sopra_me<strong>di</strong>a: integer;<br />

me<strong>di</strong>a: real;<br />

begin<br />

somma_voti:=0; me<strong>di</strong>a:=0; quanti_sopra_me<strong>di</strong>a:=0;<br />

writeln(Quanti alunni ci sono? );<br />

readln(num_alu);<br />

for i:=1 to num_alu do<br />

begin<br />

writeln(Inserisci voto prossimo alunno: );<br />

readln(voto);<br />

somma_voti:=somma_voti + voto<br />

end;<br />

me<strong>di</strong>a:=somma_voti /num_alu;<br />

Dopo aver impiegato solo pochi millesimi <strong>di</strong> secondo per scrivere questo co<strong>di</strong>ce, vi accorgete <strong>di</strong> … non sapere<br />

come andare avanti! Infatti ora che avete calcolato la me<strong>di</strong>a dovreste contare i voti che la superano, ma non avete<br />

più i voti a <strong>di</strong>sposizione per confrontarli ! Infatti la variabile voto conterrà solo l’ultimo voto letto. I precedenti sono<br />

stati sommati nella variabile somma_voti e poi soprascritti ciascuno dal successivo (questo ovviamente perché la<br />

readln utilizza nel ciclo sempre la stessa variabile per leggere i voti).


Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 112<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

ŁŁ Una prima orripilante soluzione (sicuri <strong>di</strong> esservi laureati in Informatica??) potrebbe essere quella <strong>di</strong> chiedere<br />

una seconda volta all’operatore gli stessi voti per contare quelli sopra la me<strong>di</strong>a<br />

(si immagina <strong>di</strong> continuare il programma <strong>di</strong> prima):<br />

for i:=1 to num_alu do<br />

begin<br />

writeln(Inserisci voto prossimo alunno: );<br />

readln(voto);<br />

if voto>me<strong>di</strong>a then<br />

quanti_sopra_me<strong>di</strong>a:= quanti_sopra_me<strong>di</strong>a + 1<br />

end;<br />

Stiamo scherzando? A parte le furiose proteste dell’utente che si vede costretto a ripetere l’inserimento<br />

dei dati, provate a pensare al rischio <strong>di</strong> inserire dati <strong>di</strong>versi dai precedenti con risultati del tutto<br />

falsati…<br />

ATTENZIONE: la seconda ‘soluzione’ che viene proposta qui <strong>di</strong> seguito è, come il primo, solo un esempio. Come<br />

tale è NECESSARIO per apprezzare e capire l’uso del nuovo strumento <strong>di</strong> programmazione che presenterò, ma non<br />

deve essere ‘stu<strong>di</strong>ato’.<br />

ŁŁ Seconda soluzione (lascio decidere a voi se più o meno orripilante della precedente): ‘banale,<br />

come ho fatto a non pensarci prima: basta usare una variabile <strong>di</strong>versa per ogni voto!’<br />

Program me<strong>di</strong>e;<br />

var<br />

i, somma_voti, num_alunni, quanti_sopra_me<strong>di</strong>a: integer;<br />

me<strong>di</strong>a: real;<br />

voto1, voto2, voto3, voto4, , voto32 (aiuto!): integer;<br />

begin<br />

soma_voti:=0; me<strong>di</strong>a:=0; quanti_sopra_me<strong>di</strong>a:=0;<br />

writeln(Quanti alunni ci sono? );<br />

readln(num_alu);<br />

Il bello arriva adesso! Poiché non si sta usando una sola variabile (voto) per leggere i voti, non è possibile usare un<br />

ciclo per leggere i voti! E’ necessario leggere il primo. Poi se il numero <strong>di</strong> alunni è maggiore <strong>di</strong> 1, significa che devo<br />

sicuramente leggerne anche un secondo; poi se il numero <strong>di</strong> alunni è anche maggiore <strong>di</strong> due significa che devo<br />

leggerne almeno anche un terzo e così via …<br />

writeln(Inserisci voto primo alunno: );<br />

readln(voto1);<br />

somma_voti:= somma_voti + voto1<br />

if num_alu>1 then<br />

begin<br />

writeln(Inserisci voto secondo alunno: );<br />

readln(voto2);<br />

somma_voti:= somma_voti + voto2<br />

end;


e così via fino a …<br />

if num_alu>2 then<br />

begin<br />

writeln(Inserisci voto prossimo alunno: );<br />

readln(voto3);<br />

somma_voti:= somma_voti + voto3<br />

end;<br />

if num_alu>31 then<br />

begin<br />

writeln(Inserisci voto 32-mo alunno: );<br />

readln(voto32);<br />

somma_voti:= somma_voti + voto<br />

end;<br />

me<strong>di</strong>a:=somma_voti / num_alu; (* alleluia *)<br />

e ora ripetiamo tutto per contare quelli sopra la me<strong>di</strong>a<br />

if voto1>me<strong>di</strong>a then<br />

quanti_sopra_me<strong>di</strong>a:= quanti_sopra_me<strong>di</strong>a + 1;<br />

if num_alu>1 then<br />

if voto2>me<strong>di</strong>a then<br />

quanti_sopra_me<strong>di</strong>a:= quanti_sopra_me<strong>di</strong>a + 1;<br />

ecc. ecc. (abbiate pietà !)<br />

Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 113<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

Non so voi, ma io ho già perso <strong>di</strong> vista l’obiettivo che ci eravamo posti… Immaginate se invece degli<br />

alunni <strong>di</strong> una sola classe considerassimo quelli dell’intero istituto per una qualche statistica!


ED ECCO LA SOLUZIONE …<br />

Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 114<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

Quello che ci serve è un contenitore (si tratterà poi pur sempre <strong>di</strong> una variabile, anche se <strong>di</strong> tipo particolare) capace<br />

<strong>di</strong> contenere non un solo voto ma un insieme <strong>di</strong> voti. Il nome del contenitore in questo modo sarebbe uno solo,<br />

risolvendo l’inconveniente del proliferare selvaggio del numero delle variabili; inoltre, come vi spiegherò tra poco,<br />

sarà ancora possibile usare i cicli per elaborare con poche righe <strong>di</strong> co<strong>di</strong>ce tutti i dati.<br />

VOTI<br />

7 4 5 6 6 8 8 5<br />

voti[1] voti[2] voti[3] voti[4] voti[5] voti[6] ecc.<br />

• Il nome del contenitore è VOTI. Questo è il nome che il programmatore ha deciso <strong>di</strong> dare all’ARRAY.<br />

Quando l’array è come quello nel <strong>di</strong>segno soprastante (lineare, mono<strong>di</strong>mensionale) è anche chiamato<br />

vettore. Esistono array bi<strong>di</strong>mensionali (pensate ad una griglia tipo battaglia navale, o ad uno schema tipo<br />

parole crociate) chiamati matrici.<br />

• I dati non sono depositati nel vettore alla rinfusa ma tenuti in ‘scomparti’ separati.<br />

• Ogni ‘scomparto’ occupa una precisa posizione (1, 2, 3, 4 ecc.) specificata tramite un numero detto in<strong>di</strong>ce<br />

che va in<strong>di</strong>cato tra parentesi quadrate dopo il nome dell’array come in voti[1], voti[2] ecc.<br />

Ecco come si <strong>di</strong>chiara in Pascal una variabile array adatta a contenere 32 interi:<br />

var (* NOTA. sarebbe meglio mettere una costante invece del 32:<br />

voti: array[1 .. 32] of integer; array[1 .. NUM_ALU] of integer *)<br />

Per poter passare gli array ai sottoprogrammi è però meglio abituarsi fin da subito a <strong>di</strong>chiarare prima un nuovo tipo<br />

<strong>di</strong> dati e solo dopo le variabili <strong>di</strong> quel tipo:<br />

NOTA: la sezione type deve essere scritta prima della sezione var<br />

type<br />

vet_int = array[1 .. 32] of integer; (* vet_int sta per vettore <strong>di</strong> interi *)<br />

var<br />

voti: vet_int;<br />

OSSERVAZIONI<br />

• Quando si <strong>di</strong>chiara un tipo non si usano i due punti ma l’uguale (… = array … )<br />

• Nella sezione var è poi possibile <strong>di</strong>chiarare tutte le variabili <strong>di</strong> quel tipo che si vogliono: tutte<br />

corrisponderanno a contenitori per 32 interi<br />

Voti è <strong>di</strong> tipo vet_int, cioè un array[1 .. 32] of integer, esattamente come prima. Anche un parametro <strong>di</strong> un<br />

sottoprogramma potrà essere <strong>di</strong>chiarato <strong>di</strong> tipo vet_int (la <strong>di</strong>citura array[1 ..32] of integer non sarebbe infatti<br />

accettata come tipo <strong>di</strong> un parametro).


Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 115<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

Una volta specificata la posizione tra parentesi quadre la scrittura in<strong>di</strong>ca esattamente una variabile del tipo contenuto<br />

nel vettore. Detto con altre parole: vet[7] è, a tutti gli effetti, una variabile integer e può essere usata dove siete<br />

abituati ad usare una qualsiasi altra variabile integer:<br />

Writeln(Dimmi un valore e lo memorizzerò nella posizione 4 del vettore);<br />

readln( voti[4] );<br />

writeln(Ora visualizzo il contenuto del sesto elemento del vettore: );<br />

writeln( voti[6] );<br />

if vet[2]>x then <br />

repeat<br />

<br />

until x>vet[5];<br />

x:=6 * vet[3] 2*vet[7]<br />

writeln ('Dimmi una posizione e visualizzerò il contenuto <strong>di</strong> quellelemento: );<br />

readln (posizione);<br />

writeln ( voti[posizione] );<br />

L’ultimo esempio è particolarmente interessante: invece <strong>di</strong> specificare un numero preciso, come in<strong>di</strong>ce è possibile<br />

mettere una variabile integer. Questo dà al programmatore la possibilità <strong>di</strong> scan<strong>di</strong>re, passare in rassegna, più<br />

elementi del vettore cambiando in un ciclo il valore della variabile in<strong>di</strong>ce:<br />

for i:=1 to num_voti do<br />

writeln( voti[i] );<br />

Cercate <strong>di</strong> capire bene questo spezzone <strong>di</strong> co<strong>di</strong>ce: è alla base <strong>di</strong> tutte le elaborazioni con gli array che vi saranno<br />

spiegate in questo corso!<br />

La variabile i, grazie al ciclo, assume tutte le posizioni del vettore e <strong>di</strong> volta in volta in<strong>di</strong>vidua nel ciclo elementi<br />

successivi, uno dopo l’altro dal primo all’ultimo. La prima volta che viene eseguito il ciclo la i vale 1 e quin<strong>di</strong> la<br />

writeln corrisponde a writeln(voti[1]) e viene quin<strong>di</strong> visualizzato il contenuto del primo elemento del vettore. Poi la<br />

i <strong>di</strong>venta 2, e viene elaborato il secondo elemento e così via fino all’ultimo in<strong>di</strong>cato nel ciclo (num_voti).<br />

E’ arrivato il momento <strong>di</strong> mettere tutto insieme e risolvere elegantemente il problema <strong>di</strong> partenza …


Program me<strong>di</strong>e;<br />

const<br />

MAX_ALUNNI=32;<br />

type<br />

vettore_32_interi = array[1 .. MAX_ALUNNI] of integer;<br />

var<br />

voti: vettore_32_interi;<br />

i, somma_voti, num_alunni, quanti_sopra_me<strong>di</strong>a: integer;<br />

me<strong>di</strong>a: real;<br />

begin<br />

somma_voti:=0; me<strong>di</strong>a:=0; quanti_sopra_me<strong>di</strong>a:=0;<br />

writeln(Quanti alunni ci sono? );<br />

readln(num_alu);<br />

(* CARICAMENTO DEI DATI NEL VETTORE *)<br />

for i:=1 to num_alu do<br />

begin<br />

writeln(Inserisci voto prossimo alunno: );<br />

readln(voti[i]);<br />

somma_voti:=somma_voti + voti[i]<br />

end;<br />

me<strong>di</strong>a:=somma_voti / num_alu;<br />

(* I DATI SONO ANCORA NEL VETTORE: CONTROLLIAMO TUTTI *)<br />

for i:=1 to num_alu do<br />

if voti[i]>me<strong>di</strong>a then<br />

quanti_sopra_me<strong>di</strong>a:= quanti_sopra_me<strong>di</strong>a + 1;<br />

writeln(Risultato: ,quanti_sopra_me<strong>di</strong>a);<br />

end.<br />

OSSERVAZIONI<br />

Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 116<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

a) Se gli alunni <strong>di</strong>ventassero anche 10000 il co<strong>di</strong>ce rimarrebbe identico! Sarebbe sufficiente mo<strong>di</strong>ficare il<br />

valore della costante MAX_ALUNNI<br />

b) Non è obbligatorio usare tutte le posizioni del vettore: al massimo ce ne sono <strong>di</strong>sponibili MAX_ALUNNI<br />

ma il valore comunicato al computer (num_alu) potrebbe anche essere inferiore. A <strong>di</strong>re la verità, il<br />

programma dovrebbe controllare che questo valore sia compreso tra 1 e MAX_ALUNNI perché<br />

tentare <strong>di</strong> usare un elemento prima dellinizio del vettore o dopo la fine è un gravissimo errore che<br />

spesso porta al blocco del programma ed eventualmente del computer stesso


Caso <strong>di</strong> stu<strong>di</strong>o 2: MENU<br />

Attraverso questo esempio imparerete a passare i vettori ai sottoprogrammi.<br />

Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 117<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

Un menu presenta sullo schermo un elenco <strong>di</strong> opzioni delle quali una sola sarà quella scelta dall’utente. Un menu<br />

può presentarsi a tutto schermo, come nell’esempio qui sotto a sinistra, o sotto forma <strong>di</strong> barra dei menu, come<br />

nell’esempio qui sotto a destra.<br />

Scegli il gioco che preferisci<br />

1. Tennis<br />

2. Poker<br />

3. Scacchi<br />

4. ESCI<br />

SCEGLI => _<br />

File Mo<strong>di</strong>fica<br />

Apri<br />

Salva<br />

Chiunque abbia provato ad usare un programma per computer sa che sono veramente tante le situazioni in cui è<br />

necessario <strong>di</strong>alogare con l’utente presentando un menu <strong>di</strong> scelte.<br />

Limitandoci alla tipologia rappresentata qui sopra a sinistra, fondamentalmente lo scopo <strong>di</strong> un menu è sempre lo<br />

stesso: scrivere un titolo, sotto <strong>di</strong> questo scrivere un certo numero <strong>di</strong> scelte e leggere la risposta dell’utente.<br />

Situazione perfetta per la scrittura <strong>di</strong> un sottoprogramma: comunico titolo, opzioni, numero <strong>di</strong> queste ultime e come<br />

valore <strong>di</strong> ritorno ci si aspetterà il numero della scelta fatta. Naturalmente il sottoprogramma provvederà anche a<br />

rifiutare scelte impossibili (valori minori <strong>di</strong> 1 o più gran<strong>di</strong> del numero delle opzioni previste), a visualizzare<br />

messaggi <strong>di</strong> errore e a far ripetere la scelta fino a che l’utente non in<strong>di</strong>cherà forzatamente una delle opzioni previste:<br />

Titolo<br />

n. opzioni<br />

opzione 1<br />

opzione 2<br />

opzione 3<br />

opzione 4<br />

Ecc.<br />

MENU<br />

numero della scelta<br />

fatta


Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 118<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

La chiamata alla funzione menu che corrisponde all’esempio cui ci stiamo riferendo sarebbe allora:<br />

scelta:=menu(Scegli il gioco che preferisci,4,TennisPokerScacchiESCI)<br />

Un’altra situazione tipica è quella in cui, segnalando un errore, si vuole chiedere all’utente come procedere.<br />

Immaginiamo che si sia verificato un errore <strong>di</strong> registrazione <strong>di</strong> un documento:<br />

scelta:=menu(Errore <strong>di</strong> scrittura su <strong>di</strong>sco,3,RiprovaIgnoraannulla)<br />

Scusi prof., mi sa che ha <strong>di</strong>menticato un parametro nel secondo esempio (lo studente me<strong>di</strong>o finge clamorosamente <strong>di</strong><br />

essere <strong>di</strong>sinteressato a quello che scrivi, ma prova a sbagliare una virgola …): devono essere sei (il titolo, numero<br />

opzioni e le quattro scelte).<br />

E già, ma se le scelte sono solo 3 come nel secondo esempio? O se ne servissero 12?? L’unica soluzione ora alla<br />

vostra portata sarebbe quella <strong>di</strong> prevedere il massimo numero possibile <strong>di</strong> scelte, ignorando quelle che <strong>di</strong> volta in<br />

volta non servono; la <strong>di</strong>chiarazione dell’intestazione <strong>di</strong> questa funzione sarebbe un vero ‘mostro’ che potrebbe<br />

assomigliare alla seguente:<br />

Function menu(titolo: string; num_opzioni: integer;<br />

scelta1,scelta2,scelta3,scelta4,,sceltaN: string): integer;<br />

dove N rappresenta il massimo numero <strong>di</strong> scelte utilizzabile; se le opzioni fossero <strong>di</strong> più la situazione non sarebbe<br />

gestibile a meno <strong>di</strong> mo<strong>di</strong>ficare il sottoprogramma.<br />

Chiamare questa funzione sarebbe veramente una ‘pena’. Immaginiamo infatti <strong>di</strong> voler chiedere una conferma<br />

all’utente (in questo caso le opzioni sarebbero solo due: sì o no):<br />

scelta:=menu(Sei sicuro,2,SiNo)<br />

anche se le opzioni sono solo due, TUTTI i parametri devono comunque essere sempre in<strong>di</strong>cati (qui si immagina che<br />

quelli inutili siano in<strong>di</strong>cati come stringa nulla (‘’)<br />

Anche il co<strong>di</strong>ce della funzione sarebbe piuttosto pesante ed intricato:<br />

Function menu(titolo: string; num_opzioni: integer;<br />

scelta1,scelta2,scelta3,scelta4,,sceltaN: string): integer;<br />

begin<br />

writeln(titolo); (* e fino a qui *)<br />

e qui immaginatevi, un po come nel caso della me<strong>di</strong>a, tutta una serie <strong>di</strong> if then che a seconda del<br />

numero <strong>di</strong> opzioni specificato vanno o non vanno a stampare la variabile stringa che corrisponde a<br />

quellopzione; per semplicità mi limito solo ad iniziare <br />

writeln(1 ,scelta1);<br />

if num_opzioni>1 then<br />

writeln(2 ,scelta2);<br />

if num_opzioni>2 then<br />

writeln(3 ,scelta3);<br />

ecc.<br />

ED ECCO LA SOLUZIONE CON GLI ARRAY …<br />

La soluzione che sfrutta gli array prevede invece <strong>di</strong> passare le opzioni sotto forma <strong>di</strong> vettore <strong>di</strong> stringhe:<br />

type vettore_stringhe = array[1 .. 20] of string;<br />

Function menu(titolo: string; num_opzioni: integer; scelte: vettore_stringhe): integer;<br />

var i,scelta: integer;<br />

begin<br />

writeln(titolo);<br />

for I:=1 to num_opzioni do<br />

writeln(I, , scelte[i]);<br />

Stampa tutte le opzioni precedute dalla I e da un trattino: 1<br />

– opzione1, 2 – opzione 2 ecc.


writeln(Scegli -> );<br />

readln(scelta);<br />

menu:=scelta<br />

end;<br />

OPERAZIONI TIPICHE CON GLI ARRAY<br />

In tutti gli esempi che seguono si immaginano valere le seguenti <strong>di</strong>chiarazioni:<br />

Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 119<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

const<br />

MAX_ELEMENTI=100;<br />

type<br />

vettore_interi = array[1 .. MAX_ELEMENTI] of integer;<br />

var<br />

vettore: vettore_interi; N: integer; (* N è il numero <strong>di</strong> elementi da usare *)<br />

1. Caricamento <strong>di</strong> un vettore <strong>di</strong> N elementi, con N < MAX_ELEMENTI<br />

for i:=1 to N do<br />

begin<br />

writeln(Inserire elemento n. , i)<br />

readln( vettore[i] )<br />

end;<br />

Ve<strong>di</strong>amo la stessa operazione fatta con un sottoprogramma. Poiché non inten<strong>di</strong>amo avvantaggiarci <strong>di</strong> un valore<br />

restituito da una funzione, useremo una procedura.<br />

Essa riceverà il vettore da caricare per in<strong>di</strong>rizzo: <strong>di</strong>versamente non potremmo mo<strong>di</strong>ficare in modo permanente i<br />

valori degli elementi del vettore passato alla procedura. Il secondo parametro corrisponde al numero <strong>di</strong> elementi da<br />

caricare (chi usa la procedura deve assicurarsi <strong>di</strong> non passare come numero <strong>di</strong> elementi un valore superiore alla<br />

capacità massima del vettore).<br />

procedure carica_vettore(VAR vet: vettore_interi; N: integer);<br />

var i: integer;<br />

begin<br />

for i:=1 to N do<br />

begin<br />

writeln(Inserire elemento n. , i)<br />

readln( vet[i] )<br />

end;<br />

end;<br />

Per semplicità non è stato controllato l’input dell’utente<br />

(che potrebbe essere num_opzioni)<br />

NOTA: caricare un vettore <strong>di</strong><br />

stringhe, piuttosto che <strong>di</strong> reali<br />

comporterebbe solo la mo<strong>di</strong>fica<br />

delle <strong>di</strong>chiarazioni nella sezione<br />

type.<br />

NOTA<br />

Qualcuno potrebbe obiettare che la procedura risulta costituita da un numero <strong>di</strong> righe <strong>di</strong> co<strong>di</strong>ce superiore rispetto<br />

alla soluzione che non usa i sottoprogrammi. Ricor<strong>di</strong>amoci però che la procedura potrà essere chiamata tutte le volte<br />

che si vuole per caricare vettori dello stesso tipo (ANCHE in programmi <strong>di</strong>versi: basta copiarla/incollarla od<br />

includerla in una libreria). Qualche volta si tratterà <strong>di</strong> voti, altre volte <strong>di</strong> temperature, pesi od altezze ecc. La<br />

procedura non conosce il significato degli interi che sta caricando: svolge in modo anonimo il suo compito: riempire<br />

<strong>di</strong> interi un vettore che riceve come parametro, punto.


Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 120<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

Suggerimento: prova a rendere ancora più generale la procedura prevedendo un terzo parametro corrispondente ad<br />

un messaggio personalizzato che solleciti l’utente in modo appropriato. In questo modo invece del generico ‘Inserire<br />

elemento n.’, potranno essere usati messaggi tipo ‘Inserire la temperatura n.’, o ‘Inserire il peso dell’oggetto n.’ ecc.<br />

2. Visualizzazione degli elementi <strong>di</strong> un vettore <strong>di</strong> N elementi, con N < MAX_ELEMENTI<br />

for i:=1 to N do<br />

writeln( vettore[i] );<br />

Procedura per visualizzare gli elementi <strong>di</strong> un vettore. Riceve il vettore (per<br />

valore, visto che non dobbiamo mo<strong>di</strong>ficare in alcun modo il suo contenuto!)<br />

ed il numero <strong>di</strong> elementi da considerare.<br />

NOTA: chi usa la procedura deve assicurarsi <strong>di</strong> non passare come numero <strong>di</strong><br />

elementi un valore superiore alla capacità massima del vettore.<br />

procedure visualizza_vettore(vet: vettore_interi; N: integer);<br />

var i: integer;<br />

begin<br />

for i:=1 to N do<br />

writeln( vet[i] )<br />

end;


Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 121<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

3. Somma dei valori degli elementi <strong>di</strong> un vettore <strong>di</strong> N elementi, con N < MAX_ELEMENTI<br />

somma:=0;<br />

for i:=1 to N do<br />

somma:=somma + vettore[i] ;<br />

4. Me<strong>di</strong>a degli elementi <strong>di</strong> un vettore <strong>di</strong> N elementi, con N < MAX_ELEMENTI<br />

NOTA: questo esempio è particolarmente interessante se utilizziamo i sottoprogrammi. Infatti la funzione che<br />

calcola la me<strong>di</strong>a può richiamare quella, già scritta, che calcola la somma!<br />

somma:=0;<br />

for i:=1 to N do<br />

somma:=somma + vettore[i] ;<br />

me<strong>di</strong>a:= somma / N;<br />

Funzione per la stessa operazione.<br />

function somma_vettore(vet: vettore_interi; N: integer):integer;<br />

var tot: integer;<br />

begin<br />

tot:=0;<br />

for i:=1 to N do<br />

tot:=tot + vettore[i] ;<br />

somma_vettore:=tot<br />

end;<br />

Funzione per la stessa operazione.<br />

function me<strong>di</strong>a_vettore(vet: vettore_interi; N: integer):integer;<br />

begin<br />

me<strong>di</strong>a_vettore:= somma_vettore(vet,N) <strong>di</strong>v N<br />

end;<br />

NOTA: un sottoprogramma può passare ad un altro i suoi stessi parametri,<br />

con le stesse modalità viste sino ad ora (per valore o per in<strong>di</strong>rizzo).


Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 122<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

5. Posizione dell’elemento con valore minimo in un vettore <strong>di</strong> N elementi, con N < MAX_ELEMENTI<br />

L’algoritmo è basato su questo ragionamento: all’inizio ipotizzo che il minimo sia l’elemento che si trova in<br />

posizione 1. Quin<strong>di</strong> controllo tutti gli altri elementi, dal secondo in poi, e tutte le volte che trovo una posizione con<br />

un valore più piccolo <strong>di</strong>venta quest’ultima la nuova posizione del minimo.<br />

posizione_min:= 1;<br />

for i:=2 to N do<br />

if vettore[i] < vettore[posizione_min] then (* trovata posizione i con valore più piccolo *)<br />

posizione_min := i;<br />

Funzione per la stessa operazione.<br />

function posizione_min(vet: vettore_interi; N: integer):integer;<br />

var pos_min:integer;<br />

begin<br />

pos_min:= 1;<br />

for i:=2 to N do<br />

if vet[i] < vet[pos_min] then (* trovata posizione i con valore più piccolo *)<br />

pos_min := i;<br />

posizione_min := pos_min<br />

end;<br />

NOTA: se nel vettore ci sono più elementi minimi con lo stesso valore, viene in<strong>di</strong>viduato il primo (quello con in<strong>di</strong>ce<br />

minore). Infatti quando viene confrontato il primo minimo con gli altri la con<strong>di</strong>zione vet[i] < vet[pos_min] non è<br />

verificata e l’aggiornamento della posizione non viene effettuata. Aggiungendo l’uguaglianza al confronto (vet[i]<br />

e pos_min con pos_max per<br />

ottenere l’algoritmo desiderato.


Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 123<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

7. Ricerca della posizione <strong>di</strong> un valore in un vettore <strong>di</strong> N elementi, con N < MAX_ELEMENTI<br />

writeln(Quale elemento cerchi? );<br />

readln(cercato);<br />

posizione:=0;<br />

for i:=1 to N do<br />

if vettore[i] = cercato then<br />

posizione:=i;<br />

function cerca_vettore(vet: vettore_interi; N: integer; cercato: integer):integer;<br />

var posizione: integer;<br />

begin<br />

posizione:=0;<br />

for i:=1 to N do<br />

if vet[i] = cercato then<br />

posizione:=i;<br />

cerca_vettore:=posizione<br />

end;<br />

Ed ecco la soluzione con il repeat, nel caso si voglia il primo elemento uguale a quello che si sta cercando. Questa<br />

soluzione può essere anche molto più efficiente <strong>di</strong> quella con il for. Ad esempio se gli elementi del vettore fossero<br />

<strong>di</strong>ecimila e quello che si sta cercando fosse nella quarta posizione, il ciclo for andrebbe inutilmente a controllare gli<br />

altri 9996, mentre il ciclo repeat si fermerebbe al quarto!<br />

posizione:=0; i:=0;<br />

repeat<br />

i:=i+1;<br />

if vettore[i] = cercato then<br />

posizione:=i<br />

until (i=N) or (posizione0);<br />

OSSERVAZIONI<br />

• Se l’elemento non viene trovato la variabile posizione rimarrà<br />

0. E’ importante controllarla prima <strong>di</strong> usarla! Ad esempio: if<br />

posizione>0 then (* elemento trovato, possiamo procedere<br />

*)<br />

• Se nel vettore l’elemento cercato appare più volte, il ciclo for<br />

localizza l’ultimo (comunque la scansione arriva ad N); per<br />

localizzare il primo è necessario controllare ‘manualmente’ la<br />

ricerca usando un ciclo repeat (come mostrato più avanti)<br />

function cerca_vettore(vet: vettore_interi; N: integer; cercato: integer):integer;<br />

var posizione,i: integer;<br />

begin<br />

posizione:=0; i:=0;<br />

repeat<br />

i:=i+1;<br />

if vettore[i] = cercato then<br />

posizione:=i<br />

until (i=N) or (posizione0);<br />

cerca_vettore:=posizione<br />

end;


7. Or<strong>di</strong>namento dei valori in un vettore <strong>di</strong> N elementi, con N < MAX_ELEMENTI<br />

METODO PER SCAMBIO – BUBBLE SORT<br />

Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 124<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

Quello che sto per presentare è un metodo non molto efficiente quando il numero degli elementi supera il centinaio,<br />

ma ha il pregio <strong>di</strong> essere breve, relativamente semplice ed abbastanza facile da ricordare. Appartiene alla famiglia<br />

degli algoritmi <strong>di</strong> or<strong>di</strong>namento (SORT) detti per scambio perché adotta una scansione <strong>sistema</strong>tica che confronta<br />

‘ciecamente’ tutti gli elementi e scambia quelli fuori posto tra loro. Lo scambio <strong>di</strong> elementi è usato in modo pesante:<br />

altri algoritmi adottano tecniche che ‘ragionano’ <strong>di</strong> più sulla situazione del vettore od adottano tecniche più<br />

intelligenti (ripagate solo se il numero degli elementi è sufficientemente elevato) <strong>di</strong>minuendo il numero <strong>di</strong> scambi<br />

necessari. Ovviamente questi algoritmi sono più <strong>di</strong>fficili …<br />

Prima <strong>di</strong> esaminare il co<strong>di</strong>ce Pascal conviene presentare l’algoritmo in modo ‘informale’ aiutandosi anche con una<br />

simulazione grafica. Come punto <strong>di</strong> partenza consideriamo il seguente vettore <strong>di</strong>sor<strong>di</strong>nato:<br />

9 8 2 6 3<br />

L’algoritmo procede in questo modo:<br />

or<strong>di</strong>namento<br />

1. confronta la prima posizione con la seconda: se il numero contenuto nella prima posizione è maggiore <strong>di</strong> quello<br />

nella seconda scambia i numeri:<br />

confronto scambio<br />

9 8 2 6 3 9 8 2 6 3<br />

NOTA: tenete d’occhio il numero 9; come una bolla d’aria nell’acqua risalirà verso la ‘superficie’ ( la parte finale<br />

del vettore), da cui il nome dell’algoritmo (bubble=bolla sort=or<strong>di</strong>namento)<br />

2. spostati <strong>di</strong> una posizione verso destra e ripeti il confronto/scambio<br />

2 3 6 8 9<br />

3. ripeti il punto 2 fino a raggiungere la penultima posizione (che verrà confrontata con l’ultima)<br />

risultato<br />

8 9 2 6 3<br />

8 9 2 6 3 8 9 2 6 3 8 2 9 6 3<br />

8 2 9 6 3 8 2 9 6 3 8 2 6 9 3<br />

8 2 6 9 3 8 2 6 9 3 8 2 6 3 9


Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 125<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

Dopo questo primo ‘giro’ (passata) l’elemento più grande del vettore (il 9) ha raggiunto la sua giusta posizione. Ora<br />

si ripete il tutto fermandoci però alla penultima posizione (l’ultima, come detto prima, è già or<strong>di</strong>nata e va lasciata<br />

stare!). Per ricordarcelo, sfumiamo in grigio l’ultima casella del vettore.<br />

8 2 6 3 9 8 2 6 3 9 2 8 6 3 9<br />

2 8 6 3 9 2 8 6 3 9 2 6 8 3 9<br />

2 6 8 3 9 2 6 8 3 9 2 6 3 8 9<br />

Ora ripetiamo ma fermandoci alla terzultima posizione (le ultime due sono in or<strong>di</strong>ne)<br />

2 6 3 8 9<br />

nessuno scambio<br />

2 6 3 8 9 2 6 3 8 9 2 3 6 8 9<br />

NOTA: il vettore sarebbe già in or<strong>di</strong>ne, ma l’algoritmo ‘non se ne accorge’ e, ciecamente, continua i confronti:<br />

2 3 6 8 9 Nessuno scambio<br />

2 3 6 8 9<br />

2 3 6 8 9<br />

Ed ecco la co<strong>di</strong>fica in pascal:<br />

(* per n 1 volte *)<br />

for i:=1 to n - 1 do<br />

(* parti dalla prima posizione e confronta<br />

tutte le coppie fino alla n i *)<br />

for j := 1 to n - i do<br />

(* se trovi due celle a<strong>di</strong>acenti non<br />

in or<strong>di</strong>ne, scambiale *)<br />

if vettore[j] > vettore[j+1] then<br />

begin<br />

tmp:=vettore[j];<br />

vettore[j]:=vettore[j+1];<br />

vettore[j+1]:=tmp<br />

end;<br />

l’algoritmo si ferma quando ha ripetuto il processo n-1 volte<br />

2 6 3 8 9<br />

procedure bubble_sort(VAR vet: vettore_interi; n: integer);<br />

var i,j: integer;<br />

begin<br />

for i:=1 to n - 1 do<br />

for j := 1 to n - i do<br />

if vet[j] > vet[j+1] then<br />

begin<br />

tmp:=vet[j];<br />

vet[j]:=vet[j+1];<br />

vet[j+1]:=tmp<br />

end<br />

end;


7. Or<strong>di</strong>namento dei valori in un vettore <strong>di</strong> N elementi, con N < MAX_ELEMENTI<br />

METODO PER SELEZIONE – PER RICERCA DEL MINIMO<br />

Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 126<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

Questo algoritmo appartiene alla categoria chiamata ‘per selezione’ perché invece <strong>di</strong> scambiare in continuazione<br />

celle a<strong>di</strong>acenti tra loro, cerca il miglior numero da spostare, riducendo <strong>di</strong> molto il numero degli scambi. In dettaglio:<br />

1. Partendo dalla prima posizione, localizza la cella con valore minimo; pren<strong>di</strong> il minimo e mettilo in prima<br />

posizione; il numero in prima posizione mettilo nella cella in cui hai trovato il minimo:<br />

cerca il minimo scambio risultato<br />

9 8 2 6 3<br />

2. Spostati in avanti <strong>di</strong> una casella ed ignora la prima (già in or<strong>di</strong>ne): cerca il minimo nella parte del vettore<br />

‘avanzata’; mettilo in seconda posizione; il numero in seconda posizione mettilo nella cella in cui hai trovato il<br />

minimo. Ripeti il proce<strong>di</strong>mento fino alla penultima posizione (a quel punto il numero in ultima posizione è per<br />

forza in or<strong>di</strong>ne.<br />

Ed ora il co<strong>di</strong>ce pascal:<br />

for i:=1 to n-1 do<br />

begin<br />

pos_min:=i;<br />

(* cerco la posizione del minimo<br />

tra i e ultimo elemento *)<br />

for j:=i+1 to n do<br />

if vet[j] < vet[pos_min] then<br />

pos_min:=j;<br />

(* scambio con il minimo *)<br />

tmp := vet[i] ;<br />

vet[i] := vet[pos_min];<br />

vet[pos_min]:=tmp<br />

end;<br />

9 8 2 6 3 2 8 9 6 3<br />

2 8 9 6 3 2 8 9 6 3 2 3 9 6 8<br />

2 3 9 6 8 2 3 9 6 8 2 3 6 9 8<br />

2 3 6 9 8 2 3 6 9 8 2 3 6 8 9<br />

procedure selezione_sort(VAR vet: vettore_interi; n: integer);<br />

var i,j: integer;<br />

begin<br />

for i:=1 to n-1 do<br />

begin<br />

pos_min:=i;<br />

for j:=i+1 to n do<br />

if vet[j] < vet[pos_min] then<br />

pos_min:=j;<br />

tmp := vet[i] ;<br />

vet[i] := vet[pos_min];<br />

vet[pos_min]:=tmp<br />

end<br />

end;


INTRODUZIONE ALL’USO DEI RECORD<br />

Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 127<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

I dati <strong>di</strong> un vettore o <strong>di</strong> una matrice devono essere tutti uguali: o tutti integer, o tutti real o string ecc. Questo limite<br />

porta a notevoli complicazioni anche per situazione semplici. Immaginiamo infatti <strong>di</strong> voler rappresentare la seguente<br />

scheda:<br />

Persona<br />

Cognome<br />

Nome<br />

Eta<br />

Fine-Persona<br />

Usando gli array sono possibili due soluzioni:<br />

1. Tre vettori in parallelo: uno per i cognomi, uno per i nomi<br />

ed uno per le età;<br />

2. Una matrice (<strong>di</strong> stringhe) a due colonne (la prima per il<br />

cognome e la seconda per il nome) ed un vettore per le<br />

età.<br />

Gestire vettori o matrici e vettori in parallelo non è molto agevole … Per fortuna molti linguaggi <strong>di</strong> programmazione<br />

consentono al programmatore <strong>di</strong> definire nuovi tipi <strong>di</strong> dato, strutturati come meglio si crede, e <strong>di</strong> <strong>di</strong>chiarare poi<br />

variabili <strong>di</strong> quel tipo. Uno <strong>di</strong> questi tipi <strong>di</strong> dato è il record.<br />

Ve<strong>di</strong>amo l’esempio della scheda anagrafica visto poc’anzi:<br />

program ProvaRecord;<br />

Rossi Ver<strong>di</strong> Bianchi<br />

Mario Giovanni Sandra<br />

21 34 15<br />

Soluzione con tre vettori<br />

Rossi Ver<strong>di</strong> Bianchi<br />

Mario Giovanni Sandra<br />

Type Una_Persona<br />

TPersona = record<br />

Cognome: string;<br />

Cognome<br />

Nome: string;<br />

Eta: integer<br />

Nome<br />

End;<br />

…<br />

Eta<br />

Var<br />

Una_Persona: TPersona;<br />

21 34 15<br />

Soluzione con una matrice ed un vettore<br />

Il nuovo tipo si chiama TPersona (la T, per nulla obbligatoria, aiuta comunque a ricordare che si tratta <strong>di</strong> un ‘T’ipo). I<br />

nuovi tipi devono essere definiti in una sezione specifica, dopo le costanti: la sezione type. Potete naturalmente<br />

definire anche più <strong>di</strong> un tipo. Il tipo TPersona contiene tre valori (chiamati campi del record): cognome, nome ed eta<br />

(con i nomi delle variabili è sempre meglio evitare gli accenti…).<br />

Non è possibile lavorare <strong>di</strong>rettamente con i tipi (non potete ad esempio scrivere integer:=3 !) ma è necessario<br />

<strong>di</strong>chiarare variabili <strong>di</strong> quel tipo. Nella sezione var trovate quin<strong>di</strong> la variabile Una_Persona <strong>di</strong> tipo TPersona.<br />

Qui sopra potete vedere, a fianco della definizione del nuovo tipo, una rappresentazione grafica del record: l’intero<br />

rettangolo esterno è il record; all’interno i componenti. Ma come si ‘riempiono’ <strong>di</strong> dati le caselle tratteggiate?


Ecco il comando per memorizzare la stringa ‘Rossi’ nel campo cognome:<br />

Una_Persona . cognome := ’ROSSI’;<br />

QUINDI: si inizia con il nome della variabile record (Una_Persona),<br />

si mette un punto, e si in<strong>di</strong>ca il campo da usare (cognome).<br />

Similmente si potrebbe procedere con il nome e l’età.<br />

Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 128<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

I campi <strong>di</strong> un record sono a tutti gli effetti variabili come quelle che già conoscete. E’ quin<strong>di</strong> possibile scrivere:<br />

readln(Una_Persona.nome); (* lettura da tastiera *)<br />

…<br />

writeln(‘La persona si chiama: ‘, Una_Persona.Cognome, ’ – ‘, Una_Persona.nome);<br />

…<br />

if Una_Persona.cognome=’Rossi’ then …<br />

NOTA: <strong>di</strong>menticarsi il nome della variabile record (a meno <strong>di</strong> utilizzo del with, spiegato più avanti) è un grave errore:<br />

cognome:=’rossi’ DI QUALE PERSONA ??? ci potrebbero essere tante variabili <strong>di</strong> tipo<br />

TPersona …<br />

Il costrutto with<br />

Immaginiamo <strong>di</strong> avere una variabile record con il nome piuttosto lungo e con molti campi all’interno. E’ scomodo<br />

riscrivere il nome della variabile tutte le volte che si vuole usare un suo campo; se poi questi ultimi sono molti è<br />

ancora più scomodo. Il Pascal mette a <strong>di</strong>sposizione un costrutto (with, cioè con …) che consente <strong>di</strong> scrivere una volta<br />

sola il nome della variabile record finchè non si ha finito <strong>di</strong> usare i suoi campi:<br />

with Una_Persona do<br />

begin<br />

readln(cognome);<br />

readln(nome);<br />

readln(eta)<br />

end;<br />

Vettori <strong>di</strong> record<br />

Una famiglia è fatta <strong>di</strong> più persone: Una_Famiglia: array[1..3] of TPersona;<br />

Ecco come dare un valore al primo record del vettore:<br />

Una_Famiglia[1].cognome:=’Giorgio’;<br />

Una_Famiglia[1].eta:=’Albenghi’;<br />

Una_Famiglia[1].eta:=43;<br />

Cognome<br />

Nome<br />

Tra il begin e end del costrutto with invece <strong>di</strong> scrivere:<br />

Readln(Una_Persona.cognome);<br />

è sufficiente scrivere:<br />

Readln(cognome);<br />

Attenzione! Il seguente è un errore piuttosto comune:<br />

Una_Famiglia.eta[3]:=23<br />

Sarebbe il field eta ad essere considerato un vettore!<br />

Quin<strong>di</strong> una_famiglia è prima <strong>di</strong> tutto un vettore: dopo il suo nome è necessario in<strong>di</strong>care quale elemento si intende<br />

usare (una_famiglia[1]). Poi, essendo ogni elemento del vettore un record si continua con la sintassi <strong>di</strong> questi ultimi<br />

(un punto ed il nome del campo da usare).<br />

Eta<br />

ROSSI


Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 129<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

Ecco un esempio interessante sul come usare il costrutto with con i vettori <strong>di</strong> record. Si tratta <strong>di</strong> un ciclo <strong>di</strong><br />

caricamento del vettore della famiglia:<br />

For i:=1 to num_componenti do<br />

With Una_Famiglia[i] do<br />

begin<br />

Readln(cognome);<br />

Readln(nome);<br />

Readln(eta)<br />

end<br />

Record <strong>di</strong> record<br />

(* con l’i-mo componente fai … *)<br />

Un field <strong>di</strong> un record può tranquillamente essere a sua volta un record: definiamo prima un record per le date e poi<br />

un record per i compleanni (che conterranno il record della data):<br />

Type<br />

TData=record (* un record normale *)<br />

Gg,mm,aa: integer;<br />

End;<br />

TCompleanno=record<br />

Data: TData; (* il record Data è contenuto nel record TCompleanno *)<br />

Nominativo: string;<br />

End;<br />

Var<br />

MioCompleanno: TCompleanno;<br />

accesso ai componenti senza with accesso ai componenti con with<br />

MioCompleanno.Data.gg := 30;<br />

MioCompleanno.Data.mm := 10;<br />

MioCompleanno.Data.aa := 1963;<br />

MioCompleanno.Nominativo:= ‘Fabrizio Camuso’;<br />

with MioCompleanno do<br />

begin<br />

with Data do<br />

begin<br />

gg:=30; mm:=10; aa:=1963<br />

end<br />

Nominativo:=’Fabrizio Camuso’<br />

end;<br />

Attenzione. Sarebbe un errore:<br />

with MioCompleanno do<br />

begin<br />

gg:=30; mm:=10; aa:=1963;<br />

end<br />

perchè gg, mm ed aa non sono <strong>di</strong>rettamente campi <strong>di</strong><br />

MioCompleanno ma del record Data in esso contenuto


Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 130<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

ESEMPIO : scrivere un programma per la gestione <strong>di</strong> una biblioteca che preveda la ricerca dei dati <strong>di</strong> un libro per<br />

autore, o per titolo o per e<strong>di</strong>tore<br />

program libri;<br />

uses crt;<br />

const<br />

MAX_LIBRI=50;<br />

type<br />

TLibro=record<br />

co<strong>di</strong>ce: integer;<br />

autore,titolo,e<strong>di</strong>tore: string;<br />

end;<br />

var<br />

biblioteca: array [1..MAX_LIBRI] of TLibro;<br />

numero_libri,cont,scelta: integer; da_cercare: string;<br />

begin<br />

numero_libri:=0;cont:=0;scelta:=0;da_cercare:='';<br />

clrscr;<br />

repeat<br />

write('Quanti sono i libri (max. ',MAX_LIBRI,') ? ');<br />

readln(numero_libri);<br />

if (numero_libriMAX_LIBRI) then<br />

writeln('Valore non accettabile, riprova ...')<br />

until (numero_libri>0) and (numero_libri


epeat<br />

clrscr;<br />

writeln('RICERCA CODICI LIBRI PER ...');<br />

writeln('1 - Ricerca per autore');<br />

writeln('2 - Ricerca per titolo');<br />

writeln('3 - Ricerca per e<strong>di</strong>tore');<br />

writeln('4 - Fine operazioni');<br />

writeln;<br />

repeat<br />

write('Scegli un opzione: ');<br />

readln(scelta);<br />

if (scelta4) then<br />

writeln('Scelta non corretta');<br />

until (scelta>0) and (scelta


I FILE – GESTIONE TRADIZIONALE (non DBMS)<br />

Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 132<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

Il file (archivio) è una collezione <strong>di</strong> dati registrati su un supporto <strong>di</strong> massa (floppy <strong>di</strong>sk, hard <strong>di</strong>sk, CD, DVD, nastro<br />

ecc.). Da un punto <strong>di</strong> vista fisico tutti i file sono memorizzati come una sequenza <strong>di</strong> byte: è il modo con cui questi<br />

ultimi sono interpretati che fa la <strong>di</strong>fferenza. Ad esempio, i 1000 byte che costituiscono un certo file potrebbero essere<br />

intesi come <strong>di</strong>eci schede (record) da 100 byte con le informazioni <strong>di</strong> un cliente della <strong>di</strong>tta. E <strong>di</strong> ciascun record i primi<br />

10 byte potrebbero rappresentare il co<strong>di</strong>ce del cliente, i successivi 30 il nominativo ecc. (i cosiddetti ‘fields’, campi).<br />

Ma, in un contesto completamente <strong>di</strong>verso 1000 byte potrebbero rappresentare la co<strong>di</strong>fica binaria <strong>di</strong> un’immagine o<br />

<strong>di</strong> un suono.<br />

Sui supporti, i byte sono <strong>di</strong> solito trattati a blocchi (cioè non viene mai letto/scritto un solo byte alla volta) secondo<br />

una tecnica <strong>di</strong> sud<strong>di</strong>visione dello spazio a <strong>di</strong>sposizione che <strong>di</strong>pende dal supporto (su un floppy <strong>di</strong>sk tracce<br />

concentriche sud<strong>di</strong>vise in ‘spicchi’ ad in<strong>di</strong>viduare settori, su un CD un’unica traccia a spirale, sul nastro bande<br />

magnetiche perpen<strong>di</strong>colari o elicoidali ecc.). Le letture/scritture avvengono in aree <strong>di</strong> transito in RAM chiamate<br />

buffer(s). Quando si leggono dati da un <strong>di</strong>spositivo, uno o più blocchi vengono memorizzati nel buffer <strong>di</strong> lettura e da<br />

lì prelevati man mano che l'applicazione ne fa richiesta (in modo da non effettuare inutili letture fisiche dei supporti,<br />

in quanto probabilmente a più richieste <strong>di</strong> lettura <strong>di</strong> dati da parte dell'applicazione corrisponderà una sola lettura <strong>di</strong><br />

dati dal <strong>di</strong>sco al buffer). Similmente i dati da registrare su file sono prima accumulati in un buffer <strong>di</strong> scrittura fino al<br />

riempimento <strong>di</strong> quest'ultimo e solo al raggiungimento <strong>di</strong> questa situazione vengono fisicamente scritti sul <strong>di</strong>sco<br />

(evitando in questo modo che ad ogni richiesta <strong>di</strong> scrittura da parte dell'applicazione avvenga una <strong>di</strong>spen<strong>di</strong>osa<br />

scrittura fisica sul <strong>di</strong>sco). Ogni <strong>di</strong>spositivo ha poi particolarità legate alla sua natura: un hard <strong>di</strong>sk può essere formato<br />

da più piatti ed il relativo drive avere molte testine <strong>di</strong> lettura/scrittura, un floppy ha invece un solo piatto (un film<br />

flessibile e sottile come un capello a forma <strong>di</strong> cerchio) ed una sola coppia <strong>di</strong> testine.<br />

Riassumendo, la logica <strong>di</strong> controllo <strong>di</strong> questi <strong>di</strong>spositivi è assai <strong>di</strong>versificata, sia perché l’accoppiata supporto e relativo<br />

drive è formata da <strong>di</strong>spositivi elettromeccanici <strong>di</strong>versi, sia perché <strong>di</strong>verse sono le tecniche con cui si può decidere <strong>di</strong><br />

usarli per <strong>di</strong>sporre dello spazio <strong>di</strong> memorizzazione (quante tracce gestire sul floppy? come organizzare la spirale <strong>di</strong> bit<br />

su <strong>di</strong> un CD?).<br />

Agli albori dell’informatica la gestione dei supporti era sotto la completa responsabilità dei programmatori, costretti a<br />

confrontarsi <strong>di</strong>rettamente con l’hardware (aspetto fisico) oltre che a decidere come usare lo spazio messo a<br />

<strong>di</strong>sposizione (aspetto logico). Detto in altre parole doveva occuparsi sia della gestione fisica delle informazioni che <strong>di</strong><br />

quella logica: cioè pilotare i <strong>di</strong>spositivi ed allo stesso tempo gestire i byte in modo da poterli interpretare come<br />

informazioni utili (quali archivi creare? Come strutturarli in campi informativi? Come mettere in relazione i record un<br />

archivio con quelli <strong>di</strong> un altro?). Questa doppia ‘responsabilità’ porta con se parecchi problemi tra cui, mettendo in<br />

evidenza i più eclatanti, è opportuno citare i seguenti:<br />

Necessità <strong>di</strong> conoscere in modo approfon<strong>di</strong>to l’hardware da controllare.<br />

Come conseguenza del punto precedente aumenta la complessità dell’attività <strong>di</strong> programmazione.<br />

I programmi sono molto legati all’hardware: quando quest’ultimo cambia essi vanno mo<strong>di</strong>ficati.<br />

L’efficienza nella gestione dei supporti <strong>di</strong>pende dalla competenza del programmatore.<br />

Poca uniformità: ogni programmatore adotta tecniche <strong>di</strong>verse <strong>di</strong> allocazione/gestione dello spazio<br />

Notevole spreco <strong>di</strong> tempo/uomo: ogni programmatore co<strong>di</strong>fica routine già co<strong>di</strong>ficate da altri (chi meglio, chi<br />

peggio).<br />

Basso livello <strong>di</strong> astrazione nella programmazione: lo sviluppatore invece <strong>di</strong> concentrarsi solo su aspetti logici<br />

legati al problema è costretto a gestire altri dettagli.<br />

Ci sono anche i soliti vantaggi <strong>di</strong> una programmazione a basso livello:<br />

controllo accurato dell’hardware<br />

possibilità, se si è BRAVI programmatori, <strong>di</strong> ottimizzare il co<strong>di</strong>ce per il massimo delle prestazioni


Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 133<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

Solo con l’avvento <strong>di</strong> sistemi operativi sufficientemente evoluti (DOS, UNIX, windows NT) il programmatore ha<br />

potuto svincolarsi dagli aspetti hardware ed in parte anche da quelli logici. Il modulo del <strong>sistema</strong> operativo chiamato<br />

‘file system’, infatti:<br />

si occupa della gestione hardware (file system fisico): pilota <strong>di</strong>spositivi anche molto <strong>di</strong>versi tra loro (floppy,<br />

hard <strong>di</strong>sk, CD ROM, nastri ecc.) per la registrazione e la lettura <strong>di</strong> blocchi <strong>di</strong> byte;<br />

mette a <strong>di</strong>sposizione delle entità logiche chiamate file (file system logico) gestite tramite un nome ed una<br />

posizione (path, cammino) in una struttura, <strong>di</strong> solito gerarchica (<strong>di</strong>rectory, sotto <strong>di</strong>rectory ecc.); mette in oltre a<br />

<strong>di</strong>sposizione coman<strong>di</strong> per creare, eliminare, spostare, rinominare i file; questi coman<strong>di</strong> possono non solo essere<br />

imparti grazie ad una riga <strong>di</strong> comando (MS DOS, Unix, Linux) o ad un ambiente a finestre (Windows, Unix,<br />

Linux) ma anche in un programma;<br />

L’esame dettagliato del modulo file system viene svolto nel corso <strong>di</strong> Sistemi. Nel corso <strong>di</strong> Informatica interessa invece<br />

cogliere gli aspetti più legati allo sviluppo del software. Che <strong>di</strong>re allora dei linguaggi <strong>di</strong> programmazione? Come si<br />

sono evoluti in risposta a queste problematiche?<br />

A livello <strong>di</strong> linguaggi ‘assembly’ il tutto si risolve attraverso chiamate a servizi del <strong>sistema</strong> operativo (nel caso <strong>di</strong> MS<br />

DOS attraverso il noto meccanismo degli interrupt software, come dovrebbe esservi chiarito nel corso <strong>di</strong> sistemi).<br />

I linguaggi <strong>di</strong> terza generazione (‘C’, Pascal, Basic, Cobol ecc.) offrono istruzioni più evolute (apri un file, chiu<strong>di</strong>lo,<br />

leggi dal file, scrivi su <strong>di</strong> esso, spostati ad una certa posizione ecc.) che sono poi tradotte dal compilatore negli stessi<br />

meccanismi a basso livello dell’assembly.<br />

Da alcuni anni la gestione delle informazioni è <strong>di</strong>ventata talmente importante per il successo dei sistemi informativi e<br />

le funzionalità richieste talmente sofisticate da rendere insufficienti le funzionalità offerte dai sistemi operativi e, <strong>di</strong><br />

conseguenza, dei programmi che vi fanno affidamento. Questo fatto giustifica l’esistenza sul mercato <strong>di</strong> complessi<br />

software de<strong>di</strong>cati esclusivamente alla gestione delle basi <strong>di</strong> dati aziendali: i DBMS (data base management system:<br />

saranno oggetto <strong>di</strong> un’ampia porzione del corso <strong>di</strong> Informatica <strong>di</strong> quinta).<br />

Vista l’esistenza dei DBMS perché parlare delle tecniche ‘tra<strong>di</strong>zionali’ in presenza <strong>di</strong> strumenti talmente avanzati? Ecco<br />

alcuni buoni motivi:<br />

i requisiti hardware/software (microprocessore, RAM, spazio su hard <strong>di</strong>sk) richiesti dai DBMS sono decisamente<br />

elevati: in più <strong>di</strong> una situazione non sono semplicemente <strong>di</strong>sponibili e l’unica alternativa (spesso perfettamente<br />

adeguata) è rappresentata dalle tecniche ‘tra<strong>di</strong>zionali’;<br />

certe elaborazioni sono molto <strong>di</strong>fficili da realizzare con i linguaggi specifici per i data base: questi linguaggi<br />

‘pagano’ la loro indubbia semplicità che li rende alla portata anche del non programmatore con una minore<br />

‘potenza’; il problema è così sentito che in molti linguaggi per data base sono stati reintrodotti costrutti <strong>di</strong><br />

programmazione tipici dei linguaggi tra<strong>di</strong>zionali<br />

per certe operazioni la velocità delle tecniche tra<strong>di</strong>zionali è impareggiabile: nessun DBMS supera in velocità<br />

linguaggi come il Pascal o il C per scrivere e leggere file <strong>di</strong> testo ( e non <strong>di</strong>menticate che le pagine WEB in HTML<br />

sono file <strong>di</strong> testo …)<br />

conoscere le tecniche passate aiuta a capire, apprezzare e sfruttare molto meglio ciò che le nuove mettono a<br />

<strong>di</strong>sposizione.


TIPI DI FILE<br />

Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 134<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

1. File <strong>di</strong> testo (text file): sono sequenze <strong>di</strong> bytes con valori numerici nell’intervallo valido per la co<strong>di</strong>fica dei<br />

caratteri per un certo <strong>sistema</strong> operativo. Nel caso <strong>di</strong> MS DOS / Windows (co<strong>di</strong>fica ASCII) questi file sono<br />

terminati dal carattere con co<strong>di</strong>ce 26 (CTRL Z). La fine <strong>di</strong> un file viene in<strong>di</strong>cata con la sigla EOF (End Of File,<br />

fine del file). L’inizio con BOF (Begin Of File, inizio del file)<br />

I file <strong>di</strong> testo sono sud<strong>di</strong>visi in righe. Ogni riga è terminata dai byte CR (Carriage Return, ritorno carrello) co<strong>di</strong>ce<br />

ASCII 13) e LF (Line Feed, avanzamento <strong>di</strong> riga, co<strong>di</strong>ce ASCII 10).<br />

Attenzione: le righe sono terminate o solo da CR o solo da LF o da tutti e due a seconda del <strong>sistema</strong> operativo in<br />

uso (con MS DOS/Windows le righe sono terminate da CR seguito da LF).<br />

Windows è in grado <strong>di</strong> trattare anche caratteri UNICODE, uno standard internazionale che consente, grazie<br />

all’uso <strong>di</strong> due byte per co<strong>di</strong>ficare ciascun carattere, <strong>di</strong> rappresentare set <strong>di</strong> caratteri molto ricchi. Per lo stesso<br />

motivo i linguaggi più recenti (Delphi con il suo Object Pascal ne è un esempio) sono in grado <strong>di</strong> definire variabili<br />

carattere aderenti a questo standard.<br />

Ecco come potremmo rappresentare graficamente un file <strong>di</strong> testo:<br />

BOF<br />

questa è la prima riga del file <strong>di</strong> testo. Può contener anche spazi come questi !! CR LF<br />

io sono la seconda RIGA bla bla bla bla bla! CR LF<br />

io sono la riga più corta !!! CR LF<br />

CR LF<br />

CR LF<br />

CR LF<br />

CR LF EOF<br />

EOF<br />

Le istruzioni per scrivere e leggere dati dai file <strong>di</strong> testo sono line-oriented nel senso che permettono <strong>di</strong> leggere o<br />

scrivere intere righe del file e non solamente una parte dei caratteri <strong>di</strong> una certa riga. Abbiamo così coman<strong>di</strong> per<br />

registrare una riga oppure per leggerla da un file. Una grossa limitazione dei file <strong>di</strong> testo in Pascal è che le righe<br />

già inserite non possono essere mo<strong>di</strong>ficate (riscritte).<br />

I file testuali sono il tipo <strong>di</strong> file meno sofisticato e meno efficiente per rappresentare situazioni reali <strong>di</strong> una certa<br />

complessità ma allo stesso tempo assicurano la massima portabilità tra elaboratori con <strong>di</strong>versi sistemi operativi e<br />

tra periferiche altrimenti incompatibili. Qualsiasi linguaggio su qualsiasi piattaforma hardware/software è infatti<br />

in grado <strong>di</strong> gestire file <strong>di</strong> testo. Nel peggiore dei casi si renderà necessaria una (semplice) transco<strong>di</strong>fica (ad<br />

esempio da ASCII a EBCDIC).<br />

Non sono inoltre particolarmente adatti a contenere informazioni non testuali: qualsiasi co<strong>di</strong>fica binaria (la<br />

rappresentazione interna <strong>di</strong> un numero in virgola mobile, il co<strong>di</strong>ce operativo od un operando <strong>di</strong> un’istruzione<br />

assembly, un immagine, un suono un’animazione) contenente un byte con valore 26 verrebbe considerato,<br />

inesorabilmente, EOF. Naturalmente ci sarebbe sempre il modo <strong>di</strong> co<strong>di</strong>ficare gli stessi dati usando solo byte<br />

<strong>di</strong>versi da EOF, CR e LF ma la soluzione risulterebbe meno efficiente e poco elegante. Meglio usare i file non<br />

testuali (ve<strong>di</strong> sotto).


Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 135<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

2. File binari (‘binary file’ o non <strong>di</strong> testo): a livello fisico sono ancora formati da una sequenza <strong>di</strong> byte il cui<br />

valore non viene però interpretato come un carattere ASCII. Così, mentre un byte con valore (decimale) 65<br />

viene interpretato come la ‘A’ in un file <strong>di</strong> testo, lo stesso valore potrebbe specificare una tonalità <strong>di</strong> colore <strong>di</strong><br />

un’immagine in un file jpeg (uno dei formati grafici più <strong>di</strong>ffusi).<br />

La fine <strong>di</strong> questi file non è segnalata dal carattere CTRL Z ma attraverso altri meccanismi (ad esempio tenendo<br />

traccia della lunghezza in byte del file). Anche i byte con valore numerico 13 e 10 (i CR e LF dei file <strong>di</strong> testo) non<br />

hanno un significato particolare.<br />

Le istruzioni <strong>di</strong> lettura/scrittura per questi tipi <strong>di</strong> file permettono tipicamente <strong>di</strong> leggere blocchi <strong>di</strong> byte <strong>di</strong><br />

<strong>di</strong>mensioni specificate dal programmatore.<br />

Sono adatti per memorizzare qualsiasi tipo <strong>di</strong> file, testo compreso (ovviamente non potremo sfruttare le primitive<br />

<strong>di</strong> lettura e le particolarità dei file <strong>di</strong> testo). Sono file binari le traduzioni in linguaggio macchina dei programmi,<br />

immagini, suoni, archivi <strong>di</strong> basi <strong>di</strong> dati per i quali si è preferito questo formato a quello testuale ecc.<br />

3. File tipizzati: sono file per i quali le unità informative (chiamate record in Pascal) hanno una struttura (un tipo)<br />

ben precisa. Come i record Pascal gestiti in RAM, i record su file sono comodamente sud<strong>di</strong>visi in campi (fields).<br />

Ad esempio:<br />

type<br />

TCalciatori=Record<br />

matricola: integer;<br />

cognome: string;<br />

nome: string;<br />

ecc.<br />

end;<br />

var FileCalciatori: file of TCalciatori;<br />

Le istruzioni <strong>di</strong> lettura/scrittura sono in grado <strong>di</strong> leggere/scrivere un intero record per volta, <strong>di</strong> spostarsi per<br />

leggere/scrivere in una posizione qualsiasi del file, e, operazione impossibile con i file <strong>di</strong> testo, <strong>di</strong> mo<strong>di</strong>ficare<br />

(riscrivere) una scheda.<br />

Nota: riba<strong>di</strong>sco che a livello fisico i file sono comunque tutti formati da una sequenza <strong>di</strong> byte; la <strong>di</strong>fferenziazione che<br />

ne facciamo è a livello logico. Una parte della letteratura informatica si limita a <strong>di</strong>stinguere tra file <strong>di</strong> testo e non <strong>di</strong><br />

testo (i file tipizzati vengono considerati file binari).


TIPI DI ORGANIZZAZIONE (ACCESSO)<br />

Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 136<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

L’organizzazione fa riferimento alle modalità con cui è possibile accedere ai dati sui supporti:<br />

Sequenziale: i dati possono essere letti/scritti in modo strettamente lineare; per leggere/scrivere un dato è<br />

necessario leggere i precedenti per posizionare il <strong>di</strong>spositivo <strong>di</strong> lettura/scrittura nel punto giusto; il tempo per<br />

accedere ad un dato è quin<strong>di</strong> molto <strong>di</strong>pendente dalla sua posizione sul supporto; ad esempio, per un nastro<br />

magnetico l’unico tipo <strong>di</strong> organizzazione possibile è quella sequenziale: se il nastro è all’inizio, il tempo per<br />

leggere il primo blocco <strong>di</strong> dati è assai inferiore al tempo necessario per leggere un blocco che sta a metà o alla<br />

fine del nastro.<br />

Random: se è possibile raggiungere un blocco qualsiasi <strong>di</strong> dati impiegando praticamente lo stesso tempo<br />

in<strong>di</strong>pendentemente dalla posizione del blocco. Ad esempio il tempo necessario a pilotare una delle testine <strong>di</strong><br />

lettura/scrittura in un punto qualsiasi della superficie <strong>di</strong> un piatto <strong>di</strong> un hard <strong>di</strong>sk, partendo da una posizione<br />

qualsiasi, non è esattamente zero ma è comunque abbastanza piccolo da ritenere (quasi) identico il tempo<br />

necessario a leggere uno qualsiasi dei blocchi che costituiscono un file.<br />

NOTA: la <strong>di</strong>stinzione tra organizzazione <strong>di</strong> un file tra sequenziale e random è in<strong>di</strong>pendente dalle capacità fisiche del<br />

supporto ed è stabilita dalla modalità <strong>di</strong> accesso a quel file. Ad esempio, l’hard <strong>di</strong>sk a livello fisico è sicuramente<br />

random ma nulla vieta <strong>di</strong> organizzare la registrazione <strong>di</strong> un file in modo che il suo accesso sia comunque sequenziale<br />

(pensate ad un file <strong>di</strong> testo in Pascal).<br />

Nella letteratura informatica ci si riferisce alle seguenti come ad organizzazioni a se stanti ma in realtà sono tutte<br />

costruite su quella ‘random’:<br />

Con in<strong>di</strong>ci: quando l’accesso ai dati è velocizzato dalla gestione <strong>di</strong> strutture (su file separati o nello stesso file<br />

dei dati) che, grazie alla loro organizzazione, consentono <strong>di</strong> risalire rapidamente alla posizione in cui trovare i<br />

dati. Il modo <strong>di</strong> realizzare l’in<strong>di</strong>ce può variare grandemente. In<strong>di</strong>viduato un campo informativo in relazione al<br />

quale si vuole velocizzare l’accesso (ricerca per co<strong>di</strong>ce o per cognome, ecc.) l’in<strong>di</strong>ce conterrà una copia <strong>di</strong> tutti i<br />

valori contenuti nel file dati per quel campo (tutti i co<strong>di</strong>ci o tutti i cognomi ecc.) affiancati dalla posizione in cui<br />

sul file dati completo si trovano gli altri dati per quel record.<br />

L’in<strong>di</strong>ce è mantenuto in una forma che rende assai veloce trovare i valori che interessano (un certo co<strong>di</strong>ce o un<br />

certo cognome ecc.): ad esempio potrebbe essere or<strong>di</strong>nato (per sfruttare una ricerca <strong>di</strong>cotomica) o strutturato<br />

come un albero binario <strong>di</strong> ricerca; quest’ultima scelta è alla base della fortunatissima tecnica che sfrutta una<br />

forma mo<strong>di</strong>ficata degli alberi binari, chiamata ‘B+ tree’; essa è utilizzata per molte implementazioni del<br />

linguaggio Cobol, <strong>di</strong> molte librerie per la gestione degli archivi <strong>di</strong>sponibili per gli altri linguaggi, e <strong>di</strong> alcuni famosi<br />

DBMS.<br />

Qualunque sia forma dell’in<strong>di</strong>ce, l’idea <strong>di</strong> base è che, grazie alla sua natura or<strong>di</strong>nata, saranno necessarie poche<br />

letture su <strong>di</strong> esso per localizzare il valore che interessa ed accedere all’in<strong>di</strong>rizzo in cui si trova il resto dei dati nel<br />

file principale.<br />

Arisi 4<br />

Bini 2<br />

Bruni 3<br />

Calce 8<br />

<br />

L’in<strong>di</strong>ce è or<strong>di</strong>nato<br />

per cognome. I dati<br />

<strong>di</strong> Arisi si trovano<br />

nel quarto record del<br />

file dei dati<br />

Calcestruzzi Mario V. Milano Impiegato<br />

Bini Giorgio V. Corta Studente<br />

Bruni Massimo V. Lunga Studente<br />

Arisi Massimo V. Larga Avvocato<br />

Il file dati principale non è or<strong>di</strong>nato rispetto ad alcun campo. L’accesso<br />

è me<strong>di</strong>ato dall’uso dell’in<strong>di</strong>ce. Se l’accesso è random, ottenuta<br />

dall’in<strong>di</strong>ce la posizione dei dati <strong>di</strong> Arisi sarà sufficiente una sola<br />

lettura.


Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 137<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

A titolo <strong>di</strong> esempio consideriamo un file <strong>di</strong> dati anagrafici contenenti un milione <strong>di</strong> record. Una ricerca <strong>di</strong>cotomica<br />

sul file in<strong>di</strong>ce nel caso più sfortunato richiederà 20 letture (20 =(circa) log2(1000000) che è il numero <strong>di</strong> volte<br />

che può essere <strong>di</strong>mezzato un intervallo pari ad un milione) più una ventunesima nel file dati vero e proprio.<br />

Me<strong>di</strong>amente, in realtà, il numero <strong>di</strong> letture sarà minore <strong>di</strong> 20. Paragonate questo numero <strong>di</strong> letture con il<br />

numero me<strong>di</strong>o <strong>di</strong> letture nel file dati principale <strong>di</strong>sor<strong>di</strong>nato (500000 !) per rendervi conto del guadagno…<br />

Qualcuno, giustamente, starà ‘storcendo il naso per la puzza <strong>di</strong> bruciato’: non sarebbe meglio tenere or<strong>di</strong>nato il<br />

file principale e sfruttare la ricerca <strong>di</strong>cotomica o qualsiasi altro algoritmo pensato per la gestione degli in<strong>di</strong>ci<br />

(evitando l’uso <strong>di</strong> questi ultimi)? In linea <strong>di</strong> principio questo sarebbe possibile ma molto più lento perché il file<br />

principale è <strong>di</strong> solito molto più grande dell’in<strong>di</strong>ce. Tenere aggiornato l’in<strong>di</strong>ce è invece un compito assai più<br />

leggero e, grazie a particolari strutturazioni quali quella prevista dalla tecnica ‘B+ tree’, può essere fatto<br />

praticamente in tempo reale senza rallentamenti avvertibili dall’utente. Inoltre è possibile avere più in<strong>di</strong>ci per<br />

accedere ai dati secondo <strong>di</strong>verse chiavi <strong>di</strong> ricerca (per co<strong>di</strong>ce, per cognome, per in<strong>di</strong>rizzo ecc.): sarebbe<br />

veramente troppo oneroso rior<strong>di</strong>nare il file principale in base al campo che <strong>di</strong> volta in volta interessa! Oppure<br />

mantenere tante copie del file dati quanti sono i criteri <strong>di</strong> or<strong>di</strong>namento desiderati! Naturalmente tutto ha un<br />

limite: troppi in<strong>di</strong>ci tenderanno comunque a rallentare le operazioni a causa del sovraccarico necessario alla loro<br />

gestione (per ovviare in parte a questo problema nei moderni DBMS è possibile creare ‘al volo’ degli in<strong>di</strong>ci<br />

temporanei che verranno <strong>di</strong>strutti al termine dell’<strong>elaborazione</strong> al fine <strong>di</strong> non protrarre inutilmente l’aggravio<br />

della loro gestione).<br />

Relative: quando le unità informative (record) sono in<strong>di</strong>viduate da un numero che corrisponde alla loro<br />

posizione. E’ così possibile, ad esempio, chiedere <strong>di</strong> inserire un record alla posizione 20 (anche se non sono<br />

state occupate tutte le precedenti) piuttosto che alla 5. Similmente è possibile chiedere <strong>di</strong> leggere un record ad<br />

una posizione qualsiasi. Tra i linguaggi che supportano questa organizzazione ricor<strong>di</strong>amo molti <strong>di</strong>aletti del BASIC<br />

e del COBOL.<br />

Hashing. Non sempre è conveniente applicarla. L’idea è che il processore impiega a fare un calcolo molto<br />

meno tempo <strong>di</strong> quello necessario a leggere dati dai supporti <strong>di</strong> massa: si tenta allora trovare una formula (legge<br />

<strong>di</strong> trasformazione) che, partendo da una chiave <strong>di</strong> accesso (ad esempio il cognome) calcoli la posizione del<br />

resto dei dati. Ad esempio potremmo stabilire che, dato un cognome <strong>di</strong> una persona, il resto dei dati si trova nel<br />

record alla posizione corrispondente al numero dei caratteri del cognome. In questo modo, volendo trovare i<br />

dati <strong>di</strong> ‘Rossi’ sapremmo in modo super rapido che si trovano sul quinto record del file (Rossi è una stringa <strong>di</strong> 5<br />

caratteri). Ovviamente salta subito all’occhio il problema <strong>di</strong> questa formula troppo semplice: molti cognomi sono<br />

lunghi 5 caratteri e tutti i relativi record dovrebbero essere memorizzati nella stessa posizione … In effetti il<br />

problema dell’hashing sta proprio nella <strong>di</strong>fficoltà <strong>di</strong> trovare una legge <strong>di</strong> trasformazione appropriata. Un altro<br />

esempio <strong>di</strong> legge: sommare i valori ASCII delle lettere corrispondenti ai cognomi. Anche questa legge genera<br />

posizioni uguali (sapresti <strong>di</strong>re perché e valutare se è migliore rispetto alla precedente?) … Purtroppo è<br />

impossibile trovare una legge <strong>di</strong> trasformazione perfetta. I problemi maggiori sono:<br />

conflitti <strong>di</strong> posizione: quando i valori dei campi <strong>di</strong> due record <strong>di</strong>versi generano lo stesso in<strong>di</strong>rizzo; nell’esempio <strong>di</strong><br />

prima tutti gli anagrammi <strong>di</strong> un cognome generano lo stesso in<strong>di</strong>rizzo (vista la proprietà commutativa della<br />

somma); due tra le due tecniche più usate per aggirare questo problema sono o applicare in successione altre<br />

leggi <strong>di</strong> trasformazione per generare in<strong>di</strong>rizzi <strong>di</strong>versi o gestire delle vere e proprie aree <strong>di</strong> ‘overflow’, come<br />

in<strong>di</strong>cato nello schema<br />

ROSSI<br />

SORSI<br />

RISSO<br />

Somma dei<br />

co<strong>di</strong>ci ASCII<br />

Co<strong>di</strong>ce cognome<br />

322 ROSSI<br />

167 SORSI<br />

201 RISSO<br />

400<br />

1200<br />

seguente;<br />

Ipotizziamo <strong>di</strong> dover inserire<br />

per primo il record <strong>di</strong> ROSSI;<br />

la legge <strong>di</strong> trasformazione<br />

generebbe l’in<strong>di</strong>rizzo 400,<br />

usato per questo record;<br />

l’inserimento <strong>di</strong> SORSI<br />

richiederebbe <strong>di</strong> usare lo<br />

stesso in<strong>di</strong>rizzo: trovandolo già<br />

occupato si memorizza il


Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 138<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

nuovo record in un’area speciale (<strong>di</strong> overflow, cioè <strong>di</strong> traboccamento) che inizia all’in<strong>di</strong>rizzo 1200 (valore scelto a<br />

caso); il 1200 viene memorizzato nel record <strong>di</strong> ROSSI per poter ritrovare in un secondo momento all’area <strong>di</strong><br />

overflow. Per memorizzare RISSO si trova, ovviamente, già occupata la solita posizione 400. Si rileva (grazie al<br />

1200 memorizzato prima) che esiste già un’area <strong>di</strong> owerflow. Quest’ultima viene fatta scorrere fino a supeare<br />

l’ultimo record che con<strong>di</strong>vide con tutti gli altri lo stesso valore generato dalla legge <strong>di</strong> trasformazione. Se c’è<br />

ancora spazio viene aggiunto in coda il record <strong>di</strong> RISSO. Dovendo reperire le informazioni <strong>di</strong> RISSO sarà<br />

necessario scorrere l’area <strong>di</strong> overflow vanificando in parte il vantaggio dell’hashing, ma se le aree <strong>di</strong> overflow<br />

non sono esasperate è possibile ancora un notevole guadagno nei tempi complessivi.<br />

<strong>di</strong>stribuzione ottimale dei valori generati: immaginando <strong>di</strong> non ammettere omonimie (come potrebbe accadere<br />

per la descrizione dei prodotti in un magazzino) non è <strong>di</strong>fficile inventarsi una legge <strong>di</strong> trasformazione hash che<br />

non causa conflitti; considerate questo algoritmo: facciamo corrispondere ogni lettera del cognome a numeri<br />

primi crescenti. Per ROSSI la R corrisponde al 2, la O al 3, la prima S al 5, la seconda S al 7 e la I all’11. Poi<br />

eleviamo ogni numero primo alla potenza corrispondente al co<strong>di</strong>ce ASCII delle lettere corrispondenti. Così<br />

eleveremo il 2 alla 82 (co<strong>di</strong>ce ASCII della R), il 5 alla 83 (co<strong>di</strong>ce ASCII della S), il 7 ancora alla 83 e così via. Poi<br />

moltiplichiamo tra loro i numeri ottenuti: il risultato è l’in<strong>di</strong>rizzo richiesto. La matematica ci assicura (teorema<br />

dell’unicità della scomposizione in potenze <strong>di</strong> numeri primi <strong>di</strong> un numero) che due cognomi <strong>di</strong>versi genereranno<br />

due in<strong>di</strong>rizzi <strong>di</strong>versi. Parrebbe <strong>di</strong> aver risolto il problema dei conflitti con un solo calcolo per qualsiasi campo da<br />

in<strong>di</strong>cizzare <strong>di</strong> natura alfanumerica! Immaginate però <strong>di</strong> dover inserire il prodotto ‘ZUCCA CON ZENZERO E<br />

ZAFFERANO’; il valore generato dall’algoritmo è MOLTO grande (MOLTO più grande del corrispondente decimale<br />

con tutti 9 !!). Il numero eccede anche le capacità <strong>di</strong> memorizzazione del più grande hd esistente, anche<br />

immaginando <strong>di</strong> associare un singolo byte su <strong>di</strong> esso ad un in<strong>di</strong>rizzo <strong>di</strong>verso …. Potremmo scoprire, quin<strong>di</strong>, che<br />

per memorizzare anche solo il primo record dovremmo farlo ad un in<strong>di</strong>rizzo impossibile da far corrispondere<br />

nella pratica: un miliardo <strong>di</strong> miliar<strong>di</strong>. Dovremmo creare tutti i record vuoti corrispondenti alle posizioni<br />

precedenti esaurendo (tempo permettendo) anche la batteria <strong>di</strong> hard <strong>di</strong>sk più capiente dell’universo …<br />

Morale: la tecnica hash è utile solo in casi particolari da analizzare attentamente quando la velocità <strong>di</strong><br />

reperimento dei dati è il fattore critico, <strong>di</strong>versamente l’organizzazione ad in<strong>di</strong>ci risulta più conveniente e<br />

sufficientemente performante.<br />

OPERAZIONI DI BASE PER LA GESTIONE DEI FILE IN PASCAL<br />

Qualunque sia l’organizzazione fisica possiamo in<strong>di</strong>viduare alcune operazioni <strong>di</strong> base tipiche. Per ciascuna <strong>di</strong> queste è<br />

importante, oltre al risultato, essere in grado <strong>di</strong> rilevare i motivi <strong>di</strong> eventuali errori (fine dello spazio su <strong>di</strong>sco, <strong>di</strong>sco in<br />

avaria, <strong>di</strong>sco non trovato, file non trovato ecc.). Il controllo degli errori inevitabilmente rende il co<strong>di</strong>ce più pesante,<br />

più <strong>di</strong>fficile da leggere: per questo motivo vedremo solo alcuni esempi seguendo i quali lo studente volenteroso (J)<br />

potrà perfezionare tutto il resto del co<strong>di</strong>ce.<br />

NOTA: le considerazioni che seguono sono specifiche del linguaggio Pascal ma mantengono gran parte della loro<br />

vali<strong>di</strong>tà anche per linguaggi <strong>di</strong>versi. Ogni volta <strong>di</strong>fferenzieremo tra file <strong>di</strong> testo e file tipizzati.<br />

Collegamento al file esterno – procedura ASSIGN<br />

Un file viene in<strong>di</strong>viduato dal suo nome e dal suo in<strong>di</strong>rizzo fisico (path). Ad esempio<br />

C:\dati\2002\lettere\clienti\milano\ecc.\lettera.doc. In un programma Pascal per comandare le varie operazioni con<br />

un certo file si invece un nome logico scelto dal programmatore (ad esempio lettera). Il nome logico è più pratico e<br />

rende anche possibile cambiare il nome e la posizione <strong>di</strong> un file su <strong>di</strong>sco senza dover necessariamente mo<strong>di</strong>ficare il<br />

programma (ad esempio memorizzando i nomi e le posizioni fisiche dei file su un file a parte, questo sì fisso, che<br />

viene letto all’inizio del programma e con il cui contenuto vengono inizializzati tutti i nomi <strong>di</strong> file logici che si<br />

intendono usare).


Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 139<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

Prima <strong>di</strong> poter usare un nome logico, che poi altro non è che una variabile <strong>di</strong> tipo speciale, è necessario in<strong>di</strong>care a<br />

quale file fisico lo si vuole far corrispondere.<br />

FILE DI TESTO<br />

Seguendo l’esempio <strong>di</strong> prima si vuole fa corrispondere il nome logico lettera al file fisico<br />

C:\dati\2002\lettere\clienti\milano\ecc.\lettera.doc. Si ricorre alla procedura standard Assign:<br />

Assign( , )<br />

Applichiamola al nostro esempio:<br />

var lettera: text; (* lettera è il nome logico; il tipo text in<strong>di</strong>ca che si tratta <strong>di</strong> un file <strong>di</strong> testo *)<br />

…<br />

Assign(lettera, ‘C:\ dati\2002\lettere\clienti\milano\ecc.\lettera.doc’)<br />

Ora, e non prima, è possibile comandare le altre operazioni …<br />

Possiamo rendere il nostro programma in grado <strong>di</strong> funzionare con <strong>di</strong>versi file fisici senza essere costretti a mo<strong>di</strong>ficare<br />

il suo co<strong>di</strong>ce: è sufficiente in<strong>di</strong>care nell’assign una variabile stringa come nome fisico del file:<br />

var lettera: text; nomeFisico: string;<br />

…<br />

writeln(‘Con quale file vuoi lavorare? ‘;<br />

readln(nomeFisico);<br />

Assign(lettera, nomeFisico)<br />

La procedura assign deve essere usata una volta sola prima <strong>di</strong> iniziare a lavorare con il file. Ovviamente dovremo<br />

comandare un assign per ciascun file che inten<strong>di</strong>amo usare.<br />

FILE TIPIZZATI<br />

Non c’è nessuna <strong>di</strong>fferenza nell’uso dell’assign. Dobbiamo però <strong>di</strong>chiarare il file in modo che sia riconosciuto come<br />

tipizzato:<br />

type<br />

TPersona=record<br />

cognome: string[30]; (* e' obbligatorio definire il numero dei caratteri della stringa ! *)<br />

co<strong>di</strong>ce: integer;<br />

end;<br />

fpersona=file of TPersona; (* senza il tipo non potremmo passare un file come parametro … *)<br />

var<br />

elenco: fPersona;<br />

…<br />

assign(elenco, ‘C:\dati.dat’)


Creazione – procedura REWRITE<br />

Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 140<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

L’assegnazione da sola non basta per usare un file. E’ necessario crearlo. Un file appena creato risulterà vuoto e non<br />

si è obbligati ad inserire subito nuovi dati (anche se <strong>di</strong> solito lo si fa). Un file creato e lasciato vuoto risulterà con 0<br />

byte nelle cartelle <strong>di</strong> windows. Non confondete quin<strong>di</strong> la creazione con l’inserimento dei dati.<br />

La creazione può fallire per vari motivi:<br />

- il percorso/nome del file fornito dal programmatore non è valido<br />

- il supporto <strong>di</strong> massa non è <strong>di</strong>sponibile (nastro non montato, floppy non inserito, CD non scrivibile)<br />

- il supporto è in avaria (floppy o hard <strong>di</strong>sk rotti …)<br />

- spazio sul supporto esaurito (anche se è <strong>di</strong>fficile che non ci sia spazio almeno per creare il file vuoto)<br />

Ma ve<strong>di</strong>amo la sintassi dell’istruzione <strong>di</strong> creazione (identica per file <strong>di</strong> testo e tipizzati):<br />

rewrite(nome_file_logico)<br />

Quin<strong>di</strong>, continuando gli esempi dell’istruzione precedente (assign) senza ripetere la <strong>di</strong>chiarazione dei tipi e delle<br />

variabili:<br />

TESTO TIPIZZATI<br />

assign(lettera, ‘…’); assign(elenco);<br />

rewrite(lettera) rewrite(elenco);<br />

ATTENZIONE!!!<br />

come già detto, la sintassi è identica<br />

Se comandate una rewrite su un file già presente in quella posizione e con lo stesso nome, quest’ultimo verrà<br />

eliminato, e si ricomincerà con un file vuoto. Più avanti vi verrà spiegata una tecnica per verificare la presenza <strong>di</strong> un<br />

vecchio file senza <strong>di</strong>struggerlo e chiedere conferma prima <strong>di</strong> procedere con la rewrite.<br />

Chiusura – procedura CLOSE<br />

Ora il file esiste (vuoto) ed il programmatore potrebbe continuare con le istruzioni che aggiungono righe/schede.<br />

Oppure terminare subito le operazioni. In ogni caso il termine delle operazioni con un certo file va segnalato con il<br />

comando CLOSE: close(nome_file_logico). Di nuovo, non ci sono <strong>di</strong>fferenze tra file <strong>di</strong> testo e tipizzati.<br />

TESTO TIPIZZATI<br />

assign(lettera, ‘…’); assign(elenco);<br />

rewrite(lettera) rewrite(elenco);<br />

close(lettera) close(elenco)<br />

Questa è ovviamente la sequenza dei coman<strong>di</strong> nel caso il programmatore decidesse <strong>di</strong> creare i file senza registrare<br />

alcun dato su <strong>di</strong> essi (ovviamente potrà farlo in seguito, ‘riaprendo’ i file). Diversamente immaginate tra la rewrite e<br />

la close le istruzioni che aggiungono nuove righe (file <strong>di</strong> testo) o schede (file tipizzati).<br />

N OTA: close restituisce anche al <strong>sistema</strong> operativo tutte le risorse usate per la gestione del file.


Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 141<br />

Gestione Archivi Tra<strong>di</strong>zionali - versione 3.1 Settembre 2004<br />

Scrittura – le procedure APPEND (file <strong>di</strong> testo), WRITELN (file <strong>di</strong> testo) e WRITE (file tipizzati)<br />

Le procedure writeln e write permettono rispettivamente <strong>di</strong> aggiungere una riga ad un file <strong>di</strong> testo o un record ad un<br />

file tipizzato.<br />

Con il Pascal, per i file <strong>di</strong> testo ed i tipizzati gli inserimenti possono essere effettuati solo in fondo al file; non c’è<br />

modo <strong>di</strong> inserire nuove informazioni in mezzo al file senza crearne uno nuovo.<br />

L’operazione <strong>di</strong> inserimento potrebbe fallire:<br />

- lo spazio sull’unità è esaurito<br />

- l’unità non è <strong>di</strong>sponibile (ad esempio floppy non inserito)<br />

- l’unità è andata in avaria<br />

- il file è già stato aperto da un utente in modalità che impe<strong>di</strong>sce gli inserimenti agli altri utenti<br />

FILE DI TESTO<br />

Ad un file <strong>di</strong> testo possono essere esclusivamente aggiunte righe in fondo al file. Non è possibile, ad esempio,<br />

posizionarsi in una posizione interme<strong>di</strong>a ed inserire una riga. Il file deve essere stato prima pre<strong>di</strong>sposto o con rewrite<br />

(creando un file vuoto siamo ovviamente in fondo al file …) o con la procedura append(nome_file_logico).<br />

Quest’ultima deve essere usata quando si vogliono fare aggiunte ad un file <strong>di</strong> testo che contiene già alcune righe.<br />

Senza l’append dovremmo sempre creare da capo i file <strong>di</strong> testo riscrivendo anche le vecchie righe (vi ricordo che<br />

rewrite è <strong>di</strong>struttiva!).<br />

L’istruzione che aggiunge le righe al file è una variante dell’arcinota writeln. Semplicemente dovremo in<strong>di</strong>care il nome<br />

del file logico su cui scrivere e la stringa che rappresenta la riga da aggiungere: writeln(nome_file_logico, stringa).<br />

Dopo ogni writeln sul file, il punto <strong>di</strong> inserimento è automaticamente fatto avanzare.<br />

Come al solito, gli esempi che seguono fanno riferimento alle <strong>di</strong>chiarazioni dei paragrafi precedenti.<br />

File creato e scritto imme<strong>di</strong>atamente<br />

Var riga: string;<br />

begin<br />

Assign(lettera,’c:\...’);<br />

rewrite(lettera); (* FILE AZZERATO! *)<br />

repeat<br />

writeln(‘Inserire la riga da scrivere sul file’);<br />

readln(riga);<br />

if riga’FINE’ then<br />

writeln(lettera, riga);<br />

until riga=’FINE’;<br />

close(lettera);<br />

end.<br />

Aggiunga <strong>di</strong> righe ad un file esistente<br />

Var riga: string;begin<br />

begin<br />

Assign(lettera,’c:\...’);<br />

append(lettera); (* VECCHIE RIGHE CONSERVATE!*)<br />

repeat<br />

writeln(‘Inserire la riga da scrivere sul file’);<br />

readln(riga);<br />

if riga’FINE’ then<br />

writeln(lettera, riga);<br />

until riga=’FINE’;<br />

close(lettera);<br />

end


Ricorsione versione 2.02 - Febbraio 2005<br />

In entrambi gli esempi chi usa il programma comunica che le righe da aggiungere sul file <strong>di</strong> testo sono terminate<br />

<strong>di</strong>gitando la stringa ‘FINE’.<br />

FILE TIPIZZATI<br />

E’ molto importante notare che una scheda (record) viene letta/scritta tutta insieme, non un campo alla volta. Per le<br />

operazioni deve essere <strong>di</strong>chiarata una variabile record dello stesso tipo delle schede che si vogliono leggere/scrivere.<br />

Per aggiungere una scheda dovremo prima memorizzare i dati nella variabile record e poi comandare la registrazione<br />

<strong>di</strong> quest’ultima nel file tipizzato.<br />

L’istruzione che materialmente aggiunge la scheda è write(nome_file_logico, variabile_record_con_dati).<br />

Dopo ogni write sul file, il punto <strong>di</strong> inserimento è automaticamente fatto avanzare.<br />

File creato e scritto imme<strong>di</strong>atamente<br />

type<br />

TPersona=record<br />

cognome: string[30]; (* [30]: ricordatevi il numero <strong>di</strong> caratteri! *)<br />

co<strong>di</strong>ce: integer;<br />

end;<br />

var<br />

elenco: file of TPersona; (* la variabile file; si sarebbe anche potuto definire prima un tipo ‘file of TPersona’ *)<br />

unaPersona: TPersona;<br />

begin<br />

assign(elenco,'c:\prova.dat');<br />

(* crea il file azzerandolo, pronto per la prima scheda *)<br />

rewrite(elenco);<br />

repeat<br />

writeln('Inserimento da tastiera <strong>di</strong> un record da trasferire poi sul file');<br />

write('Inserire un co<strong>di</strong>ce: (zero per finire)' );<br />

readln(unaPersona.co<strong>di</strong>ce);<br />

write('Inserire un cognome: ');<br />

readln(unaPersona.cognome);<br />

if unaPersona.co<strong>di</strong>ce0 then<br />

write(elenco,unaPersona); AGGIUNGE LA SCHEDA IN FONDO AL FILE ED AVANZA<br />

until unaPersona.co<strong>di</strong>ce=0;<br />

close(elenco);<br />

end.<br />

Chi usa il programma comunica che le schede da aggiungere sono terminate <strong>di</strong>gitando come co<strong>di</strong>ce il valore zero.<br />

Autore: Fabrizio Camuso (camuso@bigfoot.com) Pagina 1 <strong>di</strong> 177


Ricorsione versione 2.02 - Febbraio 2005<br />

Aggiunga <strong>di</strong> righe ad un file esistente – PROCEDURA SEEK E FUNZIONE FILESIZE<br />

L’append non esiste ma può essere simulata spostandosi in fondo al file con un comando speciale. Un grosso<br />

vantaggio, infatti, dei file tipizzati è la possibilità <strong>di</strong> spostarsi su una scheda qualsiasi senza dover leggere quelle che<br />

la precedono. Il comando è seek(nome_file_logico, posizione). ATTENZIONE: si inizia a contare da zero!! Quin<strong>di</strong> la<br />

posizione del primo record è la zero, quella del secondo record è due e così via. Per spostarsi sul primo record allora<br />

si userà seek(file , 0), per spostarsi invece sul decimo seek(file, 9).<br />

Per simulare l’append dovremmo allora spostarci oltre l’ultimo record. Se ci fossero N record l’istruzione sarebbe<br />

seek(file, N): infatti seek(file, N-1) in<strong>di</strong>viduerebbe proprio l’N-mo record ed in<strong>di</strong>cando N come posizione ci<br />

posizioneremmo sul record N+1 (cioè sul nuovo che abbiamo intenzione <strong>di</strong> inserire), sempre in virtù del fatto che si<br />

conta da 0. Già, ma come si fa a sapere quanti record ci sono nel file? Un metodo brutale potrebbe essere quello <strong>di</strong><br />

leggerli tutti fino a raggiungere la fine del file (EOF) ma sarebbe molto <strong>di</strong>spen<strong>di</strong>oso. Per fortuna esiste un comando<br />

specifico (solo per i file tipizzati; secondo voi perché ?): filesize(nome_file_logico).<br />

Inoltre, prima <strong>di</strong> poter usare seek il file deve essere pre<strong>di</strong>sposto all’uso (aperto) con la procedura<br />

reset(nome_file_logico), da usare ovviamente dopo l’assign.<br />

I coman<strong>di</strong> che portano oltre la fine del file sono allora:<br />

reset(nome_file_logico); QUESTO COMANDO PREDISPONE IL FILE ALL’USO, LO ‘APRE’<br />

seek( nome_file_logico, filesize(nome_file_logico) )<br />

Ed ecco un esempio completo:<br />

type<br />

TPersona=record<br />

cognome: string[30]; (* e' obbligatorio definire il numero dei caratteri della stringa ! *)<br />

co<strong>di</strong>ce: integer;<br />

end;<br />

fpersona=file of TPersona; (* senza il tipo non potremmo passare un file come parametro … *)<br />

var<br />

elenco: fPersona;<br />

…<br />

assign(elenco, ‘C:\dati.dat’);<br />

reset(elenco);<br />

seek(elenco, filesize(elenco ); SPOSTATI OLTRE L’ULTIMA SCHEDA PRESENTE NEL FILE<br />

repeat<br />

writeln('Inserimento da tastiera <strong>di</strong> un record da trasferire poi sul file');<br />

write('Inserire un co<strong>di</strong>ce: (zero per finire)' );<br />

readln(unaPersona.co<strong>di</strong>ce);<br />

write('Inserire un cognome: ');<br />

readln(unaPersona.cognome);<br />

if unaPersona.co<strong>di</strong>ce0 then<br />

write(elenco,unaPersona); LA SCHEDA IN FONDO AL FILE ED AVANZA<br />

until unaPersona.co<strong>di</strong>ce=0;<br />

close(elenco);<br />

Autore: Fabrizio Camuso (camuso@bigfoot.com) Pagina 2 <strong>di</strong> 177


Ricorsione versione 2.02 - Febbraio 2005<br />

Lettura – la procedura RESET, READLN (file <strong>di</strong> testo), READ (file tipizzati)<br />

Per usare un file già creato in precedenza senza <strong>di</strong>struggerlo lo si deve prima pre<strong>di</strong>sporre all’uso con la procedura<br />

reset(nome_file_logico).<br />

Con reset in pratica si notifica l’uso del file al <strong>sistema</strong> operativo pre<strong>di</strong>spone le risorse necessarie (area <strong>di</strong> memoria<br />

RAM per i trasferimenti da e verso i <strong>di</strong>schi, tabelle per tenere traccia delle operazioni ecc.). Queste strutture<br />

occupano memoria centrale preziosa, per cui bisognerebbe tenere ‘aperti’ solo i file necessari.<br />

Quasi tutti i linguaggi <strong>di</strong>stinguono tra <strong>di</strong>versi tipi <strong>di</strong> apertura (Cobol, ‘C’) a seconda delle operazioni <strong>di</strong> I/O che si<br />

intendono fare: sola lettura, solo scrittura, lettura / scrittura, aggiunta a fine file (append). E’ impossibile in questa<br />

sede <strong>di</strong>scutere tutte le varianti: consultate attentamente il manuale del linguaggio in uso.<br />

Anche l’apertura può fallire:<br />

- il percorso/nome del file non è valido<br />

- il supporto <strong>di</strong> massa non è <strong>di</strong>sponibile (nastro non montato, floppy non inserito, CD non scrivibile)<br />

- il supporto è in avaria (floppy o hard <strong>di</strong>sk rotti …)<br />

- il file è già stato aperto da un utente in modalità esclusiva (blocca i tentativi <strong>di</strong> accesso <strong>di</strong> tutti gli altri)<br />

Con il Pascal, dopo l’apertura con reset <strong>di</strong> un file <strong>di</strong> testo si è pronti per leggere la prima riga. Dopo l’apertura <strong>di</strong> un<br />

file tipizzato si è pronti per leggere il primo record. Una funzione fondamentale per una lettura corretta sia dei file <strong>di</strong><br />

testo che <strong>di</strong> quelli tipizzati è la funzione EOF(nome_file_logico): che restituisce TRUE se siamo alla fine del file, FALSE<br />

altrimenti.<br />

Ve<strong>di</strong>amo alcuni esempi concreti <strong>di</strong> lettura.<br />

FILE DI TESTO<br />

… <strong>di</strong>chiarazioni …<br />

(* riapriamo il file per lettura *)<br />

reset(lettera);<br />

(* leggiamo tutte le righe fino alla fine *)<br />

while not eof(lettera) do<br />

begin<br />

readln(lettera,riga);<br />

writeln(riga)<br />

end;<br />

close(lettera);<br />

FILE TIPIZZATI<br />

… <strong>di</strong>chiarazioni …<br />

(* riapriamo il file per lettura *)<br />

reset(elenco);<br />

(* leggiamo tutte le schede fino alla fine *)<br />

while not eof(elenco) do<br />

begin<br />

readln(elenco,unaPersona);<br />

writeln(unaPersona.cognome,' - ',unaPersona.co<strong>di</strong>ce)<br />

end;<br />

close(lettera);<br />

Autore: Fabrizio Camuso (camuso@bigfoot.com) Pagina 3 <strong>di</strong> 177


Ricorsione versione 2.02 - Febbraio 2005<br />

Non per<strong>di</strong>amo la bussola – puntatore all’area <strong>di</strong> lavoro, BOF ed EOF<br />

Nell’uso <strong>di</strong> un file è sempre molto importante rendersi conto in che punto dello stesso si sta lavorando. Possiamo<br />

immaginare un ‘puntatore’ che in<strong>di</strong>ca la posizione:<br />

file appena creato; è vuoto;<br />

siamo all’inizio del file (BOF,<br />

Begin Of File) e<br />

contemporaneamente alla fine<br />

(EOF, End Of File)<br />

NOTA BENE<br />

Tutte le operazioni <strong>di</strong> lettura/scrittura spostano sempre il puntatore in avanti <strong>di</strong> una ‘posizione’ (una<br />

riga per i file <strong>di</strong> testo, una scheda per i file tipizzati) verso la fine del file (EOF, End Of File).<br />

Dopo la creazione siamo all’inizio, ma anche alla fine (file è vuoto!) e quin<strong>di</strong> pronti per aggiungere<br />

nuovi blocchi <strong>di</strong> dati. Ad ogni inserimento <strong>di</strong> una riga/scheda la posizione viene spostata<br />

automaticamente oltre la fine del file e si è pronti per aggiungere un nuovo blocco <strong>di</strong> dati.<br />

Dopo aver letto una riga/scheda (se il file già ne contiene) la posizione si sposta automaticamente in<br />

avanti e si è pronti per leggere la successiva.<br />

Aggiornamento (QUESTA OPERAZIONE E’ POSSIBILE SOLO PER I FILE TIPIZZATI)<br />

Il file deve esistere e deve essere prima aperto con reset; si deve localizzare la scheda (record) da aggiornare cioè<br />

posizionarsi in corrispondenza del punto a partire dal quale è necessario sovrascrivere le vecchie informazioni con<br />

quelle nuove.<br />

Poi si legge la vecchia scheda, per due motivi:<br />

• recuperare quelle informazioni della scheda che non devono cambiare per poterle riscrivere uguali al<br />

momento della riscrittura della scheda; <strong>di</strong>versamente dovremmo costringere chi usa il programma a<br />

reinserirle (improponibile); ad esempio in una scheda anagrafica potrebbe cambiare solo l’in<strong>di</strong>rizzo: la<br />

lettura della vecchia scheda recupera tutte le altre informazioni (cognome, nome, co<strong>di</strong>ce, ecc.) che<br />

verranno riscritte uguali insieme al nuovo in<strong>di</strong>rizzo;<br />

• poter proporre le vecchie informazioni sul video facilitando chi usa il programma nell’inserimento dei<br />

campi del record che devono essere cambiati;<br />

La lettura della vecchia scheda fa però avanzare al record successivo e non siamo perciò più posizionati nel punto<br />

giusto per l’aggiornamento. Se scrivessimo in questa situazione andremmo ad aggiornare in realtà la scheda<br />

successiva (od aggiungeremmo una nuova scheda se fossimo avanzati oltre la fine del file). E’ quin<strong>di</strong> necessario<br />

ripetere il posizionamento con seek prima <strong>di</strong> riscrivere la scheda.<br />

BOF<br />

EOF<br />

il file contiene già dei dati; siamo posizionati in un<br />

punto interme<strong>di</strong>o per leggere un blocco <strong>di</strong> dati;<br />

solo con i file tipizzati potremmo anche riscrivere<br />

(aggiornare) il blocco dei dati<br />

L’aggiornamento può fallire:<br />

- i parametri in<strong>di</strong>cati sono errati<br />

- l’unità non è <strong>di</strong>sponibile (ad esempio il floppy è stato nel frattempo tolto)<br />

- l’unità è andata in avaria<br />

Autore: Fabrizio Camuso (camuso@bigfoot.com) Pagina 4 <strong>di</strong> 177<br />

BOF<br />

EOF<br />

siamo alla fine del file; in questa<br />

posizione possiamo aggiungere<br />

nuovi blocchi <strong>di</strong> dati


Ricorsione versione 2.02 - Febbraio 2005<br />

Dopo l’aggiornamento ci si trova poi all’inizio dell’unità informativa successiva.<br />

Ecco un esempio che propone la sequenza delle operazioni per mo<strong>di</strong>ficare solo il cognome della persona<br />

corrispondente al secondo record (posizione 1, ricordatevi che si conta da zero …):<br />

type<br />

TPersona=record<br />

cognome: string[30]; (* e' obbligatorio definire il numero dei caratteri della stringa ! *)<br />

co<strong>di</strong>ce: integer;<br />

end;<br />

fpersona=file of TPersona; (* senza il tipo non potremmo passare un file come parametro … *)<br />

var<br />

elenco: fPersona;<br />

…<br />

assign(elenco, ‘C:\dati.dat’); reset(elenco);<br />

(* ora mo<strong>di</strong>fichiamo il secondo record la sequenza standard e':<br />

- seek alla posizione della scheda da mo<strong>di</strong>ficare<br />

- lettura della scheda con vecchi dati<br />

ATTENZIONE: a questo punto abbiamo superato la scheda da aggiornare<br />

- acquisizione nuovi dati che vengono sostituiti a quelli<br />

vecchi nella scheda<br />

- torniamo in<strong>di</strong>etro alla posizione giusta per scrittura ripetendo<br />

la seek iniziale (può anche essere fatta subito dopo la lettura, per non <strong>di</strong>menticarsene …)<br />

- scrittura su file (aggiornamento) della scheda con i dati mo<strong>di</strong>ficati *)<br />

seek(elenco,1); (* seek prima della scheda da mo<strong>di</strong>ficare *)<br />

read(elenco, unaPersona); (* lettura scheda con vecchi dati *)<br />

writeln(‘-------- VECCHI DATI -----‘);<br />

writeln(unaPersona.cognome,' - ',unaPersona.co<strong>di</strong>ce);<br />

write('Inserire il nuovo cognome: ');<br />

readln(unaPersona.cognome);<br />

(* riposizionamoci nel posto giusto per la scrittura *)<br />

seek(elenco,1);<br />

(* ora la variabile record contiene il vecchio co<strong>di</strong>ce ed il nuovo cognome: riscriviamola sul file *)<br />

write(elenco,unaPersona);<br />

Autore: Fabrizio Camuso (camuso@bigfoot.com) Pagina 5 <strong>di</strong> 177


Ricorsione versione 2.02 - Febbraio 2005<br />

Eliminazione<br />

FILE DI TESTO<br />

Non è possibile eliminare una riga da un file <strong>di</strong> testo. L’unica possibilità è quella <strong>di</strong> ricopiare tutte le righe meno quelle<br />

da cancellare in un nuovo file (operazione assai <strong>di</strong>spen<strong>di</strong>osa).<br />

FILE TIPIZZATI<br />

Di nuovo, non è possibile eliminare fisicamente un record. Però, grazie alla possibilità <strong>di</strong> riscrittura si<br />

può‘contrassegnare’ una scheda in qualche modo convenzionale scelto dal programmatore. Ad esempio potrebbe<br />

esserci un campo ‘co<strong>di</strong>ce’ e decidere che se contiene zero la scheda è da considerarsi eliminata. Di tanto in tanto<br />

potremmo comandare in momenti <strong>di</strong> basso utilizzo dell’archivio (<strong>di</strong> notte, ad esempio) una riorganizzazione<br />

ricopiando in un nuovo file le schede non ‘eliminate’.<br />

Come controllare gli esiti delle operazioni richieste (GESTIONE DEGLI ERRORI)<br />

Il <strong>sistema</strong> operativo, sollecitato da coman<strong>di</strong> visti, può dare conferma in merito al loro sod<strong>di</strong>sfacimento oppure<br />

segnalare un errore. I casi che portano al fallimento possono essere vari: spazio sul supporto esaurito, il supporto<br />

in<strong>di</strong>cato non è scrivibile (CD ROM), il supporto o un altro <strong>di</strong>spositivo necessario per il suo uso (ad es. il controller<br />

dell’hd) è in avaria, le risorse <strong>di</strong> <strong>sistema</strong> non consentono al momento l’operazione (troppi programmi in uso, troppi<br />

utenti collegati, troppi file in uso, collegamento ad Internet al momento interrotto ecc.), il programma (e <strong>di</strong><br />

conseguenza l’utente che lo ha eseguito) non ha l’autorizzazione necessaria, il percorso in<strong>di</strong>cato nel nome del file<br />

non esiste o non è corretto, il nome del file in<strong>di</strong>cato non è corretto ecc.<br />

La gestione <strong>di</strong> queste situazioni può avvenire fondamentalmente in due mo<strong>di</strong>:<br />

Il <strong>sistema</strong> operativo rileva le anomalie ed agisce automaticamente: spesso questo significa un messaggio sullo<br />

schermo e la terminazione forzata del programma che ha generato l’errore. In qualche caso, ma con uno sforzo<br />

notevole, il programmatore può sostituire all’azione standard del <strong>sistema</strong> operativo le sue ‘routines’ <strong>di</strong> gestione<br />

degli ‘interrupt’ relativi (avete imparato a vostre spese come questo sia <strong>di</strong>fficile da realizzare, soprattutto in<br />

‘assembly)’.<br />

Il programma in esecuzione riceve una notifica <strong>di</strong> errore, dopo un’operazione tentata, ed ha la possibilità <strong>di</strong><br />

gestirlo e <strong>di</strong> ripristinare il funzionamento del programma in modo ‘accettabile’. La notifica può avvenire tramite:<br />

un valore restituito dalla funzione <strong>di</strong> I/O tentata: ad esempio la ‘fopen’ del ‘C’ dovrebbe restituire un puntatore<br />

ad un file (una struttura a record usata per le operazione <strong>di</strong> I/O con il corrispondente file fisico) ma potrebbe<br />

restituire ‘null’ in caso <strong>di</strong> insuccesso;<br />

un controllo esplicito del programmatore dopo aver tentato un’operazione: ad esempio in Pascal è possibile<br />

chiamare la funzione IOResult: se il valore restituito è 0 allora l’operazione è andata a buon fine, <strong>di</strong>versamente il<br />

valore in<strong>di</strong>ca il tipo <strong>di</strong> errore (ad esempio 1=file non trovato, 2=<strong>di</strong>sco pieno ecc.)<br />

l’uso <strong>di</strong> una struttura <strong>di</strong> controllo ad alto livello specifica: gli ambienti <strong>di</strong> programmazione più avanzati prevedono<br />

potenti strutture <strong>di</strong> programmazione per la gestione degli errori; ad esempio l’Object Pascal offre il costrutto ‘try<br />

… except …’ in cui dopo il termine ‘try’ (tenta) si mettono le istruzioni che potenzialmente potrebbero causare<br />

errori; nella sezione ‘except’, quasi come in un ‘case’ si elencano, usando dei nomi significativi, gli errori che si<br />

intendono ‘intercettare’ ed i relativi sottoprogrammi scritti dal programmatore per gestirli; molti <strong>di</strong>aletti del Basic<br />

(tra cui Visual Basic e Visual Basic for Application, VBA, il linguaggio degli applicativi Office) offrono un costrutto<br />

analogo: ‘on error ….’<br />

Autore: Fabrizio Camuso (camuso@bigfoot.com) Pagina 6 <strong>di</strong> 177


Ricorsione versione 2.02 - Febbraio 2005<br />

ESEMPIO COMPLETO 1<br />

L’esempio seguente mostra come procedere alla creazione <strong>di</strong> un file controllando prima se ne esiste già uno<br />

vecchio.<br />

Se esiste l’operazione <strong>di</strong> reset va a buon fine e possiamo accorgercene dal valore 0 restituito dalla funzione<br />

IOResult: in questo caso si chiede conferma a chi sta usando il programma prima <strong>di</strong> procedere con una rewrite<br />

<strong>di</strong>struttiva.<br />

Se non esiste l’operazione <strong>di</strong> reset fallisce e possiamo accorgercene dal valore <strong>di</strong>verso da 0 restituito dalla funzione<br />

IOResult: in questo caso si può procedere con una rewrite <strong>di</strong>struttiva senza chiedere conferma.<br />

type<br />

TPersona=record<br />

cognome: string[30]; (* e' obbligatorio definire il numero dei caratteri della stringa ! *)<br />

co<strong>di</strong>ce: integer;<br />

end;<br />

fpersona=file of TPersona; (* senza il tipo non potremmo passare un file come parametro … *)<br />

var<br />

elenco: fPersona;<br />

begin<br />

(* la riga che segue abilita alla gestione degli errori e va inserita così com’è su una riga isolata;<br />

se la vostra tastiera non ha le graffe vi ricordo che potete tenere premuto ALT <strong>di</strong> sinistra e <strong>di</strong>gitare 123 o 125 sul<br />

tastierino numerico con tasto ‘Bloc Num’ del tastierino attivato *)<br />

{$I-}<br />

assign(elenco,'c:\prova.dat');<br />

reset(elenco);<br />

risposta:=SI;<br />

if IOResult=0 then<br />

begin<br />

writeln('Il file esiste gia'': procedo? (SI/NO)');<br />

readln(risposta);<br />

close(elenco)<br />

end;<br />

if risposta=’SI’ then<br />

begin<br />

rewrite(elenco);<br />

ecc. ecc.<br />

close(elenco)<br />

end;<br />

end.<br />

Autore: Fabrizio Camuso (camuso@bigfoot.com) Pagina 7 <strong>di</strong> 177<br />

NOTA. Nel caso volessimo fare un aggiornamento o una lettura<br />

da un file la logica sarebbe semplicemente invertita: se non si<br />

riesce ad aprire il file ci si ferma; se ci si riesce si procede …<br />

IOResult fornisce co<strong>di</strong>ci numerici <strong>di</strong>versi a seconda delle<br />

situazioni e volendo essere più precisi potremmo usare un case:<br />

case IOResult of<br />

1: ….<br />

2: ….<br />

End.<br />

Ecco i co<strong>di</strong>ci più comuni (sequenza completa nell’help in linea<br />

del turbo pascal cercando ‘run time error codes’)<br />

2 File not found<br />

3 Path not found<br />

15 Invalid drive number<br />

100 Disk read error<br />

101 Disk write error<br />

102 File not assigned<br />

103 File not open<br />

104 File not open for input<br />

105 File not open for output<br />

150 Disk is write-protected<br />

152 Drive not ready<br />

156 Disk seek error<br />

157 Unknown me<strong>di</strong>a type<br />

158 Sector Not Found<br />

162 Hardware failure


Ricorsione versione 2.02 - Febbraio 2005<br />

ESEMPIO COMPLETO 2<br />

In questo secondo esempio gestiamo una semplice scheda <strong>di</strong> dati anagrafici; per sfruttare al meglio l’istruzione seek<br />

immaginiamo che ogni scheda sia in<strong>di</strong>viduata da un co<strong>di</strong>ce numerico che facciamo corrispondere alla sua posizione<br />

come record. Quin<strong>di</strong> se si cerca la scheda con co<strong>di</strong>ce 7 si utilizza seek(file, 6) e così via. Il co<strong>di</strong>ce viene mantenuto<br />

strettamente progressivo ed è assegnato in automatico dal programma<br />

Questo esempio fa uso <strong>di</strong> procedure e funzioni con parametri. In alcuni casi fa anche il controllo degli errori con i file.<br />

Il programma tiene sempre sotto controllo il numero dei record presenti nell’archivio (per poter generare in<br />

automatico il co<strong>di</strong>ce da assegnare alle schede). In partenza, infatti, se il file è vuoto mette la variabile inseriti a zero<br />

altrimenti gli assegna il numero <strong>di</strong> record presenti nel file. Il programma tiene poi aggiornato questo contatore ad<br />

ogni inserimento o cancellazione. La cancellazione non avviene in modo fisico ma mettendo a FALSE un boolean del<br />

record (campo attivo).<br />

Le scelte <strong>di</strong> chi usa il programma sono gestite da una funzione menu che riceve l’elenco delle voci da presentare (un<br />

vettore <strong>di</strong> stringhe) e restituisce il valore della scelta fatta.<br />

program ProvaFileTipizzati;<br />

(* e' necessario <strong>di</strong>sabilitare l'intercettazione degli errori <strong>di</strong> I/O da<br />

parte del Turbo Pascal al fine <strong>di</strong> gestirli in modo personalizzato *)<br />

{$I-} (* non cancellare ! *)<br />

const MAX_VOCI_MENU=9;<br />

type<br />

voci_menu=array[1..MAX_VOCI_MENU] of string;<br />

TPersona=record<br />

co<strong>di</strong>ce: integer;<br />

cognome: string[30];<br />

attivo: boolean; (* false: record da considerare cancellato *)<br />

end;<br />

fDati=file of TPersona;<br />

var<br />

dati: fDati; i,scelta:integer; ultimo_co<strong>di</strong>ce,unCo<strong>di</strong>ce: longint;<br />

nomeFile,riga: string; unaPersona: TPersona;<br />

proce<strong>di</strong>: boolean; conferma: char;<br />

menu_database: voci_<br />

menu;<br />

(* restituisce true se il file il cui nome logico è passato come parametro esiste, false altrimenti *)<br />

function esiste(var f: fdati): boolean;<br />

var esito: boolean;<br />

begin<br />

reset(f);<br />

if IOResult=0 then<br />

begin<br />

close(f);<br />

esito:=true<br />

end<br />

else<br />

esito:=false;<br />

esiste:=esito<br />

end;<br />

Autore: Fabrizio Camuso (camuso@bigfoot.com) Pagina 8 <strong>di</strong> 177


Ricorsione versione 2.02 - Febbraio 2005<br />

(* riceve un vettore <strong>di</strong> stringhe ed il numero <strong>di</strong> queste da considerare e presentare come menu sullo schermo;<br />

intitola anche il menu con un altro parametro stringa; restituisce il numero della voce del menu scelta *)<br />

function menu(voci: voci_menu; n_voci: integer;titolo: string): integer;<br />

var i,voce_scelta: integer;<br />

begin<br />

repeat<br />

writeln(titolo);writeln;<br />

for i:=1 to n_voci do<br />

writeln(voci[i]);<br />

write('Scegli -> ');<br />

readln(voce_scelta);<br />

if (voce_sceltan_voci) then<br />

begin<br />

writeln('Scelta errata!! (UN TASTO PER RIPROVARE)');<br />

readln<br />

end<br />

until (voce_scelta>0) and (voce_scelta


Ricorsione versione 2.02 - Febbraio 2005<br />

repeat<br />

scelta:=menu(menu_database,7,'Gestione Nominativi');<br />

case scelta of<br />

1:begin (* azzeramento *)<br />

if ultimo_co<strong>di</strong>ce>0 then<br />

begin<br />

writeln('Attualmente sono inseriti ',ultimo_co<strong>di</strong>ce, ' nominativi; confermi <strong>di</strong>struzione? (s/n)');<br />

readln(conferma);<br />

if (conferma='s') or (conferma='S') then<br />

begin<br />

rewrite(dati);<br />

ultimo_co<strong>di</strong>ce:=0<br />

end<br />

end;<br />

writeln('Fatto!! INVIO per continuare');<br />

readln<br />

end;<br />

2:begin (* inserimento *)<br />

inc(ultimo_co<strong>di</strong>ce);<br />

with unaPersona do<br />

begin<br />

co<strong>di</strong>ce:=ultimo_co<strong>di</strong>ce;<br />

write('Inserire cognome: ');<br />

readln(cognome);<br />

attivo:=true<br />

end;<br />

reset(dati);<br />

seek(dati, FileSize(dati)); (* append ... *)<br />

write(dati,unaPersona);<br />

close(dati)<br />

end;<br />

3:begin (* ricerca *)<br />

if ultimo_co<strong>di</strong>ce>0 then<br />

begin<br />

write('Inserire il co<strong>di</strong>ce che interessa: ');readln(unCo<strong>di</strong>ce);<br />

if unCo<strong>di</strong>ce0 then<br />

begin<br />

write('Inserire il co<strong>di</strong>ce che interessa: ');readln(unCo<strong>di</strong>ce);<br />

if unCo<strong>di</strong>ce


Ricorsione versione 2.02 - Febbraio 2005<br />

begin<br />

writeln('Vecchi dati: ',unaPersona.co<strong>di</strong>ce, ' - ',unaPersona.cognome);<br />

write('Inserire nuovo cognome: ');<br />

readln(unaPersona.cognome);<br />

(* per riscrivere devo rimettermi PRIMA del record *)<br />

seek(dati,unCo<strong>di</strong>ce-1);<br />

write(dati,unaPersona);<br />

writeln('Fatto!')<br />

end<br />

else<br />

writeln('Nominativo cancellato')<br />

end<br />

else<br />

writeln('Ci sono solo ',ultimo_co<strong>di</strong>ce,' nominativi!')<br />

end<br />

else<br />

writeln('File Vuoto');<br />

writeln('INVIO per continuare');<br />

readln<br />

end;<br />

5:begin (* cancellazione *)<br />

if ultimo_co<strong>di</strong>ce>0 then<br />

begin<br />

write('Inserire il co<strong>di</strong>ce che interessa: ');readln(unCo<strong>di</strong>ce);<br />

if unCo<strong>di</strong>ce0 then<br />

visualizza_file(dati)<br />

else<br />

writeln('File Vuoto');<br />

writeln('INVIO per continuare');<br />

readln<br />

end<br />

end<br />

until scelta=7;<br />

end.<br />

Autore: Fabrizio Camuso (camuso@bigfoot.com) Pagina 11 <strong>di</strong> 177


Ricorsione versione 2.02 - Febbraio 2005<br />

RICORSIONE<br />

Esistono problemi tutto sommato semplici da descrivere che portano ad algoritmi incre<strong>di</strong>bilmente complicati se<br />

trattati con gli strumenti <strong>di</strong> programmazione visti sino ad ora… Ad esempio: molte situazioni reali sono adatte per<br />

essere rappresentate con una struttura chiamata albero. Un albero è formato da elementi chiamati no<strong>di</strong> collegati da<br />

archi:<br />

figlio<br />

foglia<br />

nodo<br />

arco<br />

fratelli<br />

Ra<strong>di</strong>ce (root)<br />

Autore: Fabrizio Camuso (camuso@bigfoot.com) Pagina 12 <strong>di</strong> 177<br />

Nel <strong>di</strong>segno i no<strong>di</strong> sono rappresentati dai cerchietti, gli archi dai segmenti<br />

che li congiungono. In realtà si tratta <strong>di</strong> un caso particolare <strong>di</strong> albero<br />

chiamato albero binario, in quanto ogni nodo può avere al massimo due<br />

figli (ma anche uno o nessuno). Gli alberi più complessi (generici, senza<br />

limiti sul numero <strong>di</strong> figli) possono comunque essere rappresentati con un<br />

albero binario costruito in modo ‘furbo’ (scoprirete più avanti nel corso<br />

come …).<br />

Il nodo iniziale, quello in cima per intenderci, è chiamato root (ra<strong>di</strong>ce). I<br />

no<strong>di</strong> terminali (quelli senza figli) sono chiamati foglie. I figli <strong>di</strong>retti <strong>di</strong> uno<br />

stesso nodo sono fratelli tra loro.<br />

Qualche esempio d’uso: in un gioco un albero può rappresentare per ogni possibile ‘mossa’ le risposte<br />

dell’avversario e per ciascuna <strong>di</strong> queste le possibili contromosse e così via; i sistemi operativi utilizzano strutture ad<br />

albero per memorizzare la struttura delle cartelle e relative sotto cartelle; un albero genealogico; l’organigramma<br />

delle figure <strong>di</strong> un’azienda (boss, capi reparto, <strong>di</strong>pendenti ecc.); alcune delle tecniche più sofisticate <strong>di</strong> gestione degli<br />

archivi usano strutture ad albero (B+ tree).<br />

E’ ovvio che ciò che conta è il ‘contenuto’ <strong>di</strong> ogni nodo. Per un gioco (scacchi ad esempio) ogni nodo potrebbe<br />

memorizzare la situazione appena prima o appena dopo una certa mossa (la matrice della scacchiera). Il <strong>sistema</strong><br />

operativo potrebbe memorizzare l’elenco dei file in quella <strong>di</strong>rectory (non è proprio così …); per l’albero genealogico<br />

i dati anagrafici della persona associata a ciascun nodo ecc.<br />

Ma come si rappresentano gli archi? La soluzione classica prevede la gestione tramite memoria <strong>di</strong>namica: ogni nodo<br />

memorizza i puntatori ai figli. Ad esempio:<br />

type<br />

Pun=^Nodo;<br />

Nodo=record<br />

ParteInformativa: string;<br />

sx: Pun; (* memorizza il puntatore al figlio <strong>di</strong> sinistra *)<br />

dx: Pun (* memorizza il puntatore al figlio <strong>di</strong> destra *)<br />

end<br />

OK, immaginiamo <strong>di</strong> avere l’albero già perfettamente costruito e che la variabile puntatore inizio punti al primo<br />

nodo (root). Ed ecco la sfida: scrivere un programma che stampi l’elenco <strong>di</strong> tutte le parti informative memorizzate<br />

nell’albero. E’ una richiesta banale: se non riuscissimo a fare almeno questo l’albero sarebbe inutilizzabile! Da un<br />

punto <strong>di</strong> vista logico si tratta <strong>di</strong> visitare tutti i no<strong>di</strong> e per ciascuno stampare la parte informativa. Sfido chiunque a<br />

trovare una soluzione che faccia uso <strong>di</strong> cicli: essa esiste ma è <strong>di</strong> elevata complessità. E’ anche quasi incomprensibile<br />

… Ed è pure lunga alcune decine <strong>di</strong> righe. Guardate invece l’eleganza, la semplicità e l’estrema leggibilità <strong>di</strong> questa<br />

soluzione (ricorsiva):


procedure StampaAlbero(nodo: pun);<br />

begin<br />

if nodoNIL then<br />

begin<br />

1. writeln(nodo^.ParteInformativa);<br />

2. StampaAlbero(nodo^.sx);<br />

3. StampaAlbero(nodo^.dx)<br />

end<br />

end;<br />

Allocazione <strong>di</strong>namica della memoria versione 2.5 Luglio 2003<br />

NOTA: ho numerato le righe per<br />

facilitare il successivo commento<br />

del co<strong>di</strong>ce.<br />

1 Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it )


Allocazione <strong>di</strong>namica della memoria versione 2.5 Luglio 2003<br />

Considerando l’if’ sono solo sei righe !!! Sembra quasi impossibile… La stampa completa è comandata con<br />

StampaAlbero(inizio) dove inizio è il puntatore al primo nodo dell’albero (la ra<strong>di</strong>ce). La logica <strong>di</strong> esecuzione è assai<br />

semplice: stampare la parte informativa del nodo <strong>di</strong> cui si è ricevuto il puntatore e chiamare quin<strong>di</strong> prima la stampa<br />

pe il figlio <strong>di</strong> sinistra ( StampaAlbero(nodo^.sx) ) e poi per quello <strong>di</strong> destra ( StampaAlbero(nodo^.dx) ). Il<br />

meccanismo si ripete sui rispettivi figli sinistra/destra fino a raggiungere le foglie.<br />

La procedura chiama quin<strong>di</strong> sé stessa (ricorre ai servizi <strong>di</strong> sé stessa, da cui il termine ricorsione) ma ogni volta<br />

passando un puntatore che la fa avvicinare alle foglie. Con queste ultime verranno chiamate delle ‘StampaAlbero’<br />

con parametro uguale a NIL (le foglie non hanno no<strong>di</strong> sotto <strong>di</strong> esse e quin<strong>di</strong> hanno il valore NIL nei loro puntatori sx<br />

e dx) terminando la catena delle chiamate.<br />

StampaAlbero(inizio^.sx)<br />

StampaAlbero(inizio^.sx^.sx)<br />

<br />

StampaAlbero(nil)<br />

StampaAlbero(inizio)<br />

StampaAlbero(nil)<br />

StampaAlbero(inizio^.dx)<br />

StampaAlbero(inizio^.dx^.dx)<br />

<br />

StampaAlbero(nil)<br />

E’ molto importante capire che in un certo istante avremo più copie della procedura StampaAlbero attivate (ma una<br />

sola ‘funzionante’). Infatti la prima chiamata ( StampaAlbero(inizio) ) chiama sé stessa con il puntatore al suo figlio<br />

<strong>di</strong> sinistra e deve poi letteralmente rimanere in attesa alla riga n. 2 aspettando la fine dell’esecuzione <strong>di</strong><br />

StampaAlbero(inizio^.sx). E quest’ultima dopo aver stampato la parte informativa del suo nodo aspetterà alla stessa<br />

riga per lo stesso motivo e così via. Riferendosi al <strong>di</strong>segno precedente, verranno chiamate in sequenza ben 4<br />

StampaAlbero che esplorano tutto la parte a sinistra dell’abero, fino a raggiungere la prima foglia (l’ultimo nodo in<br />

basso a sinistra). Solo dopo che il meccanismo avrà fatto esplorare tutti i no<strong>di</strong> a sinistra della ra<strong>di</strong>ce, la procedura<br />

che ha ricevuto inizio come puntatore e che è stata fino ad ora in attesa potrà continuare la sua esecuzione e<br />

chiamare sé stessa sul figlio <strong>di</strong> destra: <strong>di</strong> nuovo si mette in attesa per la fine dell’esecuzione <strong>di</strong> questa chiamata che<br />

prima farà esplorare tutti i no<strong>di</strong> alla destra della ra<strong>di</strong>ce. Solo allora la prima procedura terminerà ‘raggiungendo il<br />

suo end’.<br />

Quando la procedura è chiamata con NIL non fa niente ma si limita a terminare, senza richiamare più sé stessa ed<br />

interrompendo la catena delle chiamate. E’ molto importante che si raggiunga una situazione <strong>di</strong> terminazione:<br />

<strong>di</strong>versamente si andrebbe avanti fino all’esaurimento della memoria messa a <strong>di</strong>sposizione per la ricorsione (stack)<br />

mandando in crash il processo <strong>di</strong> esecuzione e, forse, causando anche il blocco dell’elaboratore.<br />

Un algoritmo ricorsivo corretto prima o poi invece si ferma perché raggiunge la con<strong>di</strong>zione <strong>di</strong> terminazione che è<br />

chiamata la base della ricorsione (in realtà potrebbe ancora esaurire la memoria se ha bisogno <strong>di</strong> troppi passi per<br />

essere concluso, ma concettualmente l’algoritmo è corretto). Pensate alla base della ricorsione come al caso che<br />

termina la ‘catena’ delle chiamate. Nel nostro esempio la base è rappresentata da nodo=NIL: la con<strong>di</strong>zione dell’if<br />

non è verificata e la procedura non fa nulla (soprattutto termina e non fa altre chiamate a se stessa).<br />

E’ anche importante notare che ogni chiamata ricorsiva impegna la procedura con un problema più semplice<br />

dell’originale: la prima procedura attivata deve stampare l’intero albero; la seconda il sottoalbero <strong>di</strong> sinistra, la terza<br />

il sottoalbero <strong>di</strong> sinistra ancora e così via fino a che, in profon<strong>di</strong>tà, una procedura riceverà un puntatore ad una<br />

foglia; a questo punto avviene l’ultima chiamata alla procedura che riceverà un puntatore NIL: la procedura non fa<br />

nulla e la ‘catena’ si ferma.<br />

NOTA: LA PARTE CHE SEGUE (1 PAGINA) E’ UN APPROFONDIMENTO PIUTTOSTO DIFFICILE DA<br />

SEGUIRE. E’ FACOLTATIVA.<br />

ATTENZIONE: DOPO LA PARTE FACOLTATIVA LA DISPENSA PROSEGUE!!<br />

2 Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it )


Allocazione <strong>di</strong>namica della memoria versione 2.5 Luglio 2003<br />

Esaminiamo in dettaglio cosa accade: la procedura viene invocata una prima volta e riceve il puntatore al primo<br />

nodo. Se questo è NIL (cioè se l’albero è vuoto) si ferma subito, come deve essere. Altrimenti stampa la parte<br />

informativa del primo nodo. Poi invoca se stessa sul figlio <strong>di</strong> sinistra con l’istruzione StampaAlbero(nodo^.sx). A<br />

questo punto le procedure attivate sono due! La prima ad essere cronologicamente chiamata, StampaAlbero(inizio),<br />

che rimane in attesa alla riga 2 aspettando il termine <strong>di</strong> StampaAlbero(nodo^.sx) che rappresenta la seconda<br />

procedura attivata in or<strong>di</strong>ne <strong>di</strong> tempo. E’ molto importante capire che l’istruzione della riga 3,<br />

StampaAlbero(nodo^.dx), della prima procedura attivata non verrà eseguita fino a che non terminerà quella della riga<br />

2.<br />

La seconda procedura attivata inizia la sua esecuzione e stampa la parte informativa del nodo puntato dal parametro<br />

che ha ricevuto (se <strong>di</strong>verso da NIL). Poi effettua a sua volta una chiamata ricorsiva, attivando una terza copia della<br />

procedura StampaAlbero inviando come parametro il puntatore al suo figlio <strong>di</strong> sinistra. E così via. Ecco<br />

schematizzata la sequenza sul <strong>di</strong>segno dell’albero:<br />

5<br />

NIL<br />

4<br />

6<br />

7<br />

NIL<br />

8<br />

3<br />

9<br />

11<br />

NIL<br />

Gestione dello stack per la ricorsione<br />

2<br />

10<br />

12<br />

1<br />

I numeri in<strong>di</strong>cano ovviamente la sequenza cronologica. Solo quelli vicino alle<br />

frecce continue in<strong>di</strong>cano però l’attivazione <strong>di</strong> una nuova ‘incarnazione’ della<br />

procedura. Le frecce tratteggiate in<strong>di</strong>cano il raggiungimento della base della<br />

ricorsione che fa terminare, relativamente a quella parte dell’albero, la catena<br />

delle chiamate. Ad esempio, la procedura attiva al punto 4, dopo aver<br />

stampato la sua parte informativa fa la chiamata ricorsiva passando come<br />

parametro NIL (infatti non ha figli). La procedura chiamata (punto 5) non fa<br />

allora nulla e termina. Il controllo ritorna allora alla procedura del punto 4<br />

(seguite la freccia tratteggiata numerata 6) che è in attesa alla riga 2 del<br />

co<strong>di</strong>ce e che può finalmente fare la seconda chiamata ricorsiva<br />

(corrispondente alla riga 3 del co<strong>di</strong>ce) passando ancora NIL (anche il figlio <strong>di</strong><br />

destra non esiste). La procedura invocata (numerata con 7) termina e<br />

restituisce il controllo ancora una volta a quella numerata con 4. Quest’ultima<br />

ha infine esaurito le sue istruzioni e termina, restituendo il controllo a quella<br />

numerata con 3, che chiama 10, che chiama 11 (con nil) e così via …<br />

Tutte queste ‘incarnazioni’ della stessa procedura con<strong>di</strong>vidono lo stesso co<strong>di</strong>ce. Non si pensi cioè che se il<br />

meccanismo ricorsivo ha bisogno <strong>di</strong> 20 chiamate, in memoria sia presente 20 volte il co<strong>di</strong>ce della procedura. Quello<br />

che accade è che quando un’incarnazione <strong>di</strong> una procedura ad un qualche passo della ricorsione ha bisogno <strong>di</strong><br />

chiamare sé stessa, il suo stato (una sua copia delle variabili locali con i valori fino a quel momento calcolati e un<br />

‘segnalibro’ all’istruzione cui era arrivata) è memorizzato in un area <strong>di</strong> memoria speciale, chiamata stack. Quando<br />

il controllo è restituito, anche molti passi dopo, ad una procedura, il suo stato è ripreso dallo stack ed essa può<br />

continuare dal punto in cui era stata interrotta. Lo stack è una struttura <strong>di</strong>namica LIFO (Last In First Out) in cui<br />

l’ultimo dato inserito è il primo che può essere ripreso. Per fare un paragone pensate ad una pila <strong>di</strong> piatti: l’ultimo<br />

piatto lavato sarà il primo ad essere ripreso per il risciacquo. Questo modo <strong>di</strong> funzionare è essenziale per poter<br />

accedere nell’or<strong>di</strong>ne giusto agli stati delle <strong>di</strong>verse procedure: infatti è quella chiamata per ultima che riavrà il<br />

controllo per prima e continuerò dal punto in cui era stata interrotta. Osservando il grafico precedente: la 3 chiama la<br />

4 (e lo stato della 3 viene salvato); la 4 chiama la 5 (e lo stato della 4 viene salvato); la 5 finisce: deve riprendere la<br />

4, è il suo stato che serve, non quello della 3! La 4, chiamata per ultima, ha bisogno prima della 3 del suo stato: gli<br />

stati devono essere ripristinati nell’or<strong>di</strong>ne inverso in cui sono stati salvati. Uno stack (realizzato come un vettore <strong>di</strong><br />

byte in una zona della RAM) si comporta esattamente in questo modo. Nella simulazione sottostante, proc sta per<br />

Proc 4 chiama proc 5<br />

procedura.<br />

Proc 1 chiama proc 2<br />

Lo stato <strong>di</strong> proc 1<br />

viene salvato sullo<br />

stack<br />

Proc 1 è messa in<br />

attesa<br />

Proc 2 chiama proc 3<br />

Lo stato <strong>di</strong> proc 2<br />

viene salvato sullo<br />

stack<br />

Proc 2 è messa in<br />

attesa<br />

Proc 1 è sempre in<br />

attesa<br />

Stato proc 2<br />

Stato proc 1 Stato proc 1<br />

Proc 3 chiama proc 4<br />

Lo stato <strong>di</strong> proc 3<br />

viene salvato sullo<br />

stack<br />

Proc 3 è messa in<br />

attesa<br />

Proc 1 e 2 sono<br />

sempre in attesa<br />

Stato proc 3<br />

Stato proc 2<br />

Stato proc 1<br />

Lo stato <strong>di</strong> proc 4<br />

viene salvato sullo<br />

stack<br />

Proc 4 è messa in<br />

attesa<br />

Proc 1, 2 e 3 sono<br />

sempre in attesa<br />

Stato proc 4<br />

Stato proc 3<br />

Stato proc 2<br />

Stato proc 1<br />

Proc 5 termina<br />

Lo stato <strong>di</strong> proc 4<br />

viene tolto dallo stack<br />

Proc 4 riprende dalla<br />

riga alla quale si era<br />

interrotta<br />

Proc 1, 2 e 3 sono<br />

sempre in attesa<br />

Stato proc 3<br />

Stato proc 2<br />

Stato proc 1<br />

3 Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it )


RIPRENDE LA PARTE NON FACOLTATIVA DELLA DISPENSA!!<br />

Allocazione <strong>di</strong>namica della memoria versione 2.5 Luglio 2003<br />

In alcuni problemi è proponibile sia una soluzione iterativa (con i cicli) che ricorsiva. Quest’ultima risulterà più<br />

lenta nell’esecuzione e consumerà più memoria (salvare lo stato occupa tempo e spazio) ma più naturale se il<br />

problema ha una natura essa stessa ricorsiva. In questi ultimi casi spesso la soluzione ricorsiva è MOLTO più<br />

semplice da programmare, al punto da risultare l’unica strada percorribile… La ricorsione si è rivelata uno<br />

strumento assai utile nel campo dell’Intelligenza Artificiale (IA) dove strutture complesse come gli alberi sono<br />

all’or<strong>di</strong>ne del giorno per rappresentare la conoscenza ed il percorso decisionale per la soluzione <strong>di</strong> un problema.<br />

QUALCHE ESEMPIO DI SOTTOPROGRAMMA RICORSIVO<br />

Per definire un algoritmo ricorsivo dobbiamo sempre in<strong>di</strong>viduare:<br />

Esempio 1: calcolo del fattoriale.<br />

In<strong>di</strong>chiamo con Fatt(N) il fattoriale <strong>di</strong> un numero intero >=0. E’ il prodotto <strong>di</strong> tutti gli interi da 1 a N. Ad esempio<br />

Fatt(6) = 6 * 5 * 4 * 3 * 2 * 1. Per definizione si stabilisce che Fatt(0) è 1. E’ una funzione assai usata in<br />

matematica: ad esempio nel calcolo delle probabilità (combinazioni, <strong>di</strong>sposizioni semplici o con ripetizione,<br />

coefficienti binomiali ecc.).<br />

L’obiettivo è darne una definizione ricorsiva (esprimere un fattoriale usando ancora un fattoriale ma calcolato su un<br />

valore più semplice). Si ragiona così: qual è il caso più semplice <strong>di</strong> calcolo del fattoriale? Quando N è 0: infatti<br />

possiamo subito affermare senza bisogno <strong>di</strong> fare calcoli che il risultato è 1. E se dobbiamo calcolare Fatt(4)? Il<br />

risultato imme<strong>di</strong>ato non lo possiamo calcolare ma possiamo <strong>di</strong>re che è 4 * Fatt(3). Infatti Fatt(3)=3*2*1 e quin<strong>di</strong><br />

4*Fatt(3) equivale a 4 * 3*2*1, il calcolo <strong>di</strong> partenza.<br />

Abbiamo allora ricondotto un caso più complesso, Fatt(4), al calcolo <strong>di</strong> un caso più semplice, Fatt(3). Come <strong>di</strong>re:<br />

non so calcolare <strong>di</strong>rettamente Fatt(4) ma se riesco a calcolare Fatt(3) risalgo attraverso una moltiplicazione a Fatt(4).<br />

Fatt(3) è ancora troppo ‘<strong>di</strong>fficile’ da calcolare ma lo si riconduce a Fatt(2): Fatt(3) = 3 * Fatt(2). Fatt(2) = 2 *<br />

Fatt(1). Fatt(1) = 1 * Fatt(0). Ma Fatt(0) so che vale uno senza bisogno <strong>di</strong> ricalcolare nessun altro fattoriale: ho<br />

raggiunto il caso base, la base della ricorsione che termina la catena delle chiamate ricorsive!<br />

Schematicamente<br />

1 se N =0<br />

Fatt(N) =<br />

La base della ricorsione: è il caso più semplice, quello per il quale sappiamo subito ‘calcolare’ il<br />

risultato. Quello a cui il meccanismo ricorsivo tenta <strong>di</strong> ricondursi un poco alla volta, passo dopo<br />

passo.<br />

Il passo ricorsivo: quando non siamo <strong>di</strong> fronte al caso più semplice dobbiamo tentare <strong>di</strong><br />

esprimerlo attraverso una ‘formula’ che richiama lo stesso sottoprogramma che stiamo scrivendo<br />

ma con argomenti semplificati che ci avvicinano al caso che rappresenta la base della ricorsione<br />

N * Fatt(N-1) se N>0<br />

function Fatt(N: integer): real;<br />

begin<br />

if N=0 then<br />

Fatt:=1<br />

else<br />

Fatt:= N * Fatt(N-1)<br />

end;<br />

NOTA: come valore restituito ho scelto un real in quanto il<br />

calcolo del fattoriale molto rapidamente porta al<br />

superamento della capacità <strong>di</strong> una variabile integer.<br />

4 Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it )


Esempio 2: calcolo <strong>di</strong> x y con x ed y interi positivi.<br />

In<strong>di</strong>chiamo con XallaY(x,y) il sottoprogramma.<br />

Allocazione <strong>di</strong>namica della memoria versione 2.5 Luglio 2003<br />

L’obiettivo è darne una definizione ricorsiva (esprimere una potenza usando ancora una potenza ma calcolata su un<br />

valore più semplice). Si ragiona così: qual è il caso più semplice ? Quando si chiede <strong>di</strong> elevare un numero alla zero.<br />

In questo caso infatti per definizione il risultato è 1. Un numero elevato alla prima può essere calcolato come il<br />

numero stesso moltiplicato la sua potenza zero. Infatti x 1 = x * x 0 = x * 1 = x. Abbiamo allora ricondotto un caso<br />

più complesso al calcolo <strong>di</strong> uno <strong>di</strong> cui sappiamo subito il risultato (x 0 ). Ora che sappiamo calcolare un numero<br />

elevato alla prima siamo in grado <strong>di</strong> calcolare i quadrati, Infatti un numero al quadrato può essere calcolato come il<br />

numero stesso moltiplicato per la sua potenza prima: x 2 = x * x 1 . E così via: x 3 = x * x 2 = x * x * x 1 = x * x * x * x 0 ;<br />

x 4 = x * x 3 ecc.<br />

Abbiamo allora ricondotto in generale un caso più complesso, x y , al calcolo <strong>di</strong> un caso più semplice, x * x y-1 .<br />

Schematicamente<br />

XallaY(x,y) =<br />

Esempio 3: calcolo <strong>di</strong> x*y con x ed y interi, y0.<br />

In<strong>di</strong>chiamo con XperY(x,y) il sottoprogramma.<br />

L’obiettivo è darne una definizione ricorsiva (esprimere un prodotto usando ancora un prodotto ma calcolato su un<br />

valore più semplice). Si ragiona così: qual è il caso più semplice ? Quando si chiede <strong>di</strong> moltiplicare un numero per<br />

1: il risultato è il numero stesso. Se invece dobbiamo calcolare un numero per 2 possiamo esprimere il calcolo come<br />

il numero sommato al prodotto del numero per 1: x * 2 = x + (x*1). Di nuovo abbiamo ricondotto un prodotto<br />

complesso al calcolo <strong>di</strong> un prodotto più semplice <strong>di</strong> cui sappiamo subito il risultato (x*1). E così via: x * 3 = x + x*2<br />

= x + x + x*1; x * 4 = x + x * 3 ecc.<br />

Schematicamente<br />

XperY(x,y) =<br />

1 se y =0<br />

x * XallaY(x,y-1) se y>0<br />

x se y =1<br />

x + XperY(x,y-1) se y>0<br />

function XallaY(x,y: integer): real;<br />

begin<br />

if y=0 then<br />

XallaY:=1<br />

else<br />

XallaY:= x * XallaY(x,y-1)<br />

end;<br />

NOTA: come valore restituito ho scelto un real in quanto il<br />

calcolo <strong>di</strong> una potenza molto rapidamente può portare al<br />

superamento della capacità <strong>di</strong> una variabile integer.<br />

function XperY(x,y: integer): real;<br />

begin<br />

if y=1 then<br />

XperY:=x<br />

else<br />

XperY:= x + XperY(x,y-1)<br />

end;<br />

NOTA: come valore restituito ho scelto un real in quanto il<br />

calcolo <strong>di</strong> prodotto può portare abbastanza facilmente al<br />

superamento della capacità <strong>di</strong> una variabile integer.<br />

Nota: matematicamente un prodotto è in effetti una somma ripetuta … x * 4 = x + x + x + x che è esattamente<br />

quanto fa la ricorsione!<br />

5 Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it )


Esempio 4: calcolo <strong>di</strong> x+y con x ed y interi.<br />

In<strong>di</strong>chiamo con XpiuY(x,y) il sottoprogramma.<br />

Allocazione <strong>di</strong>namica della memoria versione 2.5 Luglio 2003<br />

L’obiettivo è darne una definizione ricorsiva (esprimere una somma usando ancora una somma ma calcolata su un<br />

valore più semplice). Si ragiona così: qual è il caso più semplice ? Quando si chiede <strong>di</strong> sommare zero ad un numero:<br />

il risultato è il numero stesso. Se invece dobbiamo sommare 1 possiamo esprimere il calcolo come 1 più il risultato<br />

della somma tra il numero e 0; infatti: x + 1 = 1 + (x+0). Di nuovo abbiamo ricondotto un prodotto complesso al<br />

calcolo <strong>di</strong> un prodotto più semplice <strong>di</strong> cui sappiamo subito il risultato (x+0). E così via: x + 4 = 1 + (x+3) = 1 + 1 +<br />

(x+2) = 1 + 1 + 1 + (x+1) = 1 + 1 + 1 + 1 + (x+0) = 1 + 1 + 1 + 1 + x.<br />

Schematicamente<br />

XpiuY(x,y) =<br />

Esempio 5: calcolo della serie <strong>di</strong> Fibonacci (esempio <strong>di</strong> doppia chiamata ricorsiva)<br />

La serie <strong>di</strong> Fibonacci è la seguente: 0 1 1 2 3 5 8 13 21 34 55 … dove ogni elemento è la somma dei due precedenti<br />

(ad eccezione del primo e del secondo che sono per definizione 0 ed 1).<br />

In<strong>di</strong>chiamo con Fib(N) l’ennesimo numero della sequenza (si parte dalla posizione 0!). Ad esempio Fib(0)=0;<br />

Fib(1)=1; 2; Fib(7)=13.<br />

L’obiettivo è darne una definizione ricorsiva: un numero verrà appunto espresso come somma dei 2 numeri <strong>di</strong><br />

Fibonacci che lo precedono, fino ai primi due che sono fissati per definizione. Fib(4) = Fib(3) + Fib(2) =<br />

Fib(2)+Fib(1) + Fib(1)+Fib(0) = Fib(1) + Fib(0) + Fib(1) + Fib(1) + Fib(0) = 3.<br />

Schematicamente<br />

Fib(N) =<br />

x se y =0<br />

1 + XpiuY(x,y-1) se y>0<br />

0 se N =0, 1 se N=1<br />

Esercizi suggeriti:<br />

• calcolo <strong>di</strong> A-B<br />

• calcolo <strong>di</strong> A / B<br />

• inversione <strong>di</strong> una stringa<br />

Fib(N-2)+Fib(N-1) se N>1<br />

procedure XperY(x,y: integer): integer;<br />

begin<br />

if y=0 then<br />

XpiuY:=x<br />

else<br />

XpiuY:= 1 + XpiuY(x,y-1)<br />

end;<br />

procedure Fib(N: integer): integer;<br />

begin<br />

if N=0 then<br />

Fib:=0<br />

else<br />

if N=1 then<br />

Fib:=1<br />

else<br />

Fib:=Fib(N-2) + Fib(N-1)<br />

end;<br />

6 Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it )


LIMITI DELLA MEMORIA ALLOCATA STATICAMENTE<br />

LIMITE 1<br />

Consideriamo la seguente <strong>di</strong>chiarazione:<br />

var<br />

voti: array[1..10] of integer;<br />

Allocazione <strong>di</strong>namica della memoria versione 2.5 Luglio 2003<br />

Il programmatore sta in<strong>di</strong>cando al compilatore che intende usare un vettore <strong>di</strong> 10 interi. Il compilatore <strong>di</strong><br />

conseguenza, nella traduzione in linguaggio macchina, pre<strong>di</strong>spone (alloca) la quantità <strong>di</strong> RAM (byte) necessaria.<br />

Con la maggior parte dei linguaggi <strong>di</strong> programmazione, quando il programma è mandato in esecuzione non c’è modo<br />

<strong>di</strong> mutare questa situazione (aumentare ad esempio il numero <strong>di</strong> elementi nel vettore). L’unico modo per aggiungere<br />

elementi al vettore è quello <strong>di</strong> mo<strong>di</strong>ficare il sorgente e ricompilare il programma.<br />

Immaginate la scena: un programmatore ‘tirchio’ <strong>di</strong>mensiona un vettore con troppo pochi elementi; un cliente, a<br />

mille chilometri <strong>di</strong> <strong>di</strong>stanza, usa il programma in modo da esaurire il vettore: è costretto ad attendere l’intervento del<br />

programmatore, con grave <strong>di</strong>sagio (ed arrabbiatura).<br />

Esagerare con il numero <strong>di</strong> elementi del vettore potrebbe comportare uno spreco <strong>di</strong> RAM inaccettabile (se<br />

me<strong>di</strong>amente il numero <strong>di</strong> elementi usati del vettore è sensibilmente inferiore al massimo). Decidere invece <strong>di</strong><br />

memorizzare i dati su <strong>di</strong>sco farebbe crollare le prestazioni.<br />

Naturalmente non saremo sempre così ‘sfortunati’: i mesi <strong>di</strong> un anno sono sempre do<strong>di</strong>ci, ed i giorni al massimo<br />

366; gli alunni <strong>di</strong> una classe <strong>di</strong>fficilmente supereranno i quaranta, ecc. Diciamo che il problema è particolarmente<br />

evidente in quelle situazioni in cui il programmatore non può fare una stima esatta o quasi delle necessità.<br />

Pensate, ad esempio, al gioco degli scacchi: è impossibile prevedere quante ‘mosse’ durerà la partita. Quanti<br />

elementi dovrà allora avere il vettore che memorizza le mosse? Se poi volessimo impostare un livello <strong>di</strong> gioco<br />

<strong>di</strong>fficile il computer dovrà allora valutare molte posizioni per ogni mossa. Ma qualcuno preferirà un livello <strong>di</strong> gioco<br />

per principianti. Alcuni software permettono ad<strong>di</strong>rittura <strong>di</strong> giocare più partite in contemporanea. Quanta memoria<br />

RAM serve, quind,i per far funzionare il programma? Che cosa dovrebbe <strong>di</strong>chiarare il programmatore nella sezione<br />

VAR ?<br />

LIMITE 2<br />

Lo spazio allocato staticamente in RAM non può essere<br />

mo<strong>di</strong>ficato (aumentato o <strong>di</strong>minuito) durante l’esecuzione del<br />

programma.<br />

Immaginate ora <strong>di</strong> avere un elenco <strong>di</strong> nomi in un vettore e <strong>di</strong> averlo or<strong>di</strong>nato alfabeticamente (operazione ‘costosa’).<br />

Immaginiamo anche che il vettore abbia dei ‘posti liberi’ in fondo. Ogni ulteriore inserimento rappresenta<br />

un’operazione piuttosto onerosa se vogliamo mantenere or<strong>di</strong>nato l’elenco. Infatti non basta aggiungere in fondo il<br />

nuovo nome: non è detto, infatti, che l’elenco rimanga or<strong>di</strong>nato. E’ assai più probabile che il giusto posto per il<br />

nuovo nome sia in una posizione interme<strong>di</strong>a. Sarà allora necessario spostare verso destra <strong>di</strong> una posizione una parte<br />

degli elementi per fare spazio al nuovo. Come si <strong>di</strong>ceva, l’inserimento in queste con<strong>di</strong>zioni è un’operazione piuttosto<br />

onerosa ed accettabile solo se gli inserimenti a vettore già or<strong>di</strong>nato sono rari: purtroppo sono molteplici le<br />

applicazioni in cui è vero io il contrario … Non va meglio nel caso si debba eliminare un elemento interme<strong>di</strong>o<br />

(perché?).<br />

Una struttura allocata staticamente è inefficiente per<br />

frequenti operazioni <strong>di</strong> inserimento ed eliminazione.<br />

7 Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it )


LIMITE 3<br />

Allocazione <strong>di</strong>namica della memoria versione 2.5 Luglio 2003<br />

Date un’occhiata alla struttura dati <strong>di</strong>segnata qui a lato (si chiama albero). Essa è adatta per<br />

rappresentare situazioni in cui esiste una gerarchia tra gli elementi da memorizzare (struttura<br />

<strong>di</strong>rectory, organigramma aziendale, mosse e contromosse in un gioco ecc.). Beh, penso sarete<br />

d’accordo nell’affermare che non è facile rappresentarla usando gli array. E che <strong>di</strong>re in merito<br />

alla rappresentazione <strong>di</strong> una rete stradale: in questo caso non c’è ad<strong>di</strong>rittura nessun or<strong>di</strong>ne tra<br />

i no<strong>di</strong> da collegare!<br />

ALLOCAZIONE DINAMICA DELLA MEMORIA<br />

Una struttura allocata staticamente è<br />

inadeguata per rappresentare strutture non<br />

lineari.<br />

Il programmatore può invece scegliere, nei casi in cui risulta conveniente, <strong>di</strong> gestire la memoria <strong>di</strong>namicamente.<br />

Significa che avrà a <strong>di</strong>sposizione delle istruzioni con cui comandare durante l’esecuzione del programma la<br />

allocazione <strong>di</strong> altro spazio per le variabili ma anche per restituire lo spazio che non serve più.<br />

UN’ APPLICAZIONE CONCRETA<br />

Immaginiamo che ci sia stato commissionato un programma per monitorare in tempo reale gli ingressi allo SMAU.<br />

In particolare si vogliono inserire le località <strong>di</strong> provenienza dei visitatori in un elenco che deve essere consultabile<br />

con la massima velocità per poter effettuare statistiche in tempo reale. Da subito viene scartata l’idea <strong>di</strong> utilizzare i<br />

file: troppo lenti. Si utilizzerà la RAM cercando comunque <strong>di</strong> non ‘sprecarla’ per non compromettere le prestazioni<br />

del <strong>sistema</strong>. Si decide <strong>di</strong> organizzare le informazioni in questo modo: blocchi (vettori) da 20 stringhe ciascuno fino<br />

ad un massimo <strong>di</strong> 1000 blocchi. Fissiamo anche la lunghezza <strong>di</strong> ogni stringa: 50 caratteri. All’inizio si comincia con<br />

un solo blocco (vettore): esaurito il primo si chiederà memoria per il secondo e così via ...<br />

Rifletti ...<br />

Capita la <strong>di</strong>fferenza? Non verranno pre<strong>di</strong>sposti da subito 1000 vettori da 20 stringhe! Verrà dato spazio ai vettori in<br />

RAM progressivamente, man mano che se ne avvertirà l’esigenza ...<br />

L’uso della memoria <strong>di</strong>namica consentirà <strong>di</strong> occupare all’inizio solo la memoria necessaria per le 20 stringhe del primo<br />

blocco e <strong>di</strong> occuparne dell’altra solo quando veramente necessario. E’ facile convincersi che in questo modo la<br />

memoria eventualmente sprecata è quella corrispondente a 19 stringhe, nell’ipotesi <strong>di</strong> usare solo la prima stringa<br />

dell’ultimo blocco allocato.<br />

Rifletti ...<br />

Pur non essendo ottimale (rimane il limite delle 20*1000 blocchi=20000 stringhe) in questo modo riusciamo a gestire<br />

una GROSSA quantità <strong>di</strong> stringhe ottimizzando l’uso della memoria. In seguito estenderemo la tecnica per gestire<br />

elenchi ‘infinitamente’ lunghi (ad esaurimento RAM ... o spazio su <strong>di</strong>sco visto che tutti i moderni sistemi operativi<br />

supportano la gestione virtuale della RAM usando il <strong>di</strong>sco come estensione ‘lenta’ <strong>di</strong> quest’ultima)<br />

8 Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it )


Allocazione <strong>di</strong>namica della memoria versione 2.5 Luglio 2003<br />

Proce<strong>di</strong>amo con gradualità. Prima preoccupiamoci della gestione <strong>di</strong>namica <strong>di</strong> un singolo vettore <strong>di</strong> 20 stringhe, cioè <strong>di</strong><br />

un solo blocco. Poi estenderemo la gestione a 1000 blocchi. Come si fa a chiedere al Pascal <strong>di</strong> allocare spazio per un<br />

vettore <strong>di</strong> 20 stringhe? Ed il vettore si userà come tutti gli altri vettori? Un passo alla volta ...<br />

Iniziamo con il definire un tipo che rappresenta un singolo blocco:<br />

type<br />

vettore = array[1..20] of string[50]; (* il punto <strong>di</strong> partenza è un normale vettore <strong>di</strong> stringhe *)<br />

pun = ^vettore; (* il tipo blocco ‘punta’ ad oggetti <strong>di</strong> tipo vettore; si parla <strong>di</strong> tipo puntatore *)<br />

(* il carattere ^ è fondamentale per in<strong>di</strong>care che si tratta <strong>di</strong> un puntatore! *)<br />

var<br />

blocco: pun (* variabile puntatore che memorizzerà l’in<strong>di</strong>rizzo <strong>di</strong> un blocco quando sarà creato *)<br />

La variabile blocco è <strong>di</strong> tipo pun, cioè (risalendo la catena delle <strong>di</strong>chiarazioni) è un puntatore ad oggetti <strong>di</strong> tipo<br />

vettore (si scrive ^vettore). Al momento non sta puntando a nulla perché il programmatore non ha ancora chiesto<br />

l’allocazione <strong>di</strong> memoria per il blocco <strong>di</strong> stringhe.<br />

NOTA BENE: blocco non può essere usata come una variabile qualsiasi. Ad esempio producono un errore le seguenti<br />

istruzioni:<br />

NO !!! writeln(blocco) o readln(blocco) o blocco:=13; NO !!!<br />

Un puntatore è una variabile speciale. Serve a contenere in<strong>di</strong>rizzi <strong>di</strong> oggetti creati nella RAM. Quin<strong>di</strong> prima deve<br />

essere creato un oggetto e poi si memorizza nel puntatore dove inizia in memoria quell’oggetto. Usando poi il<br />

puntatore potremo scrivere dati in quel blocco <strong>di</strong> memoria, recuperarli, <strong>di</strong>struggere il blocco <strong>di</strong> memoria quando non<br />

servirà più.<br />

APPROFONDIMENTO<br />

La <strong>di</strong>mensione effettiva <strong>di</strong> un puntatore <strong>di</strong>pende da come il processore gestisce gli in<strong>di</strong>rizzi <strong>di</strong> memoria e dalla<br />

capacità del compilatore <strong>di</strong> sfruttare appieno le possibilità offerte da un processore. Il Turbo Pascal è in grado <strong>di</strong><br />

gestire sia in<strong>di</strong>rizzi <strong>di</strong> 2 byte, cioè 16 bit (memoria <strong>di</strong> al massimo 2^16 – 1 = 64Kbyte, sia in<strong>di</strong>rizzi <strong>di</strong> 4 byte (32bit,<br />

memoria segmentata DOS) in<strong>di</strong>candoli con una coppia numero <strong>di</strong> segmento (64K possibili segmenti) + posizione nel<br />

segmento (da 0 a 64K).<br />

Per chiarire in modo inequivocabile che un puntatore non sta in<strong>di</strong>viduando nessun oggetto in memoria lo si inizializza<br />

con un valore speciale: NIL (nulla).<br />

blocco := NIL;<br />

In questo modo il programmatore può verificare se il puntatore sta puntando ad un oggetto valido (uno degli errori<br />

più pericolosi che si possano commettere è infatti l’uso <strong>di</strong> un puntatore che non sta puntando a nulla):<br />

if bloccoNIL then … uso ‘sicuro’ del puntatore …<br />

9 Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it )


L’istruzione new: allocazione della memoria<br />

Torniamo all’esempio precedente e continuiamo dal punto in cui ci eravamo interrotti<br />

type<br />

vettore = array[1..20] of string[50];<br />

pun = ^vettore;<br />

var<br />

blocco: pun<br />

Allocazione <strong>di</strong>namica della memoria versione 2.5 Luglio 2003<br />

E’ arrivato il momento <strong>di</strong> creare un blocco (vettore) e <strong>di</strong> memorizzare il suo in<strong>di</strong>rizzo nella variabile blocco.<br />

new(blocco);<br />

Letteralmente: crea un nuovo oggetto del tipo in<strong>di</strong>viduato dalla variabile puntatore. Poiché blocco è un puntatore ad<br />

oggetti <strong>di</strong> tipo vettore e questi ultimi sono array <strong>di</strong>20 stringhe, è appunto un vettore <strong>di</strong> 20 stringhe che viene creato<br />

in memoria. Poiché ogni stringa occupa 50 carratteri, il blocco complessivo <strong>di</strong> byte occupati è 20*50=1000 byte.<br />

Ecco quello che accade più in dettaglio: il <strong>sistema</strong> operativo, se la memoria non è esaurita, riserva un blocco <strong>di</strong> tanti<br />

byte consecutivi quanti sono quelle necessari a memorizzare 20 stringhe da 50 caratteri (circa 1000 byte). Non è<br />

dato sapere a priori dove si troverà nella RAM questo blocco ma ciò che conta è sapere l’in<strong>di</strong>rizzo del suo primo byte:<br />

è il suo in<strong>di</strong>rizzo ad essere memorizzato nella variabile puntatore blocco. Considerando la situazione rappresentata<br />

nel <strong>di</strong>segno soprastante, ho immaginato che il <strong>sistema</strong> operativo abbia trovato posto a partire dal byte all’in<strong>di</strong>rizzo<br />

1436: è questo il valore che viene memorizzato nella variabile blocco. Se non c’è spazio il <strong>sistema</strong> operativo<br />

restituisce NIL.<br />

E’ molto importante capire che al momento della scrittura del programma il vettore non esiste ancora. E neppure alla<br />

partenza del programma: è necessario il comando esplicito new. NOTA: la new deve essere comandata una sola volta<br />

per tutto il vettore. Nel caso il vettore non servisse più sarà possibile restituire la memoria al <strong>sistema</strong> operativo e<br />

riutilizzare il puntatore blocco per un altro vettore (subito o in un secondo momento).<br />

Utilizzo della memoria allocata<br />

blocco<br />

1436<br />

Il valore <strong>di</strong> bocco è lin<strong>di</strong>rizzo del primo<br />

byte allocato; i byte non contengono<br />

ancora niente.<br />

Immaginando <strong>di</strong> aver già creato l’oggetto vettore, proviamo a memorizzare una stringa nel suo primo elemento:<br />

blocco^[1]:='Sono la prima stringa del blocco';<br />

New(un_PBlocco)<br />

RAM<br />

...<br />

...<br />

1436<br />

10 Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it )<br />

1434<br />

1435


Attenzione a non usare blocco come se fosse esso stesso il vettore <strong>di</strong> stringhe:<br />

blocco[1]:=’una stringa’ NO!!<br />

Allocazione <strong>di</strong>namica della memoria versione 2.5 Luglio 2003<br />

Il puntatore infatti non è l’oggetto ma l’in<strong>di</strong>rizzo dell’oggetto. Per passare dall’in<strong>di</strong>rizzo all’oggetto è necessario<br />

in<strong>di</strong>care il simbolo ^ dopo il nome del puntatore. In progressione: blocco è l’in<strong>di</strong>rizzo, blocco^ è l’oggetto puntato,<br />

cioè il vettore e blocco^[1] è il suo primo elemento<br />

Ed ora un ciclo for per valorizzare a stringa vuota i restanti elementi<br />

for i:=2 to 20 do<br />

blocco^[i] := ‘’;<br />

Ed un altro ciclo for che visualizza tutte le stringhe:<br />

for i:=1 to 20 do<br />

writeln( blocco^[i] );<br />

L’istruzione <strong>di</strong>spose: restituzione della memoria<br />

Quando il vettore ha esaurito la sua utilità possiamo restituire con il comando <strong>di</strong>spose la memoria al <strong>sistema</strong><br />

operativo, cioè deallocarla:<br />

<strong>di</strong>spose(blocco); blocco:=nil;<br />

end.<br />

NOTA BENE: dopo la <strong>di</strong>spose il puntatore perde <strong>di</strong> significato. E’ un GRAVE errore tentare <strong>di</strong> usare un puntatore<br />

deallocato. Purtroppo, il Turbo Pascal non mette automaticamente a nil un puntatore deallocato (con altri linguaggi<br />

accade) ed è allora buona pratica farlo personalmente.<br />

E’ altrettanto GRAVE deallocare un puntatore che non punta a niente (NIL): <strong>di</strong> solito va in crash il programma.<br />

Rifletti ...<br />

Il fatto che la memoria possa essere restituita quando non serve più rappresenta già un grosso vantaggio rispetto<br />

all’uso <strong>di</strong> un normale vettore <strong>di</strong> stringhe. Il prezzo da pagare è una certa complicazione delle operazioni e, cosa ben<br />

più tangibile, un maggiore rischio <strong>di</strong> commettere errori gravi.<br />

In effetti se la quantità <strong>di</strong> memoria non rappresenta un problema conviene sovra<strong>di</strong>mensionare gli array standard ed<br />

evitare complicazioni. D’altra parte in molte situazioni la quantità <strong>di</strong> memoria è il problema principale (provate a<br />

pensare se un programma <strong>di</strong> grafica tipo PhotoShop dovesse allocare decine <strong>di</strong> megabyte <strong>di</strong> RAM per niente per ogni<br />

immagine ad alta risoluzione che poi non sarà veramente caricata in memoria...).<br />

ATTENZIONE: SE AVETE CAPITO GLI ARGOMENTI FIN QUI ESPOSTI AVETE RAGGIUNTO COMPLETAMENTE GLI<br />

OBIETTIVI!!<br />

IL SEGUITO PUO’ ESSERE CONSIDERATO UN APPROFONDIMENTO, NON ALLA PORTATA DI TUTTI.<br />

11 Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it )


Allocazione <strong>di</strong>namica della memoria versione 2.5 Luglio 2003<br />

Dopo le prime 20 stringhe avremmo esaurito lo spazio, nè più nè meno che con un vettore classico. La soluzione<br />

consiste nell’usare un vettore <strong>di</strong> puntatori, <strong>di</strong> 1000 puntatori per l’esattezza.<br />

Intanto è necessario <strong>di</strong>chiarare il vettore <strong>di</strong> puntatori:<br />

type<br />

P1000Blocchi=array[1..1000] of blocco;<br />

var<br />

Localita: P1000Blocchi;<br />

Ora abbiamo a <strong>di</strong>sposizione il tipo vettore <strong>di</strong> 1000 puntatori a blocchi <strong>di</strong> 20 stringhe ciascuno e relativa variabile.<br />

(* inizializzo il vettore <strong>di</strong> puntatori *)<br />

for i:=1 to 1000 do<br />

Localita[i]:=nil;<br />

E’ utile rappresentare graficamente la situazione (solo con i primi elementi del vettore <strong>di</strong> puntatori):<br />

(* chie<strong>di</strong>amo spazio per due vettori: il primo ed il quinto, senza un motivo particolare, giusto per prova *)<br />

new( Localita[1] );<br />

new( Localita[5] );<br />

localita[1]<br />

localita[5]<br />

NIL<br />

NIL<br />

NIL<br />

NIL<br />

localita<br />

Localita[1]^<br />

Localita[5]^<br />

Solo due puntatori hanno a questo punto collegati due blocchi. Tutte le caselle dei vettori contengono valori casuali.<br />

(* inizializzo le stringhe dei vettori, ma solo per i puntatori a blocco <strong>di</strong>versi da nil, ovviamente *)<br />

for i:=1 to 1000 do<br />

if Localita[i]nil then<br />

for j:=1 to 20 do<br />

Localita[i]^[j]:='';<br />

NIL<br />

NIL<br />

NIL<br />

NIL<br />

NIL<br />

NIL<br />

Ora tutte le caselle dei vettori contengono la stringa nulla. Notate il controllo if localita[i]NIL che seleziona solo gli<br />

elementi del vettore <strong>di</strong> puntatori che hanno collegato veramente un vettore <strong>di</strong> stringhe (il primo ed il quinto<br />

elemento, nel nostro caso).<br />

NIL<br />

12 Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it )


Allocazione <strong>di</strong>namica della memoria versione 2.5 Luglio 2003<br />

Localita è il nome <strong>di</strong> un vettore. Localita[i] accede all’i-mo elemento <strong>di</strong> quel vettore (un puntatore). Localita[i]^<br />

accede all’elemento puntato che è a sua volta un vettore <strong>di</strong> stringhe. Localita[i]^ [j] è il j-mo elemento <strong>di</strong> questo<br />

vettore, cioè la j-ma delle 20 stringhe <strong>di</strong> quel vettore. Vi gira la testa?<br />

(* memorizzo una stringa nel quarto elemento del primo vettore *)<br />

Localita[1]^[4]:='le cose si complicano ...';<br />

(* ed una nel terzo elemento del quinto vettore *)<br />

Localita[5]^[3]:='ma non piu'' <strong>di</strong> tanto!';<br />

localita[1]<br />

localita[5]<br />

writeln('---------------------------'); writeln('Stringhe nel vettore n. 1: '); writeln('---------------------------');<br />

for i:=1 to 20 do<br />

writeln( (Localita[1]^)[i] );<br />

readln;<br />

writeln('---------------------------'); writeln('Stringhe nel vettore n. 5: '); writeln('---------------------------');<br />

for i:=1 to 20 do<br />

writeln( Localita[5]^[i] );<br />

readln;<br />

(* deallochiamo i vettori <strong>di</strong> stringhe *)<br />

for i:=1 to 1000 do<br />

if localita[i]nil then<br />

begin<br />

<strong>di</strong>spose(localita[i]);<br />

localita[i]:=nil<br />

end<br />

end.<br />

Notate , <strong>di</strong> nuovo, il controllo sul puntatore a NIL prima <strong>di</strong> deallocare. Deallocare con <strong>di</strong>spose un puntatore a NIL è<br />

un GRAVE errore e <strong>di</strong> solito porta al crash del programma.<br />

Rifletti ...<br />

NIL<br />

NIL<br />

NIL<br />

NIL<br />

Localita[1]^<br />

Localita[5]^<br />

Quanta memoria può far risparmiare la gestione <strong>di</strong>namica? A volte moltissima ! Se ad esempio si scoprisse che<br />

me<strong>di</strong>amente solo metà delle stringhe sono necessarie, la gestione statica staticamente userebbe sempre e comunque<br />

1.000.000 <strong>di</strong> byte (20.000 stringhe x 50 caratteri) mentre quella <strong>di</strong>namica circa la metà: 520.000 (2 byte a puntatore<br />

x 1000 puntatori + 10.000 stringhe x 50 caratteri) !!<br />

Risparmi a parte la gestione <strong>di</strong>namica restituisce memoria nei momenti in cui non serve e migliora le prestazioni del<br />

<strong>sistema</strong> operativo e, quin<strong>di</strong>, dell’elaboratore.<br />

QUI TERMINA L’APPROFONDIMENTO.<br />

Localita[1]^[4]<br />

Le cose si complicano ..<br />

Localita[5]^[3]<br />

ma non più <strong>di</strong> tanto!<br />

13 Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it )


Gli errori più comuni<br />

Allocazione <strong>di</strong>namica della memoria versione 2.5 Luglio 2003<br />

• Usare un puntatore prima <strong>di</strong> averlo ‘agganciato’ con l’istruzione new ad un blocco <strong>di</strong> memoria valido<br />

• Usare un puntatore <strong>di</strong>menticandosi il simbolo <strong>di</strong> puntatore (^):<br />

se p è un puntatore ad un intero:<br />

new(p);<br />

SI’: p:=3; NO: p^:=3<br />

• Usare un puntatore dopo averlo ‘sganciato’ con l’istruzione <strong>di</strong>spose dal blocco <strong>di</strong> memoria cui puntava:<br />

se p è un puntatore ad un intero:<br />

new(p);<br />

p^:=3;<br />

<strong>di</strong>spose(p); ora p non punta più a nulla e non è più utilizzabile<br />

writeln( p^);<br />

NO !! p non ha più significato: l’area <strong>di</strong> memoria cui puntava è stata restituita con <strong>di</strong>spose al <strong>sistema</strong><br />

operativo che potrebbe utilizzarla per altri scopi; usare l’in<strong>di</strong>rizzo non più valido contenuto in p potrebbe<br />

portare a conseguenze anche molto gravi per la stabilità del <strong>sistema</strong>.<br />

• Sbagliare la posizione del simbolo <strong>di</strong> puntatore (^) con vettori o record. Ecco una rassegna <strong>di</strong> casi:<br />

TYPE<br />

p= ^string;<br />

vett : array[1..10] of p;<br />

rec=record<br />

co<strong>di</strong>ce: integer;<br />

pun: p<br />

end;<br />

vetRec=array[1..10] of rec;<br />

recVet=record<br />

co<strong>di</strong>ce: integer;<br />

v: array[1..10] of p<br />

end;<br />

VAR<br />

vpun: vett;<br />

unRec: rec;<br />

vrec: vetRec;<br />

unRecVet: recVet;<br />

14 Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it )


I record con il Turbo Pascal versione 1.3 Luglio 2003<br />

SI’: new( vpun[3] ); SI’: vpun[3]^:=’ciao’; NO: new(vpun)<br />

NO: vpun^[3]:=’ciao’; (giusto se vpun fosse un puntatore a vettore <strong>di</strong> stringhe e non un vettore <strong>di</strong> puntatori)<br />

SI’: new(unRec.pun); SI’: unRec.pun^:=’ciao’;<br />

NO: unRec^.pun:=’ciao’; (giusto se unRec fosse un puntatore ad un record contenente una stringa pun)<br />

SI’: new( vrec[2].pun ); SI’: vrec[2].pun^:=’ciao’;<br />

SI’: new (unRecVet.v[2]); SI’: unRecVet.v[2]^:=’ciao’;<br />

In generale è sufficiente ricordarsi <strong>di</strong> in<strong>di</strong>care il simbolo <strong>di</strong> puntatore (^) dopo il nome del puntatore (con la<br />

particolarità che v[i] e non ‘v’ è il nome del puntatore quando ‘v’ è un vettore.<br />

LISTE SEMPLICI<br />

Pur con i vantaggi visti, un limite alle stringhe gestibili nell’esempio precedente esiste comunque: ventimila. Ve<strong>di</strong>amo<br />

<strong>di</strong> superarlo. Realizzeremo una concatenazione <strong>di</strong> elementi (lista) che potrà essere espansa senza limiti (RAM<br />

permettendo).<br />

Nel <strong>di</strong>segno P rappresenta il puntatore che in<strong>di</strong>vidua l’ingresso alla lista; ogni elemento (nodo) della lista ha una<br />

parte informatica (INFO) che può essere qualsiasi cosa: un semplice intero, una stringa, un vettore ecc. ogni nodo è<br />

collegato al successivo con un puntatore. Il punto <strong>di</strong> ingresso in<strong>di</strong>vidua la testa della lista e all’altro capo troviamo<br />

invece la cosiddetta coda della lista.<br />

Una lista ha anche altri vantaggi, oltre a quello <strong>di</strong> poter ‘crescere’ indefinitamente: se gli elementi sono in or<strong>di</strong>ne<br />

(alfabetico, per esempio) l’aggiunta <strong>di</strong> un nuovo elemento, mantenendo l’or<strong>di</strong>namento, è questione <strong>di</strong> pochissime<br />

operazioni. Anche l’eliminazione <strong>di</strong> un elemento ha un costo estremamente inferiore rispetto ai vettori.<br />

BASTA ARRAY? Il fatto che la lista sia ‘forte’ proprio dove gli array sono ‘deboli’ non significa che debba essere d’ora<br />

in poi preferita ad essi (anzi!). Le liste hanno purtroppo delle controin<strong>di</strong>cazioni, che sono comuni a tutte le strutture<br />

realizzate con la memoria <strong>di</strong>namica: , ma è ancora troppo presto per parlarne…<br />

Graficamente rappresenteremo le liste come segue:<br />

P<br />

P<br />

P<br />

testa della lista<br />

NIL<br />

Quando la lista è vuota. P, il puntatore che rappresenta il suo punto <strong>di</strong> ingresso, vale NIL. Si<br />

arriva in questa situazione o perché ancora nessun elemento è stato inserito alla lista o perché<br />

sono stati tutti eliminati.<br />

NIL<br />

La lista come esempio <strong>di</strong> ADT<br />

P<br />

Quando la lista è formata da un solo elemento, puntato da P. L’elemento è costituito<br />

logicamente da due parti: quella informativa ed un puntatore all’elemento che<br />

eventualmente segue nella lista (vale NIL se si è alla fine)<br />

NIL Una lista in uno stato interme<strong>di</strong>o.<br />

La lista è un esempio <strong>di</strong> ADT (Abstract Data Type, Tipo <strong>di</strong> Dato Astratto): nuovi tipi definiti dal programmatore per i<br />

quali è definito un ben preciso insieme <strong>di</strong> operazioni.<br />

Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 1<br />

NIL<br />

coda della lista


I record con il Turbo Pascal versione 1.3 Luglio 2003<br />

Ai programmatori non interessa come sia realizzato internamente l’ADT (potrebbe essere costruito usando vettori<br />

piuttosto che record per memorizzare i dati, usare un metodo <strong>di</strong> or<strong>di</strong>namento piuttosto che un altro ecc.). Al<br />

programmatore interessa solo sapere come ottenere i servizi <strong>di</strong> un ADT cioè quali coman<strong>di</strong> può inviare ad un<br />

esemplare <strong>di</strong> un ADT e quali risultati ottiene.<br />

Per fare un esempio, è come se il Pascal offrisse solo il tipo integer, con le operazioni che tutti conoscete. Il<br />

programmatore potrebbe aggiungere l’ADT real o string con tutte le operazioni note. Chi userà gli ADT non si<br />

preoccuperà <strong>di</strong> come i real e le string sono memorizzate e gestite: gli basta sapere come creare variabili <strong>di</strong> quei tipi,<br />

come assegnare loro valori, fare operazioni con esse, come visualizzarle sullo schermo ecc. E non sarà possibile<br />

chiedere operazioni non previste per quell’ADT (moltiplicare due stringhe, per esempio).<br />

Per l’ADT lista semplice potremmo definire almeno le seguenti operazioni: aggiunta <strong>di</strong> un elemento all’inizio o alla fine<br />

della lista (o in punto interme<strong>di</strong>o), aggiunta <strong>di</strong> un elemento mantenendo la lista in or<strong>di</strong>ne, eliminazione <strong>di</strong> un<br />

elemento, estrazione (l’elemento tolto non viene <strong>di</strong>strutto ma resta a <strong>di</strong>sposizione del programmatore), ricerca <strong>di</strong> un<br />

elemento, visita <strong>di</strong> tutti gli elementi <strong>di</strong> una lista, controllo per sapere se la lista è vuota, <strong>di</strong>struzione della lista<br />

Prima <strong>di</strong> scrivere le operazioni ve<strong>di</strong>amo come rappresentare gli elementi della lista: un elemento deve contenere due<br />

cose, la parte informativa ed il puntatore all’elemento che lo segue nella lista. Sono due informazioni completamente<br />

<strong>di</strong>verse, per cui la struttura dati più naturale da usare è il record.<br />

Verrebbe spontaneo scrivere la <strong>di</strong>chiarazione del record nel modo seguente (immaginando che la parte informativa<br />

sia un semplice intero, realizzando così una lista <strong>di</strong> integer …):<br />

type<br />

elemento=record<br />

inf: integer; (* informazione *)<br />

pun: ^elemento (* puntatore *)<br />

end;<br />

Da un punto <strong>di</strong> vista logico è corretto: pun è il puntatore all’elemento successivo che è in effetti <strong>di</strong> tipo elemento, per<br />

cui un puntatore a questo tipo si scrive proprio ^elemento. Purtroppo il compilatore non conosce ancora esattamente<br />

cosa sia elemento quando <strong>di</strong>chiariamo pun perché stiamo proprio definendo elemento e non abbiamo ancora finito!<br />

Capite? Ci stiamo mordendo la coda: per definire elemento dovremmo già sapere cosa sia elemento!<br />

Il Pascal offre una scappatoia: prima della definizione <strong>di</strong> elemento possiamo definire un tipo puntatore ad esso. Si<br />

chiama <strong>di</strong>chiarazione forward (in avanti). E’ un’eccezione: il compilatore sa che dovrebbe trovare la <strong>di</strong>chiarazione <strong>di</strong><br />

cosa sia elemento dopo:<br />

type<br />

puntatore= ^elemento; (* OK: il compilatore si aspetta <strong>di</strong> trovare dopo cosa sia elemento *)<br />

elemento=record<br />

inf: integer; (* informazione *)<br />

pun: puntatore (* il compilatore sa già cos’è il tipo puntatore *)<br />

end;<br />

Una volta definiti i tipi è necessario <strong>di</strong>chiarare una variabile che rappresenta l’inizio della lista:<br />

var<br />

InizioLista: puntatore;<br />

NO !!<br />

Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 2


InizioLista<br />

Proviamo a creare un nodo isolato <strong>di</strong> questa lista:<br />

begin<br />

(*creiamo il primo nodo*)<br />

new(InizioLista);<br />

InizioLista^.inf:=12;<br />

InizioLista^.pun:=nil;<br />

Creiamo un secondo elemento (per il momento non collegato al primo):<br />

var<br />

InizioLista: puntatore;<br />

NuovoNodo: puntatore:<br />

…<br />

new(NuovoNodo);<br />

NuovoNodo^.inf:=24;<br />

NuovoNodo^.pun:=nil;<br />

I record con il Turbo Pascal versione 1.3 Luglio 2003<br />

NOTA BENE Non è possibile usare ancora InizioLista per creare il secondo nodo: perderemmo il primo:<br />

new(InizioLista);<br />

InizioLista ^.inf:=24;<br />

InizioLista ^.pun:=nil<br />

NIL<br />

Da qualche parte in memoria i dati del primo elemento ci sono ancora: avendo però cambiato l’in<strong>di</strong>rizzo memorizzato<br />

nel puntatore con la seconda new, sono <strong>di</strong>ventati irraggiungibili!! InizioLista porta ora al secondo nodo, perdendo la<br />

possibilità <strong>di</strong> usare il primo.<br />

Ora concateniamo i due no<strong>di</strong>, agganciando il secondo al primo:<br />

InizioLista^.pun:=NuovoNodo;<br />

?????<br />

InizioLista è un puntatore. InizioLista^ è l’oggetto puntato, un record:<br />

per accedere ai suoi campi informativi si usa la notazione ‘punto’.<br />

InizioLista^.inf è quin<strong>di</strong> la sua parte informativa, InizioLista^.pun quella<br />

puntatore. Dopo queste istruzioni possiamo rappresentare graficamente<br />

la lista così:<br />

InizioLista<br />

InizioLista<br />

NIL<br />

Nella casella puntatore del primo nodo (la casella dopo il 12) viene memorizzato l’in<strong>di</strong>rizzo del secondo (NuovoNodo)<br />

NIL<br />

NuovoNodo<br />

InizioLista<br />

NuovoNodo<br />

NIL<br />

Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 3<br />

NIL<br />

NIL


I record con il Turbo Pascal versione 1.3 Luglio 2003<br />

In effetti il secondo nodo è raggiunto in questo momento da due puntatori: quello usato per crearlo e quello del nodo<br />

che lo precede (realizzando così la catena).<br />

Dopo aver visto i meccanismi fondamentali, siete pronti per l’analisi <strong>di</strong> un programma abbastanza completo <strong>di</strong><br />

gestione delle liste semplici. Naturalmente tutte le operazioni verranno implementate come sottoprogrammi! Infatti<br />

sarebbe molto sconveniente scrivere un co<strong>di</strong>ce apposito per aggiungere il terzo elemento e poi il quarto ecc.<br />

Per brevità non viene mai effettuato il controllo sul buon esito delle new.<br />

Program liste_semplici;<br />

uses crt;<br />

type<br />

puntatore= ^elemento;<br />

elemento=record<br />

inf: integer; (* informazione *)<br />

pun: puntatore (* puntatore *)<br />

end;<br />

STAMPA DELLA LISTA (IN EFFETTI E’ UNA VISITA COMPLETA DI TUTTI GLI ELEMENTI DI UNA LISTA)<br />

procedure stampa_lista(p: puntatore);<br />

begin<br />

writeln('----');<br />

while pnil do<br />

begin<br />

write(p^.inf,' ');<br />

p:=p^.pun<br />

end;<br />

writeln('----');<br />

end;<br />

p è il puntatore all’inizio della lista;<br />

p:=p^.pun è l’istruzione classica con cui si passa da un nodo al<br />

successivo. Se infatti p punta ad un certo nodo, la scrittura p^.pun<br />

corrisponde al contenuto della sua casella puntatore, cioè l’in<strong>di</strong>rizzo<br />

del nodo successivo:<br />

NB: p è passato per valore; possiamo quin<strong>di</strong> mo<strong>di</strong>ficarlo senza timore<br />

<strong>di</strong> perdere l’inizio della lista…<br />

INSERIMENTO IN TESTA DI UN NUOVO ELEMENTO: riceve il puntatore 'p' ad una lista ed un valore da inserire<br />

procedure inserisci_in_testa(var p:puntatore; valore:integer);<br />

var nuovo_nodo: puntatore;<br />

begin<br />

new(nuovo_nodo); nuovo_nodo^.inf:=valore;<br />

nuovo_nodo^.pun:=p;<br />

p:=nuovo_nodo;<br />

NIL<br />

end;<br />

P<br />

E’ importante prima salvare il valore <strong>di</strong> p<br />

nella casella puntatore del nuovo nodo:<br />

<strong>di</strong>versamente (invertendo cioè l’or<strong>di</strong>ne dei<br />

due assegnamenti) si perderebbe il valore<br />

originale <strong>di</strong> p cioè del primo nodo, rendendo<br />

irraggiungibile l’intera lista!!<br />

2<br />

p:=nuovo_nodo<br />

1<br />

nuovo_nodo^.pun:=p<br />

P<br />

nuovo_nodo<br />

P^.pun<br />

P^.pun^.pun<br />

Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 4


I record con il Turbo Pascal versione 1.3 Luglio 2003<br />

(*INSERIMENTO IN CODA DI UN NUOVO ELEMENTO: riceve il puntatore 'p' ad una lista ed un valore da inserire *)<br />

procedure inserisci_in_coda(var p:puntatore; valore:integer);<br />

var nuovo_nodo,fine_lista: puntatore;<br />

begin<br />

new(nuovo_nodo); nuovo_nodo^.inf:=valore;<br />

nuovo_nodo^.pun:=nil;<br />

(* devo <strong>di</strong>stinguere il caso lista vuota *)<br />

if pnil then (* lista non vuota *)<br />

begin<br />

fine_lista:=p; (* parto dall'inizio ... *)<br />

while fine_lista^.punnil do (* ... ed avanzo fino alla fine ... *)<br />

fine_lista:=fine_lista^.pun;<br />

fine_lista^.pun:=nuovo_nodo<br />

end<br />

else<br />

p:=nuovo_nodo<br />

end;<br />

Anche se sarà poi necessario <strong>di</strong>fferenziare il caso lista vuota,<br />

queste istruzioni vanno bene in entrambi i casi.<br />

INSERIMENTO DI UN NUOVO ELEMENTO IN UNA POSIZIONE INTERMEDIA: riceve il puntatore 'p' al nodo che<br />

precederà quello da inserire<br />

procedure inserisci_in_mezzo(var p:puntatore; valore:integer);<br />

var nuovo_nodo: puntatore;<br />

begin<br />

new(nuovo_nodo); nuovo_nodo^.inf:=valore;<br />

if p=nil then (* la lista e' vuota *)<br />

begin<br />

nuovo_nodo^.pun:=nil; p:=nuovo_nodo<br />

end<br />

else<br />

begin<br />

nuovo_nodo^.pun:=p^.pun; (* aggancio successivo *)<br />

p^.pun:=nuovo_nodo (* mi faccio puntare da precedente *)<br />

end<br />

end;<br />

P<br />

Il <strong>di</strong>segno qui a lato fa evidentemente<br />

riferimento al caso lista non vuota…<br />

Il <strong>di</strong>segno qui a lato si riferisce al caso lista<br />

non vuota…<br />

P<br />

2 p^.pun:=nuovo_nodo<br />

fine_lista:=fine_lista^.pun …<br />

nuovo_nodo<br />

fine_lista<br />

nuovo_nodo 1 nuovo_nodo^.pun:=p^.pun<br />

Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 5<br />

NIL<br />

NIL


RICERCA IN UNA LISTA NON ORDINATA<br />

I record con il Turbo Pascal versione 1.3 Luglio 2003<br />

La procedura non si limita a restituire un puntatore all’elemento eventualmente trovato ma anche il puntatore a<br />

quello che eventualmente lo precede. Ho scelto questo comportamento perché per alcune operazioni, una volta<br />

trovato un elemento, è necessario <strong>di</strong>sporre anche del puntatore all’elemento precedente (ad esempio nel caso<br />

volessimo cancellare l’elemento trovato)<br />

procedure cerca_non_or<strong>di</strong>nata(p: puntatore; valore: integer; var corrente, precedente: puntatore);<br />

begin<br />

precedente:=nil; corrente:=nil;<br />

if pnil then<br />

begin<br />

corrente:=p;<br />

while (corrente^.infvalore) and (corrente^.punnil) do<br />

begin<br />

precedente:=corrente;<br />

precedente corrente<br />

corrente:=corrente^.pun<br />

end;<br />

if corrente^.infvalore then<br />

begin<br />

precedente:=nil;<br />

corrente:=nil<br />

end<br />

end<br />

end;<br />

RICERCA IN UNA LISTA ORDINATA<br />

La ricerca è effettuata su una lista non or<strong>di</strong>nata. Se valore viene trovato, in<br />

corrente viene restituito il puntatore al nodo cercato, in precedente quello al<br />

nodo che lo precedete (nil se il nodo cercato è il primo). Se il valore non<br />

viene trovato sia corrente che precedente sono messi a nil).<br />

Questa versione della ricerca lavora su una lista or<strong>di</strong>nata (<strong>di</strong>ciamo in modo crescente). Trovato un elemento il cui<br />

valore supera quello che si sta cercando non ha più senso continuare la ricerca … quelli seguenti saranno per forza<br />

tutti maggiori!<br />

procedure cerca_or<strong>di</strong>nata(p: puntatore; valore: integer; var corrente, precedente: puntatore);<br />

begin precedente:=nil; corrente:=nil;<br />

if pnil then<br />

begin<br />

corrente:=p;<br />

while (corrente^.inf


ELIMINAZIONE DI UN ELEMENTO<br />

I record con il Turbo Pascal versione 1.3 Luglio 2003<br />

Questa procedura invoca quella <strong>di</strong> ricerca per ottenere il puntatore al nodo da cancellare ed a quello che lo precede<br />

(se esiste). Sono infatti queste le informazioni necessarie per una corretta eliminazione.<br />

procedure elimina(var p: puntatore; valore: integer);<br />

var corrente,precedente: puntatore;<br />

Cancellazione in mezzo/coda alla lista.<br />

begin<br />

cerca_non_or<strong>di</strong>nata(p,valore,corrente,precedente);<br />

if correntenil then<br />

precedente corrente<br />

begin<br />

if precedentenil then<br />

(* eliminazione in mezzo o in coda *)<br />

precedente^.pun:=corrente^.pun<br />

P<br />

NIL<br />

else (* eliminazione in testa *)<br />

p:=corrente^.pun;<br />

<strong>di</strong>spose(corrente)<br />

precedente<br />

corrente<br />

end<br />

end;<br />

NIL<br />

P<br />

Cancellazione in testa alla lista.<br />

INSERIMENTO IN UNA LISTA ORDINATA<br />

Il nuovo elemento deve essere inserito mantenendo la lista or<strong>di</strong>nata.<br />

procedure inserisci_in_or<strong>di</strong>ne(var p:puntatore; valore:integer);<br />

var nuovo_nodo,precedente,corrente: puntatore;<br />

begin<br />

new(nuovo_nodo); nuovo_nodo^.inf:=valore;<br />

if p=nil then<br />

begin<br />

Inserisce nuovi valori mantenendo un<br />

nuovo_nodo^.pun:=nil; p:=nuovo_nodo<br />

or<strong>di</strong>namento crescente. La tecnica usata è la<br />

stessa del sort per inserzione (ricerca del giusto<br />

end<br />

punto <strong>di</strong> inserimento) usata per i vettori ma<br />

else<br />

grazie ai puntatori è evitato tutto il<br />

if valore


ESTRAZIONE DI UN ELEMENTO<br />

I record con il Turbo Pascal versione 1.3 Luglio 2003<br />

Estrai si <strong>di</strong>fferenzia dalla elimina perché ‘sgancia’ il nodo che contiene l’informazione cercata ma non lo <strong>di</strong>strugge:<br />

restituisce il puntatore al nodo ed è poi responsabilità del chiamante liberare la memoria.<br />

function estrai(var p: puntatore; valore: integer): puntatore;<br />

var corrente,precedente: puntatore;<br />

begin<br />

cerca_non_or<strong>di</strong>nata(p,valore,corrente,precedente);<br />

if correntenil then<br />

begin<br />

if precedentenil then (* eliminazione in mezzo o in coda *)<br />

precedente^.pun:=corrente^.pun<br />

else (* eliminazione in testa *)<br />

p:=corrente^.pun<br />

end;<br />

estrai:=corrente; (* eventualmente, nil *) end;<br />

Ora che padroneggiate (J ) i meccanismi <strong>di</strong> gestione delle liste siete in grado <strong>di</strong> capire gli svantaggi della gestione<br />

<strong>di</strong>namica della RAM (dei vantaggi abbiamo già <strong>di</strong>scusso…):<br />

• Gli algoritmi sono più <strong>di</strong>fficili ed è più facile commettere errori<br />

• Non è possibile un accesso casuale agli elementi: per usare quello in posizione ‘N’ è necessario scorrere gli<br />

‘N-1’ che precedono con un ciclo. Confrontate questa tecnica con l’imme<strong>di</strong>atezza degli array: vett[N]<br />

• A parità <strong>di</strong> tipo e <strong>di</strong> numero <strong>di</strong> elementi usa più memoria, quella usata per memorizzare i puntatori!! (ma in<br />

alcune situazioni globalmente abbiamo comunque un grosso risparmio: ricordate l’esercizio sullo SMAU e le<br />

20.000 stringhe??)<br />

Autore: Fabrizio Camuso (email: camuso@camuso.it sito web: www.camuso.it ) Pag. 8

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!