Formula 1, Campionato - Soluzione

In questo tutorial non verrà presentata la possibile soluzione nella sua interezza, ma soltano le parti interessanti, che dovranno essere opportunamente integrate per fornire la soluzione completa e compilabile. Per ottenere un programma completo e funzionante, si tratta pertanto di scrivere la funzione main che dichiara le variabili necessarie e che chiama opportunamente le funzioni descritte.

Per cominciare, si assume che vengano effettuate le seguenti dichiarazioni:

#define N_SQUADRE (10)
#define N_PILOTI (20)
#define MAX_RIGA (50)

#define POS_A_PUNTI (10)        /* Posizioni a cui si assegnano punti */

int punti_per_pos[POS_A_PUNTI] = {
    25, 18, 15, 12, 10, 8, 6, 4, 2, 1
};

I dati saranno memorizzati in array di strutture. Piloti e squadre richiedono lo stesso tipo di informazioni (nome e punteggio). Pertanto conviene utilizzare lo stesso tipo, in questo modo si eviterà di replicare le funzioni che effettuano le elaborazioni:

struct classificato {
    char nome[MAX_RIGA + 1];
    int punti;
};

Le funzioni descritte di seguito dovranno essere oppotunamente richiamate nel main. Ad esse dovranno essere passati gli argomenti corretti.

Lettura del file

La funzione per la lettura e l'elaborazione del file può essere come segue:

int leggi_gara(FILE * fin,
        struct classificato *piloti,
        struct classificato *scuderie)
{
    char buffer[MAX_RIGA + 1];
    int pos, punti;
    char nome_pilota[MAX_RIGA + 1];
    char nome_scuderia[MAX_RIGA + 1];

    pos = 0;
    while (pos < N_PILOTI && fgets(buffer, MAX_RIGA + 1, fin) != NULL) {
        sscanf(buffer, "%s %s", nome_pilota, nome_scuderia);

        if (pos < POS_A_PUNTI)
            punti = punti_per_pos[pos];
        else
            punti = 0;

        /* Cerca il pilota nell'elenco e gli assegna i punti. */
        struct classificato *pilota =
            cerca_classificato(piloti, N_PILOTI, nome_pilota);
        pilota->punti += punti;

        /* Cerca la squadra nell'elenco e gli assegna i punti. */
        struct classificato *scuderia =
            cerca_classificato(scuderie, N_SQUADRE,
                    nome_scuderia);
        scuderia->punti += punti;

        /* Passaggio alla posizione successiva. */
        pos++;
    }
    return pos;
}

La funzione legge al massimo N_PILOTI righe dal file, corrispondenti al numero di piloti che partecipano ad una sola gara, quindi ad ogni chiamata vengono letti i dati di una sola gara. Questo significa che deve essere chiamata più volte per leggere tutti i dati dal file, cosa che essere fatta in questo modo:

    while (leggi_gara(fin, piloti, scuderie) != 0) {
        /* quando leggi_gara ritorna, una gara intera e` stata letta */
        .....
    }

La funzione richiede due argomenti:

  • il file da leggere, già aperto in lettura prima di chiamare leggi_gara (S8.2.1);
  • il vettore piloti di strutture di tipo struct classificato per memorizzare i punteggi dei piloti, passato per riferimento (S6.6).

Inoltre: il ciclo while viene eseguito finché si registra il risultato di tutti i piloti attesi (pos < N_PILOTI) e, contemporaneamente, finché ci sono righe nel file. Ricorda che fgets restituisce NULL (quindi 0, quindi "falso") quando si è giunti alla fine del file e non ci sono più righe da leggere (S8.6).

Per scrivere il nome del pilota nel campo della struttura va usata la strcpy, perché l'assegnamento (operatore =) non funziona con le stringhe (S5.11).

ATTENZIONE: l'istruzione che memorizza i punti del pilota è un incremento, quindi questa funzione si aspetta che il valore dei punti sia correttamente inizializzato a zero!

Ricerca di un elemento nel vettore di piloti e scuderie

La leggi_gara usa la funzione cerca_classificato per farsi restituire l'elemento del vettore del quale aggiornare il punteggio. Questo viene fatto due volte: per il vettore piloti e per il vettore scuderie.

La funzione cerca_classificato è la stessa in entrambi i casi, in quanto opera sempre su vettori di tipo struct classificato, nel quale cerca il generico elemento il cui campo nome sia uguale al nome specificato.

