Fondamenti di Programmazione    (canale E-O) a.a. 2009-2010
Esercizi di preparazione all'esame - 15 gennaio 2010
Esercizio 1
Scrivere una funzione void clean(SList L) che preso in input il puntatore L al primo elemento di una lista ordinata di stringhe, elimina i duplicati. La funzione non deve modificare il primo elemento della lista. Gli elementi della lista hanno il seguente tipo:
typedef struct SElem {
    char          str[30];
    struct SElem *next;
} SElem, *SList;
La memoria degli elementi eliminati deve essere rilasciata. Ad esempio, se la lista è "BLUE" -> "BLUE" -> "GREEN" -> "RED" -> "RED" -> "YELLOW" allora la funzione la modifica così: "BLUE" -> "GREEN" -> "RED" -> "YELLOW".
Esercizio 2
Scrivere una funzione void invfile(char *nameF, char *newF) che legge il file di tipo testo il cui nome è nella stringa nameF, contenente una sequenza di numeri interi separati da spazi, e crea un nuovo file di testo, con nome dato dalla stringa newF, e vi scrive la sequenza inversa. Ad esempio, se il file nameF contiene la sequenza 3 7 -2 15 allora il nuovo file creato dalla funzione deve contenere 15 -2 7 3.
Esercizio 3
Scrivere una funzione int **table(int n, int (*f)(int, int)) che ritorna una matrice quadrata di dimensione n di interi, allocata dinamicamente, riempita con i valori dati dalla funzione f (l'elemento (i, j) della matrice ha valore dato da f(i, j), per 0 <= i, j < n). Gli elementi della matrice ritornata si devono poter accedere tramite la sintassi delle parentesi quadre. Ad esempio, se n = 5 e la funzione f è la funzione che ritorna il prodotto dei due interi in input, allora la chiamata table(5, f) ritorna la seguente matrice:
 0  0  0  0  0
 0  1  2  3  4
 0  2  4  6  8
 0  3  6  9 12
 0  4  8 12 16
Esercizio 4
Data una sequenza di numeri diciamo che un numero x della sequenza è un massimo locale se sia il numero che precede x che quello che lo segue sono strettamente minori di x (né il primo né l'ultimo numero della sequenza può essere un massimo locale). Scrivere una funzione che presa in input una lista di numeri restituisce un vettore contenente i massimi locali della lista (nell'ordine in cui appaiono nella lista). Il tipo degli elementi della lista è il seguente:
typedef struct Num {
    float       n;
    struct Num *next;
} Num, *NList;
Il prototipo della funzione è float *localMax(NList L, int *pcount). In *pcount restituisce la dimensione del vettore ritornato (che deve essere allocato dinamicamente). Il vettore non deve essere sovradimensionato. Ad esempio, se la lista è 12 -> 3.5 -> 7.1 -> 4 -> 3 -> 2 -> 5.2 -> 1 -> 8 -> 8 allora la funzione ritorna il vettore [7.1, 5.2] e in *pcount restituisce 2.
Esercizio 5
Si consideri il seguente tipo:
typedef struct {
    long     task;
    int      h;        //ore: 0 - 23
    int      m;        //minuti: 0 - 59
    int      s;        //secondi: 0 - 59
} TaskRec;
Scrivere una funzione int updateTasks(char *taskF, int hh, int mm, int ss) che preso in input il nome taskF di un file binario che contiene una sequenza di record di tipo TaskRec elimina da esso tutti i record il cui orario (dato dai campi h, m ed s) è antecedente all'orario dato da hh, mm ed ss (assumendo che tutti gli orari si riferiscano allo stesso giorno). La funzione ritorna il numero di record nel file aggiornato. Eliminare un record significa che gli eventuali record che vengono dopo nel file devono essere "spostati" per chiudere il "buco" lasciato dal record eliminato. Ad esempio, se inizialmente il file taskF contiene la sequenza di record: {1234, 2, 15, 23}, {2345, 7, 30, 15}, {1238, 7, 30, 11}, {2351, 13, 24, 53} allora la chiamata updateTask(taskF, 7, 30, 15) ritorna 2 e la sequenza contenuta nel file diventa: {2345, 7, 30, 15}, {2351, 13, 24, 53}.
Esercizio 6
Si consideri il seguente tipo:
typedef struct Elem {
    char *       str;         // stringa allocata dinamicamente
    struct Elem *next;
} Elem, *List;
Scrivere una funzione List minstr(List L, int (*cmp)(const char *, const char *)) che elimina dalla lista di stringhe L la stringa che risulta essere minima rispetto alla relazione d'ordine data dalla funzione cmp(). La funzione cmp(s1, s2) ritorna -1 se s1 < s2, 0 se s1 = s2 e 1 se s1 > s2. La funzione minstr() deve ritornare il puntatore alla lista modificata. Ad esempio, se la lista L è "verde" -> "giallo" -> "rosso" -> "blu" -> "rosso" allora la chiamata minstr(L, strcmp) modifica la lista così: "verde" -> "giallo" -> "rosso" -> "rosso".
Esercizio 7
Scrivere una funzione che presa in input una lista di stringhe crea una stringa uguale alla concatenazione delle stringhe della lista. Il tipo degli elementi della lista è il seguente:
typedef struct SElem {
    char *        str;
    struct SElem *next;
} SElem;
Il prototipo della funzione è char *listToStr(SElem *). Ovviamente la stringa ritornata deve essere allocata dinamicamente. Ad esempio, se la lista è "List" -> "a di es" -> "" -> "e" -> "mpio" allora la funzione deve restituire la stringa "Lista di esempio".
Esercizio 8
Si considerino i seguenti tipi:
typedef struct {
    short            str;    //1 se e' valido il campo s, 0 se e' valido il campo n
    union {
        char  s[101];        //se valido contiene una stringa di lunghezza <= 100
        int   n;             //se valido contiene un numero
    }                val;
} Rec;

typedef struct Elem {
    char *       string;      // stringa allocata dinamicamente
    struct Elem *next;
} Elem, *List;
Scrivere una funzione List updateList(List L, char *nameF) che presa in input una lista di stringhe ordinata L vi inserisce, mantenendo la lista ordinata, le stringhe contenute nel file binario il cui nome è nella stringa nameF e che contiene una sequenza di record di tipo Rec. La funzione non deve inserire stringhe che sono già presenti nella lista e l'ordinamento delle stringhe è quello dato dalla funzione della libreria standard strcmp(). Siccome la lunghezza media delle stringhe nel file è molto minore di 100, l'allocazione delle stringhe nella lista non deve sprecare memoria. La funzione ritorna il puntatore al primo elemento della lista aggiornata. La lista di input può anche essere vuota (L = NULL). Se la lista è "cane" -> "gatto" -> "lupo" e il file contiene {1, "leone"}, {0, 13}, {1, "cane"}, {1, "leone"}, {1, "aquila"} allora la funzione ritorna la lista "aquila" -> "cane" -> "gatto" -> "leone" -> "lupo".



Soluzione esercizio 1
#include <string.h>
#include <stdlib.h>

typedef struct SElem {
    char          str[30];
    struct SElem *next;
} SElem, *SList;

void clean(SList L) {
    if (L == NULL || L->next == NULL)
        return;
    SElem *e = L;
    do {
        if (strcmp(e->str, e->next->str) == 0) { //Se le stringhe sono uguali,
            SElem *p = e->next;                  //elimina il prossimo elemento.
            e->next = p->next;
            free(p);
        } else e = e->next;
    } while (e->next != NULL);
}
Soluzione esercizio 2
#include <stdio.h>

void invfile(char *nameF, char *newF) {
    FILE *f = fopen(nameF, "r");       //Apri il file in lettura
    int n, lenF = 0;
    while (fscanf(f, "%d", &n) == 1) lenF++;    //Conta gli interi nel file
    int k = 0, v[lenF];    //Vettore a dimensione variabile atto a contenere gli interi del file
    rewind(f);             //Riporta il cursore del file all'inizio
    while (fscanf(f, "%d", &(v[k])) == 1) k++;  //Copia gli interi del file nel vettore v
    fclose(f);
    FILE *nF = fopen(newF, "w");        //Crea ed apri il nuovo file
    for (k = lenF - 1; k >= 0; k--)     //Scrivi nel nuovo file gli interi in
        fprintf(nF, "%d ", v[k]);       //ordine inverso.
    fclose(nF);
}
Soluzione esercizio 3
#include <stdlib.h>

int **table(int n, int (*f)(int, int)) {
    int **M = malloc(n*sizeof(int *));    //Alloca il vettore dei puntatori alle
    int i, j;                                //righe della matrice.
    for (i = 0 ; i < n ; i++)             //Alloca le righe della matrice
        M[i] = malloc(n*sizeof(int));
    for (i = 0 ; i < n ; i++) {
        for (j = 0 ; j < n ; j++)
            M[i][j] = f(i, j);
    }
    return M;
}
Soluzione esercizio 4
#include <stdlib.h>

typedef struct Num {
    float       n;
    struct Num *next;
} Num, *NList;

/* Funzione ausiliaria che ritorna il puntatore al primo massimo locale della
 * lista a partire dall'elemento p. Se non ci sono massimi ritorna NULL. */
Num *firstMax(Num *p) {
    if (p == NULL || p->next == NULL) return NULL;
    else {
        float prec = p->n;
        p = p->next;
        while (p->next != NULL && !(p->n > prec && p->n > p->next->n)) {
            prec = p->n;
            p = p->next;
        }
        return (p->next != NULL ? p : NULL);
    }
}

float *localMax(NList L, int *pcount) {
    float *maxArray = NULL;
    int maxCount = 0;
    Num *e = L;
    while ((e = firstMax(e)) != NULL) {
        maxArray = realloc(maxArray, (maxCount + 1)*sizeof(float));
        maxArray[maxCount++] = e->n;
    }
    *pcount = maxCount;
    return maxArray;
}
Soluzione esercizio 5
#include <stdio.h>

typedef struct {
    long task;
    int  h;     //ore: 0 - 23
    int  m;     //minuti: 0 - 59
    int  s;     //secondi: 0 - 59
} TaskRec;

/* Funzione ausiliaria che ritorna 1 se l'orario (h, m, s) e' antecedente
   all'orario (hh, mm, ss), altrimenti ritorna 0. */
int ant(int h, int m, int s, int hh, int mm, int ss) {
    if (h < hh) return 1;
    if (h == hh && m < mm) return 1;
    if (h == hh && m == mm && s < ss) return 1;
    return 0;
}

int updateTasks(char *taskF, int hh, int mm, int ss) {
    FILE *f = fopen(taskF, "r+b");  //Apri il file binario in lettura e scrittura
    int nc = 0, posLast = 0, posNext = 0; //posLast manterra' la posizione in cui spostare
    TaskRec rec;                          //il prossimo record valido e posNext manterra' la
    while (fread(&rec, sizeof(rec), 1, f) == 1) {  //posizione del prossimo record da leggere.
        if (!ant(rec.h, rec.m, rec.s, hh, mm, ss)) { //Se non e' antecedente...
            fseek(f, posLast, SEEK_SET);             //spostalo (o riscrivilo)
            fwrite(&rec, sizeof(rec), 1, f);
            posLast += sizeof(rec);
            nc++;
        }
        posNext += sizeof (rec);
        fseek(f, posNext, SEEK_SET);
    }
    fclose(f);
    return nc;
}
Soluzione esercizio 6
#include <stdlib.h>

typedef struct Elem {
    char *       str;    // stringa allocata dinamicamente
    struct Elem *next;
} Elem, *List;

List minstr(List L, int (*cmp)(const char *, const char *)) {
    if (L == NULL) return NULL;
    Elem *min = L, *curr = L->next;
    while (curr != NULL) {                //Scorri la lista per trovare l'elemento
        if (cmp(curr->str, min->str) < 0) //con la stringa minima.
            min = curr;
        curr = curr->next;
    }
    Elem *prev = NULL;
    curr = L;
    while (curr != min) {          //Scorri la lista mantenendo il puntatore
        prev = curr;               //all'elemento precedente (prev).
        curr = curr->next;
    }
    if (prev == NULL) L = L->next;    //Sgancia l'elemento minimo dalla lista
    else prev->next = min->next;
    free(min->str);                   //Libera la memoria dell'elemento eliminato
    free(min);
    return L;
}
Soluzione esercizio 7
#include <stdlib.h>
#include <string.h>

typedef struct SElem {
    char *        str;
    struct SElem *next;
} SElem;

char *listToStr(SElem *L) {
    char *str;
    const SElem *p = L;
    long len = 0;               //Calcola la lunghezza della stringa che
    while (p != NULL) {         //e' uguale alla somma delle lunghezze
        len += strlen(p->str);  //delle stringhe della lista.
        p = p->next;
    }
    str = malloc(len + 1);      //Alloca la stringa e
    str[0] = '\0';              //inizializzala a vuota.
    p = L;
    while (p != NULL) {            //Concatena le stringhe della lista
        strcat(str, p->str);       //copiandole nella stringa.
        p = p->next;
    }
    return str;
}
Soluzione esercizio 8
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

typedef struct {
    short            str;    //1 se e' valido il campo s, 0 se e' valido il campo n
    union {
        char  s[101];        //se valido contiene una stringa di lunghezza <= 100
        int   n;             //se valido contiene un numero
    }                val;
} Rec;

typedef struct Elem {
    char *       string;      // stringa allocata dinamicamente
    struct Elem *next;
} Elem, *List;

/* Funzione ausiliaria che, se la stringa str non e' presente nella lista L,
   inserisce la stringa str nella lista ordinata L mantenendola ordinata. */
List insOrd(List L, const char *str)
{
    Elem **prec = &L;    //Indirizzo della locazione che eventualmente dovra' contenere
                         //il puntatore all'elemento inserito.
    while (*prec != NULL && strcmp(str, (*prec)->string) > 0)   //Scorri la lista finche'
        prec = &((*prec)->next);                       //o la lista e' finita o trovi una
                                                       //stringa maggiore od uguale a str.
    if (*prec == NULL || strcmp(str, (*prec)->string) != 0) {    //Se non e' stata trovata
                                                                 //una stringa uguale a str...
        Elem *new = malloc(sizeof(Elem));                        //alloca un nuovo elemento
        new->string = malloc(strlen(str) + 1);       //Alloca memoria per la stringa
        strcpy(new->string, str);                    //Copia la stringa
        new->next = *prec;                           //Inserisci il nuovo elemento
        *prec = new;                                 //nella lista.
    }
    return L;
}

List updateList(List L, char *nameF)
{
    FILE *f = fopen(nameF, "rb");    //Apri il file binario in lettura
    Rec r;
    while (fread(&r, sizeof(Rec), 1, f) == 1)    //Leggi ogni record del file...
        if (r.str == 1)                     //Se il record contiene una stringa
            L = insOrd(L, r.val.s);         //tenta di inserirla nella lista
    fclose(f);
    return L;
}