Tesi di Marco Gottardo

Ottimizzazione dei tempi di ricerca in alberi bilanciati.

Premessa a cura di Marco Gottardo.(download file .doc)

Premessa

Nella presente Tesi si espone una potente tecnica di accesso ai dati residenti in memoria volatile.

Lo scopo è quello di estrarre da una quantità a priori non delimitata di dati ( Set dinamico ) delle informazioni in un tempo garantito logaritmico.

La Tesi è di tipo sperimentale, ovvero contestualmente alla produzione dell'elaborato cartaceo è stato prodotto un effettivo programma sorgente che implementa le principali routine di accesso alla struttura.

Il programma ippodrom è stato scritto in linguaggio C++ perché ben si presta per la soluzione di problemi di questo tipo.

La facilità con cui il C++ è in grado di manipolare indirizzi di locazioni di memoria, ovvero di allocare dinamicamente lo spazio necessario al contenimento della struttura base rende le routine di gestione abbastanza chiare anche se notevolmente complesse.

I prerequisiti necessari per un approccio sufficientemente chiaro alla lettura della Tesi sono la Conoscenza del linguaggio C++, nozioni sui principali algoritmi di allocazione dinamica, conoscenza delle tecniche di programmazione orientate agli oggetti ( O.O.P.).

Un aiuto per la lettura si potrà trovare in appendice dove vengono riportati i principali costrutti utilizzati per implementare il programma.

Sempre in appendice sono anche riportati alcuni lavori prodotti sulla stessa base concettuale ma per utilizzi diversi e a scopo commerciale.

I programmi serre.exe e orv.exe sono stati commissionati da alcuni clienti intenzionati a servirsene per il loro lavoro.

I due programmi in appendice, dei quali si riportano solo poche informazioni, sono di notevole interesse informatico vista la loro capacità di potersi interfacciare con il mondo fisico esterno attraverso la porta seriale e la porta parallela.

La Tesi è piuttosto ricca di diagrammi di flusso e pseudo codici in modo da facilitare nella lettura coloro che siano esperti in linguaggi diversi dal C++.

Il primo diagramma di flusso esposto rappresenta il comportamento globale del programma, ovvero la sequenza di azioni che possono essere eseguite dall'operatore sulla pagina grafica principale, mentre i successivi diagrammi rappresentano il flusso all'interno delle singole funzioni.

 Alcuni dei concetti teorici utilizzati sono stati approfonditi in ambito accademico diverso dal POLITECNICO di STUDI AZIENDALI  quindi si ritiene doveroso un ringraziamento al Professor Romanin Jacur Giorgio per le lezioni di ottimizzazione su reti impartitemi durante il corso di Ricerca Operativa di cui è docente presso l'Università degli studi di Padova Facoltà di Ingegneria .

Analogamente si vogliono ringraziare i Professori Franco Bombi e Giovanni De Poli docenti di Fondamenti di Informatica per le nozioni di programmazione in C++ di primo e secondo livello.

In modo particolare durante le lezioni del prof. Bombi si sono apprese le nozioni per giungere allo sviluppo della classe Stringa, molto utilizzata nel presente elaborato.

Per avermi costantemente seguito durante la stesura della Tesi un particolare ringraziamento va al mio relatore Dott. Zecchinato Beniamino.

download frontespizio tesi in formato .doc

I.S.S.E.A. sa

POLITECNICO di STUDI AZIENDALI

Facoltà di Scienze Aziendali

Specializzazione Ingegneria Informatica

Lugano (Svizzera)

Titolo della tesi:

Ottimizzazione dei tempi di

Ricerca in alberi bilanciati

Dottorando:

Gottardo Marco – matr.n°    549/99 

Docente relatore:

Dott. Zecchinato Beniamino

 

Sessione del 10 Maggio 1999

 

Download Tesi in formato .doc

 

Casella di testo: Ottimizzazione dei tempi di ricerca in alberi bilanciati.
Si ritiene opportuno iniziare la Tesi presentando il diagramma di flusso del programma ippodrom.exe utilizzato per applicare gli algoritmi di bilanciamento degli alberi binari di cui ne è argomento. I diagrammi di Flusso sono visibili effettuando il download della tesi in formato doc  dal link sovrastante.
Dall’analisi del diagramma si nota l’azione associata ad ognuno dei sei tasti funzione a disposizione dell’utente.

 

 

 

 

 

Scopo e funzionamento del programma.

Il programma di gestione degli ippodromi riportato nella presente trattazione è il risultato della compilazione di un insieme di moduli sorgenti in C++ che utilizzano la tecnica della programmazione orientata  agli oggetti come richiesto dalle moderne tecniche di sviluppo del software.

I’interfaccia utente è costituita da una pagina grafica che riporta alcuni tasti funzionali alla pressione dei quali sono associati gli eventi di gestione di un data base utile per l’archiviazione dei dati dei cavalli presenti in un ippodromo allevamento.

Le manovre possibili da console sono la modifica della scheda identificativa dell’animale, l’inserimento di un nuovo animale nell’archivio, l’eliminazione di un dato archiviato, la visualizzazione dei dati nell’archivio, il backup su floppy dei dati memorizzati, ovviamente è previsto un tasto Quit per l’uscita dal programma e il relativo ritorno al sistema operativo della macchina.

Al fine di essere più “user friendly” il progetto comprende alcuni moduli di programma in cui sono riportate le primitive della classe dedicata alla gestione del dispositivo di puntamento seriale standard ( il mouse ).

Nel caso ci fosse una perdita del collegamento con il mouse deve comunque rimanere possibile l’accesso alle funzioni del programma in maniera manuale e ciò sarà possibile tramite la pressione del tasto corrispondente alla lettera colorata di rosso nella tastiera della stazione di lavoro.

La pagina grafica a seconda dell’azione verificatasi aprirà una finestra di dialogo con chiare indicazioni sul da farsi al fine di portare a termine la manovra  richiesta  dall’utente.

La pagina grafica è settata in modalità VGA Standard con risoluzione  640 x  480  pixel, e richiede per la corretta funzionalità il supporto di alcuni file standard di inizializzazione forniti dalla casa produttrice del pacchetto Software, il primo è EGAVGA.BGI e gestisce l’hardware della scheda video, i successivi GHOT.CHR e TRIP.CHR gestiscono i caratteri nella modalità grafica ( Font ) rispettivamente di tipo Gotico e Triplex, il primo usato solo nella schermata introduttiva presente solo nella versione accademica a titolo di presentazione del programmatore ( dovrà essere eliminata in una eventuale versione commerciale), mentre il secondo è utilizzato per le indicazioni di funzionalità dei tasti durante il funzionamento del programma.

Sono stati scelti appositamente dei colori tenui per evitare che lunghe permanenze a pagina fissa possano impressionare il monitor, si ritiene che questa soluzione sia più consona rispetto all’utilizzo di uno screen saver   visto che l’operatore impegnato nel controllo delle corse potrebbe avere necessità di accedere rapidamente alle funzioni e quindi un salva schermo sarebbe causa di rallentamenti.

Per una maggiore rapidità di accesso ai dati si è scelto un algoritmo di immagazzinamento e ordinamento con struttura ad albero binario autobilanciante del quale si riporterà nei successivi paragrafi un’esposizione teorica.

Struttura dati ad albero autobilanciante.

Per struttura ad albero si intende un grafo connesso e privo di cicli.

Tra tutti gli elementi dell’albero si distingue il primo inserito che ne costituisce la radice, mentre i successivi saranno elementi intermedi o addirittura foglie se inseriti nei livelli più profondi.

Per livello si  intende il numero di passi necessari per il raggiungimento del nodo o foglia dell’albero.

Ogni nodo o foglia dell’albero sono legati alla struttura tramite degli archi che nella memoria del calcolatore sono rappresentati da puntatori agli altri elementi della struttura.

Quando ogni elemento di un albero contiene solo due puntatori agli altri elementi della struttura si dice che esso è un albero binario.

Gli elementi discendenti da un nodo sono detti i suoi figli mentre tale nodo è detto il padre.

Preso un nodo come riferimento, se esso ha un padre ed esiste anche il padre del padre, allora l’eventuale altro figlio di quest’ultimo che si trova allo stesso livello del padre del nodo di riferimento è detto per esso lo zio. 

Si dice che un albero binario è bilanciato quando il numero di passi necessario per il raggiungimento di ogni sua foglia è costante o differisce al più di uno, d’altro canto un albero è totalmente sbilanciato quando degenera in una lista concatenata.

Gli alberi Autobilancianti, conosciuti come Alberi Rosso-Neri  sono dei particolari tipi di alberi binari con delle proprietà aggiuntive che abbinate a particolari algoritmi di bilanciamento detti “rotazioni” mantengono la profondità di ogni percorso dalla radice alle foglie dell’albero pressochè costante o differente al massimo di uno, soddisfacendo la definizione di albero bilanciato testé citata.

Ogni nodo dell’albero conterrà i campi Colore, Chiave, Sinistra, Destra, e P, e l’albero nel suo complesso potrà essere considerato di tipo Rosso-Nero se soddisfa le seguenti proprietà: 

1.      Ogni nodo o e Rosso o è Nero.

2.      Ogni foglia “vuota” terminale ( puntatore a NULL ) è Nera.

3.      Se un nodo è Rosso allora entrambi i suoi figli sono Neri.

4.      Ogni percorso semplice con origine in un qualunque nodo fino ad una foglia terminale contiene lo stesso numero di nodi neri.

 

Un importante concetto che rende possibile il bilanciamento della  struttura è quello di altezza nera definita come il numero di nodi neri incontrati da un certo nodo X ( escluso dal conteggio ) in maniera discendente fino alle foglie, se ne deduce immediatamente che ogni percorso a partire dalla radice fino alle foglie di un albero Rosso-Nero contiene lo stesso numero di nodi Neri, ha cioè la medesima altezza nera.

L’altezza nera dell’intero albero è dunque l’altezza nera della radice.

Esiste sempre il un albero Rosso-Nero un nodo Zio per il nuovo inserito se esso non è uno dei primi tre elementi inseriti.

Affinché l’altezza nera rimanga invariata durante l’inserimento di nuovi nodi, che per regola avviene sempre in Rosso, è opportuno riaggiustare di volta in volta l’albero con delle “Rotazioni” della struttura, delle volte limitate ai soli colori dei nodi interessati, altre volte invece che determinano lo spostamento di puntatori.

Lo scopo principale dell’intervento in correzione delle funzioni di rotazione sarà quello di ripristinare le proprietà che sono state violate da un’azione sull’albero Rosso-Nero.

L’eventualità di violare le proprietà di Rosso-Nero nasce da una manovra di Inserimento o cancellazione di elementi nella struttura dinamica.

Ricordiamo prima, di procedere,  alcune nozioni fondamentali per gli alberi di ricerca binari:

La struttura base è rappresentabile da  un puntatore di accesso, da un campo riservato ai dati da immagazzinare tra i quali se ne sceglie uno come chiave di ricerca, due campi puntatori a strutture identiche di norma chiamati Sinistro, Destro.

La funzione di inserimento contiene un algoritmo che collega il nuovo elemento con il puntatore Sinistro alla struttura se la sua chiave è minore del già esistente, lo collega a destra se invece dovesse risultare maggiore, tale modo di procedere assegna già un certo ordine agli elementi i quali potranno essere visitati secondo tre metodi base:

1.      Preordine:    R,S,D  ( Si visita prima la radice, poi il sottoalbero Sinistro ed infine il Destro ).

2.      Inordine:       S,R,D ( Si visita prima il sottoalbero Sinistro, poi la Radice, poi il Destro ).

3.      Postordine:  S,D,R ( Si visita prima il sottoalbero Sinistro, poi il Destro ed infine la radice ).

 

Negli alberi Rosso-Neri varranno le stesse modalità di scansione ( visita ) degli alberi binari visto che anch’essi appartengono a questa categoria, ma per avere una visualizzazione ordinata crescente delle chiavi sarà opportuno utilizzare la modalità Inordine.

Con le due sottostanti figure chiariamo come operare le rotazioni nelle due possibili maniere:

Rotazione Sinistra.

Facendo perno sull’arco Destro della radice del sottoalbero la struttura ruota verso Sinistra, per fare ciò è necessario spostare il puntatore di accesso dalla posizione iniziale  al figlio destro, ciò comporta che non può essere a Nil  il puntatore destro della radice prima della manovra.

Il puntatore del figlio destro divenuto ora radice si sposta in posizione destra della nuova foglia.

Rotazione Destra.

Facendo perno sull’arco Sinistro della radice del sottoalbero la struttura ruota verso Destra, per fare ciò è necessario spostare il puntatore di accesso dalla posizione iniziale  al figlio Sinistro, ciò comporta che non può essere a Nil  il puntatore Sinistro della radice prima della manovra.

Il puntatore del figlio Sinistro divenuto ora radice si sposta in posizione Sinistra della nuova foglia.

Il diagramma di flusso delle procedure di rotazione verrà presentato a seguito di quello della procedura che fa uso di esse, ovvero RNA_Inserisci(Nodo& z).

Procedura di inserimento nella struttura.

Essendo la struttura ad albero Rosso-Nero un sottoinsieme delle strutture classiche ad alberi binari di ricerca si usa per l'inserimento la medesima procedura operante secondo il concetto base dell'inserzione della nuova chiave nel sottoalbero sinistro se essa è minore del padre locale oppure nel sottoalbero destro se essa risulta maggiore.

In alcune applicazioni la chiave di ricerca risulta essere un numero intero per cui il concetto di maggiore o minore è piuttosto intuitivo, in altri casi invece è rappresentata da una stringa alfanumerica o alfabetica quindi il concetto di maggiore o minore ( che continua sempre a valere ) è un po’ più elaborato, sarà infatti necessario andare ad analizzare lettera per lettera delle due stringhe in comparazione le rispettive posizioni ASCII.

Secondo quanto esposto la stringa 'Marco'  risulta minore della stringa 'Marina' e quindi supponendo già inserita nell'albero la chiave 'Margot' essa risulterà avere dopo l'attivazione della funzione di inserimento come figlia sinistra la chiave 'Marco' e come figlia destra la chiave 'Margot'.

Il nuovo nodo che sta per essere inserito verrà per regola colorato preventivamente di rosso e ciò di norma comporta la violazione della proprietà per cui se un nodo è rosso ha entrambi i figli neri.

La violazione di una delle proprietà comporta l'attivazione delle routine di  rotazione e ricolorazione.

Supponiamo che nel data base siano già stati preventivamente inseriti i dati relativi ai cavalli che indichiamo per semplicità con A,C,D, e che ora si voglia procedere all'inserimento di B.

L'inserzione genera una nuova morfologia dell'albero per la quale si posso distinguere tre principali casi:

1          Il nodo Zio del nuovo inserito è Rosso.

2          Il nodo Zio del nuovo inserito è Nero è il nuovo inserito è figlio Destro del padre.

3          Il nodo Zio del nuovo inserito è Nero è il nuovo inserito è figlio Sinistro del padre.

L'attivazione della normale procedura di inserimento degli alberi binari di ricerca, relativamente all’aggiunta del nodo indicato con  B, genera la struttura riportata in figura:

Notiamo che siamo in presenza di un caso di tipo 1. (Lo zio del nuovo inserito è rosso ).

Il nodo C, rappresentato come la radice dell'albero può in realtà essere un nodo interno e quindi radice del sottoalbero in esame.   Supporre il colore del nodo C  Nero non è restrittivo.

 

Come possiamo facilmente notare l’algoritmo opera eseguendo una ricolorazione dei nodi interni relativamente al sottoalbero secondo la sequenza:

 

1.      C diventa Rosso.

2.      A diventa Nero.

3.      D diventa Nero.

4.      B rimane Rosso.

 

Analizzando la struttura ottenuta dalla rotazione dei colori intuiamo che nel caso la struttura rappresentasse un sottoalbero  la contraddizione alla regola “Se un nodo è rosso allora entranbi i suoi figli sono neri” potrebbe essere stata violata ad un livello di profondità inferiore, ma la cosa non costituisce un problema visto che è facilmente risolvibile con una nuova iterazione dell’algoritmo.

Sarà opportuno iterare fino al raggiungimento del nodo radice.

Esiste ovviamente l’opportunità che si presenti il caso simmetrico nel quale la procedura di inserimento del nuovo nodo colloca quest’ultimo come figlio sinistro anziché destro del sottoalbero preesistente.

La struttura iniziale è quella della figura:

 

Essa analogamente a quanto visto  in precedenza effettua solamente una rotazione dei colori generando la struttura nella figura sottostante:

 

Passiamo ora all’analisi di un caso di tipo 2, quello cioè dove lo Zio del nuovo inserito è Nero.    Il nuovo  nodo B  è figlio destro inserito alla Sinistra della struttura preesistente

La struttura generata dalla procedura di inserimento è:

L’algoritmo dopo aver rilevato la particolare situazione attiva la routine di rotazione Sinistra generando la struttura indicata con 1, successivamente essendo violata ancora la regola “ un nodo rosso ha entrambi i figli neri”  si dovrà attivare la routine di rotazione Destra che genererà la struttura bilanciata riportata nella figura con la dicitura 2

 

1                                                                        2

durante il normale funzionamento il programma attiva un ciclo while che prova il caso 1, se questo fallisce prova il caso 2 seguito dal caso 3.

Vediamo un esempio eseguito con chiavi di ricerca numeriche.

Nell’albero passato all’algoritmo si distingue un caso di tipo 1 relativamente al nuovo elemento inserito ( 4  nella figura es 1-a) indicato con una x.

Si esegue una rotazione dei colori che sposta l’ambiguità verso l’alto ( figura es1-b ) portando la struttura in un esempio di caso 2.

 

                

 es 1-a                         es 1-b                                     es 1-c

 

es 1-d

Nella figura es 1-c si è eseguita una ricolorazione tipica del caso 1 che ha generato la nuova contraddizione indicata dalla x nell’albero es 1-b.

Per eliminare questa nuova ambiguità analizziamo il colore dello zio rappresentato dal nodo indicato con la chiave di ricerca 14, essendo Nero applichiamo il caso 3 ovvero una rotazione verso destra.

Se lo zio ( nodo con chiave 14 ) fosse stato Rosso si sarebbe applicato il caso 1, e la radice ( chiave 11 )  sarebbe rimasta Nera.

Il diagramma di flusso che segue rappresenta la sequenza di azioni che esegue la funzione RNA_Inserisci(Nodo& z) evidenziando l’ordine in cui le routine di supporto vengono richiamate.

Analizzando il Flowchart notiamo che in prima istanza viene richiamata una procedura di inserimento canonico in un albero binario di ricerca.

Il concetto principale che sta alla base di un corretto inserimento per un nuovo nodo in un albero di ricerca è che esso discenda i collegamenti tra le varie foglie nel ramo destro se esso è maggiore di ciò che “vede” nel campo chiave del Nodo nella posizione attuale, oppure discenda a sinistra se risulta essere minore.

Quindi, definito un puntatore p assegnato in origine alla radice dell’albero e passata alla funzione di inserimento canonico  una chiave numerica o non numrica da inserire essa posizionerà p secondo il costrutto decisionale:

 

if ( k <  p à chiave)

            p = p à sin;

else

            p = p à des;

 

Subito dopo l’inserimento il nuovo nodo viene colorato di Rosso, come si vede dal secondo passo del diagramma di flusso della funzione RNA_Inserisci(Nodo &z), e questo puo’ causare le seguenti situazioni:

·        Il nodo è il primo inserito e quindi non viene violata nessuna proprietà de Rosso Neri.

·        Il nodo non è il primo inserito ma ugualmente non viene violata alcuna proprietà dei Rosso Neri essendo il nodo che ne risulterà essere il padre di colore Nero. Il nuovo nodo viene ad essere inserito come foglia.

·        Il nodo non è il primo inserito e la procedura inserisci(z) lo pone in modo che nella struttura il padre è rosso, questa è una palese violazione della proprietà 3 degli alberi Rosso Neri e richiede un riaggiustamento della struttura.

Nel caso si fosse verificato il terzo punto l’algoritmo decide come procedere in funzione, come già accennato del colore dello zio, ovvero del colore di quel nodo che è fratello del padre del nuovo inserito.

Pseudo codice della funzione RNA_Inserisci(Nodo &z);

RNA_Inserisci(Nodo &z);

1.      Inserisci(z);

2.      Coloro il nuovo inserito di Rosso.

3.      While (il padre del nuovo inserito è rosso e non sono arrivato alla radice )

4           Se (il nodo in esame è figlio sinistro)

