PROGETTO DEMOCRITOS Introduzione alla programmazione in

Transcript

PROGETTO DEMOCRITOS Introduzione alla programmazione in
PROGETTO DEMOCRITOS
Introduzione alla programmazione in Delphi
di P. Delise – I.T.C. Carli – Trieste
Indice generale
Il corso.............................................................................................................................................1
L'automa...........................................................................................................................................2
Linguaggi.........................................................................................................................................3
Il linguaggio algebrico.....................................................................................................................4
Le regole grammaticali dell'algebra.................................................................................................5
I dialetti dell'algebra: la torre di Babele...........................................................................................6
La classificazione dei linguaggi di programmazione......................................................................7
I linguaggi imperativi......................................................................................................................7
La descrizione dei dati.....................................................................................................................8
Il teorema di Iacopini - Bohm..........................................................................................................9
I comandi.......................................................................................................................................10
Operazioni aritmetico - logiche.....................................................................................................12
Esempi...........................................................................................................................................13
La descrizione in un linguaggio di progetto..................................................................................13
Lo stesso programma in Pascal......................................................................................................13
Qualche altro semplice programma in Pascal................................................................................14
La soluzione di un'equazione difficile...........................................................................................18
Delphi.............................................................................................................................................21
Gli oggetti......................................................................................................................................21
L'ambiente integrato di sviluppo (IDE) di Delphi.........................................................................22
Un primo programma.....................................................................................................................22
Alcuni oggetti................................................................................................................................23
L'equazione di secondo grado in Delphi........................................................................................25
Calcolo di π con il metodo Montecarlo.........................................................................................27
Aggiungiamo un grafico................................................................................................................28
Un programma con oggetti nuovi: la calcolatrice per angoli........................................................31
Il corso
Con il corso che segue si vuole
•
•
•
•
Avvicinare gli allievi ad una visione concettuale del calcolatore e dei linguaggi di programmazione.
Presentare agli allievi che non ne hanno ancora conoscenza le caratteristiche di base del
linguaggio pascal
Presentare agli allievi le caratteristiche di base del linguaggio Delphi
Avviare gli allievi a muoversi nell'ambiente Delphi per risolvere semplici problemi.
I programmi vengono presentati in questi appunti in una versione finita, ma in realtà
verranno costruiti passo passo e talvolta, soprattutto per i programmi in Delphi, per affinamenti
successivi. La dispensa offre pertanto la foto finale (o di alcuni momenti intermedi) e non il filmato
dello sviluppo del software stesso. I programmi sono stati tutti testati, ma non si esclude che nei vari passaggi, legati all'impaginazione, non si sia introdotto qualche errore.
Questi appunti sono stati scritti usando il programma OpenOffice.org 1.0.1.
P. Delise – I.T.C. Carli – Trieste (2003) – Dal calcolatore a Delphi
pag.1
L'automa
Definiremo il calcolatore elettronico come un automa a stati finiti e programmabile.
Esistono definizioni rigorose di automa sulle quali non ci addentreremo. Cercheremo solo di
astrarre dall'immagine concreta di un calcolatore, quella di una macchina concettuale che ci aiuti a
comprendere meglio ciò con cui abbiamo a che fare.
Il calcolatore, la lavatrice, la scuola intesa come istituzione e così via possono essere
descritti in modo abbastanza simile. Possiamo immaginarli come un qualcosa che si trova in un
certo stato iniziale che può essere controllato dall’esterno (mediante un linguaggio opportuno di
controllo), riceve dei dati in ingresso e, in conseguenza di ciò, modifica il suo stato interno e produce qualcosa che chiameremo uscita. Consideriamo, ad esempio, la lavatrice: essa si trova in uno
stato, per esempio aperta, collegata all’acqua ed alla corrente e con il programma all’inizio del ciclo
di lavaggio; riceve dei dati (la biancheria da lavare); riceve anche un controllo dall’esterno (la chiusura dello sportello, la pressione del tasto di avvio, l'impostazione del programma di lavaggio) e dopo un certo intervallo di tempo finito ha un'uscita (produce acqua sporca e biancheria pulita).
Provate a schematizzare la scuola con le stesse regole: qual è l’ingresso, qual è il
controllo, qual è l’uscita1?
Anche il calcolatore è un automa. Può ricevere una sequenza molto complessa di
controlli iniziali, espressi mediante un linguaggio imperativo (vale a dire fatto di ordini), che ne
modificano le capacità apparenti di risolvere problemi. Il calcolatore è, inoltre, in grado di memorizzare al suo interno queste istruzioni e può produrre in uscita, tra le altre cose, nuove istruzioni da
eseguire. Questa sua complessiva capacità va sotto il nome di programmabilità2.
Nella programmabilità gioca un ruolo fondamentale il linguaggio: l’automa programmabile, ed il calcolatore in particolare, è in grado di intendere degli ordini dati secondo un
certo linguaggio fatto di ordini e di controlli dello stato. Tipici comandi potrebbero essere, per un
calcolatore, leggi, scrivi, somma,... ma anche se .... allora .... altrimenti e così via.
Le istruzioni del programma vengono eseguite dall’automa seguendo un ordine sequenziale. Vale dire che, di regola, le istruzioni sono eseguite nell'ordine in cui sono state fornite
all'automa. Questo ordine viene modificato dalle istruzioni del tipo se... allora... che indicano la
capacità dell'automa di interrogare il suo stato interno e di modificare il proprio comportamento in
base allo stato stesso (ad esempio se il numero x è maggiore di 0 allora calcolane la radice, altrimenti scrivi un messaggio di errore.).
Oggi siamo più smaliziati e disincantati, ma ricordo ancora lo stupore che 30 anni fa
coglieva chi vedeva da vicino un calcolatore e la sensazione che, facilmente, l’utente inesperto poteva provare di avere davanti a sé una macchina intelligente (e la delusione che provava quando si
rendeva conto che intelligente non era). Questa sensazione di intelligenza, che il calcolatore ci dà,
non è dovuta, come forse potremmo essere indotti a pensare, alla capacità di decidere (se... allora...
altrimenti...). Molte macchine dell’uomo hanno un’analoga capacità: la serratura, ad esempio, decide: se la chiave è quella giusta, se è chiusa e se si gira nel verso giusto la chiave, si apre, altrimenti
resta nello stato in cui si trova, ma nessuno di noi attribuisce alla serratura la benché minima intelligenza.
La sensazione di intelligenza che l’automa calcolatore ci può dare deriva fondamentalmente dalla sua programmabilità, dalla capacità cioè di fare cose diverse a seconda delle
istruzioni che riceve. Un’altra caratteristica che contraddistingue il calcolatore da molte delle altre
macchine con le quali abbiamo a che fare è la complessità. Da questo punto di vista l'uomo è molto,
ma molto, più complesso di un calcolatore e molti sostengono, secondo una visione meccanicistica,
1
2
L’ingresso potrebbe essere costituito dagli alunni che si iscrivono, il controllo è dato dalle norme che regolano la
scuola e dalle istanze della società; l’uscita è data dagli alunni diplomati e da quelli che abbandonano. Rispetto alla
lavatrice la scuola è caratterizzata da un meccanismo di retroazione, vale a dire gli alunni (diplomati e non) fanno
parte della società che, sulla base delle esperienze acquisite, attraverso la definizione di nuove norme, incide
sull'operare della scuola stessa.
E' una situazione analoga a quella di un automa uomo che esegue le istruzioni per leggere un manuale che gli insegna come usare una telecamera e, una volta lette, esegue le nuove istruzioni apprese.
P. Delise – I.T.C. Carli – Trieste (2003) – Dal calcolatore a Delphi
pag.2
che persino il libero arbitrio di cui l’uomo si sente dotato sia una conseguenza della sua grandissima
complessità. Queste considerazioni, però, sulle quali sarebbe giusto e bello soffermarsi a riflettere,
esulano dal programma di questo corso e vi vengono proposte come spunto per ulteriori, eventuali,
approfondimenti personali.
Per restare, però, ancora nell’ambito filosofico, ricordiamo che il calcolatore, nella definizione iniziale, è stato detto essere un automa a stati finiti. Esso, cioè, si può collocare in un numero finito di stati (nei calcolatori moderni è un numero molto grande, ma sempre finito). Noi
abbiamo, invece, la sensazione che l’uomo, che può anche essere pensato come un automa, sia in
realtà a stati infiniti. Questa sensazione di infinitezza dei possibili stati dell'uomo è dovuta, se vogliamo restare in un ambito puramente meccanicistico, alla diversa tecnologia usata per realizzarlo
e porta a distinguere tra quello che è un sistema digitale (a stati finiti) da un sistema analogico (a
stati infiniti e che varia con continuità)3. Vedremo ancora, più avanti, che la finitezza ritornerà a
farsi sentire nella definizione fondamentale di algoritmo.
Esistono, tuttavia, molti altri automi a stati infiniti, che hanno dato origine ad una vasta
famiglia di macchine, tra le quali ricorderemo, oltre agli orologi a lancette ed al regolo calcolatore, i
calcolatori analogici che, essendo molto costosi e di difficile realizzazione, non hanno avuto il
successo che forse avrebbero meritato. Oggi, nella nostra civiltà, è l'automa a stati finiti (o digitale)
che si sta imponendo come fondamentale e digitale potremmo definire la nostra civiltà.
Linguaggi
Abbiamo visto che per controllare un automa programmabile abbiamo bisogno di un
linguaggio, intelligibile all’automa e a chi lo controlla, con il quale il controllore descrive le operazioni da compiere. Il linguaggio è una specie di protocollo di comunicazione tra i due.
Un linguaggio è uno strumento con il quale si sostituiscono simboli ad idee ed oggetti; esso ci
consente di descrivere determinate relazioni esistenti
nella realtà operando direttamente sui simboli anziché
sulla realtà stessa. Alla base di ogni linguaggio c’è un
alfabeto, vale a dire un insieme di segni primitivi4 usati
per costruire i messaggi del linguaggio. Combinando
tra loro i segni primitivi otteniamo delle stringhe di segni che devono essere composte rispettando determinate regole grammaticali. Una stringa rispettosa di tali
regole può avere un significato o meno. Perché un automa possa eseguire una stringa, essa (significante) deve avere una corrispondenza nella realtà (significato).
Figura 1
Lo studio della corrispondenza tra significanti e significati va sotto il nome di semantica.
Nel paragrafo precedente si è già fatto presente che per controllare un automa abbiamo
bisogno di un linguaggio di tipo imperativo, vale a dire un linguaggio che ordini alla macchina, e
che abbia il periodo ipotetico (se si verifica un certa situazione allora fai questo, altrimenti fai quest’altro).
Soffermiamoci sull'alfabeto: se facciamo riferimento all'esperienza comune del
linguaggio, l'alfabeto è senza dubbio l'alfabeto tradizionale al quale, però, dovremo aggiungere altri
3
4
È strana la posizione dell’uomo: attratto sempre dall’infinito, si scontra sempre con la finitezza. Ha passato il XIX
secolo a definire bene l’infinito e a sviluppare il concetto di continuità per poi scoprire che alcune grandezze
(l’energia per esempio, ma forse anche lo spazio ed il tempo) che lui reputava variassero con continuità, non lo facevano.
Si tenga presente che il termine segno è abbastanza generico: è un segno il suono che io emetto nel pronunciare la
lettera a, ma potrebbe essere un segno un particolare movimento (si provi a dire a gesti il concetto “tu sei scemo”),
la pressione di un interruttore o l’emissione di una particolare sostanza odorosa, o addirittura una parte della struttura molecolare della sostanza odorosa (pare che le formiche comunichino tra loro così).
P. Delise – I.T.C. Carli – Trieste (2003) – Dal calcolatore a Delphi
pag.3
simboli come i numeri, le interpunzioni, i simboli delle operazioni e così via. Nei linguaggi
informatici potremo aggiungere all'alfabeto anche alcune parole che appaiono come simboli.
Mentre, ad esempio, nel linguaggio C un blocco di istruzioni inizia con il simbolo { e lo chiude con
}, nel Pascal esso inizia con la parola begin e lo termina con la parola end. Anche begin ed end potrebbero essere considerate come facenti parte dei simboli dell'alfabeto, come anche le parole div,
mod ed in generale, tutte quelle che vengono dette parole riservate del linguaggio.
I simboli dell'alfabeto possono essere combinati tra di loro secondo certe regole
grammaticali per costruire delle stringhe ben formate. La sintassi che, nei linguaggi di programmazione, regola la costruzione di queste stringhe è estremamente rigida e definisce un linguaggio privo
di ambiguità, al contrario del linguaggio corrente degli uomini. I diversi linguaggi per calcolatori
hanno grammatiche diverse; così la scritta a+=b; è grammaticalmente corretta in C, ma non in Pascal; a:=a+b; è corretta in Pascal ma non in C e così via5.
La grammatica, però, non basta. Un testo grammaticalmente corretto deve avere un significato, ed il significato attribuito ad esso da chi lo trasmette deve essere lo stesso che ad esso
viene attribuito da chi lo riceve; si dice, in termini sintetici, che deve esistere una corrispondenza tra
significante e significato6. Capita, purtroppo, molto spesso che nei programmi scritti per i calcolatori non esista la corrispondenza, per errori del programmatore, tra ciò che il programmatore voleva
ottenere e ciò che il calcolatore interpreta ed esegue. Gli errori di tipo semantico sono i più difficili
da trovare e richiedono, spesso, un paziente lavoro di analisi del testo del programma e di esecuzione controllata dello stesso. Il lavoro di individuazione degli errori semantici va sotto il nome di
debugging7.
Il linguaggio algebrico
Anche se per voi l'algebra è stata spesso un esercizio in cui, al di fuori da ogni contesto, si doveva passare da una certa espressione ad un'altra, in realtà la funzione dell'algebra è quella
di descrivere in maniera chiara e non ambigua come risolvere un problema particolare. Vediamo di
dare una descrizione formale di questo linguaggio studiandone preliminarmente l'alfabeto. Come
già visto prima, l'alfabeto è l'insieme dei simboli usati dal linguaggio. Nel linguaggio dell'algebra i
simboli vengono usati, quasi sempre, con significati particolari. Pertanto li abbiamo raggruppati per
categorie. Esistono, inoltre, altri simboli che non sono riportati.
SIMBOLI NUMERICI o CIFRE: 0 1 2 3 4 5 6 7 8 9
SIMBOLI LETTERALI: a b c d ... z [A B C ... α β γ ... π ... ]
Nei simboli letterali ho chiuso tra parentesi quadre i simboli A B C ... α β γ ... π ... in quanto
usati più di rado, a parte il simbolo π, e solo in condizioni particolari.
OPERATORI e SEPARATORI: . , + - · :  √ ... {[( )]} ± ! | xy xy ... % ∂ '
Abbiamo indicato con xy l'operazione di innalzamento a potenza per la quale non esiste un
simbolo vero e proprio, ma solo una tecnica di scrittura; lo stesso dicasi per l'indicizzazione xy.
Alcuni linguaggi di programmazione usano, per l'innalzamento a potenza, l'operatore ^ ed altri il
simbolo **, ma non il linguaggio algebrico tradizionale.
PREDICATI: = > < ≥ ≤ ...
Con il termine predicati indico dei simboli che rappresentano delle azioni, dei verbi: è uguale, è
maggiore, ...
5
6
7
Si faccia attenzione che il punto e virgola fa parte della stringa ben formata e non è un segno di interpunzione del testo.
Si pensi, ad esempio al diverso significato della frase "MI PARVE NERO" per un Italiano e per un Romano antico
(per chi non avesse molta dimestichezza con il latino, vuol dire "Mio piccolo Nerone".
La parola debug per indicare l'operazione di individuazione degli errori fa parte delle leggende informatiche. Si
narra che in uno dei primissimi calcolatori, che un giorno non funzionava, dopo lunghe indagini si fosse trovato che
la causa era un nido che degli insetti avevano fatti al suo interno. Dalla parola bug, insetto in inglese, derivò debug
che si potrebbe tradurre spulciare ma che nessuno fa.
P. Delise – I.T.C. Carli – Trieste (2003) – Dal calcolatore a Delphi
pag.4
NOMI: sen cos, tan log ...
Le regole grammaticali dell'algebra
Un'espressione algebrica è costituita, di solito, da numeri o simboli letterali, che
sottintendono dei numeri, legati tra di loro da operatori e, quasi sempre, da un predicato. Gli operatori possono essere, a seconda del loro modo di operare, prefissi, infissi o postfissi. È un operatore
prefisso, ad esempio il segno - che si premette ad un numero o un simbolo rappresentante un numero; esso precede l'entità su cui opera: -a invita a considerare l'opposto di a; è prefisso anche il
simbolo √: √x invita a considerare quel numero che moltiplicato per se stesso da x. Sono, infine,
prefissi i nomi delle funzioni: sen, cos, log, … Sono operatori infissi quelli che rappresentano, ad
esempio, le quattro operazioni, l'innalzamento a potenza, e così via. Sono operatori postfissi % e !.
Quest'ultimo, forse non lo avete incontrato; la scrittura 5! significa 1⋅2⋅3⋅4⋅5, vale a dire 120.
L'algebra se ne è impossessata, naturalmente, anche per il calcolo letterale e n! Significa il prodotto
dei primi n numeri. Anche l'apostrofo è un operatore postfisso (di derivazione).
Gli operatori infissi sono binari, operano cioè su due elementi, quello che sta alla loro
destra e quello che sta alla loro sinistra. Gli operatori prefissi o postfissi sono unari, operano cioè su
un elemento solo, quello che sta, rispettivamente, alla loro destra (prefissi) o alla loro sinistra
(postfissi).
Vale la pena notare come ci siano operatori che hanno più significati a seconda del
modo in cui vengono usati. Il segno meno può essere infisso o prefisso. Il primo indica
un'operazione tra due numeri, il secondo prende il numero opposto a quello che sta alla sua destra.
Anche il simbolo di radice √ può essere solo prefisso ed ha il significato di radice quadrata o infisso n√x ed allora significa una radice di ordine diverso. Quando un operatore assume significati diversi se usato in contesti diversi si parla, talvolta, di sovraccarico (overloading) di significati. Poiché nel linguaggio algebrico non ci può essere posto per ambiguità, deve essere sempre possibile
comprendere di quale operatore si tratti.
Gli operatori, nel linguaggio algebrico tradizionale, sono caratterizzati da precedenze.
Voi sapete già che nell'operazione 1+2⋅3−4:5 prima dovete eseguire le moltiplicazioni e le divisioni
e poi le somme e sottrazioni.
In generale si applicano le seguenti regole:
1. Si calcolano per primi gli operatori postfissi !, % e l'operatore prefisso radice
quadrata √.
2. Si eseguono gli innalzamenti a potenza e l'operatore infisso radice ennesima n√x.
3. Si esegue l'operatore prefisso -. Ricordiamo ancora che -x ha un significato diverso da quello della sottrazione y-x. Il primo è un operatore unario, il secondo è
binario.
4. Si eseguono poi le moltiplicazioni e le divisioni
5. Da ultime si eseguono le addizioni e le sottrazioni.
− 32  2 ⋅ 5
 


Se l'ordine di esecuzione delle operazioni deve essere modificato, si usano le parentesi. Vanno
sempre eseguite prima le operazioni racchiuse tra parentesi8.
3  5 ⋅ 2 = 13
 3  5 ⋅ 2 = 16
Vale la pena notare che non tutte le calcolatrici usano il sistema di precedenza algebrico; le più
8
Nei testi di matematica elementare si fa largo uso di tre livelli di parentesi, tonde, quadre e graffe. Poiché non bisogna mai porre
limiti alla divina provvidenza e potrebbe esserci la necessità di avere più di tre livelli di parentesi, nei testi di matematica superiore e nella scrittura delle formule per il calcolatore si usa un solo tipo di parentesi, le rotonde: la lettura è un po' più difficoltosa, ma non c'è pericolo di ambiguità se la formula è scritta correttamente: ad ogni parentesi aperta corrisponde la sua chiusa e
due parentesi sono corrispondenti se nell'espressione da esse racchiusa è zero la somma algebrica delle parentesi aperte (+1) e
delle parentesi chiuse (-1).
P. Delise – I.T.C. Carli – Trieste (2003) – Dal calcolatore a Delphi
pag.5
semplici eseguono le operazioni nell'ordine in cui sono scritte e la scrittura, senza parentesi di 3+5⋅2
darà come risultato 16. Nel linguaggio algebrico tradizionale, alcuni operatori (il segno di frazione
e quello di radice) svolgono anche la funzione implicita di parentesi.
2a
3
2a b
2a b
= 3
2a− b
2a− b
[
]
2a  1 =  2a  1 
3
a b
a
a− b
=
3
{
[  a2ab ]
[  a−a b ]


6
Poiché diamo una precedenza maggiore alle moltiplicazioni e divisioni rispetto alle addizioni e
sottrazioni, possiamo pensare l'espressione algebrica come composta da gruppi di elementi legati al
loro interno dalla moltiplicazione o divisione e sommati tra di loro.
2a  3ab 
1 2
ab
2
Un gruppo di elementi legati tra di loro da moltiplicazioni o divisioni e nei quali la moltiplicazione
è, addirittura, sottintesa, si dice monomio. Quando abbiamo una sequenza di monomi legati tra di
loro da operazioni di addizione e sottrazione, diremo che siamo in presenza di un polinomio9. Se
abbiamo due monomi soli con un'operazione di somma o sottrazione, diremo che abbiamo un binomio. Se abbiamo tre monomi legati da due operazioni di somma o sottrazione, diremo che abbiamo
un trinomio.
I dialetti dell'algebra: la torre di Babele
Narra la Bibbia che gli uomini vollero costruire una torre così alta da arrivare fino al
cielo e che per punirli Dio diede ad ognuno una lingua diversa e non si intesero più. Sembra incredibile come questa situazione si verifichi continuamente. Anche in un ambito così ristretto e semplice come il linguaggio algebrico, l'uomo ha sviluppato molti dialetti, uno dei quali, usato dai vostri
libri di testo, è quello noto a voi. Questo modo di scrivere le formule, non è, però, l'unico. Se volete
scrivere un'espressione algebrica in modo che il calcolatore la possa intendere, dovete scriverla in
una forma diversa, la cosiddetta scrittura lineare, in cui cioè, le espressioni si sviluppano su una riga
sola e non su più piani. Di solito, poi, in questa forma, le parentesi sono tutte rotonde e per innalzare una potenza o usate dei simboli (sono molto diffusi ^ o ** infissi tra base ed esponente: li usa Excel, ma non pascal) o usate delle funzioni: pow(b, e); pow sta per power, b per base ed e per esponente.
In questo linguaggio, la formula
3
2a2  b
2a − b
diventa 3+(2*a*a+b)/(2*a-b), nella quale si è messa in evidenza anche la moltiplicazione implicita
(2a) con l'asterisco. La formula
2a
3
a b
a
a− b
diventa 3+((2*a/(a+b))/(a/(a-b)), nella quale si è omessa la parentesi per raccogliere 2a in quanto il
risultato non cambia. È evidente la minor leggibilità di questa scrittura. Siccome, però, essa è usata
nella programmazione dei calcolatori, è quanto mai opportuno abituarsi a tradurre le formula da un
linguaggio all'altro.
9
È una visione riduttiva del polinomio, che però ben si adatta alle nostre necessità. Lascerò ai corsi universitari la definizione
formale di polinomio.
P. Delise – I.T.C. Carli – Trieste (2003) – Dal calcolatore a Delphi
pag.6
Un matematico polacco pensò che era molto irrazionale avere operatori infissi, postfissi, prefissi,
con diverse precedenze e livelli di parentesi, ed inventò un linguaggio algebrico completamente diverso, in cui tutti gli operatori sono postfissi, alcuni si applicano ai due numeri che li precedono,
altri ad uno solo e sono tutti senza parentesi. Basta soltanto aggiungere un comando (che viene
rappresentato col simbolo ↑ e viene detto Enter) per separare la fine di un numero e l'inizio del
successivo quando non ci sono operatori tra i due.
Dovendo eseguire un'addizione a+b, si scriverà a ↑ b +; dovendo calcolare a+2b si scriverà a ↑ 2 ↑
b * +. Si è usato il simbolo * per la moltiplicazione per evitare confusioni con la x o con i punti e si
userà il simbolo ÷ per la divisione. Si noti, nell'ultima formula, la sequenza di due operatori *+, caratteristica di questo modo di scrivere le formule.
La formula (1) precedente, con le frazioni doppie, diventa:
3↑2↑a*a↑b+÷a↑a↑b-÷÷Questo metodo di scrittura delle formule algebriche venne introdotto negli anni '70 nelle calcolatrici
scientifiche della Hewlett Packard ed oggi è caduto in disuso, essendosi diffuso il cosiddetto sistema operativo algebrico nel quale, però, spesso alcuni operatori che sono prefissi, come la radice
quadrata, vengono trasformati in postfissi. Ma in realtà tutti i calcolatori, quando devono calcolare
una formula, prima la traducono dal sistema operativo algebrico in questa notazione polacca rovesciata (RPN, Reverse Polish Notation, per gli amici) per cui può essere utile addestrarsi all'uso di
questa scrittura.
La classificazione dei linguaggi di programmazione
L'automa calcolatore elettronico è, fondamentalmente, in grado di eseguire gli ordini
che gli vengono impartiti: pertanto il linguaggio che esso intende è un linguaggio imperativo.
Abbiamo già accennato (e lo vedremo più avanti in dettaglio), però, al fatto che il linguaggio della
CPU è molto elementare e pesante da utilizzare; si sono sviluppati, allora, dei linguaggi intermedi,
tra quelli naturali dell'uomo e quelli interni alle CPU. Il calcolatore stesso, mediante un opportuno
programma detto, a seconda delle sue funzionalità, interprete o compilatore, provvede a tradurre,
dal linguaggio intermedio a quello che la CPU intende ed è in grado di eseguire, gli ordini impartiti.
Nello sviluppare i linguaggi intermedi che il calcolatore traduce nel suo linguaggio interno e poi
esegue, si sono definite due parti, una descrittiva dei dati usati per risolvere il problema, ed una
imperativa, contenente gli ordini che il calcolatore deve eseguire.
Soprattutto per i problemi legati all'intelligenza artificiale, la struttura fondamentalmente imperativa dei linguaggi, è stata vissuta come fortemente limitante e si sono sviluppati
dei linguaggi in cui si privilegia la descrizione lasciando poi l'effettiva organizzazione degli ordini e
la loro esecuzione (perché alla fine il linguaggio dell'unità centrale è sempre imperativo) al programma traduttore. Si sono sviluppati così, oltre ai linguaggi imperativi, quelli dichiarativi, dei quali uno molto noto è il Prolog.
I linguaggi imperativi
Dei linguaggi che sono stati sviluppati per programmare i calcolatori, quelli che hanno
avuto sinora la massima diffusione sono i linguaggi imperativi. Essi sono costituiti da sequenze di
ordini o da periodi ipotetici nei quali la conseguenza è un ordine (se è vero … allora fa…). Ogni famiglia di unità centrali ha il suo linguaggio assembly, che fa corrispondere ad ogni istruzione
macchina un'istruzione mnemonica, ma i linguaggi più diffusi, sono quelli che sono indipendenti
dalla particolare unità centrale usata: Fortran, Cobol, Pascal, Basic e C sono alcuni tra i più
importanti.
Come già accennato prima, i programmi scritti in linguaggi imperativi sono suddivisi
in due parti: la descrizione dei dati necessari alla soluzione dei problemi e la descrizione delle opeP. Delise – I.T.C. Carli – Trieste (2003) – Dal calcolatore a Delphi
pag.7
razioni da compiere per risolvere il problema; quest'ultima rispetta, in maniera più o meno rigorosa,
i dettami del teorema di Iacopini - Bohm cui accenneremo più avanti.
La descrizione dei dati
Nel Fortran, il primo linguaggio ad alto livello ad essere sviluppato, e nel Basic, il
linguaggio più popolare, la descrizione dei dati usati nella soluzione dei problemi è molto sintetica
e, spesso, implicita, mentre il Cobol, l'Algol (ormai scomparso), il Pascal, il C e, soprattutto il C++,
danno un ampio sviluppo a questa parte.
I dati, sono le informazioni che vengono elaborate e memorizzate all'interno del calcolatore; nella memoria non vengono memorizzati numeri, ma informazioni che l'unità centrale
interpreta, a seconda delle istruzioni che le vengono impartite, come numeri, caratteri, valori logici
(vero o falso) e così via10. La descrizione dei dati ha pertanto soltanto la funzione di riservare
opportune zone di memoria per i dati stessi, assegnare a queste zone un nome per identificarle,
consentire ai programmi di eseguire controlli sulla liceità delle operazioni che sui dati si vogliono
eseguire (ad esempio il calcolo del resto nella divisione tra due numeri con la virgola non è legittimo), e così via.
Praticamente tutti i linguaggi usano, per definire una variabile, un nome che deve iniziare con una lettera e non contenere spazi ed altri caratteri speciali (alcuni linguaggi accettano il
carattere _); sono nomi leciti, allora, RADICE, A_123, PAOLO…; i linguaggi della famiglia del C
(C e C++) distinguono tra PAOLO, Paolo, paolo, paOlo, …, gli altri no.
I nomi interamente numerici sono invece usati per rappresentare le costanti numeriche;
così, ad esempio, il simbolo 13 verrà rappresentato, dalla sequenza di bit 1101 preceduti,
eventualmente, da un numero opportuno di 0. Tutti i linguaggi usano per rappresentare i numeri decimali il punto anziché la virgola come separatore tra la parte intera e la parte frazionaria.
Nel Fortran e nel Basic c'è la possibilità di definire il tipo di dato già attraverso il nome; nel Fortran tutti i dati che iniziano con I, J, K, L, M ed N sono, salvo avviso contrario, interi;
nel Basic le variabili che finiscono con il $ (NOME$) sono stringhe di caratteri, quelle che finiscono con il % (I%) sono intere, e così via. Oggi questa prassi è caduta in disuso. Si preferisce predisporre una zona del programma (di solito all'inizio delle istruzioni) nella quale si definiscono esplicitamente i dati. Ad esempio il Pascal ed il C definiscono le variabili nel seguente modo:
Pascal
var
I: integer;
R_2, PIGR: real;
C: char;
SI_NO: boolean;
C
int
float
char
int
I;
R_2, PIGR;
C;
SI_NO;
Il C non ha un tipo boolean; si usano variabili intere che
sono vere se diverse da 0 e false se uguali a 0.
La prima variabile, I, è una variabile intera; su di essa si possono eseguire solo le operazioni consentite per i numeri interi, può assumere un valore compreso tra -32768 e 32767 ed
occupa in memoria due byte consecutivi. 11
R_2 e PIGR sono reali; meglio li definisce il linguaggio C con la parola float. Infatti
un numero reale ha bisogno, in generale, di un numero infinito di cifre per essere rappresentato (si
pensi a π=3,14159… o a
2 =1,4142…) e può assumere valori che, in valore assoluto, possono
essere piccolissimi o molto grandi. Ma nel calcolatore tutto è finito, e per memorizzare un numero
10
11
Si pensi, per chiarire meglio questo concetto, al simbolo 0 della scrittura comune. A seconda dei contesti lo interpretiamo come zero o come o maiuscola. La stessa cosa avviene nel calcolatore dove la stessa sequenza di bit può essere un'istruzione, un carattere o un numero, a seconda di ciò che la CPU si attende di trovare in quel momento.
Questo intervallo e l'occupazione di memoria sono caratteristici del Turbo Pascal e del Turbo C della Borland; altri
linguaggi hanno intervalli ed occupazioni diverse; il Delphi, sempre della Borland assegna ad ogni intero 4 byte e
l'intervallo di variabilità va da –2.147.483.648 a 2.147.483.647 un numero molto grande, ma sempre finito.
P. Delise – I.T.C. Carli – Trieste (2003) – Dal calcolatore a Delphi
pag.8
reale ad esso viene assegnato un numero finito di cifre ed un esponente a cui elevare un'opportuna
base12. Così il numero π potrebbe essere scritto come 0,314159... ⋅ 101, la velocità della luce che è
299.792.458 m/s potrebbe venir memorizzata come 0,299792 ⋅ 109 m/s e la massa dell'elettrone è
0,964846 ⋅ 10-30 kg. Questo tipo di rappresentazione si dice in virgola mobile che in inglese si traduce floating point, da cui il termine float.
La variabile C è di tipo char, vale a dire che il numero 65 memorizzato nella variabile
intera I verrà interpretato come il numero 65, mentre memorizzato nella variabile C, di tipo carattere, viene interpretato come il carattere "A", come spiegato prima.
La variabile SI_NO è, infine, di tipo booleano, vale a dire che assume i valori VERO o
FALSO; generalmente la verità o falsità è definita da un bit che assume il valore 1 o 0. In C il valore 0 è falso, ogni valore diverso da 0 è vero.
Il teorema di Iacopini - Bohm
Alla base dei programmi scritti in questi linguaggi c'è il teorema di Iacopini Bohm che afferma, in
parole semplici, che ogni programma può essere scritto usando:
• la struttura sequenziale: tutti i linguaggi imperativi eseguono le istruzioni nell'ordine sequenziale con cui sono scritte, salvo avviso contrario. Il fatto che le istruzioni siano eseguite una
dopo l'altra e non in parallelo introduce un'intrinseca dipendenza dei risultati dal tempo in cui le
istruzioni stesse sono eseguite.
Se volessimo fare un esempio non legato al calcolatore, troveremmo la struttura sequenziale in
questo pezzetto di istruzioni tratte da una ricetta di cucina; in esse, eseguita un'operazione, si
passa alla successiva:
• rompere un uovo
• separare il tuorlo dall'albume
• versare il tuorlo in una tazza
• aggiungere un pizzico di sale
• …
• la struttura alternativa: tutti i linguaggi moderni prevedono un'istruzione del tipo "se una certa
condizione è vera allora esegui certe istruzioni, altrimenti esegui certe altre istruzioni". I
linguaggi di tipo assembly e le prime versioni dei linguaggi Fortran, Cobol e Basic prevedono
una struttura più semplice: "se una certa condizione è vera allora esegui questa istruzione".
Questa struttura semplificata ha bisogno di un'istruzione nuova, che è quella di salto:
normalmente, dopo aver eseguita un'istruzione, viene eseguita l'istruzione successiva, in base
alla struttura sequenziale; è necessario introdurre un'istruzione che faccia eseguire, anziché
l'istruzione successiva, un'altra istruzione di cui viene fornito l'indirizzo di posizione nella memoria. L'istruzione di salto è stata mantenuta, anche se superflua, in quasi tutti i linguaggi moderni, ma viene usata molto poco, in quanto ne risente la leggibilità dei programmi da parte
dell'uomo.
Sempre per restare nell'ambito, forse più familiare, delle ricette di cucina, possiamo esemplificare la struttura con:
• se avete a disposizione una stecca di vaniglia allora
• mettete a bollire il latte con la stecca di vaniglia dentro
• quando sta per bollire toglietelo dal fuoco
• filtrate il contenuto versandolo direttamente sullo zabaione
• altrimenti
• mettete il latte sul fuoco
• quando sta per bollire versatelo sullo zabaione
12
Negli esempi si è usata la base 10, ma il calcolatore usa la base 2; vale a dire che un numero in virgola mobile è
rappresentato nella forma 0,bbbbbbb2bbb, dove il simbolo b rappresenta una generica cifra binaria (0 o 1). Negli
esempi che seguono, però, per miglior leggibilità, si è usata la base 10.
P. Delise – I.T.C. Carli – Trieste (2003) – Dal calcolatore a Delphi
pag.9
• aggiungete un pizzico di vanillina in polvere
• mescolate bene il tutto
dove con un opportuno uso dell'indentazione, vale a dire dei rientri del margine sinistro, abbiamo evidenziato i blocchi di istruzioni che vengono eseguiti in alternativa. Nei linguaggi di programmazione l'inizio e la fine dei blocchi sono, invece, marcati da alcuni simboli chiave (begin
… end in Pascal, then … else … endif in Basic, { … } in C, e così via.
• la struttura ripetitiva: essa assume nei linguaggi diverse forme, la più classica delle quali è riassumibile nella forma per tutto il tempo che una certa condizione è vera esegui le seguenti istruzioni. Questa struttura ripetitiva non va confusa con la struttura enumerativa, sua parente povera, che dice ripeti n volte le seguenti istruzioni.
Vediamo il solito esempio tratto dalla cucina:
• ripeti fino a quando ottieni un impasto elastico ed omogeneo
• afferra un'estremità dell'impasto con la mano e stiralo
• ripiega la parte stirata sull'impasto
• se l'impasto dovesse risultare troppo duro allora
• pratica un piccolo foro nell'impasto
• aggiungi un po' d'acqua non fredda
• chiudi i lembi del foro
• ripeti fino a quando l'acqua è stata assorbita dall'impasto
• manipola l'impasto
• ruota l'impasto di 90 gradi, sempre nella stessa direzione
• forma una palla con l'impasto
• …
Nell'esempio oltre alla struttura ripetitiva iniziale, vediamo, all'interno della stessa, una struttura
alternativa semplificata (manca l'altrimenti) ed anche una seconda struttura ripetitiva.
I comandi
Generalmente il calcolatore non è in grado di cucinare (anche se nulla vieta di costruirne uno); pertanto le operazioni che esso sa fare sono di tipo diverso. Tutti i linguaggi imperativi sono in grado di eseguire alcuni comandi fondamentali:
1. eseguire operazioni aritmetico logiche usando le regole delle operazioni ed i contenuti della
memoria. In quasi tutti i linguaggi evoluti l'istruzione A*(B+C)/D significa sommare il contenuto delle memorie etichettate con B e C, moltiplicare il risultato per A e dividerlo per D.
2. assegnare ad una memoria etichettata con un nome, il risultato di un'operazione o il contenuto
di un'altra memoria. In Pascal, ad esempio, scriveremo A:=A*(B+C)/D; per assegnare alla memoria etichettata con il nome A il risultato dell'operazione indicata a destra del simbolo :=.Si
noti che, nell'esempio, A compare sia a destra del simbolo di assegnazione che a sinistra. Nel C,
nel Fortran e nel Basic, la stessa operazione si scrive, invece, A=A*(B+C)/D. 13
3. eseguire una procedura. Una procedura è un programma a sé, identificato da un nome.
L'esecuzione di una procedura da parte del programma principale consiste nel sospendere
l'esecuzione del programma stesso ed avviare l'esecuzione della procedura che, nei linguaggi
evoluti, viene chiamata per nome. Terminata l'esecuzione di questa, riprende l'esecuzione del
programma chiamante la procedura con l'istruzione successiva a quella di chiamata14. Nel chiamare le procedure si possono passare alle stesse dei valori particolari che sono necessari per
l'esecuzione; le procedure, inoltre, possono restituire dei valori al programma chiamante. I valori passati alle procedure e restituiti da esse si chiamano parametri. Per rendere concreto il signi13
14
Nel Basic, a dire il vero, l'operazione si sarebbe dovuta scrivere LET A=A*(B+C)/D, dove il verbo LET indica
esplicitamente l'operazione di trasferimento: PONI A=A*(B+C)/D. Ma tutti i linguaggi Basic hanno lasciato opzionale il verbo LET.
In parole molto povere, chiamare una procedura vuol dire: "ferma il lavoro che stai facendo, fanne un altro e,
quando hai finito, continua quello che facevi prima."
P. Delise – I.T.C. Carli – Trieste (2003) – Dal calcolatore a Delphi
pag.10
4.
5.
6.
7.
8.
ficato di procedura pensate alle istruzioni per risolvere un'equazione di secondo grado e poi
pensate ad un problema di fisica che ha bisogno al suo interno di risolvere un'equazione di secondo grado: giunti a quel punto del problema, voi sospenderete la soluzione del problema vero
e proprio, risolverete l'equazione di secondo grado e poi, con i risultati ottenuti riprenderete in
mano il problema.
ricevere informazioni dal mondo esterno (input) e mandare informazioni al mondo esterno
(output). Una volta queste operazioni venivano codificate con delle istruzioni ad hoc. Lo fanno
così, ad esempio, il Fortran ed il Basic. Il Pascal affidò queste operazioni a delle procedure
apposite, che ammettono, però, una sintassi particolare. Il linguaggio C ha definito le operazioni
di input ed output attraverso la chiamata a delle procedure la cui sintassi è quella di tutte le altre
procedure.
far finire un programma. È un'istruzione necessaria per la stessa definizione di algoritmo.
eseguire i test: Ne abbiamo parlato nel paragrafo precedente. Tutti i linguaggi realizzano i test
introducendo la parola if. Cambiano, invece, da linguaggio a linguaggio, le regole per la definizione della condizione e dei blocchi di istruzioni da eseguire
eseguire un istruzione di salto. Lo si è già accennato: le istruzioni hanno un ordine intrinseco; si
pensi, ad esempio all'ordine con cui vengono scritte su di un foglio di carta. Generalmente il
calcolatore inizia ad eseguire la prima istruzione, poi passa alla seconda, poi alla terza, e così
via. Un'istruzione può essere, però, del tipo, salta all'istruzione numero … ed in questo caso
l'ordine sequenziale non viene più rispettato. Come si è già accennato, è un istruzione di cui si
potrebbe fare a meno se l'unità centrale fosse in grado di rispettare le richieste del teorema di Iacopini Bohm, ma ciò non si verifica e quindi tutti i linguaggi di basso livello, come sono quelli
interni alle CPU fanno ricorso a questa istruzione. Al contrario, i linguaggi di livello medio alto
tendono a fare a meno di questa istruzione, anche se tutti la implementano.
eseguire un ciclo. Nelle prime versioni dei linguaggi (Fortran, Basic,…) il solo ciclo previsto
era quello enumerativo, vale a dire ripeti n volte questo blocco di istruzioni. Esso è realizzato
usando la parola chiave for (nel Fortran do). Poi pian piano è stato introdotto il ciclo per tutto il
tempo che è vera una certa condizione esegui … che è stato realizzato usando la parola chiave
while ed in alcuni linguaggi con loop while. Questo tipo di ciclo ha numerose varianti che coesistono nello stesso linguaggio. Se anziché mettere la condizione di permanenza si mette quella
di uscita15, al posto della parola while si è usata la parola until. La condizione di permanenza o
di uscita del ciclo può essere messa all'inizio del ciclo (vale a dire che se la condizione è inizialmente falsa in un ciclo while esso non viene mai eseguito) o alla fine del ciclo (ed in questo
caso il ciclo viene comunque eseguito almeno una volta). Non è detto che tutti i linguaggi
implementino tutte le forme di ciclo. L'ultimo ad averle introdotte, il Basic, le ha tutte e quattro;
il Pascal ha il ciclo while con la condizione in testa ed il ciclo repeat…until con la condizione
in coda. Il C ha realizzato i cicli solo con la condizione while.( while … oppure do… while. Nel
C, però, è stato realizzato un ciclo enumerativo for estremamente sofisticato.
Operazioni aritmetico - logiche
Anche se i francesi chiamano il calcolatore ordinateur per evidenziare le sue capacità
di organizzare i dati, già il primo calcolatore venne usato dagli Stati Uniti per fare conti, ed in particolare per preparare le nuove tabelle di tiro per i cannoni. Tutti i linguaggi hanno, quindi, sviluppato una sintassi particolare per definire le operazioni aritmetiche ed, a parte il Cobol che può
fare anche ricorso a formulazioni più verbose, usano tutti il + e - per l'addizione e la sottrazione, e *
e / per la moltiplicazione e la divisione. Usano, inoltre, le parentesi rotonde per modificare l'ordine
con cui le operazioni vengono eseguite. Se le parentesi mancano, le operazioni vengono eseguite da
sinistra verso destra, dando la precedenza alle moltiplicazioni e divisioni, rispetto alle somme e
sottrazioni. Così, la famosa formula per la soluzione dell'equazione di un'equazione di secondo grado
15
Vale a dire ripeti l'esecuzione delle seguenti istruzioni … ed interrompi quando la condizione … diventa vera.
P. Delise – I.T.C. Carli – Trieste (2003) – Dal calcolatore a Delphi
pag.11
x1,2 =
− b ± b2 − 4ac
2a
viene scritta, ricordando che in quasi tutti i linguaggi la radice quadrata si esegue chiamando la
funzione SQRT(…)16, in due righe
x1:=(-b+sqrt(b*b-4*a*c))/(2*a);
x2:=(-b-sqrt(b*b-4*a*c))/(2*a);
dove, per scrivere l'assegnazione, abbiamo usato la sintassi del Pascal. Si noti che le parentesi usate
sono solamente quelle rotonde. Le parentesi quadre e graffe, croce e delizia delle espressioni algebriche dei bienni delle superiori, vengono trasformate tutte in parentesi rotonde.
Si noti la mancanza dell'innalzamento al quadrato che è stato trasformato in b*b.
Alcuni linguaggi hanno l'innalzamento a potenza. Il Fortran avrebbe potuto scrivere, oltre che b*b,
anche b**2, ed il Basic b^2. Ma né il Pascal né il C hanno sviluppato una sintassi per indicare
l'innalzamento a potenza e ciò per il fatto che, a seconda dei casi, esistono algoritmi ottimali diversi
per eseguire l'operazione. Il Pascal, graziosamente, definisce una funzione per eseguire il quadrato
di un numero; avremmo, infatti potuto scrivere, invece che b*b, sqr(b). Il C, nella sua libreria
standard fornisce la funzione pow che qui si sarebbe usata nella forma pow(b,2).
Accanto alle variabili aritmetiche, abbiamo già visto prima, esistono quelle logiche o
booleane, che possono assumere solo il valore di vero o falso. Queste variabili, vengono spesso prodotte dagli operatori di confronto. Gli operatori di confronto sono 6 ed hanno un'ortografia quasi
identica in ogni linguaggio
Linguaggio =
>
<
≥
≤
≠
Fortran
=
>
<
>= <= <>
.EQ. .GT. .LT. .GE. .LE. .NE.
Basic
=
>
<
>= <= <>
Pascal
=
>
<
>= <= <>
C
==
>
<
>= <= !=
Accanto ai tradizionali operatori aritmetici, esistono gli operatori logici, molto usati soprattutto con
le variabili logiche, ma anche in altre occasioni perché sono molto veloci ed efficienti. Essi operano
a livello di bit. Ce ne sono complessivamente 17; siccome possono essere ricavati tutti usandone
due, nei linguaggi sono stati definiti dei simboli solo per i più frequenti.
NOT
AND
OR
XOR
0
1
0
1
0
1
1
0
0
0
1
0
1
0
0
0
0
0
0
1
1
1
1
0
1
1
1
1
L'operatore NOT agisce su una variabile sola; se un suo bit è 1 diventa 0 e se è 0 diventa 1.
L'operatore AND agisce su due variabili; se i rispettivi bit sono 1, il corrispondente bit del risultato
sarà 1, altrimenti 0. L'operatore OR agisce anche su due variabili; se i rispettivi bit sono 0, il corrispondente bit del risultato sarà 0, altrimenti 1. Anche l'operatore XOR agisce su due variabili; se i
bit rispettivi sono uguali. il corrispondente bit del risultato sarà 0, altrimenti 1. Così se un byte, che
chiamiamo A, ha i bit 10001010, ed un altro, B, ha i bit 01001000, il risultato delle operazioni logiche sarà:
A
B
10001010
01001000
NOT
01110101
10110111
A AND B
A OR B
A XOR B
00001000
11001010
11000010
Il Fortran racchiude gli operatori tra punti: .NOT., .AND., .OR.. Il C ha dei simboli speciali; ! per
NOT, & per AND, | per OR e ^ per XOR, il Pascal usa le parole AND, OR, NOT.
16
Da SQUARE ROOT, radice quadrata in inglese. Il Basic la chiama SQR, ed i nuovi linguaggi associati ai fogli
elettronici, purtroppo la hanno tradotta in RADQ.
P. Delise – I.T.C. Carli – Trieste (2003) – Dal calcolatore a Delphi
pag.12
Esempi
Concludiamo questo capitolo con un esempio, privo di significato di per sé, ma semplice ed in grado di mostrare alcune delle caratteristiche fondamentali della programmazione. L'esempio è codificato in alcuni linguaggi per evidenziarne le caratteristiche comuni e le differenze. Si sono usate le
versioni standard dei linguaggi. Per chi volesse avvicinarsi alla programmazione, si segnalano, però, in particolare i compilatori Turbo Pascal e Turbo C prodotti dalla Borland, e dei quali esiste
anche una versione specifica per Windows, l'interprete Qbasic ed il compilatore Fortran prodotti
dalla Microsoft. Per il Prolog si segnala il compilatore Visual Prolog prodotto dal Prolog Development Center, che è disponibile anche in versione shareware. Per il Logo, si segnala il MS Windows
Logo, prodotto dalla Softronics, reperibile gratuitamente attraverso Internet17.
Noi vogliamo scrivere un programma che ricevuti, attraverso la tastiera, due numeri
interi, li ordini in ordine crescente e li scriva ordinati a video.
La descrizione in un linguaggio di progetto
Poiché tutti i linguaggi di programmazione sono abbastanza innaturali per l'uomo, spesso si inizia la
scrittura di un programma descrivendolo con un linguaggio preciso, ma più vicino al linguaggio comune del programmatore. Le caratteristiche di questo linguaggio andrebbero convenute a priori.
Noi non lo faremo supponendo che esse risultino abbastanza evidenti. Il programma può essere descritto :
1) Definizione delle variabili A, B ed X (opzionale in Fortran ed in Basic)
2) Lettura dei valori delle variabili A e B che verranno immessi da tastiera
3) Se A è maggiore di B allora
a) Assegna B ad X
b) Assegna A a B
c) Assegna X ad A
4) Stampa di A e B
Lo stesso programma in Pascal
Tra parentesi graffe si racchiudono i commenti.
program ordina;
{definizione delle variabili}
var a,b,x:integer;
begin
{lettura dei valori delle variabili a e b}
readln(a,b);
{se A è maggiore di b}
if a>b then
begin
{scambia tra di loro a e b}
x:=b;
b:=a;
a:=x;
end;
writeln(a,b);
readln;
end.
Rispetto al linguaggio di progetto è stata aggiunta in fondo al programma l'istruzione readln che
legge una riga dalla tastiera. Questo per evitare che il compilatore Pascal chiuda la finestra
dell'esecuzione senza che si possano leggere i risultati. Questa istruzione verrà aggiunta per sistema
a tutti i programmi.
17
Ma per il quale gli autori invitano ad inviare un'offerta alla National Multiple Sclerosis Society.
P. Delise – I.T.C. Carli – Trieste (2003) – Dal calcolatore a Delphi
pag.13
Qualche altro semplice programma in Pascal
Vediamo un altro programma che esegua un ciclo; questo programma produrrà la tabella della funzione seno per angoli in radianti da -π/2 a π/2 con passo di 1/10 di radiante.
program sin;
{tabulazione della funzione seno}
var xi,xf,x:real;{xi è l'angolo iniziale,
xf l'angolo finale della tabella ed
x l'angolo che, di volta in volta, viene elaborato}
begin
xi:=-3.141592 / 2; {anziché leggerli da tastioera assegno i valori iniziali}
xf:=-xi;
x:=xi;
while x<=xf do
{fino a quando x<=xf...}
begin
writeln(x:5:2,' ',sin(x):7:4);
x:=x+0.1;
{cambio il valore ad x; senza questa istruzione
il programma non si fermerebbe mai e stamperebbe sempre
lo stesso valore per x e sen(x)}
end;
readln;
end.
In questo esempio troviamo alcune caratteristiche importanti dei programmi:
1. Il ciclo while … do begin … end; che esegue le istruzioni all'interno del blocco begin
… end per tutto il tempo in cui la condizione scritta a fianco alla parola while è vera.
2. L'istruzione writeln accanto alle informazioni da stampare mostra il formato di stampa nella
forma, ad esempio x:4:2 che significa stampa x riservandogli 4 posizioni di stampa di cui due
dopo la virgola.
3. L'istruzione x:=x+0.1 che non va assolutamente confusa con l'espressione algebrica x=x+0.1 che
sarebbe sempre falsa. Essa significa somma ad x 0.1 ed assegna il valore di nuovo ad x.
Vediamo un esempio molto semplice dal punto di vista della programmazione, ma un
po' più complesso da un punto di vista della logica. Calcoliamo l'area di un rettangolo, ma
controlliamo prima la correttezza dei dati in ingresso (operazione che si dovrebbe fare sempre).
Program rettangolo;
{calcolo dell'area di un rettangolo}
var b,h,s:real;
begin
write('Base del rettangolo: ');
readln(b);
write('Altezza del rettangolo: ');
readln(h);
if (b>0) and (h>0) then
begin
s:=b*h;
writeln('L''area è :',s:10:4);
end
else {la base o l'altezza sono minori di 0; l'and della condizione vera
diventa or nella condizione falsa: legge di De Morgan}
begin
if b<0 then
begin
writeln('La base è minore di 0');
end;
if h<0 then
begin
writeln('L''altezza è minore di 0');
P. Delise – I.T.C. Carli – Trieste (2003) – Dal calcolatore a Delphi
pag.14
end;
end;
end.
Notiamo, in questo esempio che:
1. L'istruzione write invece dell'istruzione writeln che lascia il cursore a fianco dell'ultimo carattere scritto anziché andare a capo.
2. La sequenza di due apostrofi consecutivi (pessima da un punto di vista formale; molto più elegante il C con il carattere di escape \) per rappresentare l'apostrofo.
3. La presenza di due tipi di istruzione if, una con l'else ed una senza. Si noti che l'end che precede
l'else non deve avere il punto e virgola. L'inserimento del punto e virgola in quel punto è molto
frequente e causa degli errori talvolta grammaticali e talvolta semantici (e quindi estremamente
difficili da rilevare).
Vediamo infine, un classico: la soluzione di un'equazione di II grado.
Prima versione:
Program eq2;
var a,b,c,x1,x2,delta,d:real;
begin
writeln('Programma per la soluzione dell''equazione ax²+bx+c=0');
writeln;
write('A = ');
readln(a);
write('B = ');
readln(b);
write('C = ');
readln(c);
delta:=sqr(b)-4*a*c;
if delta >0 then
begin
d:=sqrt(delta);
x1:=(-b+d)/(2*a);
x2:=(-b-d)/(2*a);
writeln('Le due soluzioni sono x1 = ',x1, ' e x2 = ',x2);
end
else if delta=0 then
begin
x1:=-b/(2*a);
writeln('Una sola soluzione x = ',x1);
end
else
begin
writeln('Non ci sono soluzioni reali.');
end;
end.
In questo programma vale la pena notare il fatto che il “/2a” viene chiuso tra parentesi. Infatti
l'operatore di divisione è un operatore binario che si applica alla variabile a destra e a quella alla sua
sinistra; in mancanza della parentesi avrebbe eseguito soltanto il /2 e poi avrebbe moltiplicato il risultato per a.
Se guardiamo il programma vediamo che è ottimizzabile da molti punti di vista; alcuni
compilatori, ad esempio, calcolano in maniera più efficiente l'operazione a*2 rispetto all'operazione
2*a (sicuramente la considerazione riguardava il turbo pascal fino alla versione 4). Poi l'operazione
2*a viene ripetuta tre volte; si sarebbe potuta calcolare una volta sola aggiungendo una variabile
che viene usata ripetutamente (in realtà in questo caso il beneficio, se c'è, è minimo e la tecnica viene presentata fondamentalmente a scopo didattico. La divisione, inoltre, è un'operazione più
P. Delise – I.T.C. Carli – Trieste (2003) – Dal calcolatore a Delphi
pag.15
dispendiosa, in termini di tempo, della moltiplicazione. Infine se a fosse eguale a 0, la divisione darebbe un risultato errato. Bisogna tuttavia riconoscere che, spesso, queste ottimizzazioni (inutili nel
nostro caso, indispensabili in altri) rendono meno leggibile il programma.
Seconda versione (ottimizzata nell'ipotesi che la maggioranza delle equazioni abbiano due soluzioni):
Program eq2;
var a,b,c,x1,x2,delta,d,a2:real;
begin
writeln('Programma per la soluzione dell''equazione ax^2+bx+c=0');
writeln;
write('A = ');
readln(a);
write('B = ');
readln(b);
write('C = ');
readln(c);
if a<>0 then
begin
delta:=sqr(b)-a*c*4;
a2:=1/(a*2);
if delta >0 then
begin
d:=sqrt(delta);
x1:=(-b+d)*a2;
x2:=(-b-d)*a2;
writeln('Le due soluzioni sono x1 = ',x1, ' e x2 = ',x2);
end
else if delta=0 then
begin
x1:=-b*a2;
writeln('Una sola soluzione x = ',x1);
end
else
begin
writeln('Non ci sono soluzioni reali.');
end;
end
else
begin
writeln ('Errore: non è un''equazione di secondo grado a = 0');
end;
readln;
end.
A volte nello scrivere i programmi si preferisce raccogliere gruppi di operazioni da
compiere in contenitori che le isolano e ne evidenziano l'appartenenza ad un gruppo; questi contenitori si chiamano procedure o funzioni. Mentre nello scrivere il programma precedente ci siamo
concentrati sulla sua efficienza di calcolo, in questo ci concentreremo sulla sua leggibilità; da un
punto di vista del calcolo, invece, sarà volutamente inefficiente. Le procedure e le funzioni possono
ricevere dal programma chiamante degli argomenti racchiusi tra parentesi. Il numero ed il tipo degli
argomenti deve essere identico nel programma chiamante e nella definizione della procedura chiamata. Non i nomi, che possono variare: al primo argomento del programma chiamante verrà fatto
corrispondere il primo nella definizione della procedura, al secondo il secondo, e così via.
Terza (e ultima) versione:
Program eq2;
type soluzioni= array [1..2] of real;
P. Delise – I.T.C. Carli – Trieste (2003) – Dal calcolatore a Delphi
pag.16
var a,b,c:real;
nsoluz:integer;
x: soluzioni;
i: integer;
function delta (a,b,c: real):real;
begin
delta:=sqr(b) - 4*a*c;
end;
procedure SolEq2(a,b,c:real; var n:integer; var x:soluzioni);
begin
if delta(a,b,c) >0 then
begin
x[1]:=(-b+sqrt(delta(a,b,c)))/(2*a);
x[2]:=(-b-sqrt(delta(a,b,c)))/(2*a);
n:=2;
end
else if delta(a,b,c)=0 then
begin
x[1]:=-b/(a*2);
n:=1;
end
else
begin
n:=0;
end;
end;
begin
writeln('Programma per la soluzione dell''equazione ax^2+bx+c=0');
writeln;
write('A = ');
readln(a);
write('B = ');
readln(b);
write('C = ');
readln(c);
if a<>0 then
begin
soleq2(a,b,c,nsoluz,x);
write('Trovate ',nsoluz, ' soluzioni: ');
for i:=1 to nsoluz do
begin
write ('x',i,' = ',x[i],'
');
end;
end
else
begin
writeln ('Errore: non è un''equazione di secondo grado; a = 0');
end;
readln;
end.
Notiamo subito alcune differenze con le versioni precedenti:
1. Abbiamo introdotto le soluzioni come fossero un vettore; avremmo potuto definire direttamente
la variabile x: array [1..2] of real che significa che x è una variabile con indice che
può assumere fino ad un massimo di due valori; i valori si possono usare con le scritture x[1]
ed x[2]. Abbiamo isolato la definizione del vettore in un'istruzione type; è una prassi molto
raccomandata che consente una più semplice manutenzione dei programmi. Abbiamo, insomma,
definito un nuovo tipo di variabile, soluzioni, che è composta da due elementi, il primo avrà
l'indice 1 ed il secondo l'indice 2.
2. Abbiamo scritto una funzione delta, che, partendo da tre numeri a,b,c calcola un numero reale,
P. Delise – I.T.C. Carli – Trieste (2003) – Dal calcolatore a Delphi
pag.17
il discriminante. Se noi scrivessimo in un programma delta(1,5,6) otterremmo il numero 1;
e così via.
3. Abbiamo scritto una procedura che partendo da tre numeri a,b,c restituisce un numero n con il
numero di soluzioni trovate ed un vettore x con le soluzioni stesse. Rispetto al programma precedente la procedura può essere presa così com'è e ricopiata in altri programmi che hanno bisogno
di risolvere equazioni di secondo grado. La funzione discriminante potrebbe essere addirittura
utilizzata per decidere in presenza di quale conica ci troviamo, e così via.
4. Il programma principale, a questo punto, assume una forma estremamente elegante: legge i dati,
calcola la soluzione e ne stampa i risultati; tutto il lavoro sporco viene fatto nelle procedure o
nelle funzioni.
La soluzione di un'equazione difficile
Quello che vi sto per raccontare è vero: nel dicembre 2002 mi è stato regalato un calendario molto particolare, al posto dei santi ha i nomi dei matematici illustri nati in quel giorno, al
posto delle donne poco vestite ha problemi di matematica ed al posto dell'anno ha un'equazione18:
4
3
2
x −8180x 25090190 x −34200948100 x17481136677369=0
A naso ho supposto che l'equazione fosse legata all'anno 2003. Comunque non mi sarebbe dispiaciuto risolvere quest'equazione. Sul libro “Standard Math Tables and Functions” edito dalla Chemical Rubber & Co a pagina 393 ho trovato la formula risolutiva di una equazione di IV grado: è di
una pagina e presuppone che si sia in grado di risolvere un'equazione di terzo grado: un'altra paginetta, la 392.
Se ci accontentiamo di una soluzione approssimata possiamo seguire un'altra strada che ci avvicina ad una
matematica che probabilmente è nuova per voi ed è quella
dell'analisi numerica. La funzione, essendo una polinomiale, è
continua e definita su tutto l'asse x. Se troviamo che per un
certo valore di x la funzione ha un certo segno e che per un
altro il segno è opposto, possiamo legittimamente supporre che
tra i due punti sull'asse x la funzione tagli l'asse delle ascisse
per un numero dispari di volte. Una volta trovato un intervallo
in cui la funzione cambia di segno possiamo calcolare il valore
della funzione nel punto centrale dell'intervallo; a meno di casi
particolarmente “sfortunati” in cui la funzione valga esattamente 0, troveremo che il segno della funzione sarà o quello
dell'estremo destro dell'intervallo o quello dell'estremo sinistro.
Potremo allora ridurre l'intervallo ad un'ampiezza che sarà la
metà del precedente ed avremo comunque la certezza che al
suo interno ci saranno un numero dispari di soluzioni. Procederemo a restringere l'intervallo fino a quando la differenza tra i
due estremi sarà così piccola che per le nostre esigenze sarà
irrilevante la differenza tra di essi. Questo metodo si dice “dicotomico” ed ha, per essere applicato,
ancora un problema: dobbiamo trovare l'intervallo entro il quale la funzione cambia di segno; esistono alcuni teoremi che ci dicono quante soluzioni ha una equazione di tipo polinomiale in un
certo intervallo, ma noi seguiremo una strada più semplice ed em pirica: fisseremo un'ampiezza iniziale che ci garantisca che al suo interno non cui siano soluzioni doppie o triple e, sezionato l'asse x
sulla base di quest'ampiezza lo esploreremo alla ricerca degli intervalli “pregiati”.
Qui sotto viene riportato il testo del programma. Si tratta, ormai, di un programma
complesso. Notiamo la presenza di variabili globali, definite fuori da tutte le procedure, e locali, de18
Chi avesse l'insana curiosità di vedere il calendario è invitato a visitare il sito web www.rudimathematici.com dove
viene pubblicata gratuitamente una rivista (e-zine è il nome che viene dato a queste riviste pubblicate in rete) di giochi e problemi matematici. Il calendario è supplemento al numero 47 di dicembre 2002. Leggetelo!
P. Delise – I.T.C. Carli – Trieste (2003) – Dal calcolatore a Delphi
pag.18
finite all'interno delle procedure: le prime sono utilizzabili da tutte le procedure e dal programma
principale, le seconde no. Il programma, formalmente corretto ha almeno un errore concettuale: se
scegliamo l'intervallo dell'ampiezza minima, raggiunto il quale il programma termina, troppo piccolo (ad esempio 0.000000000000001) il programma non termina mai e perde la sua qualifica di algoritmo. Sarebbe stato più corretto o mettere una protezione sul minimo numero accettabile in fase di
input o definire piuttosto il numero massimo di iterazioni. Sarebbe poi stato formalmente più
corretto lasciare nella procedura dicotomico solo la ricerca degli zeri ed affidare al programma
principale o ad una procedura scritta ad hoc la stampa dei risultati. E allora perché l'ho fatto? Perché
avevo scritto la procedura anni fa, ma inserita in un contesto diverso e ... l'ho copiata :-)! Per inciso,
le soluzioni sono 4: 2003, 2031, 2059 e 2087, gli anni per i quali il calendario (che non riporta la
Pasqua) è valido.
program Zeri;
uses
SysUtils;
var xini, dx, xfin,x0,x1,fini,ffin,incr:real;
segno:integer;
function f(x:real):real;
begin
{ x4-8180x3+25090190x2-34200948100x+17481136677369=0 }
f:=sqr(sqr(x))-8180*x*sqr(x)+25090190*sqr(x)-34200948100*x+17481136677369;
end;
procedure dicotomico(xin,xfin:real);
var xm,fm,fi:real;
begin;
if (xin>xfin) then
begin
xm:=xin;
xin:=xfin;
xfin:=xm;
end;
write ('X0=',xin:9:5,'
X1=',xfin:9:5);
fi:=f(xin);
while abs(xin-xfin)>incr do
begin
xm:=(xin+xfin)/2;
fm:=f(xm);
if (fi*fm>0) then
begin
xin:=xm;
end
else
begin
xfin:=xm;
end;
end;
writeln(
' converge su x=',xm:9:5,
' f(x)=',fm:9:5
);
end;
begin
write('Valore iniziale della ricerca : ');
readln(xini);
write('Valore finale della ricerca
: ');
P. Delise – I.T.C. Carli – Trieste (2003) – Dal calcolatore a Delphi
pag.19
readln(xfin);
write('Passo di ricerca
: ');
readln(dx);
write('Ampiezza minima
: ');
readln(incr);
{ inizia la ricerca}
fini:=f(xini);
if xini<xfin then segno:=1
else segno:=-1;
while (xini*segno<xfin*segno) do
begin
ffin:=f(xini+dx);
if (ffin*fini<=0) then
begin
dicotomico(xini,xini+dx);
end;
xini:=xini+dx;
fini:=ffin;
end;
writeln('*** FINE DEL PROGRAMMA ***');
readln;
end.
Delphi
Delphi è un compilatore pascal molto particolare; è, infatti, predisposto per scrivere
con facilità programmi che funzionino con il sistema operativo Windows (ne è uscita una nuova
versione, che si chiama Kylix, che è in grado di svolgere le stesse funzioni in altri sistemi
operativi): ci metterà a disposizione, quindi, la possibilità di costruire pulsanti, finestre, ecc.
Esso, però, è un estensione del linguaggio pascal che introduce i cosiddetti oggetti: è
un concetto molto importante sul quale vale la pena di perdere qualche parola.
Gli oggetti
Consideriamo un oggetto del linguaggio comune: ad esempio una scarpa. Essa è caratterizzata da alcune proprietà: il colore, il tipo di suola, il suo stato (nuova, vecchia, usata,
rotta...). Essa è caratterizzata anche da alcuni metodi di utilizzo: c'è ad esempio un metodo per lucidarla, un metodo per allacciarla e così via. Quando compero delle scarpe per la pioggia, ad esempio,
chiedo sempre se “tengono l'acqua”. Voglio sapere, cioè come si comportano davanti ad un evento
particolare: Se piove... . Di solito le scarpe non viaggiano da sole, ma in coppia. L'oggetto paio di
scarpe ne contiene due ed eredita alcune proprietà dall'oggetto scarpa. Il metodo di utilizzo infilare
esiste per le scarpe, per il maglione, per l'ago. Sono in realtà dei metodi diversi: lo stesso verbo
usato per oggetti diversi ha significati diversi; si dice che c'è un overloading, un sovraccarico, di significati per quel verbo.
Quando abbiamo definito le variabili del pascal ed abbiamo visto che potevano essere
reali, intere, carattere,... senza accorgerci abbiamo attaccato ad ognuna delle proprietà, dei metodi,
degli eventi.
Ad esempio le variabili integer non hanno la proprietà di avere dei decimali, che invece hanno i numeri di tipo real; entrambi i tipi di variabili hanno il metodo somma, che si rappresenta col simbolo + ma che indica due operazioni sostanzialmente diverse, tant'è che una l'avete
appresa in prima elementare e l'altra in terza; non possiamo calcolare il resto della divisione tra due
numeri reali. e così via.
Il Delphi estende il concetto di oggetto: pensate ad un pulsante sul desktop di Windows. Questo pulsante ha delle proprietà: il testo, le dimensioni, il colore,...; ha dei metodi: ad
esempio c'è un metodo per costruirlo, per distruggerlo, ...; ci sono degli eventi ai quali deve saper
reagire: se viene fatto il click su di esso, il doppio click,....
P. Delise – I.T.C. Carli – Trieste (2003) – Dal calcolatore a Delphi
pag.20
L'ambiente integrato di sviluppo
(IDE) di Delphi
Mandando in esecuzione Delphi, si
aprono, sullo schermo quattro finestre, evidenziate
nella figura 1, che possono essere spostate sullo
schermo o anche chiuse, una indipendentemente
dall'altra.
La finestra 1 contiene il menu del programma, alcuni pulsanti fondamentali e la libreria
degli oggetti. La finestra 2 mostra la cosiddetta
Form iniziale del programma; è la finestra entro la
quale collocheremo i vari oggetti (pulsanti, liste,
Figura 1
...). La finestra 3 mette a disposizione un editor di
testi per scrivere le parti di programma che saranno necessarie. La finestra 4 mostra l'elenco delle
proprietà che possono essere definite all'atto della costruzione del programma e degli eventi caratterizzanti i singoli oggetti.
Un primo programma
Scriviamo un primo programma, difficilissimo, che, premendo un pulsante, faccia
apparire la scritta “Ciao”. Nella finestra 1, degli oggetti, troviamo alcuni oggetti fondamentali, ripartiti tra più cartelline: Standard, Additional, Win32, … . Nella cartellina Standard viene mostrato
un oggetto che ha, come icona, un pulsantino con la scritta OK. Facciamo click su quel pulsantino e
poi un altro click sulla Form1. Verrà creato un pulsante con la scritta Button1. È possibile con il
mouse modificare le dimensioni di questo oggetto, trascinandolo per i quadratini neri che lo
circondano quando gli si fa click sopra, oppure spostarlo trascinandolo dopo aver posto il mouse al
suo interno. La finestra delle proprietà mostra le proprietà personalizzabili di Button1. Una proprietà è la proprietà Caption (in italiano intestazione). Modifichiamola scrivendo, ad esempio,
“cliccami”.
Nella finestra 1 c'è un altro oggetto identificato da una lettera A maiuscola. È un
campo in cui, o all'atto della creazione o da parte del programma, può essere inserito un testo. Come
prima, facciamo un click sull'icona e poi un altro su un punto della form non occupato dal pulsante.
Apparirà una scritta “Label1” e cambierà la tabella delle proprietà, mostrando quelle del nuovo
oggetto. Andiamo su quest'ultima e modifichiamo la proprietà Font. Selezioniamo una dimensione
adeguata per il programma ed un colore che ci piaccia. Fatto questo, posizioniamoci sulla proprietà
Caption e cancelliamo la scritta Label1.
Possiamo ora fare un doppio click sul pulsante: si aprirà la finestra dell'editor di testo
con già predisposta la funzione di gestione dell'evento Click sul pulsante Button1. Tra il begin e
l'end scriveremo Label1.caption:='Ciao'; che assegna alla caption dell'oggetto label1
il valore ciao.
Il programma viene mandato in esecuzione premendo il pulsante con la freccia verde
che si trova in basso a sinistra nella finestra 1 oppure con il menu Run | Run oppure ancora con il
pulsante F9. Facendo click sul pulsante apparirà la scritta Ciao. Per chiudere il programma dovremo
fare click sul pulsante con la x, in alto a destra della form.
Da un programma così semplice abbiamo appreso alcune cose importanti:
1. Come si inseriscono gli oggetti (click sull'icona dell'oggetto e poi click sulla form).
2. Come si cambiano alcune proprietà all'atto della creazione degli oggetti.
3. Come si gestisce l'evento OnClick di un pulsante.
4. Come si accede alle proprietà di un oggetto da programma: NomeOggetto.NomeProprietà
Abbiamo appreso anche che è cambiato il concetto di output. Non più un'istruzione di
scrittura (writeln) ma la definizione di una proprietà dell'oggetto sul quale apparirà il messaggio.
P. Delise – I.T.C. Carli – Trieste (2003) – Dal calcolatore a Delphi
pag.21
Non ci interessa salvare questo programma su disco. Vedremo più avanti, con programmi un poco più complessi, come si fa.
Alcuni oggetti
Vediamo alcuni oggetti più comuni, le loro proprietà e qualche evento. Per avere un
elenco più dettagliato basta creare un oggetto di cui si vuole conoscere le proprietà sulla form, o se
esiste già, fare click su di esso (una volta sola!) e poi premere F1. Il programma di help presenterà
l'elenco completo delle proprietà, dei metodi, degli eventi e qualche volta, anche degli esempi d'uso.
Ogni oggetto ha una proprietà Name che consente di dargli un nome diverso da quello
che viene proposto automaticamente da Delphi. Gli oggetti di Delphi si chiamano Button1, Button2,
Label1, Label2,... È possibile dare agli stessi dei nomi che richiamino esplicitamente la loro funzione. Si raccomanda, però di conservare nel nome anche il tipo dell'oggetto; ad esempio, nel programma già visto, dei buoni nomi potrebbero essere BtnOk per il pulsante e LblCiao per la
scritta. Anche la Form è un oggetto e come tale può avere un nome e delle proprietà. Per accedere a
queste ultime basta fare click su un punto della Form non utilizzato da altri oggetti. Il valore che assegneremo alla proprietà Caption apparirà sulla barra del titolo.
Nelle pagine che seguono saranno presentati alcuni degli oggetti più comuni in Delphi.
Non sono tutti e la presentazione avviene solo per riassunto; l'help del programma consente di studiare più a fondo il significato dei singoli oggetti che qui sono riportati solo per dare un'idea iniziale
delle possibilità che sono offerte.
Esaminiamo intanto gli oggetti più comuni della cartellina Standard.
Ico
na
Oggetto
Descrizione
MainMenu È possibile con questo oggetto costruire i menu che verranno poi riempiti di voci
facendo un doppio click sull'icona inserita nella form. Facendo click col pulsante
di destra si apre un menu volante la cui prima voce è Menu designer. Aperto il
Menu designer è possibile inserire la prima voce a sinistra del menu. Premendo
Invio si aggiunge la prima sottovoce, poi la seconda, e così via. Se una lettera
del menu è preceduta dal carattere &, la pressione dei tasti Alt + la lettera sarà
equivalente ad un click col mouse sulla voce del menu stesso. Col pulsante di
destra è possibile aprire un sottomenu. Facendo doppio click su una voce del
menu si apre automaticamente il gestore dell'evento OnClick.
Label
Questo oggetto è in grado di mostrare sulla form il contenuto della proprietà
Caption. Questa proprietà può essere modificata a programma o in fase di creazione. È possibile fare in modo che la dimensione orizzontale dell'oggetto si
adatti automaticamente all'ampiezza del testo o fissarla. Oltre alla proprietà
Font, importanti proprietà sono Visible, Height, Width, Left e Top. Visible può
nascondere il campo (valore False) o mostrarlo (valore True), Height e Width ne
determinano l'altezza e la larghezza, Left e Top la posizione all'interno della
Form.
P. Delise – I.T.C. Carli – Trieste (2003) – Dal calcolatore a Delphi
pag.22
Ico
na
Oggetto
Descrizione
Edit
È un oggetto nel quale possono scrivere sia il programma che l'utente. La proprietà che contiene il testo è Text. Le proprietà più comuni sono le stesse di Label; merit aggiungere anche Enabled che può disabilitare o meno (False o True)
la possibilità dell'utente di modificare il contenuto. Ci sono molti eventi che sono associati a questo oggetto e che spesso il programma deve gestire: OnChange
(cosa fare se questo campo cambia), OnClick (cosa fare se si è fattoi click sul
campo), OnKeyDown (cosa fare se viene premuto un tasto della tastiera, di
qualsiasi tipo, anche Shift, Ctrl,...), OnKeyPress (cosa fare se viene premuto un
tasto che genera un carattere),
Button
Questo è il più semplice oggetto di tipo pulsante. È ridimensionabile e quindi ha
le solite proprietà Height, Width, Left e Top. Ha inoltre la proprietà Caption che
definisce il testo che apparirà sul pulsante. L'evento tipico da gestire è OnClick,
nel quale si definiscono le istruzioni da far compiere al programma qualora si
faccia click sul pulsante. Nella cartellina Additional si trovano altri due tipi di
pulsanti.
CheckBox È una casellina cui si affianca a destra un testo esplicativo che viene scritto nella
Caption. La sua proprietà fondamentale è Checked che può essere True o False.
Il valore cambia ad un click del mouse.
RadioButton
Questi pulsanti viaggiano di solito in gruppo; sono molto simili a CeckBox, con
la differenza che mentre ogni CheckBox è indipendente nel suo stato e possono
avere tutte la proprietà Checked=True, soltanto un RadioButton di un gruppo
può assumere il valore Checked=True. Il valore cambia ad un click del mouse.
ListBox
Apre una finestra dentro alla quale compariranno degli Items che sono proprietà
della ListBox, ma ognuno è in realtà un oggetto dotato di altre proprietà e metodi. La ListBox ha un metodo Clear che cancella tutti gli Items. Come si è già
detto gli Items sono in realtà un oggetto composto da un vettore di stringhe
accessibili come Items[0], Items[1],... Ci sono complessivamente Items.Count
elementi che vanno, appunto, da 0 a Count-1. Items ha anche alcuni metodi: ad
esempio Add che consente di aggiungere una stringa alla lista, Delete, di eliminarne una. Nella ListBox gli Items potranno essere ordinati alfabeticamente la
proprietà Sorted viene messa a True.
GroupBox Serve per racchiudere esteticamente ed anche funzionalmente un gruppo di comandi. I RadioButton chiusi dentro ad un GroupBox sono mutuamente esclusivi,
vale a dire che solo uno di essi può essere Checked. La proprietà Caption viene
scritta Nell'angolo in alto a sinistra del riquadro.
Della cartella Additional si possono prendere in considerazione le altre due versioni
dei pulsanti, che consentono l'inserimento di un'icona a fianco del testo. Delle cartelline Win32, System, Data Access, ... non proponiamo nessun oggetto (anche se più avanti ne useremo uno), non
perché siano poco importanti quanto piuttosto perché di uso più sofisticato e complesso. Proponiamo di guardare con attenzione ancora la cartellina Dialogs che propone alcune importanti finestre
di dialogo.
P. Delise – I.T.C. Carli – Trieste (2003) – Dal calcolatore a Delphi
pag.23
Ico
na
Oggetto
Descrizione
OpenDia- Dialogo di apertura di un file. Ha il metodo Execute che attiva la tradizionale filog
nestra di Windows per aprire un file. Le proprietà più importanti sono FileName,
Filter (che consente di vedere solo i files con certe estensioni), InitialDir (che
consente di aprire inizialmente una cartella particolare). L'help del metodo Execute mostra un esempio di utilizzo completo dell'oggetto.
SaveDia- Dialogo di definizione di un file da usare per scrivere. È sostanzialmente identilog
co ad OpenDialog.
PrintDia- La gestione delle stampe non è sempre semplicissima. Questo dialogo predilog
sponde una finestra per la stampa.
PrinterSe- Questa finestra mostra una finestra più complessa e ricca di opzioni per la
tupDialog stampa.
L'equazione di secondo grado in Delphi
Rivediamo il solito programma delle equazioni di
secondo grado. Se il programma Delphi era già aperto, cancelliamo tutto con il comando File | New Application. Se abbiamo
appena mandato in esecuzione Delphi questo comando non
serve. Costruiamo una Form come quella della figura 2. Abbiamo intanto dato un titolo alla Form, modificandone la proprietà
Figura 2
Caption. Poi abbiamo creato tre oggetti Edit ai quali abbiamo laNome
Caption
Visible
sciato i nomi Edit1 … Edit3 ai quali abbiamo tolto ogni valore
Label1 x^2 +
True
nella proprietà Text.
Abbiamo creato 7 oggetti Label alle proprietà Label2 x +
True
Caption e Visible dei quali abbiamo assegnato i valori riportati
Label3 = 0
True
nella tabella riportata sotto alla figura. Abbiamo creato, infine,
Label4 x1
False
un pulsante di nome Button1 alla cui proprietà Caption imporreLabel5 x2
False
mo il valore “Risolvi”.
Poiché abbiamo fatto un certo lavoro, è bene proce- Label6 Label6
False
dere al suo salvataggio su disco. Un programma Delphi è soLabel7 Label7
False
stanzialmente composto da un progetto, da una o più Form e da
almeno tante Unit quante sono le Form che contengono il codice delle procedure relative alle singole Form. In questo semplice programma dobbiamo avere, pertanto, pronti 2 nomi: quello da dare
alla Unit e Form e quello da dare al progetto e che sarà lo stesso anche per il programma eseguibile.
Daremo il nome Eq2gr al progetto ed Eq2gr1 alla Unit. Noi possiamo procedere al salvataggio
delle singole Unit oppure al salvataggio completo (File | Save All). Noi faremo riferimento sempre
a quest'ultima opzione che ci garantisce comunque il salvataggio dell'intero lavoro. La prima richiesta di salvataggio è quella della Unit, cui daremo il nome di Eq2gr1 ; questa tecnica ci consente
di dare nomi progressivi alle eventuali altre unit che dovessimo scrivere (non lo faremo, ma non si
sa mai). Poi viene chiesto il salvataggio del Progetto, Eq2gr.
Facendo doppio click sul pulsante si aprirà l'editor con il gestore dell'evento Click.
Alcune righe sono già predisposte dal compilatore e qui sono evidenziate con fondo grigio. Le altre
contengono lo stesso codice dei programmi in pascal più la conversione da testo in numero e viceversa dei valori dei parametri e delle soluzioni, in quanto i campi di Delphi accettano solo valori
stringa, ma per eseguire i calcoli con i numeri abbiamo bisogno di convertirli in valori numerici.
Delphi, infine, consente l'inserimento dei commenti lunghi al massimo una riga, come con il C++,
P. Delise – I.T.C. Carli – Trieste (2003) – Dal calcolatore a Delphi
pag.24
dopo la sequenza di caratteri //.
procedure TForm1.Button1Click(Sender: TObject);
var x1,x2,a,b,c,d:real;
coda,codb,codc:integer; //conterranno il codice di errore delle funzioni val
appoggio:string; //variabile per preparare i numeri sui campi stringa
begin
// nascondo i campi label4..7; serve nelle chiamate successive alla prima
label4.visible:=false;
label5.visible:=false;
label6.visible:=false;
label7.visible:=false;
// trasformo in numeri i valori di a,b,c che sono sotto forma di testo
// coda, codb, codc saranno diversi da 0 se il testo ha caratteri non legali
val(edit1.text,a,coda);
val(edit2.text,b,codb);
val(edit3.text,c,codc);
if (coda+codb+codc<>0) then //se la somma dei tre cod è <>0 vuol dire
//che almeno uno è illegale
begin
// Visualizzazione di un messaggio di errore
MessageDlg('Uno dei parametri è illegale',mtError,[mbOK],0);
end
else
begin
// calcolo il discriminante
d:=b*b-4*a*c;
if (d<0) then
begin
// discriminante minore di 0
MessageDlg('Discriminante minore di 0',mtInformation,[mbOK],0);
end
else
begin
d:=sqrt(d);
// calcolo x1
x1:=(-b+d)/(2*a);
// rendo visibili i campi relativi ad x1
label4.visible:=true;
label6.visible:=true;
// converto il numero x1 in una stringa
str(x1,appoggio);
// la assegno alla caption di label6
label6.caption:=appoggio;
if (d>0) then
begin
// se il discriminante è > 0 ci sono due soluzioni
// preparo x2;
x2:=(-b-d)/(2*a);
label5.visible:=true;
label7.visible:=true;
str(x2,appoggio);
label7.caption:=appoggio;
end;
end;
end;
end;
A questo punto, se non abbiamo commesso errori, possiamo compilare ed eseguire il programma,
non dimenticando mai di salvare tutto prima di dare il comando Run, perché … non si sa mai.
P. Delise – I.T.C. Carli – Trieste (2003) – Dal calcolatore a Delphi
pag.25
Calcolo di π con il metodo Montecarlo
Yes, I need a drink alcoholic of course after the heavy lectures involving quantum mechanics
3, 1 4 1 5
9
2
6
5 3
5
8
9
7
9
Questa frase ci consente di ricordare abbastanza agevolmente i primi decimali di un numero che, secondo alcuni, ha qualche importanza in matematica. A parte gli scherzi, tutti noi abbiamo preso
quasi sempre per fede che il rapporto tra la circonferenza ed il suo diametro sia dato dal famoso
3,14..., π per gli amici. Ma siamo sicuri che sia proprio questo il suo valore? Esistono molti metodi
per verificarlo. Uno, simpatico per la sua semplicità, si basa su metodi probabilistici: consideriamo
un quarto di cerchio di raggio 1; la sua area sarà π/4. Osserviamo il
quarto di circonferenza inserito all'interno di un quadrato di lato 1. Se
noi disponessimo di un dado speciale in grado di generare numeri casuali reali tra 0 ed 1, anziché interi tra 1 e 6, potremmo associare a
due lanci un punto all'interno del quadrato; alcuni di questi punti, poi,
cadrebbero anche all'interno del quarto di cerchio (ed altri no); il
rapporto tra i punti generati che cadono all'interno del cerchio e quelli
che sono caduti all'interno del quadrato (tutti) dovrebbe essere eguale,
compatibilmente con le fluttuazioni dovute alla probabilità, al
rapporto tra le aree, cioè π/4. Con un numero di lanci sufficientemente grande, per la legge empirica del caso, potremo rendere queste
fluttuazioni arbitrariamente piccole. Il calcolatore è in grado di generare numeri apparentemente casuali (meglio è definirli pseudocasuali, in quanto sono comunque ottenuti mediante delle formule
deterministiche e la loro apparente casualità è dovuta alla difficile prevedibilità a priori dei risultati
e dalla loro distribuzione). Ad esempio se noi prendiamo un numero iniziale scelto a caso
(0,3287659563) e lo moltiplichiamo per un numero che abbia certe caratteristiche (p. es. 97) e
prendiamo la parte a destra della virgola decimale ed usiamo questo numero per generarne un altro
e così via, otterremo una sequenza di numeri facilmente calcolabili, compresi tra 0 ed 1 e con caratteristiche di apparente casualità. Il Pascal ci propone due funzioni per generare i numeri pseudocasuali: la prima è la funzione Random che genera un numero, la seconda è la funzione Randomize
che crea il primo numero della sequenza con criteri casuali, ad esempio partendo dall'ora
dell'orologio del computer.
Nella figura 3 troviamo il progetto
della prima versione della Form. Abbiamo introdotto due GroupBox, con le Caption “Numero
massimo di eventi” e “valore trovato”. All'interno abbiamo inserito due campi
Edit e predisposto due Button con le Caption
“Calcola” e “Chiudi”. La parte restante
della form ci servirà dopo.
Il pulsante Chiudi servirà per chiudere il programma. Facendo doppio click su di esso entriamo nell'editor del metodo Click. Basterà scrivere
la chiamata al metodo Application.
Terminate ed il programma si chiuderà faFigura 3
cendo click sul pulsante. L'altro pulsante avrà
invece un metodo più complesso che viene riportato qui sotto. Al solito sono in grigio le righe già
scritte da Delphi.
procedure TForm1.Button1Click(Sender: TObject);
var i,nlanci,s0,s1:integer;
x,y:real;
appoggio:string;
P. Delise – I.T.C. Carli – Trieste (2003) – Dal calcolatore a Delphi
pag.26
begin
val(edit1.text,nlanci,i);
s0:=0;
s1:=0;
randomize;
for i:=1 to n do
begin
x:=random;
y:=random;
s0:=s0+1;
if (x*x+y*y<=1) then s1:=s1+1;
end;
str(s1/s0*4:7:5,appoggio);
edit2.text:=appoggio;
end;
end.
Aggiungiamo un grafico
Aggiungiamo un grafico che mostri come il rapporto tra le variabili S1 ed S0
converga pian piano verso 3,14... Il contenitore dei grafici è l'oggetto PaintBox che si trova nella
cartella System. Esso ha una proprietà che si chiama Canvas (e che è posseduta anche da altri
oggetti, come ad esempio la form, per cui potremmo anche farne a meno). La proprietà Canvas è un
oggetto che ha le sue proprietà (Brush e Pen per definire la forma della penna con cui si vuole disegnare, Font per definire le caratteristiche dei testi che si inseriranno nei grafici, …) ed alcuni metodi: MoveTo, LineTo, … che consentono di disegnare. Purtroppo le cose non sono così semplici. La
Canvas ha l'origine posta in alto a sinistra, l'asse x orizzontale che va da sinistra verso destra e l'asse
y che va dall'alto verso il basso. L'unità di misura, poi è il punto, per cui se noi realizziamo una
PaintBox di 401 pixel per 401 pixel (la misura la leggiamo sullo schermo mentre trasciniamo il
rettangolo) dovremo effettuare delle trasformazioni.
• Trasformazione per y: se al posto di y riportiamo 400 – y otteniamo di orientare l'asse y come
tradizione matematica comanda. Se poi invece di y riportiamo 400- y*100 otterremo una scala
amplificata per 100 che meglio si adatta alla dimensione del nostro grafico. In generale la formula per la trasformazione sarà data da
Hpixel
Hpixel − y
1
Hy
dove Hpixel è l'altezza in pixel (nel nostro caso sarà Form1.Paintbox1.Height) ed Hy sarà invece
il massimo valore che può assumere l'asse y (nel nostro caso sarà 4). Il +1 serve perché i 401
punti vanno da 0 a 400. Le istruzioni per completare la scalatura sono isolate nella funzione Ploty.
• Trasformazione per x: l'asse x è orientato correttamente. Però dobbiamo far stare in 400 punti i
risultati di 100, 1000, 10.000, ... lanci (non più di 2.147.483.647 perché il contatore è un intero).
L'operazione di scalatura sarà, allora, semplicemente
Wpixel
x
1
Nlanci
dove Wpixel è l'ampiezza è l'ampiezza in pixel (nel nostro caso Form1.PaintBox1.Width) e
Nlanci il numero di lanci. Le istruzioni per completare la scalatura sono isolate nella funzione
Plotx.
• Poiché, se il numero di lanci è molto grande, il grafico tende a confinare la zona in cui le variazioni sono ampie in un intervallo molto ristretto assumendo poi un aspetto piatto, è preferibile in
certi casi utilizzare l'asse x in scala logaritmica. Le istruzioni per farlo sono state inserite nella
funzione con un commento.
Il testo del programma è riportato qui di seguito. Avendo parametrizzato l'ampiezza e
la larghezza dell'oggetto PaintBox, possiamo a questo punto scavalcare il vincolo iniziale di 401 e




P. Delise – I.T.C. Carli – Trieste (2003) – Dal calcolatore a Delphi
pag.27
dimensionarlo in modo che copra la massima ampiezza ed altezza possibili. Come già fatto prima
sono segnate con fondo grigio le righe che Delphi genera da solo o che avevamo già scritto
nell'esempio precedente.
unit pigreco1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, ExtCtrls;
type
TForm1 = class(TForm)
GroupBox1: TGroupBox;
Edit1: TEdit;
GroupBox2: TGroupBox;
Edit2: TEdit;
Button1: TButton;
Button2: TButton;
PaintBox1: TPaintBox;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
function ploty(y:real;mx:real):integer;
begin
ploty:=trunc(form1.PaintBox1.Height-y*form1.PaintBox1.Height/mx)-1;
end;
function plotx(x:real;mx:real):integer;
begin
// asse x lineare
plotx:=trunc(x*Form1.Paintbox1.Width/mx);
// asse x logaritmico
// if x=0 then plotx:=0
// else plotx:=trunc(ln(x)*form1.paintbox1.width/ln(mx));
end;
procedure TForm1.Button1Click(Sender: TObject);
var i,nlanci,s0,s1,py,px,nx,p10:integer;
x,y:real;
appoggio:string;
begin
val(edit1.text,nlanci,i);
nx:=nlanci div 10;
paintbox1.Repaint; // cancella il grafico precedente, se c'è
paintbox1.Canvas.pen.color:=clblack; //sceglie il colore nero
paintbox1.Canvas.moveto ( plotx(0,nlanci),ploty(4,4)); //posiziona la penna
paintbox1.Canvas.lineto (plotx(0,nlanci),ploty(0,4)); // disegna l'asse y
paintbox1.canvas.lineto(plotx(nlanci,nlanci),ploty(0,4)); // disegna l'asse x
paintbox1.Canvas.moveto (plotx(0,nlanci),ploty(3.141592,4)); // posiziona
paintbox1.Canvas.lineto(plotx(nlanci,nlanci),ploty(3.141592,4)); // disegna π
s0:=0;
P. Delise – I.T.C. Carli – Trieste (2003) – Dal calcolatore a Delphi
pag.28
s1:=0;
randomize;
paintbox1.canvas.moveto(0,0); // posiziona la penna nell'origine
paintbox1.Canvas.pen.color:=clRed; // sceglie il colore per il grafico
for i:=1 to nlanci do
begin
x:=random;
y:=random;
s0:=s0+1;
if (x*x+y*y<=1) then s1:=s1+1;
py:=ploty(s1/s0*4,4); // calcola la y in pixel
px:=plotx(i,nlanci); // calcola la x in pixel
paintbox1.canvas.lineto(px,py); //traccia la riga dalla posizione precedente
if (i mod nx = 0)then //se i è multiplo di n/10 disegna un tratto sull'asse
begin
paintbox1.canvas.moveto(px,ploty(0,4));
paintbox1.canvas.lineto(px,ploty(0,4)-5);
paintbox1.canvas.moveto(px,py); // riposiziona la penna sull'ultimo punto
end;
end;
str(s1/s0*4:7:5,appoggio);
edit2.text:=appoggio;
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
application.terminate;
end;
end.
Un programma con oggetti nuovi: la calcolatrice per angoli
Anche se ormai misurate gli angoli in radianti, sicuramente vi ricorderete di averli misurati in gradi, primi e secondi e forse avrete anche provato a calcolare somme di angoli scritti in
questo modo. È un'operazione di una certa complessità: i primi ed i secondi hanno un'aritmetica
modulo 60, i gradi modulo 360. Pensiamo allora ad un oggetto angolo, caratterizzato da tre numeri
che ne identificano l'ampiezza; pensiamo anche ad un metodo per sommare due angoli tra di loro.
Progettiamo di conseguenza una calcolatrice che esegua le somme di angoli.
Costruiamo una form come quella della figura 3. Abbiamo intanto dato un titolo alla
Form, modificandone la proprietà Caption. Poi abbiamo creato sei oggetti Edit ai quali abbiamo lasciato i nomi Edit1 … Edit6 ai quali abbiamo tolto ogni valore nella proprietà Text. Abbiamo creato 8 oggetti Label alle
proprietà Caption dei quali abbiamo assegnato i valori riportati nella tabella sotto alla figura.
Creiamo, infine, un pulsante di nome Button1
alla cui proprietà Caption imporremo il valore “+”.
Figura 4
Poiché abbiamo fatto un certo lavoro, è bene
procedere al suo salvataggio su disco. In questo semplice programma dobbiamo avere pronti 2 nomi: quello da dare alla Unit e Form e quello da dare al progetto
e che sarà lo stesso anche per il programma eseguibile. Daremo il nome Angoli al progetto ed
Angoli1 alla Unit. Ricordiamo che la prima richiesta di salvataggio è quella della Unit, cui daremo il nome di Angoli1; questa tecnica ci consente di dare nomi progressivi alle eventuali altre
unit che dovessimo scrivere (non lo faremo, ma non si sa mai). Poi viene chiesto il salvataggio del
Progetto, Angoli.
P. Delise – I.T.C. Carli – Trieste (2003) – Dal calcolatore a Delphi
pag.29
Dobbiamo ora entrare nell'editor di testi; Il moNome
Caption
do più semplice per farlo è, al solito, il doppio click sul
Label1
Primo angolo
pulsante, anche se in questo esempio dovremo muoversi
Label2
°
all'interno di tutte le istruzioni che il compilatore predispone
automaticamente. Dobbiamo, infatti, progettare un nuovo
Label3
'
oggetto, l'angolo. Esso è caratterizzato da tre proprietà, tre
Label4
''
numeri interi, che ne definiscono l'ampiezza: gradi, primi e
Label5
Secondo angolo
secondi.
Label6
°
Una volta definito l'oggetto, dobbiamo predisporre anche gli strumenti con i quali operare sull'oggetto
Label7
'
stesso: ne dobbiamo definire almeno quattro:
Label8
''
• FromText che partendo da tre campi testo, li converte in
tre valori numerici, i gradi, i primi ed i secondi
dell'angolo.
• ToText che partendo dai valori dei gradi, primi e secondi di un angolo, li converte in testi da
poter utilizzare per l'input/output.
• Sum che associa ad un terzo angolo, la somma di altri due.
• Per rendere il discorso più completo si è pensato anche di introdurre un secondo metodo Sum
che somma all'angolo in questione un altro angolo. Questa operazione di introdurre due funzioni
che si distinguono per la modalità d'uso (una ha due angoli come argomento ed una ne ha uno solo), ma che hanno lo stesso nome si dice overloading.
• Normalize che riconduce sempre i gradi a modulo 360, i primi ed i secondi a modulo 60 generando un opportuno riporto. Questa funzione sarà chiamata solo dagli altri metodi dell'oggetto
Definiremo, allora, una classe Tangolo con le proprietà gradi, primi e secondi e i
metodi che abbiamo elencato. Il metodo Normalize che viene usato solo all'interno degli altri metodi, non viene dichiarato pubblico.
Qui sotto riportiamo l'intero testo di angoli.pas segnando con fondo grigio i testi che sono stati prodotti automaticamente da Delphi.
unit Angoli1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls;
type
TForm1 = class(TForm)
Edit1: TEdit;
Edit2: TEdit;
Button1: TButton;
Edit3: TEdit;
Edit4: TEdit;
Edit5: TEdit;
Edit6: TEdit;
Label1: TLabel;
Label2: TLabel;
Label3: TLabel;
Label4: TLabel;
Label5: TLabel;
Label6: TLabel;
Label7: TLabel;
Label8: TLabel;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
P. Delise – I.T.C. Carli – Trieste (2003) – Dal calcolatore a Delphi
pag.30
public
{ Public declarations }
end;
Tangolo=class
gradi,primi,secondi:integer;
procedure normalize(var gradi,primi,secondi:integer);
public
procedure sum(a1,a2:Tangolo); overload;
procedure sum(a1: Tangolo); overload;
procedure fromtext (a,b,c:string);
procedure totext(var s1,s2,s3:string);
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure Tangolo.normalize(var gradi,primi,secondi:integer);
var riporto:integer;
begin
riporto:=secondi div 60;
secondi:=secondi mod 60;
primi:=primi+riporto;
riporto:=primi div 60;
primi:=primi mod 60;
gradi:=(gradi + riporto) mod 360;
end;
procedure Tangolo.sum(a1,a2:Tangolo);
var g,p,s:integer;
begin
g:=a1.gradi+a2.gradi;
p:=a1.primi+a2.primi;
s:=a1.secondi+a2.secondi;
normalize(g,p,s);
self.gradi:=g;
self.primi:=p;
self.secondi:=s;
end;
procedure Tangolo.sum(a1:Tangolo);
begin
self.sum(self,a1);
end;
procedure Tangolo.fromtext(a,b,c:string);
var cc,v:integer;
begin
val(a,v,cc);
self.gradi:=v;
val(b,v,cc);
self.primi:=v;
val(c,v,cc);
self.secondi:=v;
self.normalize(self.gradi,self.primi,self.secondi);
end;
procedure Tangolo.totext(var s1,s2,s3:string);
begin
str(self.gradi,s1);
str(self.primi,s2);
str(self.secondi,s3);
end;
procedure TForm1.Button1Click(Sender: TObject);
var s1,s2,s3:string;
P. Delise – I.T.C. Carli – Trieste (2003) – Dal calcolatore a Delphi
pag.31
a1,a2:Tangolo;
begin
a1:=Tangolo.create;
a2:=tangolo.create;
a1.fromtext(Edit1.text, Edit2.text,edit3.text);
a2.fromtext(Edit4.text,Edit5.text,edit6.text);
a1.sum(a1,a2);
a1.totext(s1,s2,s3);
edit1.text:=s1;
edit2.text:=s2;
edit3.text:=s3;
end;
end.
Va notata la parola riservata self che fa riferimento all'oggetto stesso. Self.gradi sono, all'interno dei
metodi di Tangolo, i gradi dell'angolo in questione, e così via. Va infine notato come non basta dichiarare gli oggetti, ma bisogna anche crearli.
P. Delise – I.T.C. Carli – Trieste (2003) – Dal calcolatore a Delphi
pag.32