Informatica 1 Prova di recupero 21 Settembre 2001 Si risolvano i seguenti esercizi. Ai fini della determinazione del voto finale il loro punteggio andrà sommato al punteggio del laboratorio. Il tempo complessivo a disposizione è di 3 ore. Chi dovesse recuperare solo la prima prova dovrà risolvere solo la parte 1 in un tempo di 1 ora e ¼. Chi dovesse recuperare solo la seconda prova dovrà risolvere solo la parte 2 in un tempo di 1 ora e ¾. Si ricorda che, una volta consegnata una qualsiasi delle due parti, il punteggio acquisito nella corrispondente prova viene annullato. NB Le soluzioni dei 3 esercizi vanno consegnate su altrettanti fogli separati, avendo cura di indicare i propri cognome e nome scritti in stampatello!- su ognuno di essi.
Recupero parte 1 (10 punti) Scrivere un programma che legge da tastiera delle righe di caratteri (di lunghezza massima 100 caratteri). Per ogni riga letta, il programma conta il numero di occorrenze di ogni vocale (sia che appaia in caratteri maiuscoli che in quelli minuscoli). Quando l'utente immette una riga vuota, il programma stampa un istogramma verticale (dall'alto al basso) con tanti asterischi quante sono le occorrenze di ogni vocale. Per esempio, se l'utente immette le seguenti righe: NEL mezzo DEL cammin DI nostra vita<enter> mi RITROVAI per una SELVA oscura<enter> CHE la diritta via era SMARRITA<enter> <enter> Il programma stampa il seguente istogramma AEIOU ***** ***** **** **** *** *** *** * * * * * * * * *
Recupero parte 2 (14 punti + 3 facoltativi) Esercizio 1 (12 punti) Si vuole scrivere un modulo Dizionario che gestisce un dizionario di parole in italiano. Questo modulo offre un tipo TipoParola (che rappresenta una parola) ed un tipo ListaDiParole (una struttura dati a lista per contenere un numero arbitrario di parole), ma nasconde all utente l implementazione del dizionario in sè. TipoParola contiene il segno della parola (cioè la parola in sè, per esempio cane ; si ricorda che nella lingua italiana la parola con segno più lungo è precipitevolissimevolmente, di 26 lettere), il suo significato (lungo al massimo 200 caratteri), ed la forma della parola (le possibili forme sono: verbo, nome, aggettivo, avverbio, preposizione, articolo, interiezione). a. (Punti 4) Scrivere l interfaccia (non l implementazione) Dizionario.h del suddetto modulo; questa definisce i tipi TipoParola e ListaDiParole (più eventualmente il tipo TipoFOrma) ed offre tutte e sole le seguenti funzioni: leggi_parola : chiede all utente di inserire da tastiera i dati relativi ad una parola e ritorna un elemento di tipo TipoParola; se l utente inserisce dei dati non validi (per esempio se la parola è vuota) viene ritornato (non stampato a video!!!) un codice di errore; aggiungi_parola : riceve in ingresso un elemento di tipo TipoParola e lo aggiunge al dizionario (se la parola già esiste nel dizionario questo rimane invariato); se la parola passata non è valida viene ritornato un codice di errore; trova_definizione : riceve in ingresso il segno di una parola, e ritorna il TipoParola che ne contiene la definizione; se la parola passata in ingresso non è presente nel dizionario viene ritornato un codice di errore; trova_sinonimi : riceve in ingresso un TipoParola, e ritorna una lista di parole sinonimi di quella passata; la lista ritornata è vuota se la parola in ingresso non è presente nel dizionario. b. (Punti 3) Scrivere un main che, utilizzando le funzioni del modulo Dizionario.h, legge i dati relativi ad una parola (se i dati immessi non sono validi devono essere reimmessi), verifica se la parola esiste nel dizionario e, se non esiste, la aggiunge al dizionario stesso. c. (Punti 5) Dare la definizione della variabile dizionario del file Dizionario.h: questa variabile contiene tutte le parole del dizionario e va implementata come un array di liste (una lista per ognuna delle 26 lettere dell alfabeto). Implementare la funzione (e solo quella!!!) trova_definizione del modulo Dizionario.h; le parole, nelle liste della variabile dizionario, possono essere memorizzate secondo l ordine alfabetico oppure no, è una scelta implementativa del programmatore.
Esercizio 2 (2 punti) Si ha la funzione ricorsiva: void Elabora( unsigned int i ) { if (i <= 1) { printf("%u\n", i); else { printf("%u", i % 2); Elabora( i / 2 ); Descrivere l'esecuzione della funzione in seguito all'invocazione Elabora(10), specificando che cosa rimane visualizzato sullo schermo alla fine dell esecuzione.
Esercizio 3 (3 punti; facoltativo) Data una funzione calcola: int calcola(int *a, int b, int *c) { *a = *a * b; b = b * 2; *c = *a - b; return *a + b + *c; ed il seguente programma: main() { int z; int *y; int x; y = &x; z = -1; *y = 1; x = 2; z = calcola(y,x,&z); Mostrare lo stato delle variabili x, y e z al termine dell'invocazione di calcola.
Soluzioni Parte 1 (recupero primo compitino) #define MAX_RIGA 100 #include <stdio.h> main() { char riga[max_riga+1]; unsigned int counter[5]; int i; /* inizializzo i contatori */ for( i=0 ; i<5 ; i++ ) counter[i] = 0; printf("inserisci riga di caratteri (max %d elementi):\n", MAX_RIGA); gets(riga); while(riga[0]!= '\000') { for( i=0 ; riga[i]!= '\000' ; i++ ) { if ( riga[i] == 'a' riga[i] == 'A' ) counter[0] += 1; else if ( riga[i] == 'e' riga[i] == 'E' ) counter[1] += 1; else if ( riga[i] == 'i' riga[i] == 'I' ) counter[2] += 1; else if ( riga[i] == 'o' riga[i] == 'O' ) counter[3] += 1; else if ( riga[i] == 'u' riga[i] == 'U' ) counter[4] += 1; printf("inserisci riga di caratteri (max %d elementi):\n", MAX_RIGA); gets(riga); printf("\naeiou\n"); while ( counter[0] > 0 counter[1] > 0 counter[2] > 0 counter[3] > 0 counter[4] > 0 ) { if (counter[0] > 0) {
printf("*"); counter[0] -= 1; else { printf(" "); if (counter[1] > 0) { printf("*"); counter[1] -= 1; else { printf(" "); if (counter[2] > 0) { printf("*"); counter[2] -= 1; else { printf(" "); if (counter[3] > 0) { printf("*"); counter[3] -= 1; else { printf(" "); if (counter[4] > 0) { printf("*"); counter[4] -= 1; else { printf(" "); printf("\n");
Parte 2 (recupero secondo compitino) Esercizio 1 Punto a: /* Dizionario.h */ #define MAX_PAROLA 26 #define MAX_DESCR 200 typedef enum {verbo, nome, agg, avv, prep, art, inter TipoForma typedef struct { char segno[max_parola+1]; char significato[max_descr+1]; TipoForma forma; TipoParola; typedef struct EL { TipoParola el; struct EL *next; ElemListaDiParole; typedef ElemListaDiParole *ListaDiParole; typedef enum {false, true bool; bool leggi_parola ( TipoParola *parola ); bool aggiungi_parola ( TipoParola parola ); bool trova_definizione ( char segno[], TipoParola *parola ); ListaDiParole trova_sinonimi ( TipoParola parola);
Punto b: /* maindizionario.c */ #include <stdio.h> #include "Dizionario.h" main (){ TipoParola parola, temp; while (!leggi_parola(&parola)){ printf("parola inserita non valida, riprova\n"); if (!trova_definizione(parola.segno, &temp) { aggiungi_parola(parola);
Punto c: /* Dizionario.c */ #include "Dizionario.h" #include <string.h> ListaDiParole dizionario[26]; /* suppongo che le liste non siano ordinate */ bool trova_parola_in_lista ( char segno[], ListaDiParole lista, TipoParola *parola ) { if (lista == NULL) return false; if ( strcmp(segno, lista->el.segno) == 0 ){ *parola = lista->el; return true; else { return trova_parola_in_lista ( segno, lista->next, parola ); bool trova_definizione ( char segno[], TipoParola *parola ) { if ( segno[0] >= a && segno[0] <= z ) { return trova_parola_in_lista( segno, dizionario[p[0] a ], parola ); else if ( segno[0] >= A && segno[0] <= Z ) { return trova_parola_in_lista( segno, dizionario[p[0] A ], parola ); else { return false;
Esercizio 2 La sequenza di chiamate ricorsive della funzione Elabora è la seguente: Elabora(10) Elabora(5) Elabora(2) Elabora(1) Alla fine rimane visualizzata la seguente sequenza di valori: 0101. La funzione Elabora infatti calcola in maniera ricorsiva la rappresentazione binaria (scritta con la cifra meno significativa a sinistra) del numero passatole. Esercizio 3 (facoltativo) Alla fine del main, la variabile x contiene il valore 4, la y punta a x, quindi *y a sua volta vale 4, mentre z vale 8. Infatti, prima della chiamata di calcola, z ha valore -1, y contiene l indirizzo di x, ed x vale 2 (quindi *y vale anch esso 3). Durante l esecuzione di calcola, x passa da 2 ad 4 con l istruzione *a = *a * b. z viene anch esso modificato (è passato per indirizzo), ma questa modifica viene poi annullata quando, al termine di calcola, il valore ritornato dalla funzione è assegnato a z. Poichè calcola ritorna 8 (la funzione era stata chiamata con z=-1, x=2, *y=2), z alla fine vale 8.