5           Porto il puntatore ausiliare y a puntare allo zio di x; 

6               Se (si verifica il 1° caso cioè lo zio del nodo è Rosso )

7               Il padre di x diventa Nero.

8               Lo zio diventa Nero e il nonno diventa Rosso.

9               Il controllo passa sul nonno

10           Altrimenti

11                Se (Lo zio del nonno è Nero)

12                 Caso 2°: il nodo è figlio destro

13                  Il controllo passa sul padre

14                  sin_rotazione(x)

15           Entro nel caso 3°

16           Il padre diventa Nero e Il nonno diventa Rosso

17           des_rotazione(sul nonno di x )

18       Altrimenti (caso simmetrico )

19    Se (il nodo in esame è figlioDestro)

20       Porto il puntatore ausiliare y a puntare allo zio ( che sta a sinistra ) di x; 

21           Se (si verifica il 1° caso cioè lo zio del nodo è Rosso )

22           Il padre di x diventa Nero.

23           Lo zio diventa Nero e il nonno diventa Rosso.

24           Il controllo passa sul nonno

25           Altrimenti

26                Se (Lo zio del nonno è Nero)

27                 Caso 2°: il nodo è figlio sinistro

28                  Il controllo passa sul padre

29                  des_rotazione(x)

30           Entro nel caso 3°

31           Il padre diventa Nero e Il nonno diventa Rosso

32           sin_rotazione(sul nonno di x )

33    Coloro la radice di Nero

34    Se (La radice è una foglia)

35    Poni la radice a Nil

36    End.

Riporto il pseudocodice della funzione sinistra rotazione dal quale poi si potrà piu’ facilmente ottenere il sorgente in C++ o qualunque altro linguaggio.

Le due funzioni di rotazione aspettano come parametro di accesso un puntatore al nodo su cui devono ruotare.

Le funzioni verranno richiamate con sin_rotazione(x)  e con des_rotazione(x) dove con x si indica il puntatore su cui si vuole effettuare la rotazione.

 

 

 

1.      Sin_rotazione(Nodo *x)   //Arriva dalla funzione chiamante l’indirizzo del nodo x

2.      Dichiaro il puntatore ausiliario y e lo posiziono dove il  campo destro di x

3.      Pongo il campo destro di x uguale al campo sinistro di y

4.      Se il campo sinistro di y non è foglia //       (yàsin != foglia)

5.            Allora Yàsinàp = x

6.      Pongo il padre di y = al padre di x

7.      Se il padre di x punta a Null

8.            Allora Radice = y

9.            Altrimenti

10.                    Se x è figlio sinistro //    cioè se vale x = = xàpàsin

11.                    Allora  pongo xàpàsin =y

12.                    Altrimenti  pongo xàpàdes = y

13.   yàsin =x //Sposto x alla sinistra di y

14.   xàp =y 

 

 

 

Il pseudo codice della funzione di rotazione destra è strutturalmente identico a quello della rotazione a sinistra dal quale deriva semplicemente scambiando ogni voce des con sin e viceversa.

 

 

 

1.      des_rotazione(Nodo *x)   //Arriva dalla funzione chiamante l’indirizzo del nodo x

2.      Dichiaro il puntatore ausiliario y e lo posiziono dove il  campo sinistro di x

3.      Pongo il campo sinistro di x uguale al campo destro di y

4.      Se il campo destro di y non è foglia //        (yàsin != foglia)

5.            Allora Yàdesàp = x

6.      Pongo il padre di y = al padre di x

7.      Se il padre di x punta a Null

8.            Allora Radice = y

9.            Altrimenti

10.                    Se x è figlio destro //    cioè se vale x = = xàpàdes

11.                    Allora  pongo xàpàdes =y

12.                    Altrimenti  pongo xàpàsin = y

13.   yàdes =x //Sposto x alla destro di y

14.   xàp =y 

 

Eliminazione di un dato da un albero bilanciato. 

Analizziamo ora gli eventi associati al tasto funzione Elimina del programma.

Come dovrebbe risultare chiaro dalla stringa di promemoria stampata sotto di esso viene resa possibile la modifica della struttura dinamica.

L’eliminazione di un dato in un albero Rosso Nero comporta generalmente la violazione di una delle quattro proprietà della struttura che dovranno essere ripristinate a cura di opportune routine.

In pratica il programma ippodrom sarebbe in grado di funzionare correttamente anche se privo di questa opzione, difatti se scarichiamo la struttura sbilanciata nel file sequenziale usando la visita “inorder” eseguita da printree per successivamente ricostruire l’albero riportando il contenuto del dbf in RAM usando RNA_Inserisci(Nodo &z), otterremo ugualmente un albero Rosso Nero. 


 

 

 

Pur mantenendo un costo computazionale O( log2 n) la funzione di cancellazione di un Nodo dall’albero è sicuramente più complicata sia dal punto di vista concettuale che dal punto di vista della necessità d’iterazione con altre routine.

Per una più chiara esposizione rendiamo lo funzione associata al tasto “Elimina” indipendente dal programma ippodrom creandogli un main program provvisorio che ne faccia utilizzo.

Le azioni base da eseguire sono riportate nei diagrammi di flusso, riportiamo ora una lista riassuntiva.

Per cancellare un Nodo sono utilizzate le funzioni:

 

1.      elimina(int &togliere,Nodo* scan)        //per posizionare il puntatore scan sul Nodo da togliere

2.      RB_cancella(Nodo *z)                            //per iniziare la sequenza di cancellazione

3.      Tree_successor(Nodo *x)           //Per cercare i puntatori corretti da modificare

4.      Tree_Minimun(Nodo *x)                         //Trova la minore chiave nell’albero       

5.      RB_DELETE_FIXUP(Nodo *x)            //ripristina le proprietà Rosso Nere gestendo le rotazioni

·        des_rotazione(Nodo *x);

·        sin_rotazione(Nodo *x);

La funzione main( ) che gestisce provvisoriamente l’albero è:

int main(){

            int index;

            char ch;

            while (k!=-1){

            cout<<"inserire chiave à ";

            cin>>k;

            if (k!=-1){

             ps=new Nodo(k);

             ch='n';//inizializza condizione uscita acquisizione nome cavallo

          cout<<"inserire nome cavallo à ";

    index=0;

                while (ch!='q'){

 

                ch=getch();

                stringanome[index]=ch;

                ps->name[index]=ch;

                index++;

                }

            RNA_Inserisci(*ps);

             }

            }

 printree(radice);

 cout<<"Chiave da eliminare à ";

 cin>>k;

  elimina(k,radice);

  printree(radice);

getch();

return 0;

}

La funzione elimina è in realtà derivata dalla funzione inserisci, essa esegue infatti una visita della struttura verificando lo stato di maggioranza o minoranza della chiave da cercare con quella attualmente puntata dal puntatore usato per la scansione.

Come output la funzione elimina restituisce il puntatore all’elemento che si vuole eliminare.

 

void elimina(int &togliere,Nodo* scan){

            if ((scan!=NULL)&&(scan->chiave!=togliere)){

                        if (scan->chiave > togliere)

                        elimina(togliere,scan->sin);

                        if (scan->chiave < togliere)

                        elimina(togliere,scan->des);

                        }

            if ((scan != NULL)&&(scan->chiave = = togliere))

            {

            gotoxy(3,24);

            cout<<"trovato elemento à"

                <<"("<<scan->chiave<<")"<<"\n";

            gotoxy(3,25);

            cout<<"la radice e' "<<scan->p->chiave;//<<radice->chiave;

            }

            if (scan = = NULL)

            {

            gotoxy(3,24);

            cout<<"elemento inesistente "<<"\n";

            }

            Nodo *canc;

            canc=new Nodo; //Genero un nodo sentinella da passare alla routine RB_cancella

            canc->chiave=scan->chiave;

            canc->col=NERO;

            canc->des=scan->des;

            canc->sin=scan->des;

            canc->p=canc->p;

            RB_cancella(canc); //Esposta nelle pagine seguenti.

}

 

 

Viene presentato ora il pseudo codice e il sorgente della funzione RB_cancella(Nodo *z).

Essa attende il input l’indirizzo del nodo da togliere trovato da “elimina”.

 

1.    Arriva dal chiamante l’indirizzo del Nodo da togliere.

2.    Se (Il nodo da togliere è una foglia)

3.          Allora ( cerco il Nodo con chiave immediatamente superiore “Successore”)

4.          Puntatore y = indirizzo Successore

5.    Se (esiste un sottoalbero sinistro)

6.          Allora (scendo con il puntatore x di un livello a sinistra)

7.          altrimenti (scendo con il puntatore x di un livello a destra)

8.    in ogni caso (faccio puntare il padre di x al padre di y )

9.    Se (il padre di y punta alla radice )

10.      Allora ( il puntatore x punta alla radice )

11.      altrimenti   Se(  y indica un figlio sinistro )

12.                          allora (faccio puntare il lato sinistro del padre di y a x )

13.                          altrimenti (faccio puntare  il lato destro del padre di y a x )

14.Se ( y è diverso da z)

15.      Allora (copio la chiave puntata da y in quella puntata da z ed gli altri eventuali campi)

16.Se ( il colore del Nodo puntato da y è Nero)

