PROGRAMMIAMO
C++ - Programmazione top down
Programmazione bottom up e top down

Le funzioni sono essenziali alla realizzazione di qualsiasi programma, anche di media complessità. In pratica non esiste nessun programma (o quasi) che venga realizzato senza fare uso delle funzioni. Anzi, di solito, le funzioni si occupano di svolgere la maggior parte dei compiti del programma, mentre il main (programma principale) funge spesso da semplice "raccordo" fra le funzioni stesse.

Nella progettazione e scrittura di un programma con le funzioni si distinguono due approcci diversi:

Entrambi gli approcci hanno i loro vantaggi e svantaggi e spesso, nella programmazione moderna, vengono usate tecniche miste fra le due. Qui vogliamo semplicemente mostrare un esempio di programmazione top down applicata a un semplice caso.

Il gioco del master mind

Il Master Mind è un gioco da tavolo, in cui due giocatori svolgono a turno il ruolo di "codificatore" e di "decodificatore. Il "decodificatore", deve indovinare il codice segreto composto dal suo avversario "codificatore". Di solito il codice segreto è composto da un numero 4 cifre decimali, tutte diverse fra loro (es. 6743).

Dopo che il codificatore ha composto il codice, il decodificatore fa il suo primo tentativo, cercando di indovinare il codice. Il codificatore, appena il suo avversario ha completato il tentativo, fornisce degli aiuti comunicando:

Non bisogna comunicare quali cifre sono giuste o sbagliate ma solo quante. Vince il primo dei due giocatori che è in grado di indovinare il codice dell'avversario.

 

Programma per il gioco del master mind

Vogliamo ora progettare un programma C per il gioco del master mind contro il computer. In pratica il decodificatore è l'utente, mentre il codificatore è il nostro programma (e non c'è uno scambio di ruoli, cioè è solo l'utente che deve indovinare il codice generato dal computer e non viceversa).

Vogliamo sviluppare il nostro progetto facendo uso della tecnica detta top down. In base a tale strategia, definiamo anzitutto la struttura generale del programma di più alto livello (main) e delle funzioni di cui ha bisogno. Ad una prima analisi il main dovrà disporre delle seguente funzioni (specificate ciascuna in base a un prototipo e a una breve descrizione):

L'ultima funzione è necessaria per verificare che l'utente fornisca tentativi validi (per esempio un tentativo come 2222 non sarebbe accettato).

In base alle precedenti funzioni siamo già in grado di scrivere il main:

#include <cstdlib>
#include <iostream>

using namespace std;

int genera(void);
int giuste_al_posto_giusto(int codice, int tentativo);
int giuste_al_posto_sbagliato(int codice, int tentativo);
int valido(int numero);

int main(int argc, char *argv[])
{
    int codice, tent, ng=0, ns=0, Ntent;
    
    codice = genera(); 			//genera il codice segreto
    
    for (Ntent=0; ng!=4; Ntent++)
          {
          
          do
            {
            cout<<"Fornisci un numero di 4 cifre diverse ";
            cin>>tent;
            }
          while (!valido(tent));	// impedisce di fornire un numero non valido
          
          ng = giuste_al_posto_giusto(codice, tent);
          ns = giuste_al_posto_sbagliato(codice, tent);
          
          cout<<"Cifre giuste al posto giusto "<<ng<<" e giuste al posto sbagliato "<<ns<<endl;
          }
    cout<<"Bravo! Hai indovinato in "<<Ntent<<" tentativi!\n";
    
    system("PAUSE");
    return EXIT_SUCCESS;
}

Si osservi che il main è stato scritto senza sapere come saranno fatte le funzioni, ma "facendo finta" che tutte le funzioni siano già disponibili al programmatore (mentre invece devono ancora essere scritte!).

Vantaggi e problemi della programmazione top down: gli stub

Il principale vantaggio della programmazione top down è quello che consente al programmatore di focalizzarsi sulla struttura generale del programma, senza doversi inizialmente occupare della realizzazione del codice a basso livello. In tal modo il programmatore può progettare con un elevato livello di astrazione e di libertà. Inoltre la scrittura del programma principale risulta grandemente semplificata.

Fra gli svantaggi, il fatto di non avere il codice delle funzioni fin dall'inizio può creare problemi. Anzitutto perché in tale modo non si può testare il programma. Infatti è possibile compilare il main senza funzioni (per verificare l'assenza di errori), ma non è possibile farlo eseguire.

Per condurre un test di funzionamento sul main prima di aver scritto nel dettaglio il codice delle funzioni, si usano i cosiddetti stub (letteralmente "mozzicone", "residuo"). Uno stub è una porzione di codice provvisoria che viene utilizzata in sostituzione del codice vero e definitivo.

Per fare un esempio pratico, riferito al nostro programma, potremmo definire provvisoriamente le nostre funzioni in questo modo:

int genera(void)
{
return 1234;
}

int giuste_al_posto_giusto(int codice, int tentativo)
{
return 2;
}

int giuste_al_posto_sbagliato(int codice, int tentativo)
{
return 2;
}

int valido(int numero)
{
return 1;
}

Naturalmente le nostre quattro funzioni non fanno assolutamente nulla di interessante (si tratta infatti di semplici stub), ma ci consentono di compilare e linkare il programma, testandone il funzionamento.

 

Scrittura e test delle funzioni

Una volta costruito e testato il programma main, è possibile procedere alla scrittura delle singole funzioni. Consideriamo, a titolo di esempio, la funziona genera per la generazione del codice segreto. Possiamo scriverla utilizzando una funzione di generazione di numeri pseudocasuali, nel seguente modo:

int caso(int min, int max); // funzione che genera un numero casuale compreso fra min e max

int genera(void)
    {
    int codice;

    do
        {
        codice = caso(1234,9999);
        }
    while (!valido(codice));

    return codice;

    }

Si noti che:

Come si vede la progettazione top down si applica ricorsivamente alla progettazione delle singole funzioni.

Per quanto riguarda il test delle funzioni, questo può essere fatto usando dei main provvisori (altri stub), i quali servono solo per verificare il corretto funzionamento delle funzioni. Per esempio, nel nostro caso il main provvisorio per testare la funzione genera potrebbe essere qualcosa del genere:

#include <cstdlib>
#include <iostream>

using namespace std;

int genera(void);

int main(int argc, char *argv[])
{
    while(1)
            cout<<genera()<<endl;
    
    system("PAUSE");
    return EXIT_SUCCESS;
}

Si osservi l'estrema semplicità del main di prova, che si limita a chiamare la funzione genera in un ciclo infinito e a visualizzare i codici generati.

 

link precedente - successiva

Sito realizzato in base al template offerto da

http://www.graphixmania.it