La funzione cerca_classificato può essere formulata come segue:

struct classificato *cerca_classificato(
        struct classificato *elenco,
        int size,
        char *nome)
{
    int i;
    for (i = 0; i < size; i++) {
        if (strcmp(elenco[i].nome, nome) == 0)
            return &elenco[i];    /* Trovato */
        if (elenco[i].nome[0] == '\0') {
            /* Trovato un record vuoto, da inizializzare 
               con il nome prima di restituirlo. */
            strcpy(elenco[i].nome, nome);
            elenco[i].punti = 0;
            return &elenco[i];
        }
    }
    /* Non si dovrebbe mai arrivare qui. */
    return NULL;
}

Alcune delle sue peculiarità:

  • la funzione cerca nel vettore elenco l'elemento il cui campo nome corrisponde alla stringa nome passata come terzo argomento;
  • il vettore elenco ha dimensione pari a size elementi;
  • il ciclo for esamina i vari elementi del vettore elenco;
  • usa la strcmp per confrontare nome con elenco[i].nome, che è il campo nome dell'elemento i-esimo del vettore;
  • se la strcmp restituisce 0 vuol dire che l'elemento è stato trovato, e ne viene restituito l'indirizzo;

Se nel ciclo viene raggiunto un elemento il cui campo nome è la stringa vuota, allora questo elemento viene inizializzato come segue:

  • nel campo nome viene copiata la stringa nome (con strcpy);
  • il campo punti viene inizializzato a zero;
  • viene restituito l'indirizzo di questo elemento.

Il funzionamento di cerca_classificato implica che gli elementi del vettore elenco siano stati tutti inizializzati con una funzione come la seguente:

void inizializza_elenco(struct classificato *elenco, int size)
{
    int i;
    for (i = 0; i < size; i++)
        elenco[i].nome[0] = '\0';
}

1) Numero di gare

Per come è strutturato il programma, per conoscere il numero di gare è sufficiente contare - nel main - il numero di volte che viene chiamata la funzione leggi_gara, per esempio come segue:

    int nGare = 0;
    while (leggi_gara(fin, piloti, scuderie) != 0) {
        /* quando leggi_gara ritorna, una gara intera e` stata letta */
        nGare++;
    }

2) Pilota vincitore

La ricerca del pilota vincitore può essere svolta con una funzione come la seguente:

int cerca_vincitore(struct classificato *elenco, int size) {
    int vincitore = 0, i;
    for (i = 1; i < size; i++) {
        if (elenco[i].punti > elenco[vincitore].punti)
            vincitore = i;
        /*
         * se due piloti hanno lo stesso punteggio vince chi viene prima
         * in ordine alfabetico
         */
        else if (elenco[i].punti == elenco[vincitore].punti &&
             strcmp(elenco[i].nome, elenco[vincitore].nome)< 0)
            vincitore = i;
    }
    return vincitore;
}

La funzione ricerca nel vettore elenco l'indice dell'elemento che ha il valore del campo punti più elevato. L'indice potrà essere usato per recuperare il nome del vincitore nel vettore piloti.

Per gestire il caso in cui due elementi abbiano lo stesso numero di punti, viene usata la funzione strcmp per determinare se un nome viene prima dell'altro, e viene mantenuto l'indice opportuno.

Ricorda che la strcmp restituisce:

  • 0 se le due stringhe sono uguali
  • un valore minore di zero se la prima stringa viene prima in ordine alfabetico rispetto alla seconda
  • un valore maggiore di zero viceversa

La funzione andrà richiamata passandole come primo argomento il vettore piloti riempito dalle chiamate alla funzione leggi_gara illustrata precedentemente. Per esempio:

    pilota_vinc = cerca_vincitore(piloti, N_PILOTI);

Attenzione alla dimensione del vettore, che in questo caso è pari a N_PILOTI.

3) Squadra vincitrice

L'individuazione della scuderia vincente avviene come per il pilota, usando la funzione cerca_vincitore illustrata precedentemente. Verrà richiamata però passandogli il vettore dei punti delle squadre, per esempio come segue:

    scuderia_vinc = cerca_vincitore(scuderie, N_SQUADRE);

Attenzione alla dimensione del vettore, che in questo caso è pari a N_SQUADRE.