17.      Allora ( chiama la routine RB_FIXUP( passandogli l’indirizzo attuale di x)

18.restituisci y

 

 

Codice sorgente in C++.

 

Nodo* RB_cancella(Nodo *z){

 

            Nodo *y;

            Nodo *x;

            if (z->sin = = NIL||z->des = = NIL)

                        y->chiave=z->chiave;

                        else y = Tree_successor(z);

                         if (y->sin != NIL ) x=y->sin;

                                   x=y->des;

                                   x->p=y->p;

                         if (y->p = = NIL ) radice=x;

                         else  if (y = = y->p->sin )

                                   y->p->sin=x;

                                   else y->p->des=x;

                         if(y->chiave!=z->chiave) z->chiave=y->chiave;

                         //copiare ora gli altri campi;

                         if (y->col = = NERO)

                                    RB_DELETE_FIXUP(x);

      return y;

      }

 

La funzione Tree_Minimun(Nodo *x) discende lungo tutti i puntatori sinistri fino a NULL .

 

Nodo* Tree_Minimun(Nodo *x){

   while ( x->sin!=NIL)

   {

   x=x->sin;

   }

   return x;  //Il valore più in basso a sinistra in un albero è il minimo valore contenuto

}

 

La funzione Tree_successor(Nodo *x) restituisce il valore di grandezza contigua al puntato.

Nodo* Tree_successor(Nodo *x){

Nodo *y,*aux;

 if ( x->des!=NIL)

            return Tree_Minimun(x->des);

 y=x->p;

   while ( y !=NIL  && x==y->des)

   {

   y=x;

   y=y->p;

   }

   return y;

}

 

void RB_DELETE_FIXUP(Nodo *x){

Nodo *w;

while(x!=radice && x->col = = NERO)

            {if (x = = x->p->sin)

             {

              w=x->p->des;

              if (x->col = = ROSSO)

                {

                w->col=NERO;

                x->p->col=ROSSO;

                sin_rotazione(x->p);

                w=x->p->des;

                }

 

                if(w->sin->col = = NERO && w->des->col = = NERO)

                 {

                  w->col=ROSSO;

                  x=x->p;

                  }

                  else

                  if (w->des->col = = NERO)

                   { w->sin->col = NERO;

                   w->col=ROSSO;

                   des_rotazione(w);

                   w=x->p->des;

                   w->col=x->p->col;

                   x->p->col=NERO;

                   w->des->col=NERO;

                   sin_rotazione(x->p);

                   x=radice;

                   }

              }//Fine corpo primo if

            else

            {

            w=x->p->sin;

              if (x->col = = ROSSO)

              {

                w->col=NERO;

                x->p->col=ROSSO;

                des_rotazione(x->p);

                w=x->p->sin;

                if(w->des->col = = NERO && w->sin->col = = NERO)

                 {

                  w->col=ROSSO;

                  x=x->p;

                 }

                  else

                  if(w->sin->col = = NERO)

                  {

                        w->des->col=NERO;

                        w->col=ROSSO;

                        sin_rotazione(w);

                        w=x->p->sin;

                        w->col=x->p->col;

                        x->p->col=NERO;

                        w->sin->col=NERO;

                        des_rotazione(x->p);

                        x=radice;

                   }

             }

 

            }

      }

             x->col=NERO;

      }//Chiude la funzione FIXUP

 

Nodo* Togli(Nodo *z){

            Nodo *y,*x;

            if (z->sin = = NIL || z->des = = NIL)

            y=z;

            y=Tree_successor(z);

            if(y->sin!=NIL)

                        x=y->sin;

                 else x=y->des;

                 if(x!=NIL)

                        x->p=y->p;

                  if (y->p=NIL) radice=x;

                  else if(y = = y->p->sin)

                            y->p->sin=x;

                            else y->p->des=x;

                  if(y!=z)

                   z->chiave=y->chiave;

            return y;

}

Complessità degli algoritmi

Un algoritmo risulta essere più o meno efficiente in funzione del numero di iterazioni o cicli macchina che esso richiede per restituire un risultato passatogli un parametro n che indica la dimensione dell’input.

Per effettuare l’analisi della complessità è necessario stabilire un’unità di misura con la quale misurare la quantità di risorse utilizzate.

I parametri secondo cui si può dare un significato alla complessità sono il tempo d’esecuzione e la quantità di memoria utilizzata.

Di norma un miglioramento del parametro occupazione di memoria comporta un peggioramento del tempo d’esecuzione e viceversa. Delle volte quindi sarà necessario trovare un compromesso tra un miglior tempo e una minor occupazione di memoria.

A seconda delle caratteristiche dell’algoritmo da implementare bisognerà decidere a priori quali di esse  siamo più interessati ad ottimizzare.

Al fine di effettuare una valutazione oggettiva della complessità dell’algoritmo dobbiamo svincolarci il più possibile dal fornire ad esso un set di dati predefiniti cercando di studiare il comportamento con l’ausilio di strumenti matematici applicati a valori molto grandi di quantità d’input.

Premesso ciò è facile intuire che la misura della complessità è di norma una funzione del numero n dei dati d’ingresso f ( n ).

Tale funzione esprime il numero d’operazioni che l’algoritmo compie quando opera su masse di dati di dimensione n.

Nell'eventualità che anche i valori numerici dei dati in input influenzassero la complessità dell’algoritmo allora si effettua l’analisi su più casi:

·        Caso migliore

·        Caso peggiore

·        Caso medio

Ogni algoritmo sarà classificato in base all’andamento della loro funzione di complessità.

Se la funzione di complessità di un algoritmo aumenta linearmente con la dimensione dei dati d’ingresso, esso sarà detto lineare, se invece è quadratica o esponenziale l’algoritmo sarà detto quadratico o esponenziale.

La funzione che misura la complessità di un algoritmo può essere espressa in termini analitici rispetto a n che è, come già detto, la dimensione dei dati fornitagli.

Ad esempio alcune funzioni di complessità possono essere:

nn2, log n,  nlog n,  3n2+5n

In taluni casi la funzione di complessità è costruita in modo che per alcuni valori di n ha la prevalenza una sua parte rispetto ad un’altra, ma superato un certo valore di avviene  un’inversione dei ruoli.

Un’evenienza di questo tipo comporta che per complessità espresse da funzioni polinomiali,  funzioni  cioè composte da più addendi, uno di essi prenda in fatto di crescita un’indiscussa predominanza nei confronti di un altro.

Diventando il secondo o successivi addendi trascurabili si afferma che la funzione è asintotica per grandi valori di  n al termine predominante.

Presa a titolo d’esempio la funzione di complessità:

f( n ) = 3n2 + 5n

si nota che dopo un certo valore n in poi la crescita dell’addendo 3n2  è enormemente maggiore di quella di 5n, pertanto l’andamento globale della complessità può essere assunto come n2 .

Se vogliamo usare una terminologia più corretta possiamo affermare che la complessità dell’algoritmo in esame è asintotica al termine predominante 3n2 o semplicemente è asintotica a 3n2.

Quanto esposto ovviamente vale solo nel caso in cui ci si sia posti in un caso nel quale l’input è sufficientemente grande da soddisfare le ipotesi iniziali di “caso peggiore”, in effetti, nella maggioranza dei casi pratici, quelli cioè in cui il numero dei dati immessi non è poi così elevato un algoritmo di complessità f( n ) =  n 2 + 43727 potrà risultare peggiore di un altro algoritmo di complessità f( n ) =  45 n 3.

In definitiva si definisce che un algoritmo ha complessità O(f(n)) se esistono opportune costanti a,b,m tali che il numero d’istruzioni che sono svolte dal programma nel caso peggiore con dati d’ingresso di dimensione n soddisfa la relazione;

"n > m           t ( n ) < a f ( n ) + b

dove t ( n ) rappresenta la funzione della quale si vuole calcolare la complessità asintotica.

Secondo la definizione data si verifica che alle funzioni  t ( n ) in tabella possono essere associate le funzioni O( f ( n ) ) indicate

 

t ( n )

O ( f ( n ) )

2 n log ( n ) + 16

O ( n log n )

0.1 n5 + 1200 n

O ( n5 )

3n + 15n5

O ( 3n)

 

Anche se come si può facilmente notare la ricerca di una funzione maggiorante introduca un’approssimazione nella valutazione di O ( f( n )) il nuovo concetto esposto è molto utile per dare un parametro di confronto tra due diversi algoritmi concorrenti verso un medesimo scopo, in poche parole ci fornisce una chiara indicazione su quale dei due è più conveniente usare.

Se definiamo, informaticamente parlando, il problema come ciò che si risolve con un algoritmo, allora possiamo affermare per induzione che il problema stesso ha la complessità del migliore algoritmo che lo può risolvere.

Non sempre è noto qual è il miglior algoritmo che può risolvere un problema; non si può nemmeno dire se la scienza attuale stia trascurando soluzioni migliori a problemi comunemente in uso, tuttavia esiste una nutrita bibliografia che può darci un’idea per una migliore soluzione applicabile a ciò che potrebbe servirci in ambito professionale.

Da quanto detto la valutazione della complessità degli algoritmi può essere utile per confrontare tra loro soluzioni diverse per lo stesso problema, nel nostro caso rappresentato dalla necessità di accedere nel minore tempo possibile a una struttura dinamica al fine di estrarne un elemento.

Una volta estratti i dati dal file sequenziale residente nella memoria di massa, come si vede dal diagramma di flusso di ippodrom.c, si costruisce una struttura dinamica ad albero bilanciato Rosso Nero.

Chiediamoci cosa può avere suggerito una soluzione di questo tipo, che come si può vedere dai sorgenti inclusi nella presente Tesi richiede un notevole sforzo per il programmatore, piuttosto che  una più snella applicazione di una lista concatenata o di un albero binario di ricerca classico.

In effetti s’intuisce facilmente che al crescere della massa di dati da gestire un data base basato su di  una lista concatenata potrebbe risultare piuttosto inefficiente poiché l’elemento per il quale possiamo effettuare una ricerca potrebbe in maniera equiprobabile trovarsi in testa, in mezzo, o addirittura in coda alla lista.

Nel caso in cui l’elemento cercato si trovasse appunto in coda si dovrà effettuare una scansione completa della lista prima della terminazione con successo della routine di estrazione. Giacché il valore di complessità significativo è quello applicato al caso peggiore se nella lista vi sono n  elementi questi sarà direttamente proporzionale a n.

Per trovare un elemento in una lista concatenata si richiede dunque una complessità algoritmica derivante dalla funzione:  

f ( n ) = k n

Dove

1.      n  =  Numero dei dati trattati dall’algoritmo

2.      k =  Numero dei passi elementari necessari alla macchina per far avanzare la scansione della lista di un  dato. 

 

Ovviamente per un grosso valore di n, un basso numero di passi necessari per la scansione di un dato divengono trascurabili nel calcolo della complessità.Tralasciando la costante moltiplicativa k si ottiene che la ricerca in una lista concatenata ha un costo computazionale:

O ( f ( n ) ) = O ( n )

 Nel caso s’inserissero nella lista concatenata un milione ( 1.000.000 ) di dati tra cui se ne vuole estrarre uno in particolare, e che l’algoritmo fosse lanciato su di un’ipotetica macchina in grado di elaborare un Milione d’istruzioni al secondo nel caso peggiore si potrà avere la risposta dopo un ordine di grandezza di un secondo.

Nel caso paradossale che il dato cercato non fosse presente nella lista la risposta se pur negativa sarà ugualmente data dopo il medesimo tempo computazionale con l’ovvio spreco di risorse che ne deriva.

Supponiamo ora di concatenare gli elementi della lista, dopo aver modificato la struttura base del Nodo secondo la figura sottostante:

Successivamente  immaginiamo che i dati da allocare dinamicamente si presentino secondo la sequenza temporale sviluppata seguendo il verso della freccia:

7

1

0

3

12

8

9

22

14

2

6

11

18

 

---------------------------------------------------------------------------------------------------------------------------> tempo.

La routine di inserimento itera il confronto tra la nuova chiave da inserire con quelle che incontra lungo i percorsi discendenti fino ad incontrare la costante NULL collocando la nuova foglia in modo che sia figlia destra se maggiore del padre o figlia sinistra se minore.

Il pseudo codice della routine dichiarato il puntatore scans per la scansione della struttura è:

if ( Nuovo dato <  scans à dato)        // Se il dato da inserire è più piccolo di quello puntato da scans

                    scans = scans à sin;       //collocalo alla sua sinistra

     else                                                //altrimenti

                    scans = scans à des;      //collocalo alla sua destra

L’attivazione della routine da origine alla struttura  che è rappresentata con un disegno suddiviso in livelli.

Dall’osservazione dell’albero si possono distinguere 4 livelli di annidamento degli elementi che rappresentano il numero di passi massimo che sono necessari per raggiungere un Nodo anche nel caso peggiore.

Una seconda osservazione porta alla constatazione che non tutte le foglie della struttura si trovano allo stesso livello, difatti il Nodo di chiave 0 si trova a livello 2 mentre quello di chiave 18 è di ben due livelli più profondo.

Il numero massimo di passi necessario per il raggiungimento di una foglia a partire dalla radice rappresenta la profondità dell’albero ed è in qualche modo anche proporzionale al tempo massimo necessario per il raggiungimento di un Nodo qualunque, in altre parole la complessità computazionale dell’algoritmo di ricerca in un albero binario.

Questa struttura è nota in bibliografia come albero binario di ricerca BST (Binary search tree).

Si deve comunque fare presente che la struttura ha avuto origine da un insieme numerico i cui elementi si sono presentati in sequenza temporale senza seguire alcuno schema, in altre parole sono stati inseriti in maniera in qualche modo casuale.

 

 

È possibile dimostrare con l’ausilio di calcoli probabilistici che l’altezza media di un albero binario di ricerca costruito in maniera casuale con n chiavi distinte  è O(log2 n), posto log2 n=h, ne consegue che la complessità dell’algoritmo di ricerca risulta dell’ordine O( h ).

Ricollegandoci all’esempio costruito per la lista concatenata eseguiamo la ricerca di un elemento sul medesimo data base contenente un milione ( 1.000.000 ) di dati nella medesima ipotetica macchina in grado di elaborare un Milione d’istruzioni al secondo nel caso peggiore si potrà avere la risposta dopo un tempo non più direttamente proporzionale al numero dei dati ma bensì all’altezza media dell’albero.

Ne consegue che nel caso medio il data mi è restituito dopo :

 

O ( f ( n ) ) = O ( log2 n )

Essendo h = log2 n si ha  O ( f ( n ) ) = O ( h ).

Se calcoliamo il logaritmo in base 2 della dimensione dell’input si ottiene che l’albero BST ha un’altezza media pari a 19.

Log2 1.000.000 =  19   ( nel caso medio )

Ne consegue che: Se il dato da estrarre è foglia per il BST allora il numero di passi necessari nel caso medio per accedervi tramite l’algoritmo di ricerca è appunto h = 19.

L’ipotetica macchina restituisce il risultato dopo diciannove microsecondi.

Un confronto diretto tra l’efficienza dei due algoritmi di ricerca, il primo applicato ad una struttura a lista concatenata e il secondo ad un BST, da un’idea di quanto può essere vantaggioso usare il secondo.

O(f(n))

Struttura

Tempo su n = 1.000.000

O(n)

Lista concatenata

K1 * 1*101 sec.

O(log n)

BST (caso medio)

K2 * 19*10-6 sec.

Come già affermato l’analisi è stata effettuata nel caso in cui i dati di input siano pervenuti in maniera del tutto priva di ordinamento.

Per mettere in crisi un algoritmo di questo tipo sarà sufficiente presentargli in input una sequenza di dati ordinati in maniera crescente o decrescente facendo sì che faccia degenerare l’albero in una lista concatenata.

In quest’artificioso caso la complessità computazionale diventa O(n) anche per il BST.

Al fine di evitare la degenerazione modifichiamo gli algoritmi di inserimento nella struttura BST, in modo che faccia ausilio delle routine di rotazione che si cureranno di mantenere l’albero bilanciato, garantendo quindi dei tempi di accesso in ogni caso pari a O(Log2 n).

Si giustifica così la scelta di una struttura ad albero Rosso Nero anche se essa comporta un notevole incremento di difficoltà nella stesura degli algoritmi.

 

Creazione dei tipi di dati astratti con l’utilizzo delle classi del C++

Il C++ consente la creazione di tipi di dato astratti per i quali è consentito accedere solo attraverso uno specifico insieme di funzioni dette i metodi della classe.

Una classe per definizione è appunto un tipo di dato astratto per il quale valgono le tre proprietà di ereditarietà, incapsulamento e polimorfismo.

Una volta creata una classe il programmatore potrà utilizzare il tipo di dato da essa generata esattamente nello stesso modo in cui potrebbe utilizzare un tipo predefinito, ne deriva che l’unico limite a cui deve sottostare questo potente linguaggio di programmazione è la fantasia dei programmatori.

Come regola base si deve tenere a punto saldo il fatto che la definizione di un nuovo tipo di dato astratto quale può ad esempio essere il ‘bottone’ utilizzato nel programma di cui è argomento la presente tesi, è definito in concomitanza con tutte le operazioni fattibili su di esso.

Il costrutto class è ciò che ci permetterà di dichiarare l’oggetto ( classe ), esso deve essere seguito dal nome che si vuole associare alla classe.

Segue una coppia di parentesi graffe aperta e chiusa che costituiscono il corpo della classe.

E’ obbligatorio chiudere il corpo della classe con il terminatore di riga punto e virgola.

Per quanto riguarda i diritti di accesso ( information hidding ), per le classi in C++ sono disponibili i costrutti private, public seguiti dal semicolon ( i due punti ).

Con questi costrutti si definiscono le modalità con cui   si potrà accedere ai dati incapsulati.

Nel campo della classe dichiarato public si può accedere da qualsiasi punto del programma, mentre al campo private,  o meglio alle sue funzioni membro o variabili, possono accedere solo quelle funzioni che sono membri della stessa classe. 

Costruttori e distruttori

Ad una classe è sempre associata una particolare funzione membro incaricata dell’allocazione dell’esemplare dinamicamente generato, talune volte questa funzione inizializza  le variabili del nuovo esemplare con valori predefiniti ( costruttore di default ), altre volte invece delega l’inizializzazione all’intervento da tastiera dell’operatore ( costruttore semplice ).

Quando durante l’esecuzione di un programma un certo esemplare diviene obsoleto si delega la sua deallocazione dalla memoria ad una particolare funzione membro detta il distruttore.

Il distruttore, che ha lo stesso nome della classe, sarà attivato  automaticamente all’uscita dal programma oppure all’uscita dallo scope (campo di visibilità )  in cui è stato evocato il costruttore dell’esemplare in questione.

Anche il costruttore porta lo stesso nome della classe quindi per far distinguere da esso il costruttore al compilatore è necessario precedere quest’ultimo con una tilde ~.

L’oggetto può essere inizializzato in più modi diversi  pertanto devono poter coesistere più costruttori, questo è possibile grazie al fatto che il C++ consente l’overload degli operatori distinguendo le funzioni non solo per il loro nome ma anche per la lista dei parametri ad esse associate.

 

Funzioni membro.

La classe contiene nella sua definizione un insieme di funzioni dette le funzioni membro che costituiscono l’insieme delle operazioni applicabili al nuovo tipo di dato astratto.

Le funzioni membro utilizzate dalla classe Mouse sono: Il costuttore Mouse( ), Attiva( ),Disattiva( )

Le funzioni di Bottone sono:  disegna( ), stampa( ),  premi( ),  rilascia( ), oltre  ad  alcune   funzioni

Inline,nel campo protetto della classe, mentre l’interfacciamento con l’uente è delegato alle funzioni

del campo pubblico che possiamo vedere nel capitolo in cui è riportato il codice sorgente bottone.h.

Analogamente si rimanda a visionare il  codice per le funzioni della classe RNA e Pannello.

 

Progettare software in C++

Per programmare in C++ è fondamentale conoscere la sostanziale differenza che vi è tra la Dichiarazione di una variabile e la definizione della stessa, nonché conoscere gli stessi concetti relativamente alle funzioni.

Distinguiamo tra definizione e dichiarazione intendendo per la prima la scrittura del codice sorgente nel file che ne definisce la struttura.  Una definizione non comporta allocazione di memoria al contrario della dichiarazione per la quale si intende l'effettiva intenzione di utilizzare la struttura di cui si compone la variabile.

In C++ tutte le funzioni hanno nome globale, sono in altre parole richiamabili da qualsiasi altra, fatta l'ovvia eccezione per le funzioni membro delle classi che vanno soggette alle regole che dominano i diritti d’accesso.

Analizziamo il seguente codice:

void sort(int * )

            f( ) {

                   sort( .......)

                   }

 

la parola chiave void davanti alla sort ( int *) assicura che la chiamata di sort all'interno di quella funzione generi una chiamata ricorsiva, e non si faccia riferimento a qualche altra void dislocata più avanti nel codice..

Direttive al preprocessore

#include

#if

#ifdef

#ifndef

 

Ogni volta che il compilatore trova la riga di comando #include eliminerà quella riga per sostituirla con il codice sorgente del file contenuto tra le parentesi angolari o tra doppi apici subito dopo citati.

Quando lo strumento Software contenuto nel compilatore chiamato preprocessore incontra un comando di include e un nome di file contenuto tra parentesi angolari esso andrà a cercare tale file nel direttorio standard del compilatore, se invece il file da includere è compreso tra doppi apici verrà cercato nel direttorio in cui avviene la compilazione.

I file standard contenuti nella libreria di default ( lib ) sono salvati con estensione .h perché essi costituiscono i file di intestazioni del nostro progetto ( dall’inglese header files ).

 

ð     #include <math.h>           Il file viene cercato in un’area di lavoro standard che contiene gli  header file delle librerie standard.

ð     #include “def.h”  Il file viene cercato nel direttorio corrente.

 

I costrutti #if, #ifdef, #ifndef, gestiscono la compilazione condizionale secondo la sintassi:

 

#if espressione

#ifdef identificatore

#ifndef identificatore

 

Il programma in esame usa in maniera massiccia le direttive al preprocessore sopracitate.

Per portare a buon fine la compilazione di un progetto C++ complesso come quello esposto è indispensabile creare un progetto (project ), strumento questo che consente al compilatore di attivarsi sempre dal modulo principale e ricreare così la giusta sequenza di inclusione degli altri moduli.

Il progetto garantisce alla funzione principale, chiamata main, di trovare già definite perché incluse sopra di essa le definizioni e i prototipi delle funzioni che dovrà usare.

Il progetto ha l’importante scopo di generare un file eseguibile con il nome ad esso associato.

 

Modulo di inizializzazione della scheda video grafica con schermata introduttiva.

Il modulo di inizializzazione grafica “ippodrom.h”  viene, come vedremo analizzando il modulo contenente il main, attivato per primo, esso si cura infatti della gestione dell’hardware installato sulla macchina. Il sorgente di questo modulo è disponibile a pagina 31.

Nel file di intestazione garphics.h sono contenuti i prototipi di alcune funzioni standard utilizzate a questo scopo,  esse sono ad esempio initgraph(&gdriver, &gmode,””), che per funzionare necessita di due parametri passati per reference, e graphresult( ) che non necessita di alcun parametro.

L’idea per il funzionamento di questa funzione è quella di definire  tre variabili di tipo intero a cui associare delle informazioni sullo stato del sistema.

La funzione predefinita “DETECT” restituisce un numero intero  che viene direttamente associato alla variabile gdriver,  il cui valore corrisponde al tipo di scheda grafica installata ( ad esempio VGA standard ).

Una volta rilevata la presenza della scheda si può procedere all’inizializzazione della medesima attivando la funzione contenuta nel modulo di intestazione graphics.h chiamata  initgraph(&gdriver, &gmode, ""), essa passa come possiamo vedere tre parametri al suo codice sorgente, il primo parametro è l’indirizzo della variabile a cui è stato assegnato un valore numerico intero corrispondente alla scheda grafica VGA, il secondo, &gmode, che esprimerà la modalità grafica, non contiene ancora alcuna informazione visto che la modalità grafica verrà assegnata come output della funzione in cui ci accingiamo ad entrare, il terzo parametro nel nostro caso non utilizzato è una stringa vuota.

Il terzo parametro di tipo intero, errorcode, verrà assegnato nella riga successiva  ad un numero intero che tabula il tipo di errore che potrebbe verificarsi durante l’inizializzazione della scheda video.

La riga di codice in questione è: errorcode = graphresult();  

Il costrutto if  verifica se il contenuto di errorcode restituito dalla routine di inizializzazione è diverso dal valore della costante intera predefinita gr0k, e in tale eventualità va ad estrarre grazie all’utilizzo della funzione grapherrormsg(errorcode); una stringa predefinita che specifica a monitor il tipo di errore accorso.

Come già detto, per un corretto funzionamento questo programma necessita il supporto di alcuni file presenti nel compilatore (EGAVGA.BGI e GHOT.CHR nella directory in cui verrà lanciato l’eseguibile ), nel caso essi non fossero presenti   il messaggio di avviso verrà lanciato in modalità testo proprio dalla funzione grapherrormsg(errorcode).

Nel caso che effettivamente si fosse verificato un blocco l’esecuzione del programma si arresta al collo di bottiglia generato dal getch( ), che attende la pressione di un tasto da tastiera, superato il collo di bottiglia si incontra un forzamento ad uscire dalla routine costituito dalla funzione exit(1).

Il programma non potrà essere riattivato fino alla risoluzione del problema sia esso hardware e software, nella maggioranza dei casi sarà sufficiente copiare nella directory in cui si trova l’eseguibile i tre files egavga.bgi, ghot.chr, trip.chr  tenedo presente che essi vengono forniti con il compilatore e si trovano di norma nel direttorio BGI del compilatore C++.

I prodotti commerciali di norma conglobano nell’eseguibile anche tali files evitando la scomodità di doverseli trascinare appresso ogni volta che si esegue l’installazione del programma.

Per rendere la conglobazione possibile sarà sufficiente creare i moduli egavga.obj, ghot.obj, trip.obj con l’ausilio del compilatore Borland C++, il Linker poi potrà inserirli nel progetto creando il file ippodrom.exe con essi integrati.

Nel caso che non si fossero verificati errori nella fase di inizializzazione il sistema entra nella modalità grafica ed è ora predisposto per il corretto funzionamento delle funzioni della libreria graphics.h.

La modalità grafica mette a disposizione una risoluzione di 640 pixel lungo l’asse x orizzontale e avente origine nell’angolo in alto a sinistra del monitor per 480 pixel con la medesima origine lungo l’asse y.

A questo punto non possiamo fare altro che sbizzarrirci in fantasiose creazioni costituite da giochi di pixel colorati, linee, figure geometriche, testi grafici eseguiti con una buona varietà di font.

Nel nostro specifico caso si sono scelti solo due tipi di carattere, il primo denominato “Triplex” perché molto in uso in prodotti commerciali data la sua chiarezza dovuta alla snellezza delle linee, il secondo “Ghotic” per dimostrare che è possibile inserire un tocco artistico alle nostre creazioni.

Vediamo un esempio di stile “Gothic” del carattere per una misura standardizzata che va dal valore 1 al valore 7.

 

 

L’esempio di scrittura gotica è stato costruito utilizzando le due righe di programmazione C++ : 

settextstyle(GOTHIC_FONT,0,7);        //predispone stile carattere

outtextxy(150,50,"Stile gothic misura 7"); //Passa la stringa alla funzione.

Analogamente per quanto riguarda il “Triplex_Font” :

La libreria standard del C++ mette a disposizione anche altri stili che in linea  generale possono soddisfare le più comuni esigenze sia in campo hobbistico che per i prodotti commerciali, per questi ultimi si consiglia ovviamente di non lasciarsi troppo trascinare da ispirazioni artistiche  e di usare quindi stili sobri il più possibili privi di fronzoli in modo da non distrarre l’utente dalla reale funzione del tasto a cui sono associati.

Ecco gli altri stili predefiniti:

Questo potente linguaggio consente comunque a quegli utenti volenterosi di crearsi stili personalizzati tramite ad esempio la costruzione di specifiche Classi.

Le funzioni utilizzate nel  programma sono quelle di:

·        Impostazione di un particolare colore   setcolor( n )  dove n è un valore intero

·        Tracciatura di linee  line(n1,n2,n3,n4) dove n1,n2 sono cordinate x,y di origine e n3,n4 sono le coordinate x,y di fine linea.

·        Impostazione del font  Settextstyle(GOTHIC_FONT,0,7), e Settextstyle(TRIPLEX_FONT,0,7) dove i tre parametri sono valori interi che identificano il tipo di carattere, l’orientamento della scritta diversificato tra orizzontale 0 e verticale 1, la dimensione della scritta secondo i canoni predefiniti che si sono già mostrati.

·        Emissione a coordinate di un testo outtextxy(150,150,"Marco") che porta il cursore alle coordinate x=150, y=150 prima di stampare la stringa compresa tra apici che rappresenta il terzo parametro di questa funzione.

·        Pulizia di una pagina grafica   cleardevice( ), utile per non avere sovrapposizione indesiderate durante la stampa di due immagini successive.

·        Anche se in questo modulo non è riportata, visto che non se ne deve curare, è di fondamentale importanza la funzione closegraph( ); che riporta il sistema in modalità testo dopo aver chiuso la modalità grafica.

Codice sorgente del modulo ippodrom.h

Viene ora presentato il modulo ippodrom.h che viene per primo richiamato dal principale (ippodrom.c) il quale oltre che a predisporre l’ambiente grafico lancia a schermo la presentazione della versione accademica del programma ippodrom.exe.

/* Versione dell'11/04/99 con intestazione della Facoltà di scienze aziendali */

#include <graphics.h>

#include <stdlib.h>

#include <stdio.h>

#include <conio.h>

 

int videoinit( ){                                                          //dichiarazione funzione inizia grafica.

   int gdriver = DETECT, gmode, errorcode; //variabili ambiente grafico

   int xmax, ymax;                                                        //dichiarazione variabili dimensione max schermo.

   initgraph(&gdriver, &gmode, "");                            //funzione inizializzante predefinita

   errorcode = graphresult( );                         //segnalazione mancata inizializzazione.

   if (errorcode != grOk)                                              //se è diverso da inizz. riuscita blocca tutto

  {                                                                               //routine gestione blocco sistema se errore.

      printf("Errore di modalit… grafica: %s/n",  //stampa messaggio di errore.

                 grapherrormsg(errorcode));                        //estrae messaggio di sistema

      printf("Premi un tasto per bloccare il programma:");

      getch();                                                                 //attende la pressione di un tasto

      exit(1);                                                                  //associa condizione d'uscita.

   }                                                                             //fine routine gestione blocco sistema in errore.

   setcolor(getmaxcolor());                                           //predispone colori

   xmax = getmaxx();                                                   //risoluzione max in x

   ymax = getmaxy();                                                   //risoluzione max in y

   setcolor(3);                                                              //predispone i colori linee blu

   line(45, 300, 580, 300);                                           //traccia linee contorno

   line(0, 0,0,ymax);

   line(0,0,xmax,0);

   line(0,ymax,xmax,ymax);

   line(getmaxx( ),getmaxy( ),xmax,0);

   setcolor(1);                                                              //predispone colore scritte

   settextstyle(GOTHIC_FONT,0,7);                          //predispone stile carattere

   outtextxy(110,150,"Gottardo Marco");                  //nome  autore

   settextstyle(GOTHIC_FONT,0,4);

   outtextxy(270,250,"Tesi di");

   settextstyle(TRIPLEX_FONT,0,4);

   outtextxy(65,20,"POLITECNICO di STUDI AZIENDALI ");

   outtextxy(85,60," Facoltà di Scienze Aziendali");

   outtextxy(230,120,"Dottorando:");

   settextstyle(TRIPLEX_FONT,0,3);

   outtextxy(100,370,"Specializzazione Ingegneria Informatica ");

   outtextxy(50,400," Docente relatore: Dott. Zecchinato Beniamino");

   settextstyle(TRIPLEX_FONT,0,2);

   outtextxy(30,310," Ottimizzazione dei tempi di ricerca in alberi bilanciati");

   getch();

   cleardevice();                                                           //pulizia prima schermata

   return 0;                                                                   //valore fittizio restituito dalla funzione

   }                                                                             //fine funzione

 

Tutto il corpo del file ippodrom.h, escluse le direttive al preprocessore si compone di un’unica funzione di tipo intera di nome videoinit( ); , essa sarà chiamata dal programma  principale come una normale funzione esterna, quindi semplicemente per nome;

Solo ed esclusivamente allo scopo di voler testare il singolo modulo è possibile munirlo di una funzione main( ); che chiami videoinit( );

Qui sotto si riporta l’esempio.

/********* Solo in fase di test usare il sottostante main() **********

 *  int main(){                                                                                              *

 *  videoinit();                                                                                             *

 *  }                                                                                                             *

 ***********************************************************/

La possibilità di rendere compilabile il singolo modulo rende il lavoro molto più agevole al programmatore visti i ripetuti riaggiustamenti di coordinate che si possono rendere necessari per rendere più gradevole l’impostazione grafica che si viene a produrre.

Una volta lanciato il modulo ippodrom.h in esecuzione con l’ausilio del programma principale appositamente creato si ottiene la schermata di presentazione riportata  in figura:

 

Schermata di presentazione generata nel modulo ippodrom.h

 

 

Con l’utilizzo di uno scanner sarebbe stato possibile inserire come file esterno anche una piccola bitmap quale ad esempio il logo della Facoltà di Scienze Aziendali.

Per una maggiore chiarezza di utilizzo il programma divide lo schermo in zone funzionali ampie in modo da favorirne il colpo d’occhio.

Il pulsante di uscita è isolato dai pulsanti dislocati in una colonna centrale,  ai quali è associata una funzione di gestione del data base, agendo su di essi  si accede ad una finestra di dialogo che fornisce agli utenti chiare indicazioni  sul da farsi.

Analogamente al tasto di ritorno al sistema operativo viene isolato anche il tasto a cui si associa la funzione di backup su floppy.

Su ogni tasto viene integrata la stringa di identificazione della funzione ad esso associata con la possibilità di agire su di esso schiacciando sulla tastiera la sua prima evidenziata in rosso, tale opportunità potrebbe risultare molto utile nel caso che il mouse fosse fuori uso.

L’intestazione eseguita con un GOTHIC_FONT di grandezza standardizzata 7 riporta il nome del programmatore ma essa sarà sostituita nelle versioni commerciali dal nome della ditta produttrice o da quello della ditta acquirente.

Le stringhe di commento al tasto sono invece eseguite in TRIPLEX_FONT, di colore rosso e poste immediatamente sotto al tasto a cui si riferiscono.

La pagina principale del programma ha l’aspetto sotto riportato.

 

 

Per ragioni di praticità di stampa l’immagine proposta per la pagina principale è falsata nei colori, lo sfondo infatti è nella realtà di colore nero.

La scelta  di un colore così scuro non è stata lasciata a ragioni puramente estetiche ma piuttosto è stata dettata dalla necessità di utilizzare un colore riposante sia per gli occhi dell’utente che per l’Hardware della macchina su cui è destinato a funzionare.

E’ noto che colori troppo intensi lasciati fissi per lunghi periodi possono danneggiare il monitor impressionando i suoi elementi fotosensibili, i colori tenui riducono drasticamente tale eventualità.

 

Funzioni del programma disponibili all’utente.

 

Una volta lancianto in esecuzione il programma e superata la schermata di presentazione il programma cerca nella directory corrente ( IPPODROM dove deve essere fatta l’installazione) il  file Ippodrom.dbf che contiene le informazioni relative ai cavalli memorizzati durante i precedenti utilizzi dell’applicativo.

Nel caso il file di database non fosse trovato compariranno a monitor delle chiare indicazioni sul da farsi per risolvere l’eccezione,  nel caso invece che fosse disponibile esso verrà letto in maniera sequenziale ma scaricato in memoria RAM a cura della funzione RNA_Inserisci(Nodo &z) la quale costruirà in maniera trasparente per l’utente l’albero rosso nero.


 

 

La presenza dell’albero rosso nero allocato in memoria può essere rilevata tramite l’azione con il mouse sul tasto “ Solo visione “ il quale attiva la funzione “printree(Nodo* w)” che si cura della visualizzazione dei cavalli memorizzati all’interno di una ben chiara finestra di dialogo.

La funzione printree(Nodo* w) è stata concepita per effettuare una visita “inorder” dell’albero, secondo cioè la sequenza Sin.àRadiceàDestr. Questo ha come conseguenza che i dati saranno visualizzati in ordine crescente delle chiavi di ricerca che qui rappresentano il  numero associato al nome del cavallo.

Se la finestra di dialogo dovesse venire riempita completamente il programma chiede di premere un tasto per proseguire cancellando l’attuale contenuto per continuare con la successiava visualizzazione.

Nella prossima figura si mostra la manovra di visualizzazione dei dati disponibili.

 

  

Inserimento di un nuovo dato in archivio.

La funzione di inserimento RNA_Inserisci(Nodo &z) è associata al tasto “Inserisci”. 

 

Come si può notare dalla lista dei parametri attesi dalla RNA_Inserisci richiede il passaggio di un indirizzo di Nodo, cio’ comporta che esse deve essere preventivamente creato e riempito.

La creazione e riempimento del Nodo da inserire avviene a cura di una funzione “fainserimento” il cui codice si trova all’interno del file principale ippodrom.c

Ad un’azione sul tasto Inserimento corriponde la comparsa della finestra di dialogocon la richiesta di inserimento della chiave di ricerca.

 

Una volta inserita la chiave di ricerca la finestra di dialogo verrà cancellata per essere sostituita con la richiesta dell’iserimento degli altri campi del nuovo Nodo.

La funzione FaiInserimento associata al tasto ha il seguente codice C++:

int faiInserimento( ){

            FILE *stream;

            CAVALLO cavallo;

            char ch,risp,car;

            int index=0;

            sound(660); //Alla pressione del tasto viene emesso un segnale acustico di conferma

            delay(100);

            nosound();

            pippo=1;

            settextstyle(TRIPLEX_FONT,HORIZ_DIR,2);

            setcolor(2);

            line(10,150,230,150);

            line(230,150,230,450);

            line(10,450,230,450);

            line(10,450,10,150);

              outtextxy(20,160,"     Funzione");

              outtextxy(20,180,"    Inserimento ");

              outtextxy(20,280,"Inserire il numero");

              outtextxy(20,300,"  del cavallo");

            /* apertura del file in modalià append */

              if ((stream = fopen("\\IPPODROM\\IPPODROM.DBF","ab"))== NULL){

              fprintf(stderr, "file in cui riversare non trovato \file.\n");

              return 1;

              }

            gotoxy(15,23);

            cin>>(k);

            if (!isspace(k)){  //Questa funzione verifica se il carattere introdotto è uno spazio standard

             cavallo.i=k;

             ps=new Nodo(k);//crea il nodo solo se valore valido

             setfillstyle(SOLID_FILL,BLACK);//pulizia finestra per uscita

              bar(10,150,230,450);//cancella la precente finestra

              line(10,150,230,150);//nuova finestra di richiesta inserimento dati cavallo

              line(230,150,230,450);

              line(10,450,230,450);

              line(10,450,10,150);

              outtextxy(20,160,"Inserimento Dati");

              settextstyle(TRIPLEX_FONT,HORIZ_DIR,1);

              outtextxy(20,230,"'N' -> Nome");

              outtextxy(20,250,"'E' -> Eta'");

              outtextxy(20,280,"'V' -> Vittorie");

              outtextxy(20,340,"'F' -> Favorito");

              risp='0';

              switch(risp=toupper(getch())){//prende sempre il caso maiuscolo

                        case 'N': ;break; //Il medesimo costrutto case viene utilizzato anche in Faimodifica

                        case 'E':cout<<"";break;

                        case 'V':cout<<"";break;

                        case 'F':cout<<"";break;

              }// Chiude il costrutto switch

             }// Chiude if controllo chiave non valida

             fclose(stream);//Chiude il file

            RNA_Inserisci(*ps);

            bar(10,150,230,450);//cancella la precente finestra

            return 0;

}

Per vedere  come viene acquisito il nome del cavallo si rimanda all’esposizione completa del codice del programma.

Modifica di un dato.

Per modificare un dato allocato nell’albero è neccessario identificare la sua posizione in esso, a tal fine viene associata al tasto “Modifica”  la funzione elimina(int &togliere,Nodo* scan) la quale restiturà l’indirizzo del Nodo di cui si vogliono modificare dei campi.


 

 

 

Una volta ottenuto l’indirizzo del dato da modificare comparirà nella finestra di dialogo la richiesta relativa al campo che si vuole modificare.

L’interfaccia utente nella finatra di dialogo è implementata dalle sottostanti righe di programma.

 

outtextxy(20,160,"Modifica Dati");

            settextstyle(TRIPLEX_FONT,HORIZ_DIR,1);

            outtextxy(20,230,"'N' -> Cambia Nome");

            outtextxy(20,255,"'E' -> Cambia Eta'");

            outtextxy(20,280,"'V' -> Cambia Vittorie");

            outtextxy(20,305,"'F' -> Cambia Favorito");

            outtextxy(20,340,"'Q' -> Uscita");

            getch();

L’utente risponderà da tastiera agendo su una delle lettere evidenziate ottenendo l’accesso ai campi desiderati del Nodo.

Il programma gestisce la risposta tramite un costrutto decisionale multivia “case” di cui se ne riporta uno stralcio:

            risp='0'; //è la variabile usata per la selezione

              switch(risp=toupper(getch())){//prende sempre il caso maiuscolo

                        case 'N':funzione acquisizione e modifica campo “nome”;break;

                        case 'E': funzione acquisizione e modifica campo “età”;break;

                        case 'V': funzione acquisizione e modifica campo “vittorie”;break;

                        case 'F': funzione acquisizione e modifica campo “favorito”;break;

              }//Chiude if controllo chiave non valida

             }//Chiude il costrutto switch

Nel complesso l’aspetto grafico della manovra di inserimento è riportato nella figura:


 

 

 

 

 

G

 

 

 

 

 

 

 

GESTIONE DEL DISPOSITIVO SERIALE DI PUNTAMENTO (MOUSE)

Un moderno programma per personal computer mette a disposizione dell'utente un sistema d’accesso e controllo visivo che consente allo stesso d’essere più "user friendly".

La gestione dei segnali provenienti dal Mouse è delegata alla sottostante semplice classe che ereditando le caratteristiche contenute in REGPAK box presente nel modulo <dos.h> rende attivo il controllo sui registri utilizzati da tale dispositivo di puntamento.

Per utilizzare la classe sarà sufficiente associare ad una sua attivazione la variabile statica : " static Mouse Mio_dito;  " dove con static si  vuole intendere che il valore locale della variabile sarà conservato all'uscita dalla funzione fino ad un’eventuale riassegnazione in un nuovo rientro, anche se la variabile non è stata passata per indirizzo.

 

#include "pannello.h"

#include <graphics.h>

#include <dos.h>

#include <conio.h>

#include <assert.h>

#include <stdlib.h>

#include "bottone.h"

 

#ifndef              MOUSE_H

#define             MOUSE_H

 

#ifndef         STATUS

#define         STATUS

                     enum Status { OFF=0, ON, PAUSE, HOP };

#endif  //      STATUS 

class Mouse{

            private:

                        /* Stato di attivita` del mouse                      */

                        Status fase;

                        /* scatola dei registri dichiarata in 'dos.h'   */

                        REGPACK Box;

                        /* per impedire l'assegnamento tra esemplari di 'Mouse' */

                        Mouse operator=(Mouse);

            public:

                        Mouse( );  // DICHIARAZIONE DEL COSTRUTTORE

                        void Attiva( ); //METODO DI ATTIVAZIONE

                        void Disattiva( );//METODO DI DISATTIVAZIONE

                        void Attendi_e_Rispondi( ); //METODO DI FUNZIONAMENTO ATTIVO

            };

 static Mouse Mio_dito;

#endif   //          MOUSE_H

 Nelle pagine seguenti vengono riportati i codici sorgenti delle funzioni membro delle classi mouse, bottone, pannello. mouse.c --  Class Mouse

#include           "mouse.h" //inclusione della dichiarazione classe mouse

#include           "pannello.h"//inclusione della dich. classe pannello

#include           "graphics.h"

#include           <iostream.h>//inclusione modulo di libreria

extern Pannello * Pannello_Attivo;//predichiarazioni funzioni esterne

extern Mouse Mio_dito;

Mouse::Mouse(){ //codice del costruttore di mouse

            fase = OFF;//predisposizione in stato off.

            }

void

Mouse::Attiva( ){ //codice funzione di attivazione mouse

            Box.r_ax = 1; //mouse on

            intr(51,&Box); //funzione attivazione segnali interrupt

            Box.r_ax = 3;

            Box.r_cx = getmaxx()/2;//predisposizione posizione centrale mouse

            Box.r_dx = getmaxy()/2;

            intr(51,&Box);

            fase = ON;

            }

void Mouse::Disattiva( ){  //funzione disattivazione mouse

            Box.r_ax = 0;//mouse off

            intr(51,&Box);

            fase = OFF;

            }

 

void Mouse::Attendi_e_Rispondi( ){  //funzione di gestione mouse

            assert(fase==ON);

/* il bottone di tipo 'Bottone' sul quale si trova la freccia del mouse al momento in cui viene premuto un tasto:            */

            Bottone * centrato;

            char tasto;

            while(1){

                        do intr(51,&Box);

/* Box.r_bx assume il valore 0 finche` nessun tasto viene premuto;  vale 1 se viene premuto il tasto di sx; 2 se viene premuto il tasto di dx; 3 se sono premuti contemporaneamente i tasti di dx e di sx e infine 4 se e` stato premuto il tasto centrale del mouse:           */

                        while(Box.r_bx = = 0&&!kbhit( ));//verifica condizione di

                        //registro bx=0 e non tasto premuto(&&! operazione NAND)

                        if(Box.r_bx){  //verifica condizione per risposta mouse

                                   int x = Box.r_cx, y = Box.r_dx;

                                   centrato = Pannello_Attivo -> Dammi_Bottone(x,y);

                                   if(centrato)

                                   switch(Box.r_bx){

                                                           /* bottone sinistro:    */

                                               case 1  :       centrato -> Aziona();

                                                                       break;

                                                           /* bottone destro:      */

                                               case 2  :           centrato -> Attiva();

                                                                       break;

                                                           /* bottoni sinistro e destro    */

                                               case 3  :           // applicazioni diverse

                                                                       break;

                                                           /* bottone centrale    */

                                               case 4  :       centrato -> Disattiva();

                                                                       break;

                                                           /* ci sono altri casi ?  */

                                               default  :       int altro = Box.r_bx;

                                                                       cout << "Il valore " << altro

                                                                      << " non e` stato assegnato\n";

                                                                       break;

                                               }

                                   }

 

                        else if(kbhit( )){

                                   tasto = getch( );

                                   centrato = Pannello_Attivo -> Dammi_Bottone(tasto);

                                   if(centrato) centrato -> Aziona( );

                                   else{

                                               sound(700);

                                               delay(50);

                                               nosound();

                                               }

                                   }

                        }

            }

 

La gestione di else discrimina tra il funzionamento del mouse e il controllo schiacciando i tasti della tastiera. il controllo funziona nella seguente maniera: Se il tasto è centrato dal puntatore del mouse viene associata al campo dammi bottone nel corrente pannello attivo il valore restituito dalla funzione dammi bottone, ora si dovrà discriminare quale dei tasti del mouse è stato premuto ad esempio dx, sx, centrale.

Ad ognuno di questi tasti viene associata una risposta particolare che gestisce l'evento.

A gestire la cosa ci pensa il costrutto switch(Box.r_bx) che è in questo programma predisposto per espansioni future.  Nel caso che il mouse risultasse non attivo, il controllo viene delegato alla tastiera, e la cosa viene gestita dal costrutto kbhit( ), il quale si cura in abbinamento con getch( ) dell'acquisizione continua di un carattere da tastiera.

Se il tasto premuto è acquisito con getch( ) corrisponde ad uno dei tasti adibiti al controllo(quelli evidenziati in rosso) allora si va ad agire sul pannello attivo e sull'esemplare di bottone attivando la funzione commuta stato.

 

I Bottoni

Una volta attivata la routine di gestione seriale del mouse, il puntatore video comparirà sul monitor, e si renderà quindi necessario definire delle posizioni "coordinate" sulle quali un'azione del mouse comporti un certo effetto.

La classe bottone, sotto riportata, crea dei rettangoli contenenti una scritta che ne ricordi la funzione da attivare. Cliccando in corrispondenza del bottone si devia il flusso del programma dentro alla funzione associata a tale bottone.

I bottoni sono pensati per essere di tipo on/off a pulsante, ma possono essere modificati per agire on/off ad interruttore, cioè rimanendo nella posizione on o off dopo l'azione.

L'insieme dei bottoni viene creato su un esemplare della classe pannello. Ovviamente, più pannelli possono essere richiamati agendo su un bottone.

 

 

Quanto segue si trova in bottone.h.

// file bottone.h

#ifndef         BOTTONE_H

#define         BOTTONE_H

#include        "graphstr.h" //inclusione del file di gestione grafica

                                        //definito dall'utente.

#ifndef         STATUS //definizione del tipo di dato numerato STATUS

#define         STATUS

            enum Status { OFF=0, ON, PAUSE, HOP };//stati possibili bottone

#endif  //      STATUS

#ifndef         DIMENSIONI  //definizione dimensione bottone

#define         DIMENSIONI

#define         MINIMA_LARGHEZZA         35

#define         MINIMA_ALTEZZA               35

#define         MINIMO_SPESSORE              3

#define         MINIMA_ETICHETTA           1   /* lunghezza minima */

#define         MASSIMA_LARGHEZZA      55

#define         MASSIMA_ALTEZZA            55

#define         MASSIMO_SPESSORE           10

#define         MASSIMA_ETICHETTA         22

#define         INTERVALLO_BOTTONE      11  /* delay in 'premi' e 'rilascia' */

#define         INTERVALLO_ETICHETTA   160 /* delay in 'stampa'        */

#endif  //      DIMENSIONI

#ifndef         FUNZIONI_BOTTONE

#define         FUNZIONI_BOTTONE

            int MIOBLACK( ), MIOLIGHTMAGENTA( );

            int sx( ),sy( ),tw( ),th( );

#endif  //      FUNZIONI_BOTTONE

 

class Bottone    //definizione della classe bottone

 {

           private:

                        /* per impedire assegnamenti disastrosi !       */

                        Bottone operator=(Bottone);

 

            protected:

                        Stringa etichetta;

                        char evidenziato[2];

                        short int indice;

 

                        /* lo stato di un bottone puo` essere: OFF (spento),  ON (acceso), PAUSE (in attesa che il pannello venga riattivato; in realtà e` stato utilizzato in 'stampa' per indicare lo stato 'azionato' di un bottone spento !); o HOP (azionato):    */

 

                        Status stato;

 

                        /* dopo l'esecuzione di 'Attiva' il booleano 'modo' ha sempre  valore VERO; in 'Aziona', prima dell'esecuzione di una delle due possibili funzioni (funzione<i>), commuta il suo valore: e` utile quando anche funzione2 != 0, cioè quando il bottone aziona alternativamente due processi differenti (es. avvio-arresto di una procedura)       */

 

                        Bool modo;

 

                        /* a[12] e` il vettore dei vertici del poligono irregolare che costituisce il bordo del bottone alla sua sinistra e sopra; i posti pari contengono l'ascissa e i dispari l'ordinata di ciascun vertice, iniziando da quello in alto a sinistra e procedendo in senso orario. Analogamente per b[12], bordo di destra e di sotto, iniziando dal vertice in basso a destra.

A[12] e B[12] sono analoghi poligoni che quando il  bottone e` a riposo coincidono esattamente con 'a' e 'b' mentre quando viene premuto diminuiscono e successivamente ripristinano la loro larghezza per dare l'effetto ombra sul bottone premuto: */

 

                        int a[12], b[12], A[12], B[12], capo[2]; //dichiarazione di

                        //quattro vettori di dodici posizioni e un vettore di due posizioni

 

                        int larghezza, altezza, bordo;

                        int colore_dorso, colore_bordo;

                        inline void init(int=0, int=0);//macroespansione inline

                        inline void Etichetta(const Stringa&);

                        inline void Altezza(int alt=MINIMA_ALTEZZA){ altezza = alt; }

                        inline void Larghezza(int lar=MINIMA_LARGHEZZA){larghezza = lar;}

                        inline void Bordo(int bor=MINIMO_SPESSORE){bordo = bor;}

 

                        /* effetti grafici      */

                        inline void disegna();

                        inline void stampa();

                        inline void premi();

                        inline void rilascia();

 

                        /* se il pannello su cui si trova il bottone e` attivo

                         * restituisce VERO     */

                        Bool PAttivo;

 

                        /* 'funzione<i>', come puntatori a funzione: variano ovviamente da bottone a bottone; sono (eventualmente) due per coprire il caso dei bottoni-alternatori (si veda sopra)     */

 

                        int (*funzione1)();

                        int (*funzione2)();

 

            public:

                        /* Primo costruttore: il bottone viene sempre creato spento:

                         * parametro 1: ascissa del vertice in alto a sx;

                         * parametro 2: ordinata del vertice in alto a sx;

                         * parametro 3: l'etichetta da stampare sul bottone;

                         * parametro 4: funzione1;

                         * parametro 5: funzione2, quando c'e`;

                         * parametro 6: colore del dorso del bottone, per default blu;

                         * parametro 7: colore del bordo del bottone, meglio se uguale

                         *      al dorso ma piu` scuro: per default blu scuro;

                         * parametro 8: indicatore del carattere dell'etichetta da

                         *      evidenziare: per default il primo.   */

Bottone(int,int,const Stringa&,int(*)( ),int(*)( )=0,int=9,int=1,short int=0);

                        /* costruttore di default: */

                        Bottone( );

                        inline Stringa Dammi_Etichetta( );

                        inline int Dammi_Larghezza( );

                        inline int Dammi_Altezza( );

                        inline int Dammi_Bordo( );

                        inline void Attiva( );

                        inline void Attendi( );

                        inline void Commuta( );

                        inline void Disattiva( );

 

                        /* se i parametri passati a 'Centrato( )' identificano un pixel contenuto nel perimetro interno del bottone, oppure se viene passato un carattere uguale a quello evidenziato sul bottone allora la funzione restituisce 1; altrimenti 0 */

 

                        inline int Centrato(int,int);

                        inline int Centrato(char);

                        int Aziona( );

                        friend class Mouse;

                        friend class Pannello;

};

#endif  //      BOTTONE_H

 

 

// File bottone.c

#include                <assert.h>

#include                 <dos.h>

#include                "bottone.h"

#include                 "graphics.h"

#include                "graphstr.h"

int sx(){ return getmaxx()/2;}

int sy(){ return getmaxy()/2;}

int tw(){ return textwidth("W");}

int th(){ return textheight("W");}

void

Bottone::init( int x, int y ){  // inizializza i vertici

            int i;

            a[0] = x;                                          a[1] = y;

            a[2] = b[10] = x+larghezza;            a[3] = b[11] = y;

            a[4] = b[8] = x+larghezza-bordo;   a[5] = b[9] = y+bordo;

            a[6] = x+bordo;                               a[7] = y+bordo;

            a[8] = b[4] = x+bordo;                    a[9] = b[5] = y+altezza-bordo;

            a[10] = b[2] = x;                              a[11] = b[3] = y+altezza;

            b[0] = x+larghezza;                         b[1] = y+altezza;

            b[6] = x+larghezza-bordo;               b[7] = y+altezza-bordo;

            for( i=4; i<10; i++ ){

                        A[i] = a[i];

                        B[i] = b[i];

                        }

            }

            /* inizializza 'etichetta' ed 'evidenziato' e se necessario aggiusta  le dimensioni del bottone affinche` la stringa possa essere  stampata su di esso:      */

void

Bottone::Etichetta(const Stringa& str){

            int ln = Lunghezza(str);

            etichetta = str;

            if(ln = = 0 || ln>MASSIMA_ETICHETTA){

                        Stringa errore = "Errore: l'etichetta del bottone '";

                        Erroregraf("errore\n");

                        }

            if( ln*tw() > larghezza-2*bordo-18 ){

                        Larghezza(larghezza+tw()*(ln*tw()-(larghezza-2*bordo-18))/tw());

                        init(a[0],a[1]);

                        }

            if( th() > altezza-2*bordo-28 ){

                        Altezza(altezza+th()*(th()-(altezza-2*bordo-28))/th());

                        init(a[0],a[1]);

                        }

            capo[0] = (a[0]+b[0]-ln*tw())/2;

            capo[1] = (a[1]+b[1]-th())/2;

            evidenziato[0] = etichetta[indice];

            evidenziato[1] = '\0';

            }

            /* 'disegna' disegna il bottone con i colori originali, cioè  colore_dorso e colore_bordo, se e` acceso; in grigio viceversa Mi chiedo se e` bene introdurre anche in 'disegna' un controllo su 'PAttivo': per ora non lo faccio perché potrebbe essere necessario disegnare un pannello, con tutti i suoi bottoni, anche  quando non e` attivo    */

 

void

Bottone::disegna( ){

assert("PAttivo\n");

            int c, c_b;

            if(stato = = ON){

                        c = colore_dorso;

                        c_b = colore_bordo;

                        }

            if(stato = = OFF){

                        c = LIGHTGRAY;

                        c_b = DARKGRAY;

                        }

            setcolor(BLACK);

            rectangle(a[0]-1,a[1]-1,b[0]+1,b[1]+1);

            setfillstyle(SOLID_FILL, WHITE);

            fillpoly(6, a);

            setfillstyle(SOLID_FILL, c);

            bar(a[6], a[7], b[6], b[7]);

            setfillstyle(SOLID_FILL, c_b);

            fillpoly(6, b);

            rectangle(a[0]-1,a[1]-1,b[0]+1,b[1]+1);

            stampa();

            }

 

 

            /* Stampa l'etichetta sul bottone      */

void

Bottone::stampa(){

            int i;

            char pippo[2];

            settextjustify(0,2);

            settextstyle(DEFAULT_FONT,HORIZ_DIR,1);

            moveto(capo[0],capo[1]);

            switch(stato){

                        case ON          :

                                   setcolor(BLACK);

                                   Outtext(etichetta);

                                   moveto(capo[0]+1,capo[1]+1);

                                   setcolor(WHITE);

                                   for(i=0; i<Lunghezza(etichetta); i++){

                                               if(i==indice){

                                                           setcolor(LIGHTRED);

                                                           outtext(evidenziato);

                                                           setcolor(WHITE);

                                                           }

                                               else{

                                                           pippo[0] = (etichetta[i]);

                                                           pippo[1] = '\0';

                                                           outtext(pippo);

                                                           }

                                               }

                                   break;

 

                        case HOP:

                                   moveto(capo[0]+2,capo[1]+2);

                                   setcolor(YELLOW);

                                   Outtext(etichetta);

                                   delay(INTERVALLO_ETICHETTA);

                                   break;

 

                        case OFF:

                                   setcolor(BLACK);

                                   Outtext(etichetta);

                                   moveto(capo[0]+1,capo[1]+1);

                                   setcolor(WHITE);

                                   Outtext(etichetta);

                                   break;

 

                        case PAUSE:

                                   moveto(capo[0]+2,capo[1]+2);

                                   setcolor(LIGHTGRAY);

                                   Outtext(etichetta);

                                   delay(INTERVALLO_ETICHETTA);

                                   break;

                        }

            }

 

void

Bottone::premi(){

            int i, colore_scuro;

            switch(stato){

                        case     ON:     colore_scuro = colore_bordo;

                                               break;

 

                                   /* non dovrebbe succedere(!): */

                        case     HOP:   colore_scuro = colore_bordo;

                                               break;

 

                        case     OFF:    colore_scuro = DARKGRAY;            // ...troppo

                                               break;                          // scuro...

 

 

                                   /* non dovrebbe succedere(!): */

                        default:        colore_scuro = DARKGRAY;

                                               break;

                        }

            setcolor(BLACK);

     //     sb_etlinestyle(SOLID_LINE, 1, 1);

 

                        /* mantenere semplicemente 'bordo' se si vuole l'effetto di  bottone premuto a fondo; scrivere invece 'bordo-1' se non si vuole eliminare il residuo di linea bianca come nella  versione originale (vedi poche righe sotto...)   */

 

            for(i=0; i<bordo; i++){

                        rectangle(a[0]+i, a[1]+i, b[0]-i, b[1]-i);

                        delay(INTERVALLO_BOTTONE);

                        }

            setfillstyle(SOLID_FILL, colore_scuro);

            bar(a[6], a[7], b[6], b[7]);

 

                        /* aggiunta d'ombra sopra e a sinistra per simulare il bottone che entra parzialmente nella sede nel caso che sia accettata la versione con 'bordo' (vedi poche righe sopra...)           */

 

            line(a[6],a[7],a[4],a[5]);

            line(a[6],a[7],a[8],a[9]);

            }

 

void

Bottone::rilascia( ){

            int i, colore_chiaro, colore_b;

            switch(stato){

                        case     ON:     colore_chiaro = colore_dorso;

                                               colore_b = colore_bordo;

                                               break;

 

                                   /* non dovrebbe succedere(!): */

                        case     HOP:   colore_chiaro = colore_dorso;

                                               colore_b = colore_bordo;

                                               break;

 

                        case     OFF:    colore_chiaro = LIGHTGRAY;

                                               colore_b = DARKGRAY;

                                               break;

 

                                   /* non dovrebbe succedere(!): */

                        default:        colore_chiaro = LIGHTGRAY;

                                               colore_b = DARKGRAY;

                                               break;

                        }

            setfillstyle(SOLID_FILL, colore_chiaro);

            bar(a[6], a[7], b[6], b[7]);

            stampa();

            for( i=bordo-1; i >= 0; i-- ){

                        setcolor(BLACK);

                        setfillstyle(SOLID_FILL, WHITE);

                        A[0] = a[0]+i;      A[1] = a[1]+i;

                        A[2] = a[2]-i;       A[3] = a[3]+i;

                        A[10] = a[10]+i;  A[11] = a[11]-i;

                        fillpoly( 6, A );

                        setfillstyle(SOLID_FILL, colore_b);

                        B[0] = b[0]-i;        B[1] = b[1]-i;

                        B[2] = b[2]+i;       B[3] = b[3]-i;

                        B[10] = b[10]-i;    B[11] = b[11]+i;

                        fillpoly( 6, B );

                        setcolor(colore_chiaro);

                        line(a[6],a[7],a[4]-1,a[5]);

                        line(a[6],a[7],a[8],a[9]-1);

                        delay(INTERVALLO_BOTTONE);

                        }

            stampa( );

            }

 

 

Bottone::Bottone( int x, int y, const Stringa& nome, int(*pf1)(), int(*pf2)(),

                        int c, int c_b, short int i){

            colore_dorso = c;

            colore_bordo = c_b;

            Larghezza( );

            Altezza( );

            Bordo( );

            init(x,y);

            indice = i;

            Etichetta(nome);

            funzione1 = pf1;

            funzione2 = pf2;

            stato = OFF;

            }

 

Bottone::Bottone( ){

            Larghezza( );

            Altezza( );

            Bordo( );

            init( );

            Etichetta("noname");

            funzione1 = 0;

            funzione2 = 0;

            stato = OFF;

            }

 

 

 

Stringa

Bottone::Dammi_Etichetta( ){

            return etichetta;

            }

 

int

Bottone::Dammi_Larghezza( ){

            return larghezza;

            }

 

int

Bottone::Dammi_Altezza( ){

            return altezza;

            }

 

int

Bottone::Dammi_Bordo( ){

            return bordo;

            }

 

 

/* accende il bottone (solo se questo si trova su di un pannello gia` attivo: vedi pannello.c)*/

void

Bottone::Attiva( ){

            modo = FALSO;

            stato = ON;

            disegna();

            }

 

void

Bottone::Attendi( ){

            if(stato = = ON) stato = PAUSE;

            }

 

void

Bottone::Commuta( ){

            if(stato = = ON)

                        Disattiva();

            if(stato = = OFF)

                        Attiva();

            }

 

void

Bottone::Disattiva( ){

            stato = OFF;

            if(PAttivo) disegna();

            }

  

int

Bottone::Centrato(int x, int y){

            if(x>=a[6]&&x<=a[4]&&y>=b[9]&&y<=b[7])

                        return 1;

 

/* si potrebbe introdurre anche un test sul bordo del bottone, per eventuali operazioni di 'move'*/

 

            else

                        return 0;

            }

 

int

Bottone::Centrato(char tasto){

            if(tasto==evidenziato[0]) return 1;

            return 0;

            }

 

            /* il booleano 'modo' "conta" le volte che il bottone viene premuto: commuta il suo valore prima di ogni chiamata a 'funzione<i>()': */

int

Bottone::Aziona( ){

            if(stato = = ON){

                        modo = ( (modo = = VERO) ? FALSO : VERO );

                        premi();

                        stato = HOP;

                        stampa();

                        stato = ON;

                        rilascia();

                        if(modo) return funzione1();      // e nel caso del bottone

                                   // di default, quando anche funzione1= = 0 ?

                                   // comunque per adesso un bottone 'di default' non e`

                                   // ancora stato creato !!

                        if(funzione2) return funzione2();

                        else return funzione1();

                        }

            if(stato = = OFF){

                        premi();

                        stato = PAUSE;

                        stampa();

                        stato = OFF;

                        rilascia();

                        }

            return stato;

            }

 

 

            /*

             *         promemoria COLORE=codice:

             *

             *           BLACK=0     RED=4                  DARKGRAY=8        LIGHTRED=12

             *           BLUE=1        MAGENTA=5      LIGHTBLUE=9        LIGHTMAGENTA=13

             *           GREEN=2     BROWN=6           LIGHTGREEN=10   YELLOW=14

             *           CYAN=3       LIGHTGRAY=7   LIGHTCYAN=11     WHITE=15

             *

             */

 

ippodrom.c

/*

 *

 *   Gottardo Marco driver di interfaccia per la gestione degli ippodromi

 *

 *           ver 1.0 Rev. 08/04/99 con funzione elimina che richiama

 *           RB_Elimina à RB_FIXUP.

 *

 */

 

#include         "pannello.h"

#include           "mouse.h"

#include           "mouse.c"

#include           "pannello.c"

#include          "bottone.h"

#include          "bottone.c"

#include          "graphstr.h"

#include          "graphstr.c"

#include           <dos.h>

#include            <io.h>

#include           "ippodrom.h"

#include           <ctype.h>

#include           <sys\stat.h>

#include           <string.h>

#include           <stdio.h>

#include           <fcntl.h>

#ifndef Chiave

#define Chiave int

#endif

#ifndef NIL

#define NIL 0

#endif

 

enum Boolean { FALSE=0,TRUE};

enum COLORE {NERO =0, ROSSO};

 

extern Pannello * Pannello_Attivo;

 

extern Mouse Mio_dito;

 

 

/* DICHIARAZIONE DELLA STRUTTURA CAVALLO ( nodo dell'albero )*/

 

struct Nodo{

            Chiave chiave;

            COLORE col;

            char name[20];

            Nodo *p,*sin,*des;

            inline Nodo(Chiave k=0,COLORE c=NERO):chiave(k),col(c),p(NIL),

            sin(NIL), des(NIL){};

            };

 

typedef

struct Nodoclone

{

   int i;

   char nome[20];

 }CAVALLO;

 

Nodo *p,*root,*radice,*foglia,*ps;

int k=0;

char stringanome[10];

 int pippo = 0;

int x,y,i,hold;

int linea_di_stampa;

 

/* LE DUE SEGUENTI PROCEDURE METTONO IN PIPPO ON/OFF DEI BOTTONI */

 int setStatusOn( )

{

 pippo = 1;

 sound(660);

 delay(100);

 nosound();

 return pippo;

}

 

int setStatusOff( )

{

 pippo = 0;

 sound(660);

 delay(100);

 nosound();

 return pippo;

}

 

void inserisci(Nodo& z){

            Nodo *y = NIL;

            Nodo *x = radice;

            while(x != NIL &&x !=foglia){

            y = x;

            if (z.chiave<x->chiave)

                        x = x -> sin;

            else

                        x = x -> des;

            }

            z.p = y;

            if (y = = NIL)

                        radice = &z;

            else

                        if(z.chiave<y->chiave)

                                   y->sin =&z;

                        else

                                   y ->des = &z;

            if (z.sin = = NIL)

                        z.sin = foglia;

            if(z.des = = NIL)

                        z.des = foglia;

            //cout<<"\t\n Ho inserito un nodo con chiave "<<z.chiave<<endl;

                        //if(radice = = &z) cout<<"\t..ed è il primo !\n";

            }

//************ Inizio codice della funzione sin_rotazione **************

void sin_rotazione(Nodo *x){

            assert(x->des!=foglia);

            //cout<<"\tInizio una rotazione sinistra su di un nodo di chiave ";

            //cout<<x->chiave<<"\n";

 

            Nodo *y = x -> des;

            x ->des = y -> sin;

            if (y->sin!=foglia)

                        y ->sin->p=x;

            y->p = x ->p;

            if (x->p = = NIL)

                        radice = y;

            else

                        if(x = = x->p->sin)

                                   x ->p->sin = y;

                        else

                                   x ->p->des = y;

                        y->sin=x;

                        x->p=y;

                        //cout<<"\tRotazione sinistra ok\n";

            }

//************* Fine del codice di sin_rotazione ************************

 

//************ Inizio del codice della funzione des_rotazione ***********

void des_rotazione(Nodo *x){

            assert(x->sin!=foglia);

            //cout<<"\tInizio una rotazione destra su di un nodo di chiave ";

            //cout<<x->chiave<<"\n";

            Nodo *y = x -> sin;

            x ->sin = y -> des;

            if (y->des!=foglia)

                        y ->des->p=x;

            y->p = x ->p;

            if (x->p = = NIL)

                        radice = y;

            else

                        if(x = = x->p->des)

                                   x ->p->des = y;

                        else

                                   x ->p->sin = y;

                        y->des=x;

                        x->p=y;

                        //cout<<"\tRotazione destra ok\n";

            }

 

//************ Fine del codice di des_rotazione *************************

 

void RNA_Inserisci(Nodo& z){

            inserisci(z);

            Nodo *x = &z;

            Nodo *y;

            x -> col = ROSSO;

            //cout<<"Ho colorato di rosso il nodo appena inserito \n";

                        while(x != radice && x -> p ->col = = ROSSO ){

                        if ( x ->p = = x->p->p->sin){

                                   //cout<<"Il padre del nodo è un figlio sinistro\n";

                                   y = x -> p -> p -> des;

                                   if (y->col = = ROSSO){ //Analisi del 1° caso

                                               //cout<<"caso 1: Lo zio del nodo è rosso\n";

                                               //cout<<"il padre diventa nero\n";

                                               x->p->col = NERO;

 

                                               //cout<<"lo zio diventa nero\n";

                                               y->col = NERO;

 

                                               //cout<<"il nonno diventa rosso\n";

                                               x-> p -> p ->col = ROSSO;

 

                                               //cout<<"il controllo passa sul nonno\n";

                                               x = x->p->p;

                                       }

                                       else{

                                               //cout<<"Lo zio del nodo è nero\n";

                                       if (x = = x->p->des){

                                               //cout<<"caso 2:il nodo è figlio destro\n";

                                               //cout<<"Il controllo passa sul padre\n";

                                               x = x -> p;

                                               //cout<<"eseguo una rotazione sinistra sul padre\n\t";

                                               sin_rotazione(x);

                                               }

                                               //cout<<"Entro nel caso tre\n";

                                               //cout<<"il padre diventa nero\n";

                                               x->p->col = NERO;

 

                                               //cout<<"il nonno diventa rosso\n";

                                               x->p->p->col = ROSSO;

                                               //cout<<"eseguo una rotazione destra sul nonno\n\t";

                                               des_rotazione(x->p->p);

                                               }

                                       }//Fine del corpo dell'IF lungo

                                       else{

                                               //cout<<"il padre del nodo è un figlio destro\n";

                                               y = x->p->p->sin;

                                               if(y->col = = ROSSO){

                                               //cout<<"caso 1: lo zio del nodo è rosso\n";

                                               //cout<<"il padre diventa nero\n";

                                               x->p->col=NERO;

 

                                               //cout<<"lo zio diventa NERO\n";

                                               y ->col = NERO;

 

                                               //cout<<"il nonno diventa rosso\n";

                                               x->p->p->col = ROSSO;

 

                                               //cout<<"il controllo passa sul nonno\n";

                                               x = x->p->p;

                                         }

                                         else{

                                           //cout<<"lo zio del nodo è nero\n";

                                          if (x = = x->p->sin){

                                               //cout<<"caso 2:il nodo è figlio sinistro\n";

                                               //cout<<"il controllo passa sul padre\n";

                                               x=x->p;

                                               //cout<<"eseguo una rotazione a destra sul padre\n\t";

                                               des_rotazione(x);

                                          }

                                          //cout<<"entro nel caso 3\n";

                                          //cout<<"il padre diventa nero\n";

                                          x->p->col = NERO;

                                          //cout<<"il nonno diventa rosso\n";

                                          x->p->p->col = ROSSO;

                                          //cout<<" eseguo una rotazione sinistra sul nonno\n\t";

                                          sin_rotazione(x->p->p);

                                        }

                                      } //fine dell'ELSE lungo

                               }//fine del ciclo while

                        //cout<<"coloro la radice di nero: la sua chiave è ";

                        //cout<<radice ->chiave<<endl;

                        radice->col=NERO;

                        if (radice = = foglia) radice = NIL;

            }

            //ovviamente bisogna fare attenzione ad non invocare RNA_Cancella

 

void printree(Nodo* w){

            int i=0;

            if(w!=NULL){

            printree(w->sin);

            gotoxy(3,linea_di_stampa);

             cout<<w->chiave<<"  ";

                        while(w->name[i] !='\n'){

                        cout<<w->name[i];

                        i++;

                        };

            cout<<"\n";

            settextstyle(TRIPLEX_FONT,HORIZ_DIR,1);

            outtextxy(45,400,"Prossima pagina");

            outtextxy(48,420,"Premi un tasto");

            if (linea_di_stampa>22)

            {

               getch();

               setfillstyle(SOLID_FILL,BLACK);//pulizia finestra per uscita

               bar(10,150,230,450);//cancella la precente finestra

               line(10,150,230,150);

               line(230,150,230,450);

               line(10,450,230,450);

               line(10,450,10,150);

               linea_di_stampa=15;

             }

            linea_di_stampa++;

            gotoxy(8,linea_di_stampa);

            printree(w->des);

            }

}

 

int backup(void)

{

   FILE *in, *out;

   if ((in = fopen("C:\\IPPODROM\\IPPODROM.DBF", "rt"))= = NULL)

   {

      setfillstyle(SOLID_FILL,BLACK);

      bar(10,150,230,450);

      outtextxy(20,250,"Impossibile aprire");

      outtextxy(20,280,"  file   sorgente");

      return 1;

   }

 

   if ((out = fopen("A:\\IPPODROM.DBF", "wt")) = = NULL)

   {

      setfillstyle(SOLID_FILL,BLACK);

      bar(10,150,230,450);

      outtextxy(20,250,"Impossibile aprire");

      outtextxy(20,280,"file  destinazione");

      return 1;

   }

 

   while (!feof(in))            //finchè non arrivi a fine file puntato da “in”

      fputc(fgetc(in), out);//acquisisci il carattere puntato da in e scaricalo su quello puntato da out

 

   fclose(in);                    //chiudi il file puntato da in

   fclose(out);                  //chiudi il file puntato da out

   return 0;

}

 

int faibackup()

{

 pippo = 1;

 sound(660);

 delay(100);

 nosound();

   line(10,150,230,150);

   line(230,150,230,450);

   line(10,450,230,450);

   line(10,450,10,150);

   settextstyle(TRIPLEX_FONT,HORIZ_DIR,2);

   setcolor(2);

   outtextxy(20,160,"Funzione di BackUp");

   settextstyle(TRIPLEX_FONT,HORIZ_DIR,1);

   outtextxy(20,230,"Inserire  un   floppy");

   outtextxy(20,250,"nel  drive ( A ) e poi");

   outtextxy(20,280,"     RETURN");

   outtextxy(20,340,"In  caso   di  errore");

   outtextxy(20,360,"inserire  un   Floppy");

   outtextxy(20,380,"e confermare (return)");

   outtextxy(20,400,"Per annullare ( a ) o");

   outtextxy(20,420,"    Tab + return");

    if (getch()!='a') backup();

    getch();

    setfillstyle(SOLID_FILL,BLACK);//pulizia finestra per uscita

    bar(10,150,230,450);

 return pippo;

}

 

void elimina(int &togliere,Nodo* scan){

            if ((scan!=NULL)&&(scan->chiave!=togliere)){

                        if (scan->chiave > togliere)

                        elimina(togliere,scan->sin);

                        if (scan->chiave < togliere)

                        elimina(togliere,scan->des);

                        }

            if ((scan != NULL)&&(scan->chiave==togliere))

            {

            gotoxy(3,24);

            cout<<"trovato elemento à"

                <<"("<<scan->chiave<<")"<<"\n";

            gotoxy(3,25);

            cout<<"la radice e' "<<radice->chiave;

            }

            if (scan = = NULL)

            {

            gotoxy(3,24);

            cout<<"elemento inesistente "<<"\n";

            }

}

 

 

int quit( ){

            sound(660);

            delay(100);

            nosound();

            Mio_dito.Disattiva();

            closegraph();

            exit(0);

            return 0;

            }

 

 

int faimodifica( ){

            int val_da_modif;

            sound(660);

            delay(100);

            nosound();

            pippo=1;

            settextstyle(TRIPLEX_FONT,HORIZ_DIR,2);

            setcolor(2);

            line(10,150,230,150);

            line(230,150,230,450);

            line(10,450,230,450);

            line(10,450,10,150);

            outtextxy(20,160,"Funzione di Modifica");

            settextstyle(TRIPLEX_FONT,HORIZ_DIR,1);

            outtextxy(20,230,"Inserire num. cavallo");

            outtextxy(20,255,"da modificare");

            gotoxy(3,23);

            cout<<("chiave da modif. -> ");

            cin>>val_da_modif;

            elimina(val_da_modif,radice);//inizia posizionandosi in val_da_modif

            getch( );

            setfillstyle(SOLID_FILL,BLACK);//pulizia finestra per uscita

            bar(10,150,230,450);//cancella la precente finestra

            setfillstyle(SOLID_FILL,BLACK);//pulizia finestra per uscita

            bar(10,150,230,450);//cancella la precente finestra

            line(10,150,230,150);

            line(230,150,230,450);

            line(10,450,230,450);

            line(10,450,10,150);

            outtextxy(20,160,"Modifica Dati");

            settextstyle(TRIPLEX_FONT,HORIZ_DIR,1);

            outtextxy(20,230,"'N' -> Cambia Nome");

            outtextxy(20,255,"'E' -> Cambia Eta'");

            outtextxy(20,280,"'V' -> Cambia Vittorie");

            outtextxy(20,305,"'F' -> Cambia Favorito");

            outtextxy(20,340,"'Q' -> Uscita");

            getch( );

            setfillstyle(SOLID_FILL,BLACK);//pulizia finestra per uscita

            bar(10,150,230,450);//cancella la precente finestra

            return pippo;

       }

 

int faiInserimento( ){

            FILE *stream;

            CAVALLO cavallo;

            char ch,risp,car;

            int index=0;

            sound(660);

            delay(100);

            nosound();

            pippo=1;

            settextstyle(TRIPLEX_FONT,HORIZ_DIR,2);

            setcolor(2);

            line(10,150,230,150);

            line(230,150,230,450);

            line(10,450,230,450);

            line(10,450,10,150);

              outtextxy(20,160,"     Funzione");

              outtextxy(20,180,"    Inserimento ");

              outtextxy(20,280,"Inserire il numero");

              outtextxy(20,300,"  del cavallo");

 

 

            /* apertura del file in modalià append */

              if ((stream = fopen("\\IPPODROM\\IPPODROM.DBF","ab"))== NULL){

              fprintf(stderr, "file in cui riversare non trovato \file.\n");

              return 1;

              }

            gotoxy(15,23);

            cin>>(k);

            if (!isspace(k)){

             cavallo.i=k;

             ps=new Nodo(k);//crea il nodo solo se valore valido

             setfillstyle(SOLID_FILL,BLACK);//pulizia finestra per uscita

              bar(10,150,230,450);//cancella la precente finestra

              line(10,150,230,150);//nuova finestra di richiesta inserimento dati cavallo

              line(230,150,230,450);

              line(10,450,230,450);

              line(10,450,10,150);

              outtextxy(20,160,"Inserimento Dati");

              settextstyle(TRIPLEX_FONT,HORIZ_DIR,1);

              outtextxy(20,230,"'N' -> Nome");

              outtextxy(20,250,"'E' -> Eta'");

              outtextxy(20,280,"'V' -> Vittorie");

              outtextxy(20,340,"'F' -> Favorito");

              risp='0';

              switch(risp=toupper(getch( ))){//prende sempre il caso maiuscolo

                        case 'N':

                        setfillstyle(SOLID_FILL,BLACK);//pulizia finestra per uscita

                        bar(10,150,230,450);//cancella la precente finestra

                        line(10,150,230,150);//nuova finestra di richiesta inserimento dati cavallo

                        line(230,150,230,450);

                        line(10,450,230,450);

                        line(10,450,10,150);

                  /*la routine acquisisce il nome del cavallo terminato con return */

                  ch='n';

                  gotoxy(5,25);

                  cout<<"nome cavallo -> ";

                        while (!isspace(ch)){

                          ch=getch();

                          stringanome[index]=ch;

                          ps->name[index]=ch;

                          cavallo.nome[index]=ch;

                          cout<<ch;

                          index++;

                        }

                        for(int completa_stringa=index;

                        completa_stringa<=20;

                        completa_stringa++) cavallo.nome[completa_stringa]=' ';

                        cavallo.nome[18]='^';//Inserimento di un carattere di sentinella nel file

                        cavallo.nome[19]='\n';

                        fwrite(&cavallo,sizeof(cavallo),1,stream);

                        fclose(stream);//Chiude il file

                        ps->name[index]='\n'; //importante condizione termine stringa

                        /*fine routine acquisizione nome cavallo */

                        ;break;

                        case 'E':cout<<"routine di inserimento età del cavallo ";break;

                        case 'V':cout<<"routine di inserimento vittorie del cavallo  ";break;

                        case 'F':cout<<" routine di inserimento cavallo favorito boolean";break;

              }// Chiude il costrutto switch

             }// Chiude if controllo chiave non valida

             fclose(stream);//Chiude il file

            RNA_Inserisci(*ps);

            bar(10,150,230,450);//cancella la precente finestra

            return 0;

}

 

int faieliminazione( ){

            sound(660);

            delay(100);

            nosound();

            int val_da_elim;

            pippo=1;

            settextstyle(TRIPLEX_FONT,HORIZ_DIR,2);

            setcolor(2);

            line(10,150,230,150);

            line(230,150,230,450);

            line(10,450,230,450);

            line(10,450,10,150);

            settextstyle(TRIPLEX_FONT,HORIZ_DIR,1);

            outtextxy(70,160," Funzione");

            outtextxy(60,190,"Eliminazione");

            gotoxy(5,23);

            cout<<("chiave da elimare -> ");

            cin>>val_da_elim;

            elimina(val_da_elim,radice);//inizia posizionandosi in val_da_elim

            getch();

            setfillstyle(SOLID_FILL,BLACK);//pulizia finestra per uscita

            bar(10,150,230,450);//cancella la precente finestra

            return pippo;

       }

int faivisione( ){

            sound(660);

            delay(100);

            nosound();

            pippo=1;

            linea_di_stampa=15;

            settextstyle(TRIPLEX_FONT,HORIZ_DIR,2);

            setcolor(2);

            line(10,150,230,150);

            line(230,150,230,450);

            line(10,450,230,450);

            line(10,450,10,150);

            settextstyle(TRIPLEX_FONT,HORIZ_DIR,1);

            outtextxy(70,160," Funzione");

            outtextxy(40,190,"Visione Archivio");

            gotoxy(10,23);

            printree(radice);

            getch();

            setfillstyle(SOLID_FILL,BLACK);//pulizia finestra per uscita

            bar(10,150,230,450);//cancella la precente finestra

            return pippo;

       }

 

 

int main( ){

   FILE *stream;

            CAVALLO cavallo;

            char ch,risp,car;

            int index=0;

            int recordSelettore;//importante per dare salti dimensionali

            long int offset;      //pari a un frame sul disco

   clrscr( );

   if ((stream = fopen("\\IPPODROM\\IPPODROM.DBF", "r+b")) == NULL)

   {

      fprintf(stderr, "Non trovo IPPODROM.DBF\n");

      return 1;

   }

   recordSelettore=1;

   while (!feof(stream))

   {

    offset=(recordSelettore-1)*sizeof(CAVALLO);

    fseek(stream,offset,0);

    fread(&cavallo, sizeof(CAVALLO), 1, stream);

    k=cavallo.i;

    //cout<<k; getch();

    ps=new Nodo(k);

   /* Importante, la sottostante riga annulla la stringa prima di scriverci

    * ciò impedisce di allocare con il nome caratteri aleatori

    */

   /*****************************************************/

   for (index=0;index<=20;index++)  ps->name[index]=' ';

   /*****************************************************/

   index=0;

    while (cavallo.nome[index]!='^'){

               if (isprint(cavallo.nome[index]))  {

                 ps->name[index]=cavallo.nome[index];

                 //cout<<cavallo.nome[index];getch();

                 index++;

                }

                else

                index++;

              }

              ps->name[index]='^';

      ps->name[index++]='\n';

   RNA_Inserisci(*ps);

   recordSelettore++;

   }

   fclose(stream);

            videoinit(); /* PORTA IL SISTEMA IN MODALITA' GRAFICA */

            /*  INTESTAZIONE DELL' AUTORE E NOME PROGRAMMA   */

            setcolor(12);

            settextstyle(TRIPLEX_FONT,HORIZ_DIR,4);

            settextjustify(CENTER_TEXT,CENTER_TEXT);

            moveto(getmaxx()/2,getmaxy()/2-220);

            outtext("Gottardo Marco");

            moveto(getmaxx()/2,getmaxy()/2-190);

            settextstyle(TRIPLEX_FONT,HORIZ_DIR,1);

            outtext("GESTIONE IPPODROMI");

            settextjustify(0,0);

/* Associazione dei bottoni alle rispettive funzioni */

            Pannello Principale("",0);

            Bottone Quit(50,20,"Quit",quit,0,9,1);

            Bottone Solovisione(260,400,"Solo visione",faivisione,0,9,1);

            Bottone Modifica(260,100,"Modifica   ",faimodifica,0,9,1);

            Bottone Inserimento(260,200,"Inserimento",faiInserimento,0,9,1);

            Bottone Elimina(260,300,"Elimina    ",faieliminazione,0,9,1);

            Bottone Backup(470,400,"Backup",faibackup,0,9,1);

            Principale.Inserisci_Bottone(Quit);

            Principale.Inserisci_Bottone(Solovisione);

            Principale.Inserisci_Bottone(Modifica);

            Principale.Inserisci_Bottone(Inserimento);

            Principale.Inserisci_Bottone(Elimina);

            Principale.Inserisci_Bottone(Backup);

            Seleziona_Pannello(Principale);

 

   /* TRACCIA LA CONTORNATURA */

   setcolor(9);

   line(0,0,639,0);

   line(639,0,639,479);

   line(679,479,0,479);

   line(0,479,0,0);

 

     /* SCRIVE I TESTI */

            setcolor(12);

            outtextxy(30,10,"Uscita Programma");

            outtextxy(110,80,"Premere la lettera evidenziata per controllo da tastiera v.1.0");

            outtextxy(240,160,"Modifica i dati di un cavallo");

            outtextxy(240,255,"Inserisce un nuovo cavallo in archivio");

            outtextxy(240,355,"Elimina dei dati in archivio");

            outtextxy(240,460,"Visualizza i dati in archivio");

            outtextxy(500,455,"Salvataggio dati");

            outtextxy(500,465,"   su floppy");

            Mio_dito.Attiva();

            Mio_dito.Attendi_e_Rispondi();

            return 0;

            }

 

Pannello.h

#ifndef             PANNELLO_H

#define PANNELLO_H

#include           "graphstr.h"

#include           "bottone.h"

#ifndef             STATUS

#define  STATUS

                        enum Status { OFF=0, ON, PAUSE, HOP };

#endif   //          STATUS

#ifndef             N_MIN_BOTTONI

#define N_MIN_BOTTONI  1

#endif

#ifndef             N_MAX_BOTTONI

#define N_MAX_BOTTONI  49

#endif

 

class Pannello{

            private:

                        Stringa nome;

 

/* contatore dei bottoni gestiti dal pannello, che appena creato non gestisce alcun bottone; i bottoni da gestire possono essere inseriti nel pannello tutti in una volta oppure anche nel mezzo del programma - purche` il loro numero non superi N_MAX_BOTTONI per ciascun pannello */

                        int n_bottoni;

 

                        /* da 1 a 15: colore del fondo del pannello e colore della scritta con il nome      */

                        int colore;

                        int colore_nome;

                        /* un puntatore per ogni bottone gestito */

                        Bottone * bottone[N_MAX_BOTTONI]; 

                        /* quando il pannello viene posto in stato di 'PAUSE' o disattivato si salva per ogni bottone l'informazione sul suo stato di attivita` prima di metterlo a sua  volta in pausa o di disattivarlo       */

                        Bool attivo[N_MAX_BOTTONI];

                        /* l'attivita` del pannello            */

                        Status stato;

                        /* il pannello attivo immediatamente prima di questo e quello  attivo dopo        */

                        Pannello * prev;

                        Pannello * next;

                        /* funzione virtuale pura da definire per ciascun pannello:         */

                        // virtual void funzione() = 0;

                        /* per evitare le copiature         */

                        Pannello operator=(Pannello);

                        void disegna();

            public:

                        /* crea il pannello con il nome e il colore          */

                        Pannello(Stringa="NONAME",int=1);

                        // ~Pannello();

                        void Inserisci_Bottone(Bottone&);

                        void Attiva();

                        void Attendi();

                        void Commuta();

                        void Disattiva();

                        /* dispone i bottoni memorizzati nel vettore dei  puntatori, dal posto indicato nel primo parametro fino a quello indicato nel secondo, a seconda del loro numero e delle dimensioni di ciascuno */

                        void Ordina(int,int);

                        /* se le coordinate sono occupate da un bottone oppure se e` stato premuto il tasto evidenziato su di un bottone restituisce un puntatore al bottone in questione */

                        Bottone * Dammi_Bottone(int,int);

                        Bottone * Dammi_Bottone(char);

 

                        /* restituisce il puntatore al bottone i-esimo, sia esso presente oppure no nel pannello, purche` l'indice passato non superi il numero massimo di bottoni gestibili N_MAX_BOTTONI. Non ancora realizzato perche` necessita di un maggiore controllo sull'indice i-esimo suddetto, che di regola differisce da pannello a pannello         */

                        // Bottone& Quale_Bottone(int);

                        friend int Seleziona_Pannello(Pannello&);

                        friend int Seleziona_e_Disattiva(Pannello&);

                        friend int Precedente(Pannello&);

                        friend int Successivo(Pannello&);

                        friend void Rinfresca(Pannello&);

};

 

#endif   //          PANNELLO_H

 

pannello.c

#include           "pannello.h"

#include           <graphics.h>

#include        "graphstr.h"

#include           <iostream.h>

#include           "ctype.h"

#include        <assert.h>

#include        <dos.h>

 Pannello * Pannello_Attivo;

void

Pannello::disegna( ){

            assert(Pannello_Attivo==this);

            int colore_corrente;

            switch(stato){

                        case ON:

                                   colore_corrente = colore;

                                   break;

 

                        case OFF:

                                   colore_corrente = LIGHTGRAY;

                                   break;

 

                        case PAUSE:

                                   colore_corrente = CYAN;

                                   break;

 

                        case HOP:

                                   colore_corrente = YELLOW;

                                   break;

                        }

            setbkcolor(colore_corrente);

            moveto(getmaxx()/2,20);

            settextstyle(TRIPLEX_FONT,HORIZ_DIR,3);

            settextjustify(CENTER_TEXT,CENTER_TEXT);

            setcolor(colore_nome);

            Outtext(nome);

            for(int i=0; bottone[i]; i++)

                        bottone[i] -> disegna();    // disegna anche tutti i bottoni

            }

 

 

Pannello::Pannello(Stringa NOME, int COLORE){

            assert(COLORE>=0&&COLORE<16);

            nome = NOME;

            n_bottoni = 0;

            prev = next = 0;

            colore = COLORE;

            switch(COLORE){

                        case     1 : colore_nome = BLACK; break;

                        case     2 : colore_nome = BLUE; break;

                        case     3 : colore_nome = BLUE; break;

                        case     4 : colore_nome = BLUE; break;

                        case     5 : colore_nome = BLUE; break;

                        case     6 : colore_nome = BLACK; break;

                        case     7 : colore_nome = BLACK; break;

                        case     8 : colore_nome = BLACK; break;

                        case     9 : colore_nome = BLACK; break;

                        case     10: colore_nome = BLACK; break;

                        case     11: colore_nome = BLUE; break;

                        case     12: colore_nome = BLUE; break;

                        case     13: colore_nome = WHITE; break;

                        case     14: colore_nome = BLUE; break;

                        case     15: colore_nome = BLUE; break;

                        default  :      colore_nome = BLACK; break;

                        }

                        /* inizializzazione del vettore dei puntatori e del vettore dei bottoni attivi           */

            for(int i=0; i<N_MAX_BOTTONI; i++){

                        bottone[i] = 0;

                        attivo[i] = FALSO;

                        }

            stato = OFF;

 

            /* quando Sequenza sarà una lista i commenti potranno essere tolti e una serie di istruzioni analoghe e contrarie dovrà essere inserita nel distruttore di Pannello;

            if(!Pannello_Attivo)

                        Sequenza.Inserisci(*this, Sequenza.fine());

            else

                        Sequenza.Inserisci(*this, Sequenza.posiz(Pannello_Attivo);

                        */

            }

 

void

Pannello::Inserisci_Bottone(Bottone& nuovo){

            if(n_bottoni+1>N_MAX_BOTTONI)

                        Erroregraf("Troppi bottoni!\n\r");

            /* verifico che i puntatori precedenti siano stati  assegnati ai bottoni già inseriti: */

            for(int i=0; i<n_bottoni; i++){

                        assert(bottone[i]);

                        if(bottone[i]==&nuovo){

                                   Stringa gia_inserito = nuovo.Dammi_Etichetta();

                                   Stringa nome_pannello = nome;

                                   Erroregraf(" e` gia` stato inserito nel pannello ",1);

                                   }

                        }

            bottone[n_bottoni] = &nuovo;

            assert(bottone[n_bottoni]);

            n_bottoni++;

            }

 

 

            /* Solo all'attivazione del pannello vengono attivati e quindi disegnati i bottoni relativi tramite l'invocazione di 'Pannello::disegna()' che richiama per ciascun bottone  'Bottone::disegna()':      */

 

void

Pannello::Attiva(){

            int i;

            /* Se nessun pannello e` ancora stato attivato:  */

            if(!Pannello_Attivo){

                        Pannello_Attivo = this;

                        stato = ON;

                        for(i=0; bottone[i]; i++){

                                   bottone[i] -> PAttivo = VERO;

                                   bottone[i] -> Attiva();

                                   }

                        disegna();

                        return;

                        }

            if(Pannello_Attivo==this) return;

            /* Se invece un pannello e` gia` stato attivato ma non e`questo, che quindi si trova in stato di OFF, PAUSE o HOP, si aggiornano i puntatori: il pannello viene PRIMA registrato come pannello attivo e DOPO vengono accesi i bottoni   */

 

            prev = Pannello_Attivo;

            Pannello_Attivo -> next = this;

            Pannello_Attivo = this;

            setbkcolor(colore);

            moveto(getmaxx()/2,20);

            settextstyle(TRIPLEX_FONT,HORIZ_DIR,3);

            settextjustify(CENTER_TEXT,CENTER_TEXT);

            setcolor(colore_nome);

            Outtext(nome);

            switch(stato){

                        case     PAUSE:          for(i=0; bottone[i]; i++){

                                                           bottone[i] -> PAttivo = VERO;

                                                           if(attivo[i])

                                                                       bottone[i] -> Attiva();

                                                           else

                                                                       bottone[i] -> Disattiva();

                                                           }

                                               break;

 

                        case     OFF:    for(i=0; bottone[i]; i++){

                                                           bottone[i] -> PAttivo = VERO;

                                                           bottone[i] -> Attiva();

                                                           }

                                               break;

 

                        case     HOP:  

                                               break;

 

                        default: break;

                        }

            stato = ON;

 

            /* Solo a questo punto viene invocata 'funzione()', che esegue le funzioni introduttive fisse del particolare pannello            */

            // funzione(); ??? Attenzione: e` ancora una virtuale pura!

            }

 

void

Pannello::Attendi( ){

            assert(Pannello_Attivo);

            assert(Pannello_Attivo==this);

            for(int i=0; bottone[i]; i++){

                        bottone[i] -> PAttivo = FALSO;

                        if(bottone[i]->stato==OFF)

                                   attivo[i] = FALSO;

                        else

                                   attivo[i] = VERO;

                        bottone[i] -> Attendi();

                        }

            stato = PAUSE;

            cleardevice();

            }

 

void

Pannello::Commuta( ){

            switch(stato){

                        case ON:

                                   Disattiva();

                                   break;

 

                        case OFF:

                                   Attiva();

                                   break;

 

                        default:

                                   break;

                        }

            }

 

 

void

Pannello::Disattiva( ){

            for(int i=0; bottone[i]; i++){

                        bottone[i] -> PAttivo = FALSO;

                        attivo[i] = FALSO;

                        bottone[i] -> Disattiva();

                        }

            stato = OFF;

            }

 

 

void

Pannello::Ordina(int i, int j){

            for(int k=i; k<=j; k++){

                        /* disponi i bottoni       */

                        return;

                        }

            }

 

Bottone*

Pannello::Dammi_Bottone(int x, int y){

            int i;

            for(i=0; bottone[i]; i++){

                        if(bottone[i]->Centrato(x,y)){

                                   return bottone[i];

                                   }

                        }

            return 0;

            }

 

Bottone*

Pannello::Dammi_Bottone(char tasto){

            int i;

            for(i=0; bottone[i]; i++){

                        if(bottone[i]->Centrato(tasto)){

                                   return bottone[i];

                                   }

                        }

            tasto = (isupper(tasto)?tolower(tasto):toupper(tasto));

            for(i=0; bottone[i]; i++){

                        if(bottone[i]->Centrato(tasto)){

                                   if(bottone[i]->PAttivo)  // superfluo

                                               return bottone[i];

                                   }

                        }

            return 0;

            }

 

int

Seleziona_Pannello(Pannello& nuovo){

            if(!Pannello_Attivo){

                        nuovo.Attiva();

                        return 0;

                        }

            Pannello_Attivo -> Attendi();

            nuovo.Attiva();

            return 1;

            }

 

int

Seleziona_e_Disattiva(Pannello& nuovo){

            if(!Pannello_Attivo){

                        nuovo.Attiva();

                        return 0;

                        }

            Pannello_Attivo -> Disattiva();

            nuovo.Attiva();

            return 1;

            }

 

int

Precedente(Pannello& beta){

            if(beta.prev)

                        Seleziona_Pannello(*beta.prev);

            else{

                        sound(2000);

                        delay(70);

                        sound(1500);

                        delay(110);

                        nosound();

                        return -1;

                        }

                        return 0;

            }

 

int

Successivo(Pannello& alpha){

            if(alpha.next)

                        Seleziona_Pannello(*alpha.next);

            else{

                        sound(2000);

                        delay(70);

                        sound(1500);

                        delay(110);

                        nosound();

                        return -1;

                        }

                        return 0;

            }

 

 

void

Rinfresca(Pannello& attivo){

            cleardevice();

            attivo.disegna();

            }

 

File contenente la struttura della scheda identificativa del cavallo e definizione della classe RNA (alberi Rosso-Neri).

 

//***************** Inizio area direttive al preprocessore *****************

#include <iostream.h>

#ifndef    BOOL_

#define    BOOL_

            enum Booleano {falso =0,vero};

#endif

#ifndef Chiave

#define Chiave int

#endif

#ifndef COLORE_

#define COLORE_

            enum COLORE {NERO =0,ROSSO};

#endif

#ifndef NIL

#define NIL 0

#endif

//**************** fine delle direttive al preprocessore ********************

 

 

//**************** Inizio area dichiarazione della struttura ****************

 

struct Nodo{

            Chiave chiave;

            COLORE col;

            Nodo *p, *sin, *des;

            inline Nodo(Chiave k=0,COLORE c=NERO):chiave(k),col(c),p(NIL),

                            sin(NIL), des(NIL){};

            inline iNODO(){

                        if (sin) { delete sin; sin = 0;}

                        if (des) { delete des; des = 0;}

                        p = 0;

                        return 0;

                        }

            };

//*************** fine della dichiarazione della struttura *************

 

 

//*************** inizio dichiarazione della classe ********************

 

class RNA{

            private:

                        Nodo *radice;

                        Nodo *foglia;

                        inline void inserisci(Nodo&);

                        inline Nodo* minimo(Nodo *)const;

                        inline Nodo* massimo(Nodo*)const;

                        inline void  sin_rotazione(Nodo*);

                        inline void  des_rotazione(Nodo*);

                        inline void  correggi(Nodo*);

                        inline void  visita_simmetrica(Nodo*)const;

            public:

                        RNA();

                        ~RNA();

                        void RNA_Inserisci(Nodo&);

                        Nodo* RNA_Cancella(Nodo*const);

                        Nodo* RNA_Cerca(const Chiave)const;

                        Nodo* RNA_Predecessore(Nodo *const);

                        Nodo* RNA_Sucessore(Nodo *const);

                        Nodo* RNA_Min();

                        Nodo* RNA_Max();

                        Chiave RNA_Dammi_Radice();

                        Booleano RNA_Radice(Nodo*)const;

                        Booleano RNA_Foglia(Nodo*)const;

                        Booleano RNA_Vuoto()const;

                        friend void Visita_Simmetrica(const RNA&);

            };

//#endif /* RNA_H */

 

// IN QUESTO MODULO SONO CONTENUTI I PROTOTIPI DELLE FUNZIONI

// MEMBRO DELLA CLASSE RNA CHE  IMPLEMENTA IL  TIPO  DI  DATO

// ASTRATTO ALBERO AUTOBILANCIANTE ROSSO/NERO.

 

#include <assert.h>

//************** Codice della funzione inserisci **********************

//*               questo è un inserimento di tipo iterativo semplice                       *

//***************************************************************

void

RNA::inserisci(Nodo& z){

            Nodo *y = NIL;

            Nodo *x = radice;

            while(x != NIL &&x !=foglia){

            y = x;

            if (z.chiave<x->chiave)

                        x = x -> sin;

            else

                        x = x -> des;

            }

            z.p = y;

            if (y==NIL)

                        radice = &z;

            else

                        if(z.chiave<y->chiave)

                                   y->sin =&z;

                        else

                                   y ->des = &z;

            if (z.sin = = NIL)

                        z.sin = foglia;

            if(z.des = = NIL)

                        z.des = foglia;

            cout<<"\t\n Ho inserito un nodo con chiave "<<z.chiave<<endl;

                        if(radice = = &z) cout<<"\t..ed è il primo !\n";

            }

//Bisogna fare attenzione ad non invocare minimo e massimo per

//una foglia, o per radice nel caso che sia radice = = NIL.

//************** Fine del codice di inserisci ************************

 

 

//************** Codice del costruttore *******************************

RNA::RNA( ){

            radice = NIL;

            foglia = new Nodo(-1);

            assert(foglia);

            settextstyle(TRIPLEX_FONT,HORIZ_DIR,2);

            setcolor(2);

            outtextxy(20,160,"E' stato  creato");

            outtextxy(20,180,"un albero binario");

            outtextxy(20,200,"autobilanciante R/B");

            }

//************** Fine del codice del costruttore **********************

 

 

 

//************** Codice del distruttore *******************************

// Si ricorda che i distruttori vengono evocati automaticamente

//all'uscita dal programma.

//*********************************************************************

RNA::~RNA(){

            if (radice)

                        delete radice;

            radice = 0;

            if (foglia)

                        delete foglia;

            foglia = 0;

            cout<<"L'albero è stato deallocato dalla memoria";

}

//************ Fine del codice del distruttore *************************

 

 

//************ Inizio codice della funzione sin_rotazione **************

void

RNA::sin_rotazione(Nodo *x){

            assert(x->des!=foglia);

            cout<<"\tInizio una rotazione sinistra su di un nodo di chiave ";

            cout<<x->chiave<<"\n";

 

            Nodo *y = x -> des;

            x ->des = y -> sin;

            if (y->sin!=foglia)

                        y ->sin->p=x;

            y->p = x ->p;

            if (x->p = = NIL)

                        radice = y;

            else

                        if(x = = x->p->sin)

                                   x ->p->sin = y;

                        else

                                   x ->p->des = y;

                        y->sin=x;

                        x->p=y;

                        cout<<"\tRotazione sinistra ok\n";

            }

//************* Fine del codice di sin_rotazione ************************

 

 

//************ Inizio del codice della funzione des_rotazione ***********

void

RNA::des_rotazione(Nodo *x){

            assert(x->sin!=foglia);

            cout<<"\tInizio una rotazione destra su di un nodo di chiave ";

            cout<<x->chiave<<"\n";

 

            Nodo *y = x -> sin;

            x ->sin = y -> des;

            if (y->des!=foglia)

                        y ->des->p=x;

            y->p = x ->p;

            if (x->p==NIL)

                        radice = y;

            else

                        if(x = = x->p->des)

                                   x ->p->des = y;

                        else

                                   x ->p->sin = y;

                        y->des=x;

                        x->p=y;

                        cout<<"\tRotazione destra ok\n";

            }

 

 

//************ Fine del codice di des_rotazione *************************

 

//************ Codice della funzione inline Inserisci ******************

//Si ricorda che le funzioni inline differiscono dalle normali funzioni

//perchè il loro codice fa subire al sorgente una macroespansione

//rendendo l'esecuzione piu' veloce visto che non è richiesto l'uso

//del run time STACK.

//**********************************************************************

void

RNA::RNA_Inserisci(Nodo& z){

            inserisci(z);

            Nodo *x = &z;

            Nodo *y;

            x -> col = ROSSO;

            cout<<"Ho colorato di rosso il nodo appena inserito \n";

                        while(x != radice && x -> p ->col == ROSSO ){

                        if ( x ->p==x->p->p->sin){

                                   cout<<"Il padre del nodo è un figlio sinistro\n";

                                   y = x -> p -> p -> des;

                                   if (y->col==ROSSO){ //Analisi del 1° caso

                                               cout<<"caso 1: Lo zio del nodo è rosso\n";

                                               cout<<"il padre diventa nero\n";

                                                x->p->col = NERO;

 

                                               cout<<"lo zio diventa nero\n";

                                               y->col = NERO;

 

                                               cout<<"il nonno diventa rosso\n";

                                               x-> p -> p ->col = ROSSO;

 

                                               cout<<"il controllo passa sul nonno\n";

                                               x = x->p->p;

                                       }

                                       else{

                                               cout<<"Lo zio del nodo è nero\n";

                                       if (x==x->p->des){

                                               cout<<"caso 2:il nodo è figlio destro\n";

                                               cout<<"Il controllo passa sul padre\n";

                                               x = x -> p;

                                               cout<<"eseguo una rotazione sinistra sul padre\n\t";

                                               sin_rotazione(x);

                                               }

                                               cout<<"Entro nel caso tre\n";

                                               cout<<"il padre diventa nero\n";

                                               x->p->col = NERO;

 

                                               cout<<"il nonno diventa rosso\n";

                                               x->p->p->col = ROSSO;

                                               cout<<"eseguo una rotazione destra sul nonno\n\t";

                                               des_rotazione(x->p->p);

                                               }

                                       }//Fine del corpo dell'IF lungo

                                       else{

                                               cout<<"il padre del nodo è un figlio destro\n";

                                               y = x->p->p->sin;

                                               if(y->col==ROSSO){

                                               cout<<"caso 1: lo zio del nodo è rosso\n";

                                               cout<<"il padre diventa nero\n";

                                               x->p->col=NERO;

 

                                               cout<<"lo zio diventa NERO\n";

                                               y ->col = NERO;

 

                                               cout<<"il nonno diventa rosso\n";

                                               x->p->p->col = ROSSO;

 

                                               cout<<"il controllo passa sul nonno\n";

                                               x = x->p->p;

                                         }

                                         else{

                                          cout<<"lo zio del nodo è nero\n";

                                          if (x == x->p->sin){

                                               cout<<"caso 2:il nodo è figlio sinistro\n";

                                               cout<<"il controllo passa sul padre\n";

                                               x=x->p;

                                               cout<<"eseguo una rotazione a destra sul padre\n\t";

                                               des_rotazione(x);

                                          }

                                          cout<<"entro nel caso 3\n";

                                          cout<<"il padre diventa nero\n";

                                          x->p->col = NERO;

                                          cout<<"il nonno diventa rosso\n";

                                          x->p->p->col = ROSSO;

                                          cout<<" eseguo una rotazione sinistra sul nonno\n\t";

                                          sin_rotazione(x->p->p);

                                        }

                                      } //fine dell'ELSE lungo

                               }//fine del ciclo while

                        cout<<"coloro la radice di nero: la sua chiave è ";

                        cout<<radice ->chiave<<endl;

                        radice->col=NERO;

                        if (radice = = foglia) radice = NIL;

            }

            //ovviamente bisogna fare attenzione ad non invocare RNA_Cancella

//************* fine del codice della funzione inline Inserisci *************

 

 

/* FILE PER CREARE IL BACKUP DEL FILE IPPODROM.DBF SUL DRIVE A */

 

#include <stdio.h>

 

int backup(void)

{

   FILE *in, *out;

 

   if ((in = fopen("C:\\IPPODROM\\IPPODROM.DBF", "rt")) = =  NULL)

   {

      setfillstyle(SOLID_FILL,BLACK);

      bar(10,150,230,450);

      outtextxy(20,250,"Impossibile aprire");

      outtextxy(20,280,"  file   sorgente");

      return 1;

   }

 

   if ((out = fopen("A:\\IPPODROM.DBF", "wt"))

       = = NULL)

   {

      setfillstyle(SOLID_FILL,BLACK);

      bar(10,150,230,450);

      outtextxy(20,250,"Impossibile aprire");

      outtextxy(20,280,"file  destinazione");

      return 1;

   }

 

   while (!feof(in))

      fputc(fgetc(in), out);

 

   fclose(in);

   fclose(out);

   return 0;

}

 

Il programma usa la classe stringa per creare alcuni eventi che possono essere associati alla pressione dei tasti.

L’implementazione della classe stringa è delegata ai due file graphstr.h che contiene la definizione della classe, graphstr.c che contiene i prototipi delle sue funzioni membro.

 

#ifndef BOOL

#define             BOOL

 

enum Bool {FALSO = 0, VERO};

// la costante FALSO deve valere 0 (zero) per ottenere che

// l'espressione !FALSO sia vera; analogamente la costante

// VERO deve avere un valore diverso da zero

 

#endif  //          BOOL

 

#ifndef         GRAPRIMS_H

#define         GRAPRIMS_H

 

#include        <stdio.h>

 

const int MAXRIGA = 100;               

// questa costante definisce la lunghezza del buffer usato in

// alcune operazioni di ingresso

 

class Stringa {

            char* s;

  public:

            Stringa (char* = 0);

            Stringa (int, int=0);

            Stringa (double, int=0, int=3);

            ~Stringa ();

            Stringa (const Stringa&);

            Stringa& operator= (const Stringa&);

            Bool operator== (const Stringa&) const;

            Bool operator< (const Stringa&) const;

            Bool operator> (const Stringa&) const;

            char& operator[] (int);

            Stringa operator+ (const Stringa&) const;

            Stringa operator+ (char) const;

            friend Stringa operator+ (char, const Stringa&);

            Stringa& operator+= (const Stringa&);

            Stringa& operator+= (char);

            Stringa operator<< (int) const;

            Stringa operator>> (int) const;

            int operator% (const Stringa&) const;

            friend int Lunghezza (const Stringa&);

            int Numero (int&) const;

            operator int ();

            operator double ();

            friend void Avviso (const Stringa&);

            friend void Errore (const Stringa&, int = 1);

            friend void Erroregraf (char*, int = 1);

            friend int Prendi (Stringa&);

            friend void Metti (const Stringa&);

            friend void Copia (char*, const Stringa&);

            friend void far Outtext(const Stringa&);

            };

 

void Avviso (char);

int Prendi (char&);

void Metti (char);

#endif  // GRAPRIMS_H

 

file graphstr.c

// costruttore di default per una stringa

#include        "graphstr.h"

#include        <assert.h>

#include        <graphics.h>

#include        <stdlib.h>

 

Stringa::Stringa (char* str) {

            if (str = = 0) {

                        s = new char[1];

                        assert(s);

                        *s = '\0';

                        }

            else {

                        for (int l = 0; str[l]; l++);   // calcola la lunghezza

                        char* sin = s = new char[l+1];

                        assert(s);

                        while ((*sin++ = *str++) != 0); // copia la stringa

                        }

            }

 

            /* costruttore che converte un intero in una stringa    */

Stringa::Stringa (int n, int campo) {

            char tmp[MAXRIGA];

            char segno;

            if (n < 0) {

                        n = -n;

                        segno = '-';    // ai numeri negativi viene prefisso '-'

                        }

            else if (n == 0)

                        segno = '0';    // il numero 0 viene convertito nella

                                                                       // stringa "0"

            else

                        segno = '\0';   // ai numeri positivi non viene prefisso

                                                                       // il segno

 

// conversione

 

            for (int i = 0; n > 0; i++) {

                        tmp[i] = n % 10 + '0';

                        n /= 10;

                        }

            if (segno)

                        tmp[i++] = segno;

 

// si allinea il risultato a destra

 

            int l;

            l = campo > i? campo : i;

            s = new char[l+1];

            assert(s);

            s[l] = '\0';

            for (int j = 0; j < campo - i; j++)

                        s[j] = ' ';

            for (; i > 0; i--)

                        s[l-i] = tmp[i-1];

            }

 

            // costruttore che converte un double in una stringa

Stringa::Stringa (double d, int campo, int decimali) {

            char tmp[MAXRIGA];

    Stringa formato = '%' + Stringa(campo) + '.' +

                        Stringa(decimali) + 'g';

            int l = sprintf(tmp, formato.s, d);

            char* sin = s = new char[l+1];

            assert(s);

            char* des = tmp;

            while ((*sin++ = *des++) != 0); // copia la stringa

            }

 

            // distruttore

Stringa::~Stringa () {

            delete   s;

            }

     

            // costruttore per copia

Stringa::Stringa (const Stringa& str) {

            for (int l = 0; str.s[l]; l++);

            char* sin = s = new char[l+1];

            assert(s);

            char* des = str.s;

            while ((*sin++ = *des++) != 0);

            }

 

            // sovraccarico dell'assegnazione

Stringa&

Stringa::operator= (const Stringa& str) {

            if (this == &str)       // si verifica che non sia str = str

                        return *this;

            for (int l = 0; str.s[l]; l++);

            delete   s;

            char* sin = s = new char[l+1];

            assert(s);

            char* des = str.s;

            while ((*sin++ = *des++) != 0);

            return *this;           // per poter effettuare assegnazioni multiple

            }

 

            // confronto fra stringhe (uguaglianza)

Bool

Stringa::operator== (const Stringa& str) const {

            for (char* sin = s, * des = str.s; *sin == *des; sin++, des++)

                        if (*sin == '\0')

                                   return VERO;

            return FALSO;

            }

 

            // confronto fra stringhe (minore)

Bool

Stringa::operator< (const Stringa& str) const {

            for (char* sin = s, * des = str.s; *sin == *des; sin++, des++)

                        if (*sin == '\0')

                                   return FALSO;

            if (*sin < *des)

                        return VERO;

            else

                        return FALSO;

            }

 

            // confronto fra stringhe (maggiore)

Bool

Stringa::operator> (const Stringa& str) const {

            for (char* sin = s, * des = str.s; *sin == *des; sin++, des++)

                        if (*sin == '\0')

                                   return FALSO;

            if (*sin > *des)

                        return VERO;

            else

                        return FALSO;

            }

 

// una stringa puo' essere indicizzata come un array

// il metodo resituisce un riferimento e quindi puo`

// essere usato a sinistra dell'"="

char&

Stringa::operator [] (int i) {

            for (int l = 0; s[l]; l++);

            assert (i >= 0 && i < l);       // verifica dell'indice

            return s[i];

            }

 

            // operatore di concatenazione Stringa + Stringa

Stringa

Stringa::operator+ (const Stringa& str) const {

            Stringa tmp;

            for (int lsin = 0; s[lsin]; lsin++);

            for (int ldes = 0; str.s[ldes]; ldes++);

            int l = lsin + ldes;

            delete   tmp.s;

            tmp.s = new char[l+1];

            assert(tmp.s);

            for (int i = 0; i < lsin; i++)

                        tmp.s[i] = s[i];

            for (; i < l; i++)

                        tmp.s[i] = str.s[i-lsin];

            tmp.s[l] = '\0';

            return tmp;

            }

 

 

            // operatore di concatenazione Stringa + char

Stringa

Stringa::operator+ (char c) const {

            Stringa tmp;

            for (int lsin = 0; s[lsin]; lsin++);

            delete   tmp.s;

            tmp.s = new char[lsin+2];

            assert(tmp.s);

            for (int i = 0; i < lsin; i++)

                        tmp.s[i] = s[i];

            tmp.s[i++] = c;

            tmp.s[i] = '\0';

            return tmp;

            }

 

 

            // operatore di concatenazione char + Stringa

Stringa

operator+ (char c, const Stringa& str) {

    Stringa tmp;

    for (int ldes = 0; str.s[ldes]; ldes++);

            delete   tmp.s;

            tmp.s = new char[ldes+2];

            assert(tmp.s);

            tmp.s[0] = c;

            for (int i = 1; i <= ldes; i++)

                        tmp.s[i] = str.s[i-1];

            tmp.s[i] = '\0';

            return tmp;

            }

           

            // operatore di concatenazione Stringa + Stringa

// il metodo restituisce un riferimento per evitare

// copiature inutili in espressioni del tipo

// s1 = s2 += s3;

Stringa&

Stringa::operator+= (const Stringa& str) {

            char* tmp = s;

            for (int lsin = 0; s[lsin]; lsin++);

            for (int ldes = 0; str.s[ldes]; ldes++);

            int l = lsin + ldes;

            s = new char[l+1];

            assert(s);

            for (int i = 0; i < lsin; i++)

                        s[i] = tmp[i];

            for (; i < l; i++)

                        s[i] = str.s[i-lsin];

            s[l] = '\0';

            delete   tmp;

            return *this;

            }

 

 

            // operatore di concatenazione Stringa + char

// il metodo restituisce un riferimento per

// evitare copiature inutili in espressioni del tipo

// s1 = s2 += c;

Stringa&

Stringa::operator+= (char c) {

            char* tmp = s;

            for (int lsin = 0; s[lsin]; lsin++);

            s = new char[lsin+2];

            assert(s);

            for (int i = 0; i < lsin; i++)

                        s[i] = tmp[i];

            s[i++] = c;

            s[i] = '\0';

            delete   tmp;

            return *this;

            }

 

            // scorrimento a sinistra

// il metodo taglia i caratteri di una stringa

// partendo da sinistra

Stringa

Stringa::operator<< (int i) const {

            assert(i >= 0);

            for (int lsin = 0; s[lsin]; lsin++);

            Stringa tmp;

            if (i < lsin) {

                        delete   tmp.s;

                        tmp.s = new char[lsin-i+1];

                        for (int k = 0; k+i <= lsin; k++)

                                   tmp.s[k] = s[k+i];

                        }

            return tmp;

            }

 

 

            // scorrimento a destra

// il metodo taglia i caratteri di

// una stringa partendo da destra

Stringa

Stringa::operator>> (int i) const {

            assert(i >= 0);

            for (int lsin = 0; s[lsin]; lsin++);

            Stringa tmp;

            if (i < lsin) {

                        delete   tmp.s;

                        tmp.s = new char[lsin-i+1];

                        for (int k = 0; k < lsin-i; k++)

                                   tmp.s[k] = s[k];

                        tmp.s[k] = '\0';

                        }

            return tmp;

            }

 

            // cerca la posizione di una sottostringa

// utilizza un algorimto a "forza bruta"

 

int

Stringa::operator% (const Stringa& modello) const {

            for (int i = 0; s[i]; i++) {

                        for (int j = i, k = 0;

                                   modello.s[k] && s[j] == modello.s[k]; j++, k++);

                        if (k > 0 && modello.s[k] == '\0')

                                   return i;

                        }

            return -1;

            }

 

            // calcola la lunghezza di una Stringa

int

Lunghezza (const Stringa& str) {

            for (int l = 0; str.s[l]; l++);

            return l;

            }

 

            // conversione di una stringa in un intero

// la conversione inizia dal primo carattere non spazio

// a partire dall'i-esimo carattere

// al termine della conversione i indica il primo

// carattere non numerico

int

Stringa::Numero (int& i) const {

            while (s[i] == ' ' || s[i] == '\t' || s[i] == '\n')

                        i++;

            if (s[i] == '\0')

                        return 0;

            int segno;

            if (s[i] == '-')

                        segno = -1;

            else

                        segno = 1;

            if (s[i] == '+' || s[i] == '-')

                        i++;

            int n = 0;

            while (s[i] >= '0' && s[i] <= '9') {

                        n = 10 * n + s[i] - '0';

                        i++;

                        }

            return segno * n;

            }

 

            // conversione di una stringa in un intero

Stringa::operator int ( ) {

            int i = 0;

            return this->Numero(i);

            }

  

            // conversione di una stringa in un double

Stringa::operator double ( ) {

            double d = 0.0;

            sscanf(s, "%lf", &d);

            return d;

            }

 

            // per scrivere una stringa sul flusso di uscita "stderr"

void

Avviso (const Stringa& str) {

            fputs(str.s, stderr);

            }

 

            // per scrivere una stringa sul flusso di uscita "stderr"

// e terminare il programma

void

Errore (const Stringa& str, int ret) {

            fputs(str.s, stderr);

            exit(ret);

            }

 

            // come sopra ma prima chiude la grafica:

void

Erroregraf (char* str, int ret)

{

            closegraph();

            fputs(str, stderr);

            exit(ret);

}

 

// estrae una riga dal flusso di ingresso  restituisce la lunghezza della riga '/n' incluso,

// 0 alla fine del file

int

Prendi (Stringa& str) {

            char tmp[MAXRIGA+1];

            if (feof(stdin))

                        return 0;

            else {

                        if (fgets(tmp, MAXRIGA, stdin)) {

                                   str = tmp;

                                   return Lunghezza(str);

                                   }

                        else

                                   return 0;

                        }

            }

 

            // inserisce una stringa sul flusso di uscita

void

Metti (const Stringa& str) {

            fputs(str.s, stdout);

            } 

            // inserisce un carattere sul flusso di uscita "stderr"

void

Avviso (char c) {

            fputc(c, stderr);

            }

 

            // estrae un carattere dal flusso di ingresso

// restituisce 0 alla fine del file

int

Prendi (char& c) {

            int i = getchar();

            if (i == EOF)

                        return 0;

            else {

                        c = i;

                        return 1;

                        }

            }

 

            // inserisce un carattere sul flusso di uscita

void

Metti (char c) {

            putchar(c);

            }

 

void

Copia (char * copia, const Stringa& modello)

{

            for (int l = 0; modello.s[l]; l++);

            copia = 0;

            copia = new char[l+1];

            assert(copia);

            char * des = modello.s;

            char * sin = copia;

            while ((*sin++ = *des++) != 0);

            sin = des = 0;

            copia[l+1] = '\0';          // superfluo

 

void far

Outtext(const Stringa& str)     //(char far *textstring)

{

            outtext(str.s);

}

Appendice

Vengono ora presentati alcuni lavori prodotti utilizzando la medesima base software del programma ippodrom.

Il primo, è stato pensato per effettuare il monitoraggio di alcuni parametri fondamentali di una serra quali la temperatura, la luminosità, l’umidità.

Nel caso che uno dei suddetti parametri subisse una variazione non consentita perché superiore ad un certo Set point preimpostato nella console Hardware,  il supervisore lancia degli allarmi, registra gli eventi nella memoria di massa, e interviene con azioni di comando che saranno interpretate ed eseguite da un P.L.C. ( Controllore a logica programmabile ) connesso tramite la porta parallela al supervisore.

La comunicazione tra il P.L.C. e il personal computer avviene tramite una routine in linguaggio assembly inclusa nel file sorgente.

Ricordiamo prima di proseguire nella spiegazzione che la porta parallela di un personal computer IBM compatibile  è un dispositivo di comunicazione a otto  bit di norma utilizzato da stampanti o plotter.

Le linee del data bus collegate alla parallela possono essere gestite dalla classe out di istruzioni Assembler.

In complemento il C++ mette a disposizione la classe in  per la gestione dei dai in lettura.

Il tipo di dato Assembler predefinito a otto bit è  byte, mentre il corrispondente tipo in C è  char.

I dati vengono trasferiti alle linee di uscita della parallela nel momento in cui si esegue l’operazione di scrittura out.

I livelli di tensione disponibili alla porta sonno gli standard TTL, ovvero 5,0 Volt per il livello logico alto e 0,5 Volt per il livello logico basso.

Quasi più importante dei livelli logichi di tensione disponibili è l’informazione sulla corrente massima che possiamo pretendere di estrarre da ogni linea della parallela.

Per non sovracaricare la porta rischiando di romperla è bene non pretendere da essa non più dei 20 milliAmpére .

Un’ altro importante ragguaglio è quello di cercare di separare galvanicamente il mondo esterno dalla circuiteria interna del computer al fine di evitare eventuali guasti che possano venire causati impulsi di tensioni indotti nei cavi in ambito industriale, a tal scopo sarà sufficiente munire ogni linea esterna di un’ optoisolatore.

I dati saranno disponibili nella porta comunemente denominata LPT1 ai Pin de 2 a 9 che rappresentano il Bus Dati da D0 a D7.

La parallela è un connettore a 25 poli di tipo Cannon D_miniature distinguibile dalla seriale perché sul lato computer viene sempre montata femmina.

Il Software di gestione incluso nel file serre.exe  è:

 

#pragma inline

#include <dos.h>

int main( ){

int port = 956;  //Indirizzo base della parallela

 

asm{

            mov     dx,port             //inserisce il numero della porta parallela

            mov     ax,temp            //valore da inviare alla porta

            out       dx,al                //invia solo i primi 8 bit

            }

return 0;

}

La pagina principale del programma di gestione delle serre mette a disposizione una chiara immagine dei punti dell’impianto in cui si andrà ad agire.

Si può notare la presenza di alcuni grafici temporali rappresentanti l’andamento delle variabili sotto controllo ( Temperatura, Luminosità, Umidità ).

Questi grafici sono generati dal calcolatore grazie ai dati che esso riceve tramite un’ulteriore porta di comunicazione, questa volta di tipo seriale, la COM 2 visto che COM 1 è di norma adoperata dal dispositivo di puntamento ( mouse ).

La gestione della porta seriale verrà esposta nella presentazione del programma orv.exe

 

 

 

 

 

 

 

 

 


 

 

 

 

I Set point per le variabili in monitoraggio vengono impostate via sftware tramite la tastiera del computer.

Nel caso che uno dei Set point venisse superato verrà attivata una routine associata all’evento che per richiesta del cliente potrà effettuare azioni anche moto specifiche, in ogni caso comunque il tutto viene registrato in file nel disco rigido sotto forma di testo.  

 

 

Sistema di supervisione dei Box di carico estrusori per la ditta O.R.V.

 

La ditta O.R.V. ( Ovattificio, Resinatura, Veneta ) con sede a Grantorto PD, leader nel settore della produzione di materiali sintetici intende installare il sistema di controllo e supervisione del sistema di carico degli estrusori delle proprie linee di produzione.

Il sistema si compone di un P.L.C. modello S7-214 prodotto dalla SIEMENS munito di espansione per l’ input, output digitale, e di un personal computer che in pagina grafica mette a disposizione dell’operatore le principali manovre che può effettuare un utente.

Il programma orv.exe, derivato da ippodrom.c include una routine di comunicazione che setta la porta COM 2 secondo i parametri attesi al P.L.C.

Ogni tasto funzione, attivabile tramite il mouse oppure da tastiera invia tramite la porta seriale il carattere su di esso evidenziato in rosso.

Il P.L.C. mette a disposizione un’area di memoria nella quale sono posti  i dati inviati dal supervisore o che sono in attesa di essere inviati ad esso.

La comunicazione è ovviamente bidirezionale, se così non fosse, un’azione impostata sullo schermo che per qualche ragione non venisse eseguita dall’Hardware passerebbe innosservata.

Molto interessante risulta essere la soluzione applicata per la selezione del del percorso tramite le opportune valvole deviatrici, nonché il sistema di interblocco che ipedisce di mandare in carico un Box mentre un percorso è già attivo.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Una pagina grafica di presentazione compare sullo schermo quando il programma viene lanciato in esecuzione.

Superata tale pagina tramite la pressione di un qualunque tasto si entra nella pagina di supervione che rappresenta abbastanza realisticamente la disposizione degli elementi componenti l’impianto.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

/*         ruotine di utilizzo porta seriale per comunicazione

*/

#include <bios.h>

#include <conio.h>

 

#define COM1       0

#define DATA_READY 0x100

#define TRUE       1

#define FALSE      0

#define SETTINGS (_COM_1200 | _COM_CHR7 | _COM_STOP1 | _COM_NOPARITY)

 

int main(void)

{

  unsigned in, out, status;

 

  _bios_serialcom(_COM_INIT, COM1, SETTINGS);

  cprintf("... _BIOS_SERIALCOM [ESC] to exit ...\r\n");

  for (;;)

  {

    status = _bios_serialcom(_COM_STATUS, COM1, 0);

    if (status & DATA_READY)

      if ((out = _bios_serialcom(_COM_RECEIVE, COM1, 0) & 0x7F) != 0)

            putch(out);

    if (kbhit())

    {

      if ((in = getch()) == '\x1B')

            break;

      _bios_serialcom(_COM_SEND, COM1, in);

    }

  }

  return 0;

}

 

/*

 

Le routine di gestione delle porte seriali usano costrutti e classi aventi prototipi in  <BIOS.H>.

Le  due  principali funzioni di settaggio, bioscom e _bios_serialcom possono venire  attivate   passandoci dei parametri costituenti le specifiche fondamentali del protocollo seriale.

La routine   è suddivisa in due blocchi fondamentali, il primo che mette il sistema in ricezione e la seconda che lo pone in trasmissione.

Il programma presentato può essere utilizzato ad esempio per connettere tra loro due personal computer al fine di scambiarsi dei caratteri che compariranno a monitor.

La modalità è una Whaiting State, i due processori sonocioè impegnati nell’attesa della ricezione perché intrappolati in un ciclo for che chiede come condizione di uscita la pressione del tasto “esc”.

Il tasto “esc” viene rilevato acquisendo il carattere: '\x1B'

Il costrutto “if” indicato ha lo scopo di verificare che il carattere di escape non si sia presentato.

 

if ((in = getch()) == '\x1B')

            break;

 

  RS-232 comunicazione  (seriale I/O)

 

 Dichiarazione

  þint bioscom(int cmd, char abyte, int port);

  þunsigned _bios_serialcom(int cmd, int port, char abyte);

 

Remarks

Entrambi bioscom e _bios_serialcom usano il BIOS interrupt 0x14 per eseguire svariati protocolli

RS-232 di comunicazione attraversola porta di  I/O standard.

 

Gli argomenti da passare alle funzioni sono:

 

  abyte  combinazione OR di bits che specificano la porta  COM da settare(ignorato se cmd = 2 o 3)

  cmd    Specifica l’operazione di I/O da eseguire.

  port   Identifies the I/O port;  0 = COM1, 1 = COM2, etc.

 

 Valore ritornato:

Per ogni valore di  cmd, entrambe le funzioni ritornano un intero a 16 bit.

Gli 8 bit più significativi del valore di ritorno sono bit di stato.

Se uno o più bit di stato sono settati a 1 significa che si è verificato un errore.

  Se nessun bit di statoè settato a 1 allora il byte è stato ricevuto senza alcun errore.

 

Gli 8 bit meno significativi del valore di ritorno dipendono dal valore specificato di cmd.

 

  0 (_COM_INIT)            I bits meno significativi sono accettati e mostrati.

  3 (_COM_STATUS)      Settato in ricezione o trasmissione