Informatica: arte e mestiere III edizione Esercizi su Web Informatica: Arte e Mestiere, Terza edizione Stefano Ceri, Dino Mandrioli, Licia Sbattella Esercizi Premessa ....................................................................................................................3 Capitolo 3 Codifica degli algoritmi in un linguaggio di alto livello.........................4 Capitolo 4 Esecuzione di programmi C su macchine reali ........................................8 Capitolo 5 Tipi di dato ..............................................................................................13 Capitolo 6 Strutture di controllo ..............................................................................18 Capitolo 7 Funzioni e procedure..............................................................................27 Capitolo 8 Introduzione alla programmazione ricorsiva .........................................32 Capitolo 9 Gestione dei file .....................................................................................35 Capitolo 10 Strutture dati dinamiche .......................................................................43 Capitolo 14 Archivi e basi di dati ............................................................................57 Capitolo 18 La visione dei sistemi informatici da parte dell’utente finale ..............59 Copyright © 2008 - The McGraw-Hill Companies srl 2 Informatica: Arte e Mestiere, Terza edizione Stefano Ceri, Dino Mandrioli, Licia Sbattella Esercizi Premessa Il presente file contiene numerosi esercizi utilizzabili dai docenti che adottano il testo “Informatica: arte e mestiere” (II edizione) come materiale didattico ausiliario. Gli esercizi contenuti nel file non costituiscono un eserciziario; pertanto non possono e non devono essere utilizzati direttamente dagli studenti dei corsi, ma sono proposti ai docenti come spunto per completare e verificare l’apprendimento durante lo svolgimento del corso e in sede d’esame. Di conseguenza, il livello di difficoltà e complessità è fortemente variabile da esercizio a esercizio; inoltre le soluzioni sono fornite in forma spesso assai schematica. Gli esercizi sono suddivisi per capitoli a seconda della pertinenza. Ovviamente, la loro numerosità e complessità varia anche in funzione del capitolo cui fanno riferimento. In alcuni casi essi sono del tutto assenti. È intenzione degli autori aggiornare periodicamente gli esercizi proposti, anche in funzione di eventuali richieste e suggerimenti, sempre benvenuti. Copyright © 2008 - The McGraw-Hill Companies srl 3 Informatica: Arte e Mestiere, Terza edizione Stefano Ceri, Dino Mandrioli, Licia Sbattella Esercizi Capitolo 3 Codifica degli algoritmi in un linguaggio di alto livello Il nucleo del linguaggio C: input/output semplificato, variabili, istruzioni condizionali e cicli, vettori. Esercizio 3.1 Si descriva un algoritmo, utilizzando il nucleo del linguaggio C descritto nel capitolo 3, che legga dallo standard input due numeri interi positivi e stampi in uscita un messaggio che indichi se i due numeri sono primi tra loro oppure no. Soluzione dell’esercizio 3.1 Due numeri m e n sono primi tra loro se e solo se vale che MCD(m, n) = 1. Il problema si riduce quindi a trovare il massimo comun divisore tra m e n e verificarne il valore. Utilizziamo l’algoritmo di Euclide descritto nell’Esempio 3.7. main() { /* Si utilizza l’algoritmo di Euclide per calcolare l’MCD */ scanf (m); scanf (n); while (m != n) if (m > n) m = m - n; else n = n - m; mcd = n; /* A questo punto si aggiungono le istruzioni che valutano l’MCD */ if (mcd == 1) printf ("I due numeri sono primi tra loro"); else printf ("I due numeri non sono primi tra loro"); } Esercizio 3.2 Si vuole memorizzare in forma compatta un elenco di parole ordinato alfabeticamente. Allo scopo si osserva che spesso due parole consecutive hanno una parte iniziale comune. Ogni parola che ha una parte iniziale comune con la precedente viene quindi rappresentata con una cifra decimale compresa tra ‘2’ e ‘9’ indicante un numero di lettere uguali a quelle nella parole precedente, seguita dalle altre lettere della parola. L’assenza di cifra all’inizio della parola ha lo stesso significato della cifra ‘0’. Ogni parola è separata dalla successiva da uno o più spazi bianchi. Esempio: abaco 3te 2bandonare 9to 2normale ammiraglio 6re bitume 3orzolo 8uto … Scrivere un programma, utilizzando il nucleo del linguaggio C descritto nel capitolo 3, che esegue lo ‘scompattamento’ di tale rappresentazione, cioè legge dallo standard input una sequenza di caratteri che rappresenta la codifica abbreviata del testo (si assuma che la sequenza sia terminata dal carattere speciale ‘#’) e la riscrive nello Copyright © 2008 - The McGraw-Hill Companies srl 4 Informatica: Arte e Mestiere, Terza edizione Esercizi Stefano Ceri, Dino Mandrioli, Licia Sbattella standard output togliendo le cifre e aggiungendo i caratteri necessari in modo da rappresentare tutte le parole per esteso. Esempio, dalla sequenza sopra riportata si ottenga la seguente: abaco abate abbandonare abbandonato bitume bitorzolo bitorzoluto … abnormale ammiraglio ammirare Si scriva poi un programma che esegue l’operazione inversa. Soluzione dell’esercizio 3.2 main() { /* Fase di inserimento del testo compatto */ ContatoreTestoCompatto = 0; scanf (carattere); while (carattere != '#') { TestoCompatto[ContatoreTestoCompatto] = carattere; ContatoreTestoCompatto = ContatoreTestoCompatto + 1; scanf (carattere); } LunghTestoCompatto = ContatoreTestoCompatto; /* Fase di scompattamento del testo */ ContatoreTestoCompatto = 0; ContatoreTestoNormale = 0; CaratterePrecedente = ' '; while (ContatoreTestoCompatto < LunghTestoCompatto) { carattere = TestoCompatto[ContatoreTestoCompatto]; /* Se il carattere non è un numero, copialo nel vettore del testo normale */ if (carattere < '0' || carattere> '9') { TestoNormale[ContatoreTestoNormale] = carattere; /* Tiene traccia dell'inizio dell'ultima parola nel testo normale */ if (CaratterePrecedente == ' ' && carattere != ' ') ContatoreInizioParola = ContatoreTestoNormale; ContatoreTestoNormale = ContatoreTestoNormale + 1; } /* Se è un numero, copia caratteri dall'ultima parola inserita nel testo normale */ else { NumLettereUguali = TestoCompatto[ContatoreTestoCompatto] - '0'; for (i = 0; i < NumLettereUguali; i++) { TestoNormale[ContatoreTestoNormale + i] = TestoNormale[ContatoreInizioParola + i]; } ContatoreInizioParola = ContatoreTestoNormale; ContatoreTestoNormale = ContatoreTestoNormale + NumLettereUguali; } ContatoreTestoCompatto = ContatoreTestoCompatto + 1; CaratterePrecedente = carattere; } /* Fase di stampa del testo scompattato */ for (i = 0; i < ContatoreTestoNormale; i++) Copyright © 2008 - The McGraw-Hill Companies srl 5 Informatica: Arte e Mestiere, Terza edizione Stefano Ceri, Dino Mandrioli, Licia Sbattella Esercizi printf (TestoNormale[i]); } Adesso scriviamo il programma che eseugue l’operazione inversa: genera il testo ‘compattato’ a partire da quello esteso. main() { /* Fase di inserimento del testo normale */ ContatoreTestoNormale = 0; scanf (carattere); while (carattere != '#') { TestoNormale[ContatoreTestoNormale] = carattere; ContatoreTestoNormale = ContatoreTestoNormale + 1; scanf (carattere); } LunghTestoNormale = ContatoreTestoNormale; /* Fase di compattamento del testo */ ContatoreTestoCompatto = 0; ContatoreTestoNormale = 0; ContatoreInizioParola = -1; ContatoreLettere = 0; ContatoreInizioParolaPrec = 0; CaratterePrecedente = ' '; while (ContatoreTestoNormale < LunghTestoNormale) { carattere = TestoNormale[ContatoreTestoNormale]; /* Tiene traccia dell'inizio della parola precedente */ /* nel testo normale */ if (CaratterePrecedente == ' ' && carattere != ' ') { ContatoreInizioParolaPrec = ContatoreInizioParola; ContatoreInizioParola = ContatoreTestoNormale; /* Controlla le lettere della parola corrente */ controlla = 1; } /* Se il carattere è presente nella corrispondente posizione */ /* della parola precedente */ if (controlla == 1 && carattere == TestoNormale[ContatoreInizioParolaPrec + ContatoreLettere]) { ContatoreLettere = ContatoreLettere + 1; ContatoreTestoNormale = ContatoreTestoNormale + 1; } /* Se il carattere non è presente nella corrispondente posizione */ /* della parola precedente*/ else { /* Non controllare più per la parola corrente*/ controlla = 0; /* Se vi erano lettere uguali, scrivi il contatore */ if (ContatoreLettere > 0) { TestoCompatto[ContatoreTestoCompatto] = ContatoreLettere + '0'; ContatoreLettere = 0; } /* Altrimenti, scrivi il carattere */ else Copyright © 2008 - The McGraw-Hill Companies srl 6 Informatica: Arte e Mestiere, Terza edizione Esercizi Stefano Ceri, Dino Mandrioli, Licia Sbattella { TestoCompatto[ContatoreTestoCompatto] = carattere; ContatoreTestoNormale = ContatoreTestoNormale + 1; } ContatoreTestoCompatto = ContatoreTestoCompatto + 1; } CaratterePrecedente = carattere; } /* Fase di stampa del testo compattato */ for (i = 0; i < ContatoreTestoCompatto; i++) printf (TestoCompatto[i]); } Copyright © 2008 - The McGraw-Hill Companies srl 7 Informatica: Arte e Mestiere, Terza edizione Esercizi Stefano Ceri, Dino Mandrioli, Licia Sbattella Capitolo 4 Esecuzione di programmi C su macchine reali Il C completo: dichiarazione delle variabili, stringhe di formato, direttiva #include. Esercizio 4.1 Scrivere un programma C che legge da tastiera una sequenza di numeri reali; la lettura termina quando la somma dei numeri immessi è maggiore di 50, e comunque non si possono immettere più di 100 numeri (se anche dopo avere immesso 100 numeri la loro somma non supera 50 la lettura termina comunque). Dopo avere letto tutti i numeri, se l’utente ha inserito almeno 3 valori, cercare se esiste una coppia di numeri tali che il loro rapporto (o il suo inverso) sia uguale al primo numero immesso e, se esiste, stamparla. Esempio di funzionamento del programma: Inserisci Inserisci Inserisci Inserisci Inserisci Inserisci Inserisci numero: numero: numero: numero: numero: numero: numero: 6.25 -2.5 20 13.863 -15.625 4 38.192 Il rapporto (o il suo inverso) tra -2.5 e -15.625 vale 6.25 Soluzione dell’esercizio 4.1 #include <stdio.h> main() { float dati[100], sum, rapp, inv_rapp; int i, j, n_dati; unsigned int trovata; sum = 0; n_dati = 0; do { printf("Inserisci numero: "); scanf("%f", &dati[n_dati]); sum += dati[n_dati]; n_dati++; } while (sum <= 50 && n_dati < 50); trovata = 0; if (n_dati >= 3) { i = 1; while (trovata == 0 && i < n_dati - 1) { j = i+1; while (trovata == 0 && j < n_dati) { if (dati[i] == 0 || dati[j] == 0) Copyright © 2008 - The McGraw-Hill Companies srl 8 Informatica: Arte e Mestiere, Terza edizione Esercizi Stefano Ceri, Dino Mandrioli, Licia Sbattella { if (dati[0] == 0) trovata = 1; } else { rapp = dati[i]/dati[j]; inv_rapp = dati[j]/dati[i]; if (rapp - dati[0] < 0.0000001 && rapp - dati[0] > -0.0000001 || inv_rapp - dati[0] < 0.0000001 && inv_rapp - dati[0] > -0.0000001) trovata = 1; } if (trovata == 1) printf(“Il rapporto (o il suo inverso) tra %f e %f vale %f\n”, dati[i], dati[j], dati[0]); j++; } i++; } } else printf(“Sono stati inseriti solo %d elementi\n”, n_dati); } Esercizio 4.2 Scrivere un programma C che esegua la seguente funzionalità. Si legge da tastiera una sequenza di n interi positivi (massimo 100 numeri; la sequenza termina quando l'utente inserisce il numero 0); dopo avere letto tutti i numeri, per ogni numero letto viene stampato, ogni volta su una riga diversa e da sinistra verso destra, un numero di asterischi pari al numero in questione. Esempio di funzionamento del programma: Inserisci Inserisci Inserisci Inserisci Inserisci *** ****** *** ***** numero numero numero numero numero (0 (0 (0 (0 (0 per per per per per terminare): terminare): terminare): terminare): terminare): 3 6 3 5 0 Soluzione esercizio 4.2 #include <stdio.h> main() { unsigned int dati[100], dato_corr; int i, j, n_dati; n_dati = 0; do { printf("Inserisci numero (0 per terminare): "); scanf("%u", &dato_corr); Copyright © 2008 - The McGraw-Hill Companies srl 9 Informatica: Arte e Mestiere, Terza edizione Stefano Ceri, Dino Mandrioli, Licia Sbattella Esercizi if (dato_corr != 0) { dati[n_dati] = dato_corr; n_dati++; } } while (dato_corr != 0 && n_dati < 100); for( i=0 ; i<n_dati ; i++ ) { for( j=0 ; j<dati[i] ; j++ ) printf("*"); printf("\n"); } } Esercizio 4.3 Scrivere un programma che legge da tastiera una sequenza di numeri interi e: a) verifica che siano tutti non negativi (nel caso si trovi un numero negativo, il programma dovrà terminare); b) stampa a video: a. la media dei numeri pari; b. la media dei numeri dispari; c. la media di tutti i numeri. La sequenza sarà composta al massimo da 100 numeri. Soluzione esercizio 4.3 #include <stdio.h> main() { unsigned int i, num_dati; int dati[100]; unsigned int negativo; int media, somma, media_pari, somma_pari, media_dispari, somma_dispari; int cont_pari, cont_dispari; /* Lettura dei dati; massimo 100 dati*/ do { printf("Quanti numeri vuoi inserire?: "); scanf("%u", &num_dati); } while (num_dati == 0 || num_dati > 100); for (i = 0; i < num_dati; i++) { printf("Inserisci numero (%u/%u): ", i + 1, num_dati); scanf("%d", &dati[i]); } /* Controlla se i dati sono tutti non negativi */ negativo = 0; i = 0; while (i < num_dati && negativo == 0) { if (dati[i] < 0) negativo = 1; i = i + 1; } Copyright © 2008 - The McGraw-Hill Companies srl 10 Informatica: Arte e Mestiere, Terza edizione Esercizi Stefano Ceri, Dino Mandrioli, Licia Sbattella /* Se i dati sono tutti non negativi, continua con l'elaborazione */ if (negativo == 0) { /* Calcola medie */ somma = 0; somma_pari = 0; somma_dispari = 0; cont_pari = 0; cont_dispari = 0; for (i = 0; i < num_dati; i++) { somma = somma + dati[i]; /* L'operatore 'percentuale' calcola il resto */ /* della divisione intera, detto 'modulo' */ if (dati[i] % 2 == 0) { somma_pari = somma_pari + dati[i]; cont_pari = cont_pari + 1; } else { somma_dispari = somma_dispari + dati[i]; cont_dispari = cont_dispari + 1; } } media = somma / num_dati; media_pari = somma_pari / cont_pari; media_dispari = somma_dispari / cont_dispari; printf ("Media totale: %d\n", media); printf ("Media numeri pari: %d\n", media_pari); printf ("Media numero dispari: %d\n", media_dispari); } /* Se c'è almeno un numero negativo, termina l’esecuzione */ else printf ("I numero devono essere tutti non negativi\n"); } Esercizio 4.4 Si scriva un programma che calcola la media, il minimo e il massimo di una sequenza di numeri reali letti dallo standard input; la sequenza viene terminata dal numero . I valori risultanti devono essere scritti sullo standard output. Soluzione dell'esercizio 4.4 #include <stdio.h> main() { float x, s, min, max; unsigned int n, num_dati; printf("Quanti numeri vuoi inserire?: "); scanf("%u", &num_dati); s = 0.0; for (n = 0; n < num_dati; n++) { printf ("Numero: "); Copyright © 2008 - The McGraw-Hill Companies srl 11 Informatica: Arte e Mestiere, Terza edizione Stefano Ceri, Dino Mandrioli, Licia Sbattella Esercizi scanf (“%f”, &x); s = s + x; if (n == 0) { min = x; max = x; } else if (x < min) min = x; else if (x > max) max = x; } if (n == 0) printf ("nessun numero inserito"); else printf ("La media è %f \n il minimo è %f \n il massimo è %f \n", s / n, min, max); } Copyright © 2008 - The McGraw-Hill Companies srl 12 Informatica: Arte e Mestiere, Terza edizione Stefano Ceri, Dino Mandrioli, Licia Sbattella Esercizi Capitolo 5 Tipi di dato Inizializzazione delle variabili, definizione di nuovi tipi, puntatori, direttiva #define. Esercizio 5.1 Si definisca un tipo di dato Giocattolo costituito dal nome del giocattolo (ad esempio “Trenino_elettrico”), dalla data di fabbricazione, e dal prezzo. Se necessario, si definiscano in precedenza ulteriori tipi di dato utili per la definizione del tipo Giocattolo. Si definisca poi un nuovo tipo Bambino, costituito dal nome del bambino e da una sequenza giocattoliPosseduti. Tale sequenza è costituita a sua volta da un array di 10 elementi, ognuno dei quali essendo di tipo Giocattolo. Si dichiarino successivamente due variabili, Giocattoli e Bambini, consistenti in due array, rispettivamante di MAX_GIOCATTOLI elementi del tipo Giocattolo e MAX_BAMBINI elementi del tipo Bambino. MAX_GIOCATTOLI e MAX_BAMBINI, pure da definire, sono i due valori costanti 20 e 30. Si può assumere che nè i nomi dei giocattoli, nè i nomi dei bambini siani più lunghi di 30 caratteri. Soluzione esercizio 5.1 #define MAX_GIOCATTOLI 20 #define MAX_BAMBINI 30 typedef struct { int giorno; int mese; int anno; } Data; typedef char string[30]; typedef struct { string nome; Data dataFabbricazione; float prezzo; } Giocattolo; typedef struct { string nome; Giocattolo giocattoliPosseduti[10]; } Bambino; Giocattolo Giocattoli[MAX_GIOCATTOLI]; Bambino Bambini[MAX_BAMBINI]; Copyright © 2008 - The McGraw-Hill Companies srl 13 Informatica: Arte e Mestiere, Terza edizione Stefano Ceri, Dino Mandrioli, Licia Sbattella Esercizi Esercizio 5.2 Si definiscano i tipi di dato che servono per contenere le informazioni di un pubblico registro automobilistico dedicato ai motoveicoli. I tipi da definire sono i seguenti. TipoDatiMotoveicolo rappresenta i dati di un motoveicolo. Questi dati si compongono di: targa del motoveicolo (7 lettere), marca del motoveicolo (massimo 15 caratteri), modello (massimo 20 caratteri), cilindrata (in cc), potenza (in kW), categoria (motorino, scooter, motocicletta, motocarro). TipoDatiProprietario rappresenta i dati di una persona (il proprietario del motoveicolo): nome (massimo 30 caratteri), cognome (massimo 40 caratteri), codice fiscale (16 caratteri). TipoVocePRA rappresenta una singola voce nel registro automobilistico; una voce si compone di 2 elementi, i dati del proprietario del motoveicolo ed i dati del motoveicolo stesso. TipoPRA rappresenta un tipo adatto a contenere i dati di un PRA. Questo tipo di dati è un elenco di voci del PRA (si suppone che un PRA non possa contenere più di 10.000 elementi), più un contatore che dice quante voci sono effettivamente presenti nel PRA. (se si ritiene utile, si possono definire altri tipi di dati, di supporto a quelli richiesti) Soluzione esercizio 5.2 typedef enum {motorino, scooter, motocicletta, motocarro} TipoCategoria; typedef struct { char targa[7]; char marca[15]; char modello[20]; int cilindrata; float potenza; TipoCategoria categoria; } TipoDatiMotoveicolo; typedef struct { char nome[30]; char cognome[40]; char codiceFiscale[16]; } TipoDatiProprietario; typedef struct { TipoDatiMotoveicolo motoveicolo; TipoDatiProprietario proprietario; } TipoVocePRA; typedef struct { TipoVocePRA elementi[10000]; int nElementi; } TipoPRA; Copyright © 2008 - The McGraw-Hill Companies srl 14 Informatica: Arte e Mestiere, Terza edizione Stefano Ceri, Dino Mandrioli, Licia Sbattella Esercizi Esercizio 5.3 Siano P, Q, R, tre puntatori a interi e x, y due variabili intere. Si dica quanto valgono rispettivamente x, y, *P, *Q, *R dopo l’esecuzione della seguente sequenza di istruzioni. x = 3; y = 5; P = &x; Q = &y; R = P; *R = 10; y = x + *Q; x = x + *P; Q = R; P = Q; Soluzione esercizo 5.3 Al termine dell’esecuzione della sequenza di istruzioni le variabili valgono: x = 20, y = 15 P, Q, R puntano tutti a x e quindi: *P = *Q = *R = 20. Esercizio 5.4 Definire un tipo di dati che contiene le informazioni riguardanti un esame sostenuto da un qualunque studente universitario. Queste informazioni si compongono di: nome dell'esame (nessun nome è più lungo di 40 caratteri), codice alfanumerico (al massimo 6 lettere, per esempio AG0012), voto compreso tra 18 e 30 e lode (si scelga un'opportuna rappresentazione dell'eventuale lode, tenendo conto che non sono ammesse le frazioni di voto), numero di crediti associati all'esame (sono ammesse le frazioni di credito, per esempio 7,5). Di ogni studente si vogliono memorizzare le seguenti informazioni: nome (si suppone che un nome non sia composto da più di 50 lettere), matricola (un numero naturale), data di immatricolazione, numero di esami sostenuti, lista degli esami sostenuti ( essendo un singolo esame descritto come in precedenza(, media dei voti riportati. Ogni studente può sostenere al massimo 29 esami. (se si ritiene utile, si possono definire altri tipi di dati, di supporto a quelli richiesti) Soluzione esercizio 5.4 typedef enum {false, true} boolean; typedef struct { unsigned short giorno; unsigned short mese; unsigned int anno; } Tipo_data; typedef struct { char nome[40]; char codice[6]; unsigned short voto; boolean lode; Copyright © 2008 - The McGraw-Hill Companies srl 15 Informatica: Arte e Mestiere, Terza edizione Stefano Ceri, Dino Mandrioli, Licia Sbattella Esercizi float crediti; } Tipo_esame; typedef struct { char nome[50]; unsigned int matricola; Tipo_data data_immatricolazione; Tipo_esame esami[29]; unsigned short numero_esami; float media; } Tipo_studente; Esercizio 5.5 Definire un tipo di dato TipoStrumentista che descrive i dati relativi ad un musicista jazz. Ogni musicista è definito da un nome (massimo 40 caratteri), da una data di nascita, e dallo strumento che suona. Ogni musicista suona un solo strumento, ed il tipo di strumento suonato può essere solo uno dei seguenti (non sono ammessi altri strumenti): pianoforte, basso, batteria, sax, tromba, trombone, flauto, clarinetto, voce. Definire un tipo TipoQuartetto che contiene i dati relativi ad un quartetto di musicisti. Ogni quartetto è caratterizzato da un nome (al massimo 30 caratteri) e da esattamente 4 musicisti (non uno di più, non uno di meno). Definire infine una variabile ElencoQuartetti che può contenere al massimo 100 quartetti. (se si ritiene utile, si possono definire altri tipi di dati, di supporto a quelli richiesti) Soluzione dell’esercizio 5.5 #define L_NOME_MUS 40 #define L_NOME_QUAR 30 #define MAX_QUARTETTI 100 typedef struct { short giorno; short mese; int anno; } TipoData; typedef enum {pianoforte, basso, batteria, sax, trombone, flauto, clarinetto, voce} TipoStrumento; typedef struct { char nome[L_NOME_MUS + 1]; TipoData data_nascita; TipoStrumento strumento; } TipoStrumentista; typedef struct { char nome[L_NOME_QUAR + 1]; TipoStrumentista musicisti[4]; } TipoQuartetto; TipoQuartetto ElencoQuartetti[MAX_QUARTETTI]; Copyright © 2008 - The McGraw-Hill Companies srl 16 Informatica: Arte e Mestiere, Terza edizione Stefano Ceri, Dino Mandrioli, Licia Sbattella Esercizi Esercizio 5.6 Si vuole scrivere un programma che tiene traccia del numero di CD acquistati per ogni mese dell’anno nell’arco di 10 anni. Definire un tipo TipoDatiCD che contiene i dati di ogni CD: il titolo (lungo al massimo 50 caratteri), l’autore (massimo 60 caratteri), l’anno di pubblicazione, il numero di copie acquistate (anche più di una, se per esempio si vuole fare un regalo). Definire un tipo di dato CDAcqInMese che contiene l’elenco dei CD comprati in un dato mese (si può assumere che non vengano acquistati più di 50 diversi titoli al mese) ed il numero di titoli diversi acquistati in quel mese. Definire una variabile globale elencoCD che contiene l’elenco dei CD acquistati mese per mese negli anni dal 1990 al 1999. Se si fa uso di tipi accessori, anche questi vanno definiti. Soluzione dell’esercizio 5.6 #include <stdio.h> #define MAX_L_TITOLO 50 #define MAX_L_AUTORE 60 #define MAX_TIT_IN_MESE 50 typedef struct { char titolo[MAX_L_TITOLO]; char autore[MAX_L_AUTORE]; int anno; int n_copie; } TipoDatiCD; typedef struct { TipoDatiCD cd[MAX_TIT_IN_MESE]; int n_titoli; } CDAcqInMese; /* Le righe di 'elencoCD' corrispondono agli anni dal 1990 al 1999, le colonne ai 12 mesi di ciascun anno */ CDAcqInMese elencoCD[10][12]; Copyright © 2008 - The McGraw-Hill Companies srl 17 Informatica: Arte e Mestiere, Terza edizione Stefano Ceri, Dino Mandrioli, Licia Sbattella Esercizi Capitolo 6 Strutture di controllo Strutture di controllo: for, switch, do-while. Esercizio 6.1 Con riferimento all’esercizio 5.1, si scriva un frammento di programma C che: Legga da tastiera, mediante opportuno dialogo con l’utente, il nome di un bambino e, se questo compare nell’elenco dei bambini, stampa l’elenco dei nomi dei giocattoli da lui posseduti. Nello scrivere il frammento di programma (ossia una sequenza di istruzioni che non costituisce necessariamente un programma completo) si assuma che l’elenco dei bambini e l’elenco dei giocattoli siano costituiti dalle variabili Bambini e Giocattoli già in memoria. Per semplicità si può assumere che l’utente non commetta errori durante il dialogo con la macchina (ad esempio non fornisca nomi troppo lunghi). Si facciano inoltre le seguenti assunzioni: • Ogni stringa di caratteri che costituisca un nome (di giocattolo o di bambino) sia terminata dal carattere “new line” (‘\n’) premendo il tasto <ENTER> • Se il bambino possiede solo k giocattoli, k < 10, gli ultimi 10 – k puntatori valgono NULL. La variabile NumBambini, che pure si assume sia già in memoria, contiene il numero di bambini effettivamente presenti nell’elenco dei bambini. Un esempio di dialogo utente-programma è il seguente (NB dopo aver battuto i caratteri che compongono il nome del bambino l’utente batte il tasto <ENTER>): Immetti il nome di un bambino: Giovanni I giocattoli posseduti da Giovanni sono: Trenino elettrico Cavallo a dondolo Soluzione dell’esercizio 6.1 #include <stdio.h> #define …. typedef …. main () { Giocattolo Bambino string boolean int Giocattoli[MaxGiocattoli]; Bambini[MaxBambini]; NomeBambino; trovato; i, k, j, LunghNomeBamb, NumBambini; /* NumBambini indica il numero di bambini presenti nell’elenco*/ … /* parte di programma omessa. Comprenderà, tra l’altro, il dialogo utente-macchina per acquisire i dati e memorizzarli nelle variabili Giocattoli e Bambini*/ printf (“Immetti il nome del bambino di cui vuoi conoscere i giocattoli:\n”); Copyright © 2008 - The McGraw-Hill Companies srl 18 Informatica: Arte e Mestiere, Terza edizione Stefano Ceri, Dino Mandrioli, Licia Sbattella Esercizi i = 0; scanf("%c", &NomeBambino[i]); while (NomeBambino[i] != '\n') { i++; scanf("%c", &NomeBambino[i]) } LunghNomeBamb = i; /* Si cerca nell’array Bambini il nome immesso */ trovato = false; k = 0; while (! trovato && k < NumBambini) { /* Si incrementa i fintanto che la condizione rimane vera */ for (i = 0; i <= LunghNomeBamb && Bambini[k].nome[i] == NomeBambino[i]; i++) ; if (i > LunghNomeBamb) trovato = true; else k++; } if (! trovato) printf (“Il bambino indicato non compare nell’elenco\n”); else { printf (“I giocattoli posseduti dal bamino sono:\n”); i = 0; while (i < 10 && Bambini[k].GiocattoliPosseduti[i] != NULL) { for (j = 0; j < 30 && Bambini[k].GiocattoliPosseduti [i] -> nome[j] != ‘\r’; j++) printf (“%c”, Bambini[k].GiocattoliPosseduti [i] -> nome[j]); i++; printf (“\n”), } } /*fine del frammento di programma*/ … } Esercizio 6.3 Realizzare un programma che legge da tastiera (e memorizza) una sequenza di caratteri alfabetici minuscoli (letti ad uno ad uno), terminata dal carattere ‘#’; se l’utente inserisce un carattere non valido (un carattere maiuscolo, un numero o un simbolo che non sia ‘#’), il carattere non va memorizzato; la sequenza può essere lunga al massimo 100 elementi (escluso lo ‘#’). Il programma riproduce sullo standard output la sequenza di caratteri, convertendone il contenuto secondo la cifratura ROT13. Questa cifratura prevede che ad ogni carattere che rappresenta la lettera tra le 26 dell'alfabeto in posizione i si sostituisca la lettera in posizione i+13 (mod 26). Ad esempio, digitando la sequenza di caratteri testodiprova#, il programma deve stampare la stringa di caratteri grfgbqvcebin. Si noti come applicando un numero pari di volte la trasformazione si riottenga il testo di partenza. Soluzione dell’esercizio 6.3 #include <stdio.h> #define MAX_NUM 100 main() Copyright © 2008 - The McGraw-Hill Companies srl 19 Informatica: Arte e Mestiere, Terza edizione Stefano Ceri, Dino Mandrioli, Licia Sbattella Esercizi { char dati[MAX_NUM], dato_corr, trasf; int i, j, n_dati; n_dati = 0; do { printf("Inserisci carattere alfabetico (# per terminare, massimo %d caratteri): ", MAX_NUM); scanf(" %c", &dato_corr); if (dato_corr <= 'z' && dato_corr >= 'a') { dati[n_dati] = dato_corr; n_dati++; } } while (dato_corr != '#' && n_dati < MAX_NUM); printf("\nSequenza trasformata:\n"); for(i = 0 ; i < n_dati ; i++) { trasf = 'a' + (dati[i]+13 - 'a') % 26; printf("%c", trasf); } printf("\n"); } Esercizio 6.4 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 ***** ***** **** **** *** *** *** * * * * * * * * * Copyright © 2008 - The McGraw-Hill Companies srl 20 Informatica: Arte e Mestiere, Terza edizione Stefano Ceri, Dino Mandrioli, Licia Sbattella Esercizi Soluzione dell’esercizio 6.4 #include <stdio.h> #define MAX_RIGA 100 main() { char riga[MAX_RIGA+1]; unsigned int counter[5]; int i; /* Inizializza i contatori */ for( i = 0 ; i < 5 ; i++ ) counter[i] = 0; /* Leggi la prima riga */ printf("Inserisci riga di caratteri (max %d elementi):\n", MAX_RIGA); i = 0; scanf("%c", &riga[i]); while (riga[i] != '\n') { i++; scanf("%c", &riga[i]); } /* Analizza la riga */ while(riga[0] != '\n') { for( i=0 ; riga[i] != '\n' ; 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; } /* Leggi la riga successiva */ printf("Inserisci riga di caratteri (max %d elementi):\n", MAX_RIGA); i = 0; scanf("%c", &riga[i]); while (riga[i] != '\n') { i++; scanf("%c", &riga[i]); } } 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 { Copyright © 2008 - The McGraw-Hill Companies srl 21 Informatica: Arte e Mestiere, Terza edizione Stefano Ceri, Dino Mandrioli, Licia Sbattella Esercizi 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"); } } Esercizio 6.5 Facendo riferimento all’Esercizio 5.5, si scriva un frammento di programma C che, per ogni quartetto senza pianoforte (cioè in cui nessuno strumentista suona il pianoforte) presente nell'elenco ElencoQuartetti, stampa a video il nome del quartetto. In seguito stampa a video il nome di tutti i quartetti con pianoforte. Nel realizzare il frammento di programma si supponga che la variabile ElencoQuartetti di cui al punto precedente sia già stata inizializzata (sia cioè già stata caricata di dati, che non devono essere riletti da tastiera), e si supponga inoltre che un'altra variabile numQuartetti, di tipo int (anch'essa già inizializzata) contenga il numero di quartetti effettivamente inseriti in ElencoQuartetti. Soluzione dell’esercizio 6.5 main() { Copyright © 2008 - The McGraw-Hill Companies srl 22 Informatica: Arte e Mestiere, Terza edizione Stefano Ceri, Dino Mandrioli, Licia Sbattella Esercizi TipoQuartetto ElencoQuartetti[MAX_QUARTETTI]; int numQuartetti[MAX_QUARTETTI]; /* inizializzazione delle variabili sopra dichiarate, da considerare già fatta */ TipoQuartetto QuartettiSenzaPiano[MAX_QUARTETTI]; TipoQuartetto QuartettiConPiano[MAX_QUARTETTI]; int numQuarSenzaPiano = 0, numQuarConPiano = 0, i, j, trovato; for (i = 0; i < numQuartetti; i++) { j = 0; trovato = 0; while (trovato == 0 && j<4) { if (ElencoQuartetti[i].musicisti[j].strumento == pianoforte) { trovato = 1; } j++; } if (trovato == 0) { QuartettiSenzaPiano[numQuarSenzaPiano] = ElencoQuartetti[i]; numQuarSenzaPiano++; } else { QuartettiConPiano[numQuarConPiano] = ElencoQuartetti[i]; numQuarConPiano++; } } printf(“Quartetti senza piano:\n”); for (i = 0; i < numQuarSenzaPiano; i++) { printf(“%s\n”, QuartettiSenzaPiano[i].nome); } printf(“Quartetti con piano:\n”); for (i=0; i<numQuarConPiano; i++) { printf(“%s\n”, QuartettiConPiano[i].nome); } } Esercizio 6.6 Scrivere un programma C che esegue le seguenti operazioni. Legge da tastiera una sequenza di esattamente 64 pacchetti di 4 bit ciascuno (si ricorda che un pacchetto di 4 bit altro non è che una sequenza di 4 interi che possono assumere solo i valori 0 o 1). Un pacchetto è ammissibile solo se contiene solo 0 e 1; pacchetti non ammissibili vanno reimmessi. Un pacchetto ammissibile è detto corretto se e solo se il numero di 1 in tutto il pacchetto è pari. Il programma, quindi, per ogni pacchetto ammissibile, verifica se il pacchetto è corretto, e, dopo avere letto tutti i pacchetti, stampa prima i pacchetti corretti, quindi quelli scorretti. Copyright © 2008 - The McGraw-Hill Companies srl 23 Informatica: Arte e Mestiere, Terza edizione Stefano Ceri, Dino Mandrioli, Licia Sbattella Esercizi Soluzione dell’esercizio 6.6 #define MAX_PACC 64 typedef int TipoPacchetto[4]; typedef enum {false, true} boolean; main() { TipoPacchetto corretti[MAX_PACC], non_corretti[MAX_PACC], corrente; int i, j, k, h, somma, n_corretti, n_non_corretti; boolean ammissibile; i = 0; j = 0; k = 0; while(i<MAX_PACC) { printf(“Pacchetto numero %d (4 bit, separare i singoli bit con degli spazi): ”, i+1); scanf(“%d %d %d %d”, &corrente[0], &corrente[1], &corrente[2], &corrente[3]); ammissibile = true; somma = 0; for(h=0 ; h<4 ; h++) { if(corrente[h] > 1 || corrente[h] < 0) { ammissibile = false; } else { somma += corrente[h]; } } if (ammissibile) { if (somma % 2 == 0) { for(h = 0 ; h < 4 ; h++) { corretti[j][h] = corrente[h]; } j++; } else { for(h = 0 ; h < 4 ; h++) { non_corretti[k][h] = corrente[h]; } k++; } i++; } else { printf(“Pacchetto immesso non ammissibile, reimmetterlo!\n”); } } n_corretti = j; n_non_corretti = k; Copyright © 2008 - The McGraw-Hill Companies srl 24 Informatica: Arte e Mestiere, Terza edizione Stefano Ceri, Dino Mandrioli, Licia Sbattella Esercizi printf(“Pacchetti corretti:\n”); for(j = 0 ; j<n_corretti ; j++) { for(h = 0 ; h < 4 ; h++) { printf(“%d”, corretti[j][h]); } printf(“\n”); } printf(“Pacchetti NON corretti:\n”); for(j = 0 ; j < n_non_corretti ; j++) { for(h=0 ; h<4 ; h++) { printf(“%d”, non_corretti[j][h]); } printf(“\n”); } } Esercizio 6.7 Con riferimento all’Esercizio 5.6, si scriva un main() che, senza curarsi di come viene inizializzata la variabile elencoCD (che supponiamo già caricata con i dati di interesse), esegue le seguenti operazioni: - chiede all’utente di inserire un anno (compreso tra 1990 e 1999); - per ogni trimestre di quell’anno (Gen-Mar, Apr-Giu, Lug-Set, Ott-Dic) stampa a video il numero di CD (contando anche le copie multiple dello stesso titolo) acquistati in quel periodo. Soluzione dell’esercizio 6.7 #include <stdio.h> #define MAX_L_TITOLO 50 #define MAX_L_AUTORE 60 #define MAX_TIT_IN_MESE 50 typedef struct { char titolo[MAX_L_TITOLO]; char autore[MAX_L_AUTORE]; int anno; int n_copie; } TipoDatiCD; typedef struct { TipoDatiCD cd[MAX_TIT_IN_MESE]; int n_titoli; } CDAcqInMese; /* Le righe di 'elencoCD' corrispondono agli anni dal 1990 al 1999, le colonne ai 12 mesi di ciascun anno */ CDAcqInMese elencoCD[10][12]; main() Copyright © 2008 - The McGraw-Hill Companies srl 25 Informatica: Arte e Mestiere, Terza edizione Stefano Ceri, Dino Mandrioli, Licia Sbattella Esercizi { int anno; int i, j, k, totale_cd_trimestre; do { printf("Inserisci un anno compreso tra 1990 e 1999: "); scanf("%d", &anno); } while (anno < 1990 || anno > 1999); /* tramuto l'anno interno nella riga corrspondente dell'array doppio 'elencoCD' */ anno = anno - 1990; /* il ciclo piu' esterno conta i trimestri, quello piu' interno i mesi del trimestre */ for(i = 0 ; I < 4 ; i++) { totale_cd_trimestre = 0; for(j = 0 ; j < 3 ; j++) { /* conto il numero di CD acquistati nel mese i*3+j e li aggiungo al totale del trimestre*/ for(k = 0 ; k < elencoCD[anno][i*3+j].n_titoli ; k++) { totale_cd_trimestre += elencoCD[anno][i*3+j].cd[k].n_copie; } } printf("Totale CD del trimestre n. %d dell'anno %d: %d\n", i+1, anno+1990, totale_cd_trimestre); } } Copyright © 2008 - The McGraw-Hill Companies srl 26 Informatica: Arte e Mestiere, Terza edizione Esercizi Stefano Ceri, Dino Mandrioli, Licia Sbattella Capitolo 7 Funzioni e procedure Funzioni e procedure, parametri e valore di ritorno; stringhe. Esercizio 7.1 Si consideri la seguente dichiarazione di tipo: typedef struct { int campo1; int campo2; int campo3; int campo4; } Struttura; Parte 1. Si scriva una funzione che riceva un parametro di tipo Struttura e produca come risultato un valore dello stesso tipo in cui i campi risultino invertiti (il valore di campo4 si trovi in campo1 e quello di campo3 in campo2). Parte 2. Si trasformi poi la funzione in una procedura che esegua la medesima operazione sulla variabile di tipo struttura che le è passata come parametro. Soluzione dell’esercizio 7.1 Parte 1. Struttura InvertiStruttura (Struttura param) { Struttura varloc; varloc.campo1 = param.campo4; varloc.campo2 = param.campo3; varloc.campo3 = param.campo2; varloc.campo4 = param.campo1; return varloc; } Parte 2. void ProcInvertiStruttura (Struttura *param) { Struttura varloc; varloc.campo1 = param->campo4; varloc.campo2 = param->campo3; varloc.campo3 = param->campo2; varloc.campo4 = param->campo1; param->campo4 = varloc.campo4; param->campo3 = varloc.campo3; param->campo2 = varloc.campo2; param->campo1 = varloc.campo1; } Copyright © 2008 - The McGraw-Hill Companies srl 27 Informatica: Arte e Mestiere, Terza edizione Stefano Ceri, Dino Mandrioli, Licia Sbattella Esercizi Esercizio 7.2 Data la seguente funzione calcola(): int calcola( int *a, int b, int *c) { *a = *a * 2; b = b * 2; *c = *c - 2; return *a + b + *c; } Qual è l'output del seguente frammento di programma? main() { int z; int *y; int x; y = &x; z = 1; *y = 2; x = 3; z = calcola(&z,x,y); printf("%d, %d",z,x); } Soluzione dell’esercizio 7.2 Il frammento di programma stampa, nell’ordine, i valori 9, 1. Infatti, prima della chiamata di calcola(), z ha valore 1, y contiene l’indirizzo di x, ed x vale 3 (quindi *y vale anch’esso 3). Durante l’esecuzione di calcola(), x passa da 3 ad 1 con l’istruzione *c = *c – 2. 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 9 (la funzione era stata chiamata con z=1, x=3, *y=3), z alla fine vale 9. Esercizio 7.3 Data la seguente 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; Copyright © 2008 - The McGraw-Hill Companies srl 28 Informatica: Arte e Mestiere, Terza edizione Stefano Ceri, Dino Mandrioli, Licia Sbattella Esercizi 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(). Soluzione dell’esercizio 7.3 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. Esercizio 7.4 Si scriva un insieme di dichiarazioni di tipo per la rappresentazione dello stato di un apparecchio TV. Esso deve contenere una descrizione dello stato del televisore (acceso/spento, canale, posizione dei vari controlli: luminosità, volume, ecc.). Si scrivano poi i prototipi (non le definizioni complete) di alcune funzioni corrispondenti ai comandi. Ad esempio, una funzione cambiaCanale() potrebbe assegnare al televisore il canale impostato dall’utente; un’ulteriore funzione incrementaCanale(), innescata dalla pressione del tasto corrispondente, dovrebbe determinare il passaggio dal canale attuale al successivo; ecc. Successivamente si scrivano le definizioni di almeno due delle procedure suddette. Soluzione dell’esercizio 7.4 #include <stdio.h> #define INCREMENTO 0.1 typedef enum {false, true} boolean; typedef struct { boolean acceso; unsigned int canale; float luminosita; float saturazione; float volume; } Televisore; /* Tra 0 e 1 */ /* Tra 0 e 1 */ /* Tra 0 e 1 */ /* Prototipi delle funzioni */ void cambiaCanale (Televisore *stato, unsigned int nuovoCanale); void accendiSpegni (Televisore *stato); void incrementaCanale (Televisore *stato); void decrementaCanale (Televisore *stato); void incrementaVolume (Televisore *stato); Copyright © 2008 - The McGraw-Hill Companies srl 29 Informatica: Arte e Mestiere, Terza edizione Stefano Ceri, Dino Mandrioli, Licia Sbattella Esercizi void decrementaVolume (Televisore *stato); void incrementaSaturazione (Televisore *stato); void decrementaSaturazione (Televisore *stato); void incrementaLuminosita (Televisore *stato); void decrementaLuminosita (Televisore *stato); main() { Televisore stato; /* Per esempio, accendiamo e spegniamo il televisore */ stato.acceso = false; printf ("%d", stato.acceso); accendiSpegni (&stato); printf ("%d", stato.acceso); accendiSpegni (&stato); printf ("%d", stato.acceso); } /* Definizione delle funzioni */ void incrementaCanale (Televisore *stato) { if (stato->canale < 99) stato->canale = stato->canale + 1; } void incrementaVolume (Televisore *stato) { if (stato->volume + INCREMENTO <= 1) stato->volume = stato->volume + INCREMENTO; } void accendiSpegni (Televisore *stato) { stato->acceso = ! stato->acceso; } … definizione delle funzioni rimanenti. Esercizio 7.5 Si scriva un programma che: • Legge dallo standard input una sequenza di coppie di numeri reali {<xi,yi>} (per comodità si può assumere che tale sequenza non contenga più di KMAX elementi, essendo KMAX un valore costante noto a priori). • Per ogni coppia <xi,yi> calcola il valore zi della funzione f(xi,yi) = 20⋅x2i - y3i. • Stampare sullo standard output la sequenza di valori { zi } in ordine crescente. Ad esempio, in corrispondenza della sequenza di input {<0,5>, <2,4>, <1,3>} il programma deve stampare in uscita la sequenza {-125, -7, 16} E’ particolarmente apprezzata la costruzione di un programma che sia facilmente modificabile in modo da risolvere lo stesso problema facendo però riferimento ad una diversa funzione f(xi,yi) (ad esempio f(xi,yi) = sin(xi). cos3(yi)) Se lo si ritiene opportuno è possibile fare uso di adeguati sottoprogrammi di servizio (ad esempio una procedura di ordinamento) senza codificarli ma limitandosi a dichiararne opportunamente l’interfaccia. Soluzione dell’esercizio 7.5 #include <stdio.h> Copyright © 2008 - The McGraw-Hill Companies srl 30 Informatica: Arte e Mestiere, Terza edizione Stefano Ceri, Dino Mandrioli, Licia Sbattella Esercizi #define KMAX 10 typedef struct { float x; float y; } Coppia; /* Dichiarazione prototipi */ float eleva (float base, unsigned int esp); float funzione (Coppia c); void ordina (float ris[], unsigned int n); main() { float risultati[KMAX]; unsigned int nCoppie, i; Coppia c; printf ("Quante coppie (max %u): ", KMAX); scanf ("%u", &nCoppie); for (i = 0; i < nCoppie; i++) { printf ("x: "); scanf ("%f", &c.x); printf ("y: "); scanf ("%f", &c.y); risultati[i] = funzione(c); } ordina (risultati, nCoppie); for (i = 0; i < nCoppie; i++) printf ("risultato: %f\n", risultati[i]); } /* Definizione delle funzioni */ /* Si assume che la funzione ordina() sia fornita dall'esterno */ float eleva (float base, unsigned int esp) { unsigned int i; float tot = 1.0; for (i = 0; i < esp; i++) tot = tot * base; return tot; } float funzione (Coppia c) { return 20 * eleva (c.x, 2) - eleva (c.y, 3); } Copyright © 2008 - The McGraw-Hill Companies srl 31 Informatica: Arte e Mestiere, Terza edizione Esercizi Stefano Ceri, Dino Mandrioli, Licia Sbattella Capitolo 8 Introduzione alla programmazione ricorsiva La ricorsione. Esercizio 8.1 E’ data la funzione ricorsiva: int elabora(int n1, int n2) { int a, b; a = n1 + 1; b = n2 - 1; if (b <= 0) return a; else return alabora(a,b); } Descrivere l'esecuzione della funzione in seguito all'invocazione: elabora(4,3). Soluzione dell’esercizio 8.1 La sequenza di chiamate ricorsive della funzione elabora() è la seguente: elabora(4,3) elabora(5,2) elabora(6,1) Alla fine il valore ritornato dalla chiamata elabora(4,3) è 7. Esercizio 8.2 Sia data la funzione ricorsiva seguente: 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. Soluzione dell’esercizio 8.2 La sequenza di chiamate ricorsive della funzione elabora() è la seguente: elabora(10) Copyright © 2008 - The McGraw-Hill Companies srl 32 Informatica: Arte e Mestiere, Terza edizione Esercizi Stefano Ceri, Dino Mandrioli, Licia Sbattella 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 8.3 Si consideri la seguente funzione, che calcola l’n-simo numero di Fibonacci in modo ricorsivo: int fibonacci(unsigned int n) { if (n == 0) return 0; else if (n == 1) return 1; else return fib(n – 1) + fib(n – 2); } Si descriva la computazione di fib(4). Soluzione dell’esercizio 8.3 La sequenza di chiamate ricorsive della funzione fibonacci() è la seguente: fibonacci(4) fibonacci(3) + fibonacci(2) fibonacci(2) + fibonacci(1) + fibonacci(1) + fibonacci(0) fibonacci(1) + fibonacci(0) + 1 + 1 + 0 1+0+1+1+0 3 Esercizio 8.4 Data la seguente definizione ricorsiva: f(x) = 0; f(x) = 2 + f(f(x-2)-2); per x = 0 per x > 0 1. Si determini per quali valori interi di x è definita la funzione e quale è il suo valore. 2. Si scriva una funzione ricorsiva che realizzi la funzione stessa. Il prototipo della funzione sia il seguente: int f1 (int x); 3. Si scriva una funzione che realizzi la funzione e restituisca come parametro il numero di chiamate ricorsive effettuate. Il prototipo della funzione sia il seguente: int f2 (int x, int *numChiamate); Copyright © 2008 - The McGraw-Hill Companies srl 33 Informatica: Arte e Mestiere, Terza edizione Stefano Ceri, Dino Mandrioli, Licia Sbattella Esercizi NB: si supponga che, al momento della prima chiamata, il valore di numChiamate sia correttamente inizializzato a 0. Soluzione dell’esercizio 8.4 Parte 1. La funzione è definita solo per x = 0. Parte 2. int f1(int x) { if (x == 0) return 0; else if (x > 0) return 2 + f(f(x-2)-2); else { printf ("ERRORE x=%d\n", x); exit(); } } Parte 3. int f2(int x, int *numChiamate) { *numChiamate = *numChiamate + 1; if (x == 0) return 0; else if (x > 0) return 2 + f(f(x-2, numChiamate)-2, numChiamate); else { printf ("ERRORE x=%d, nunChiamate=%d\n", x, *numChiamate); exit(); } } Copyright © 2008 - The McGraw-Hill Companies srl 34 Informatica: Arte e Mestiere, Terza edizione Stefano Ceri, Dino Mandrioli, Licia Sbattella Esercizi Capitolo 9 Gestione dei file I file di testo e i file binari. Esercizio 9.1 Con riferimento all’esercizio 5.4 si scriva la funzione inserisciMedia() che legge un file binario contenente i dati di tutti gli studenti di un ateneo, privi della media dei voti riportati e lo aggiorna inserendo nell’apposito campo la media suddetta. La funzione dovrà a sua volta far uso di un’ulteriore, appropriata, funzione che calcoli la media. E’ sufficiente fornire la dichiarazione di tale funzione ausiliaria, senza fornirne la definizione completa. Soluzione dell’esercizio 9.1 #include <stdio.h> typedef enum {false, true} boolean; typedef struct { unsigned short giorno; unsigned short mese; unsigned int anno; } Tipo_data; typedef struct { char nome[40]; char codice[6]; unsigned short voto; boolean lode; float crediti; } Tipo_esame; typedef struct { char nome[50]; unsigned int matricola; Tipo_data data_immatricolazione; Tipo_esame esami[29]; unsigned short numero_esami; float media; } Tipo_studente; /* La funzione media() non viene implementata */ float media (Tipo_esame esami[], unsigned short numEsami); void inserisciMedia(char nomeFIle[]); main() { /* Il main del programma... */ } Copyright © 2008 - The McGraw-Hill Companies srl 35 Informatica: Arte e Mestiere, Terza edizione Stefano Ceri, Dino Mandrioli, Licia Sbattella Esercizi void inserisciMedia(char nomeFile[]) { FILE *fileDati; Tipo_studente studente; fileDati = fopen(nomeFile, "r+"); fread (&studente, sizeof(studente), 1, fileDati); while (! feof(fileDati)) { studente.media = media (studente.esami, studente.numero_esami); fread (&studente, sizeof(studente), 1, fileDati); /* Torna indietro per scrivere le modifche sullo stesso record */ fseek (fileDati, -sizeof(studente), SEEK_CUR); fwrite (&studente, sizeof(studente), 1, fileDati); } fclose (fileDati); } Esercizio 9.2 Con riferimento all’Esercizio 5.5 si definiscano i tipi TipoStrumentista e TipoQuartetto come nell'esercizio suddetto. Si definisca inoltre un tipo TipoConcerto costituito dal nome della località in cui il concerto si è tenuto (massimo 40 caratteri), dalla data in cui si è tenuto il concerto, e dal quartetto che ha tenuto il concerto. Si supponga poi di avere un file binario contenente un elenco di concerti. Si supponga inoltre che il file sia già stato aperto in modalità (binaria) lettura/scrittura. Si risolva a scelta una delle seguenti varianti. Variante a Dopo avere dichiarato Variante b Dopo avere dichiarato Variante c Dopo avere dichiarato eventuali variabili globali, definire una funzione CancellaConcertiDiQuartetto() che riceve in ingresso il nome di un quartetto, e crea un nuovo file (sempre binario), di nome “NuovaListaConcerti.dat”, contenente l'elenco di tutti i concerti esclusi quelli del quartetto il cui nome è stato passato alla funzione. La funzione ritorna 1 se l'operazione è andata a buon fine, 0 altrimenti. Il file originario con l'elenco dei concerti rimane immutato. eventuali variabili globali, definire una funzione CancellaConcertiDiQuartetto() che riceve in ingresso il nome di un quartetto, e cancella dal file con l'elenco dei concerti tutti i concerti tenuti dal quartetto il cui come è stato passato alla funzione. La funzione ritorna 1 se l'operazione è andata a buon fine, 0 altrimenti; essa modifica il file originario con l'elenco dei concerti. In questa variante è ammesso (anzi, è consigliato) aprire file temporanei, in aggiunta al file originario. eventuali variabili globali, definire una funzione CancellaConcertiDiQuartetto() che riceve in ingresso il nome di un quartetto, e cancella dal file con l'elenco dei concerti tutti i concerti tenuti dal quartetto il cui come è stato passato alla funzione. La funzione ritorna 1 se l'operazione è andata a buon fine, 0 altrimenti; essa modifica il file originario con l'elenco dei concerti. In questa variante non è ammesso aprire file temporanei aggiuntivi, si può lavorare solo sul file originario. Copyright © 2008 - The McGraw-Hill Companies srl 36 Informatica: Arte e Mestiere, Terza edizione Stefano Ceri, Dino Mandrioli, Licia Sbattella Esercizi Suggerimento per le varianti b e c: per troncare un file f alla lunghezza data dalla posizione corrente nel file, l’istruzione da usare è: ftruncate(fileno(f), ftell(f)); Soluzione dell’esercizio 9.2 Definizione dei tipi; parte comune a tutte le varianti. #define L_NOME_MUS 40 #define L_NOME_QUAR 30 #define MAX_QUARTETTI 100 #define L_NOME_CONC 40 typedef struct { short giorno; short mese; int anno; } TipoData; typedef enum {pianoforte, basso, batteria, sax, trombone, flauto, clarinetto, voce} TipoStrumento; typedef struct { char nome[L_NOME_MUS + 1]; TipoData data_nascita; TipoStrumento strumento; } TipoStrumentista; typedef struct { char nome[L_NOME_QUAR + 1]; TipoStrumentista musicisti[4]; } TipoQuartetto; typedef struct { char nome_loc[L_NOME_CONC + 1]; TipoData data; TipoQuartetto quartetto; } TipoConcerto; Variante a. #include <string.h> #include <stdio.h> FILE *f_concerti; int CancellaConcertiDiQuartetto(char nome_quar[]) { TipoConcerto curr_conc; FILE *nf = fopen(“NuovaListaConcerti.dat”, “wb”); if (nf == NULL) return 0; rewind(f_concerti); while (! feof(f_concerti)) Copyright © 2008 - The McGraw-Hill Companies srl 37 Informatica: Arte e Mestiere, Terza edizione Stefano Ceri, Dino Mandrioli, Licia Sbattella Esercizi { if (fread(&curr_conc, sizeof(TipoConcerto), 1, f_concerti) == 1) { if (strcmp(curr_conc.quartetto.nome, nome_quar) != 0) { if (fwrite(&curr_conc, sizeof(TipoConcerto), 1, nf) != 1) { fclose(nf); return 0; } } } else { fclose(nf); return 0; } } if (fclose(nf) == 0) return 1; else return 0; } Variante b. #include <string.h> #include <stdio.h> FILE *f_concerti; int CancellaConcertiDiQuartetto(char nome_quar[]) { TipoConcerto curr_conc; FILE *tmp_f = fopen(“_temp.dat”, “wb+”); if (tmp_f == NULL) return 0; rewind(f_concerti); while (! feof(f_concerti)) { if (fread(&curr_conc, sizeof(TipoConcerto), 1, f_concerti) == 1) { if (strcmp(curr_conc.quartetto.nome, nome_quar) != 0) { if (fwrite(&curr_conc, sizeof(TipoConcerto), 1, tmp_f) != 1) { fclose(tmp_f); return 0; } } } else { fclose(tmp_f); return 0; } } rewind(f_concerti); Copyright © 2008 - The McGraw-Hill Companies srl 38 Informatica: Arte e Mestiere, Terza edizione Stefano Ceri, Dino Mandrioli, Licia Sbattella Esercizi rewind(tmp_f); while (!feof(tmp_f)) { if (fread(&curr_conc, sizeof(TipoConcerto), 1, tmp_f) == 1) { if (fwrite(&curr_conc, sizeof(TipoConcerto), 1, f_concerti) != 1) { fclose(tmp_f); return 0; } } else { fclose(tmp_f); return 0; } } /* tronco il file all’ultima posizione in cui ho scritto (il file riscritto è in generale più corto del file originario */ ftruncate(fileno(f_concerti), ftell(f_concerti)); if (fclose(tmp_f) == 0) return 1; else return 0; } Variante c. #include <string.h> #include <stdio.h> FILE *f_concerti; int CancellaConcertiDiQuartetto(char nome_quar[]) { TipoConcerto curr_conc; /* tengo due indici nel file: la prossima posizione in cui devo scrivere (pos_next_write) e la prossima posizione da cui devo leggere (pos_next_read) */ long pos_next_write, pos_next_read; rewind(f_concerti); pos_next_write = ftell(f_concerti); while (!feof(f_concerti)) { if (fread(&curr_conc, sizeof(TipoConcerto), 1, f_concerti) == 1) { if (strcmp(curr_conc.quartetto.nome, nome_quar) != 0) { /* se il concerto è da mantenere, lo riscrivo nel file f_concerti alla prossima posizione in cui devo scrivere. Prima di scrivere, però, devo memorizzare la posizione corrente nel file perchè a questa posizione dovrò poi tornare per ricominciare a leggere. */ pos_next_read = ftell(f_concerti); /* mi sposto nel file alla posizione in cui devo scrivere, e poi effettivamente scrivo. */ if (fseek(f_concerti, pos_next_write, SEEK_SET)) return 0; if (fwrite(&curr_conc, sizeof(TipoConcerto), 1, f_concerti) != 1) return 0; Copyright © 2008 - The McGraw-Hill Companies srl 39 Informatica: Arte e Mestiere, Terza edizione Stefano Ceri, Dino Mandrioli, Licia Sbattella Esercizi /* memorizzo la posizione corrente, che è la prossima posizione in cui dovrò scrivere, e mi riporto nella posizione che è la prossima da cui devo leggere */ pos_next_write = ftell(f_concerti); if (fseek(f_concerti, pos_next_read, SEEK_SET)) return 0; } } else return 0; } /* mi riporto sulla posizione che è la prossima in cui scrivere (cioè appena dopo l’ultimo concerto che va conservato, e tronco il file (il file riscritto è in generale più corto del file originario */ if (fseek(f_concerti, pos_next_write, SEEK_SET)) return 0; ftruncate(fileno(f_concerti), ftell(f_concerti)); return 1; } Esercizio 9.3 Si abbia un file (binario) “FileFatture.dat” contenente fatture. Ogni fattura sia del tipo TipoFatture dichiarato qui sotto: typedef struct { unsigned int NumFattura; char Nome[30]; char Cognome[40]; float Importo; char PartitaIva [12]; /*altri campi irrilevanti per l’esercizio*/ } TipoFatture; Si scriva una procedura che aggiorna una (e una sola!) fattura del file cambiandone l’importo da Lire a Euro (si ricorda che un Euro vale 1936,27 Lire). La procedura riceve come parametro il numero d’ordine della fattura nel file (si assuma che il numero di fattura – denotato dal campo NumFattura – sia anche la posizione assunta dalla fattura nel file, cioè che la fattura numero 1 sia la prima, quella numero 2 la seconda, ecc.). Prima di procedere alla codifica della procedura si dichiarino eventuali variabili globali da essa utilizzate. Si supponga che il file sia già aperto al momento della chiamata della procedura. Soluzione dell’esercizio 9.3 FILE *file_fatture; void aggiorna(unsigned int num_ord) { TipoFatture fattura; long pos_corr; pos_corr = ftell(file_fatture); fseek(file_fatture, sizeof(TipoFatture)*(num_ord-1), SEEK_SET); fread(&fattura, sizeof(TipoFattura), 1, file_fatture); Copyright © 2008 - The McGraw-Hill Companies srl 40 Informatica: Arte e Mestiere, Terza edizione Stefano Ceri, Dino Mandrioli, Licia Sbattella Esercizi fattura.Importo = fattura.Importo/1936.27; fseek(file_fatture, sizeof(TipoFatture)*(num_ord-1), SEEK_SET); fwrite(&fattura, sizeof(TipoFattura), 1, file_fatture); fseek(file_fatture, pos_corr, SEEK_SET); /*anche se non esplicitamente richiesto, la procedura lascia inalterata la posizione corrente del file*/ } Esercizio 9.4 Si scriva un programma che legge un file di parole appartenenti al vocabolario italiano o al vocabolario inglese e le ristampa su due file separati, uno per le parole inglesi e uno per quelle italiane. Si assuma che il programma abbia accesso (in memoria o su file) ai vocabolari delle due lingue. NB1. Si ponga attenzione a casi particolari come il fatto che una parola del file originario non si trovi in nessuno dei due vocabolari o che si trovi in entrambi, ecc. NB2. Non è necessaria una codifica in tutti i dettagli del programma completo: si può invece far uso di opportune procedure limitandosi a definire con precisione il loro uso (la loro interfaccia) senza per questo codificarne l'implementazione. Soluzione dell’esercizio 9.4 #include <stdio.h> typedef enum {false, true} boolean; /* Le funzioni inVocabolarioItaliano() e inVocabolarioInglese() non sono implementate… */ boolean inVocabolarioItaliano (char parola[]); boolean inVocabolarioInglese (char parola[]); void separaParoleItaIng (char nomeFilePar[], char nomeFileParIta[], char nomeFileParIng[]); main() { char nomeFileParole[100], nomeFileParoleIta[100], nomeFileParoleIng[100]; printf ("Nome file di parole: "); scanf ("%s", nomeFileParole); printf ("Nome file di parole italiane: "); scanf ("%s", nomeFileParoleIta); printf ("Nome file di parole inglesi: "); scanf ("%s", nomeFileParoleIng); separaParoleItaIng (nomeFileParole, nomeFileParoleIta, nomeFileParoleIng); } /* Si assume che una parola non sia lunga più do 50 lettere */ /* Se la parola esiste in entrambi i vocabolari, viene scritta in entrambi i file */ void separaParoleItaIng (char nomeFilePar[], char nomeFileParIta[], char nomeFileParIng[]) { FILE *fileParole, *fileParoleIta, *fileParoleIng; char parola[50]; fileParole = fopen (nomeFilePar, "r"); fileParoleIta = fopen (nomeFileParIta, "w"); fileParoleIng = fopen (nomeFileParIng, "w"); fscanf (fileParole, "%s", parola); while (! feof (fileParole)) Copyright © 2008 - The McGraw-Hill Companies srl 41 Informatica: Arte e Mestiere, Terza edizione Stefano Ceri, Dino Mandrioli, Licia Sbattella Esercizi { if (inVocabolarioItaliano (parola)) fprintf (fileParoleIta, "%s\n", parola); if (inVocabolarioInglese (parola)) fprintf (fileParoleIng, "%s\n", parola); fscanf (fileParole, "%s", parola); } fclose (fileParole); fclose (fileParoleIta); fclose (fileParoleIng); } Copyright © 2008 - The McGraw-Hill Companies srl 42 Informatica: Arte e Mestiere, Terza edizione Esercizi Stefano Ceri, Dino Mandrioli, Licia Sbattella Capitolo 10 Strutture dati dinamiche Le liste. Esercizio 10.1 Parte a. Si considerino la tradizionale dichiarazione: struct EL { int Info; struct EL *Prox; }; typedef struct EL ElemLista; typedef ElemLista *ListaDiInteri; E la seguente funzione: int sconosciuta (ListaDiInteri Lista) { int x = 0; ListaDiInteri cursore; cursore = Lista; while (cursore != NULL) { if (cursore->Info > x) x = cursore->Info; cursore = cursore->Prox; } return x; } Si dica qual’è il risultato della funzione sconosciuta quando vengono eseguite le seguenti chiamate: sconosciuta (L1) sconosciuta (L2) L1 ed L2 essendo, rispettivamente le liste rappresentate nella figura seguente. Copyright © 2008 - The McGraw-Hill Companies srl 43 Informatica: Arte e Mestiere, Terza edizione Esercizi Stefano Ceri, Dino Mandrioli, Licia Sbattella L1 6 4 18 -4 4 L2 In generale, qual’è il risultato prodotto dalla funzione per una generica lista di interi? Parte b. Si ricodifichi la funzione sconosciuta in forma ricorsiva. Soluzione dell’esercizio 10.1 Parte a. La funzione produce come risultato 18, quando applicata alla lista L1 e 0 quando applicata a L2. In generale essa il valore massimo contenuto nella lista se essa contiene almeno un numero positivo; 0 in caso contrario (lista vuota o contenente solo numeri non positivi). Parte b. Si definisca in primo luogo la funzione ausiliaria max(): Int max(int p1, p2) { if (p1 > p2) return p1; else return p2; } Successivamente la funzione sconosciuta può essere ricodifcata come segue, in forma ricorsiva: int sconosciuta (ListaDiInteri Lista) /*la funzione produce il valore massimo contenuto nel parametro Lista se essa contiene almeno un numero positivo; 0 in caso contrario */ { if (Lista == NULL) return 0; else return (max(Lista->Info, sconosciuta(Lista->Prox)); } Copyright © 2008 - The McGraw-Hill Companies srl 44 Informatica: Arte e Mestiere, Terza edizione Stefano Ceri, Dino Mandrioli, Licia Sbattella Esercizi Esercizio 10.2 Si vuole realizzare una serie di funzioni per la manipolazione di un archivio di numeri di carte di credito. Ogni carta di credito è definita dai seguenti dati: nome del titolare della carta (massimo 80 caratteri), numero della carta di credito (un codice di esattamente 16 cifre), massimale di spesa mensile per la carta (in euro). Il programma deve essere sviluppato nei seguenti punti: Punto a. Definire il tipo TipoCartaDiCredito, che contiene i dati di una singola carta. Definire quindi i tipi TipoElemListaCarte e TipoListaCarte (con l’ovvio significato degli identificatori), necessari per definire una lista di carte di credito realizzata mediante puntatori. Dichiarare i prototipi di tutte e sole le seguenti funzioni (senza darne ancora l’implementazione!): • carica_lista_da_file(): riceve in ingresso il nome del file binario in cui è memorizzato l’archivio e ritorna una lista contente i dati dell’archivio; si assuma che il file binario contenga una sequenza di dati TipoCartaDiCredito; la lista ritornata dalla funzione non è ordinata secondo nessun criterio particolare; se non è possibile recuperare i dati da file, la funzione ritorna una lista vuota; • scarica_lista_su_file(): riceve in ingresso una lista di carte di credito ed un nome di file, e scrive la lista nel dato file; se il file già esiste, viene cancellato, altrimenti viene creato ex-novo; la funzione ritorna 1 se la scrittura avviene con successo, 0 altrimenti; • stampa_ListaCarte(): riceve in ingresso una lista di carte di credito, e la stampa a video; • estrai_carte_da_lista(): riceve in ingresso una lista di carte di credito, e ne ritorna una nuova, che contiene solo le carte di credito la cui ultima cifra è ‘7’. Punto b. Implementare la funzione carica_lista_da_file() dichiarata al punto a). Eventuali librerie standard del C usate per implementare la funzione vanno dichiarate prima del corpo della funzione stessa, mediante l’apposita clausola include. Eventuali ulteriori sottoprogrammi ausiliari dovranno essere definiti completamente. Punto c Implementare la funzione estrai_carte_da_lista dichiarata al Punto a. Eventuali librerie standard del C usate per implementare la funzione vanno dichiarate prima del corpo della funzione stessa, mediante l’apposita clausola #include. Eventuali ulteriori sottoprogrammi ausiliari dovranno essere definiti completamente. Si fornisca una duplice implementazione della funzione: una versione iterativa e una ricorsiva. Copyright © 2008 - The McGraw-Hill Companies srl 45 Informatica: Arte e Mestiere, Terza edizione Stefano Ceri, Dino Mandrioli, Licia Sbattella Esercizi Soluzione dell’esercizio 10.2 Punto a. #define MAX_L_NOME 80 typedef struct { char nome[MAX_L_NOME+1]; char numero[16]; float massimale; } TipoCartaDiCredito; struct EL { TipoCartaDiCredito info; struct EL *next; }; typedef struct EL TipoElemListaCarte; typedef TipoElemListaCarte *TipoListaCarte; TipoListaCarte carica_lista_da_file (char *nome_file); int scarica_lista_su_file (char *nome_file, TipoListaCarte lista); void stampa_ListaCarte (TipoListaCarte lista); TipoListaCarte estrai_carte_da_lista (TipoListaCarte lista); Punto b. #include <stdlib.h> TipoListaCarte carica_lista_da_file (char *nome_file) { FILE *f; TipoCartaDiCredito carta; TipoElemListaCarte *ptr_el; TipoListaCarte l = NULL; f = fopen(nome_file, “rb+”); if(f == NULL) return NULL; while (fread(&carta, sizeof(TipoCartaDiCredito), 1, f) == 1) { ptr_el = malloc(sizeof(TipoElemListaCarte)); ptr_el->info = carta; ptr_el->next = l; l = ptr_el; } return l; } Punto c (versione iterativa). #include <stdlib.h> TipoListaCarte estrai_carte_da_lista (TipoListaCarte lista) { TipoListaCarte new_l = NULL; Copyright © 2008 - The McGraw-Hill Companies srl 46 Informatica: Arte e Mestiere, Terza edizione Stefano Ceri, Dino Mandrioli, Licia Sbattella Esercizi TipoElemListaCarte *curr = lista, *ptr_new_el; while (curr != NULL) { if (curr->info.numero[16] == ‘7’) { ptr_new_el = malloc(sizeof(TipoElemListaCarte)); ptr_new_el->info = curr->info; ptr_new_el->next = new_l; new_l = ptr_new_el; } curr = curr->next; } return new_l; } Punto c (versione ricorsiva). #include <stdlib.h> TipoListaCarte estrai_carte_da_lista (TipoListaCarte lista) { TipoElemListaCarte *ptr_new_el = NULL; if (lista == NULL) return NULL; if (lista->info.numero[16] == ‘7’) { ptr_new_el = malloc(sizeof(TipoElemListaCarte)); ptr_new_el->info = lista->info; ptr_new_el->next = estrai_carte_da_lista(lista->next); return ptr_new_el; } else return estrai_carte_da_lista(lista->next); } Esercizio 10.3 Si vuole realizzare un programma GestisciNomi che realizza le seguenti funzionalità: • legge dal file “ElencoNomi” un elenco di nomi (ogni nome si trova su una riga separata); • man mano che i nomi vengono letti, essi vengono inseriti in una lista, la quale viene mantenuta ordinata secondo l’ordine alfabetico; • quando non ci sono più nomi da leggere dal file, la lista viene stampata a video; • viene creata una nuova lista, che contiene gli stessi elementi della precedente, ma in ordine inverso rispetto all’originale (NB: la lista originale deve essere lasciata inalterata); • la lista inversa viene stampata. Si precisa che il file da cui vengono letti i nomi è in formato carattere (non binario!), con ogni nome su una riga diversa; si presume che non ci siano righe vuote. Ogni riga è lunga al massimo 80 caratteri. Il programma deve essere sviluppato nei seguenti punti: Punto a. Definire i tipi TipoElemListaNomi e TipoListaNomi (con l’ovvio significato degli identificatori), quindi dichiarare i prototipi di tutte e sole le seguenti funzioni (senza darne ancora l’implementazione!): Copyright © 2008 - The McGraw-Hill Companies srl 47 Informatica: Arte e Mestiere, Terza edizione Stefano Ceri, Dino Mandrioli, Licia Sbattella • • • • Esercizi init_ListaNomi(): inizializza una lista di nomi alla lista vuota; inserisci_nome(): riceve in ingresso un nome ed una lista di nomi, ed inserisce il nome nella lista, in modo che la lista rimanga ordinata in ordine alfabetico; se l’inserimento avviene con successo ritorna true, altrimenti false; stampa_ListaNomi(): riceve in ingresso una lista di nomi, e la stampa a video nome per nome, riga per riga; inverti_ListaNomi(): riceve in ingresso una lista di nomi, e ne ritorna una nuova, in cui l’ordine degli elementi nella lista è invertito. Punto b. Scrivere un main che, utilizzando le funzioni dichiarati al punto precedente (e la libreria standard del C; le librerie che vengono usate devono essere indicate con l’apposita clausola #include prima del main), realizza il programma GestisciNomi delineato in precedenza. Punto c. Implementare la funzione (e solo quella!) inverti_ListaNomi() dichiarata al Punto a. Eventuali librerie standard del C usate per implementare la funzione vanno dichiarate prima del corpo della funzione stessa, mediante l’apposita clausola #include. Eventuali ulteriori sottoprogrammi ausiliari dovranno essere definiti completamente. La funzione inverti_ListaNomi() deve essere implementata sia in versione iterativa che in versione ricorsiva. Soluzione dell’esercizio 10.3 Punto a. #define MAX_L_NOME 80 struct EL { char info[MAX_L_NOME + 1]; struct EL *next; }; typedef struct EL TipoElemListaNomi; typedef TipoElemListaNomi *TipoListaNomi; typedef enum {false, true} boolean; void init_ListaNomi (TipoListaNomi *lista_I); boolean inserisci_nome (char nome[], TipoListaNomi *lista_Ins); void stampa_ListaNomi (TipoListaNomi lista_S); TipoListaNomi inverti_ListaNomi(TipoListaNomi lista_Inv); Punto b. #include <stdio.h> main() { TipoListaNomi lista_Att, inversa; char nome[MAX_L_NOME + 1]; FILE *f; f = fopen(“ElencoNomi”, “r”); if(f == NULL) Copyright © 2008 - The McGraw-Hill Companies srl 48 Informatica: Arte e Mestiere, Terza edizione Stefano Ceri, Dino Mandrioli, Licia Sbattella Esercizi printf(“Errore nell’apertura del file\n”); else { init_ListaNomi(&lista_Att); while (! feof(f)) { fgets(nome, MAX_L_NOME + 1, f); inserisci_nome(nome, &lista_Att); } stampa_ListaNomi(lista_Att); inversa = inverti_ListaNomi(lista_Att) stampa_ListaNomi(inversa); } } Punto c (versione iterativa). #include <stdlib.h> #include <string.h> TipoListaNomi inverti_ListaNomi (TipoListaNomi lista_Inv) { TipoListaNomi inversa; TipoElemListaNomi *cursor, *temp; init_ListaNomi(&inversa); cursor = lista_Inv; while(cursor != NULL) { temp = malloc(sizeof(TipoElemListaNomi)); strcpy(temp->info, cursor->info); temp->next = inversa; inversa = temp; cursor = cursor->next; } return inversa; } Punto c (versione ricorsiva). #include <stdlib.h> #include <string.h> void inserisci_in_coda(TipoListaNomi *lista, char nome[]) { TipoElemListaNomi *temp; if (lista == NULL) { temp = malloc(sizeof(TipoElemListaNomi)); temp->next = NULL; strcpy(temp->info, (*lista)->info); *lista = temp; } else inserisci_in_coda(&((*lista)->next), nome); } TipoListaNomi inverti_ListaNomi (TipoListaNomi lista_Inv) Copyright © 2008 - The McGraw-Hill Companies srl 49 Informatica: Arte e Mestiere, Terza edizione Stefano Ceri, Dino Mandrioli, Licia Sbattella Esercizi { TipoElemListaNomi *inversa; if(lista == NULL) return NULL; else { inversa = inverti_ListaNomi(lista_Inv->next); inserisci_in_coda(&inversa, lista_Inv->info); return inversa; } } Esercizio 10.4 Si considerino le seguenti dichiarazioni: /* xxx denota una qualsiasi dichiarazione del tipo TipoElemento che qui non interessa Sull’insieme dei valori di TipoElemento è definita una relazione d’ordine totale implementata dalla funzione precede() dichiarata in seguito*/ typedef xxx TipoElemento; typedef enum {false, true} boolean; /* precede() ritorna true o false a seconda che elem1 preceda o no elem2 nella relazione d’ordine definita su TipoElemento*/ boolean precede (TipoElemento elem1, TipoElemento elem2); Punto a. Si definiscano i tipi TipoElementoLista e TipoLista per costruire liste di elementi di tipo TipoElemento basate su puntatori. Punto b. Si scriva una funzione merge(), codificandola sia in modo ricorsivo che iterativo, che riceva come parametri due liste ordinate in ordine crescente rispetto alla relazione implementata dalla funzione precede (l’i-esimo elemento precede l’i+1-esimo) contenenti elementi del tipo TipoElemento e produce come risultato una nuova lista, pure ordinata, contenente tutti e soli gli elementi contenuti nelle due liste di ingresso. La funzione non deve alterare le liste di ingresso; perciò il loro contenuto dovrà essere copiato in nuovi elementi che comporranno la nuova lista. Per semplicità si può assumere che sia possibile assegnare direttamente variabili di tipo TipoElemento (cioè che si possa scrivere a=b, se a e b sono due variabili di tipo TipoElemento). Eventuali ulteriori sottoprogrammi ausiliari dovranno essere definiti completamente. La figura sottostante fornisce un esempio di come deve operare la funzione nel caso TipoElemento sia int. Copyright © 2008 - The McGraw-Hill Companies srl 50 Informatica: Arte e Mestiere, Terza edizione Esercizi Stefano Ceri, Dino Mandrioli, Licia Sbattella L1 3 L2 L 10 5 3 5 24 56 10 24 56 Suggerimento per realizzare la versione non ricorsiva della funzione Si definisca una funzione ausiliaria copia() che riceva come parametro una lista e ne copi il contenuto in un’altra lista. La funzione merge() si potrebbe comportare nel modo seguente: - Se entrambe le liste passate alla funzione sono vuote, si ritorna ovviamente la lista vuota. - Se una sola delle due liste è vuota si copia l’altra nella lista risultato e si ritorna quest’ultima. - Altrimenti (entrambe le liste non sono vuote): o Si inizializza la lista risultato con il minore tra i primi elementi delle due liste. o Si inizializzano opportuni puntatori correnti sia delle liste di ingresso che della lista risultato (il puntatore corrente alla lista risultato punta sempre all’ultimo elemento della lista). o Indi, finché entrambe le liste di ingresso non sono state percorse completamente: Si aggiunge in fondo alla lista risultato (tramite il suo puntatore corrente) l’elemento minore tra quelli puntati dai puntatori correnti delle liste di ingresso, avanzando di conseguenza i vari puntatori. Copyright © 2008 - The McGraw-Hill Companies srl 51 Informatica: Arte e Mestiere, Terza edizione Stefano Ceri, Dino Mandrioli, Licia Sbattella Esercizi o Quando si è giunti in fondo a una delle due liste si copia la parte rimanente dell’altra lista in fondo alla lista risultato. NB. Se si segue il suggerimento, non è indispensabile codificare la procedura ausiliaria copia(): all’occorrenza è sufficiente dichiararla. Funzioni ausiliare diverse da copia(), invece, vanno completamente definite. Soluzione dell’esercizio 10.4 Parte a struct EL { TipoElemento info; struct EL *next; }; typedef struct EL TipoElementoLista; typedef TipoElementoLista *TipoLista; Parte b (versione non ricorsiva). TipoLista merge (TipoLista lista1, TipoLista lista2) { TipoLista curr1, curr2, curr, temp, lista_unione; /*prototipo procedura ausiliaria che copia in lista2 il contenuto di lista1*/ void copia(TipoLista lista1, TipoLista *lista2); lista_unione = NULL; curr1 = lista1; curr2 = lista2; if (curr1 == NULL && curr2 == NULL) return lista_unione; else if (curr1 == NULL) copia(lista2, &lista_unione); else if (curr2 == NULL) copia(lista1, &lista_unione); else { lista_unione = malloc(sizeof(TipoElementoLista)); lista_unione->next = NULL; curr = lista_unione; if (precede(curr1->info, curr2->info)) { lista_unione->info = curr1->info; curr1 = curr1->next; } else { lista_unione ->info = curr2->info; curr2 = curr2->next}; } while (curr1 != NULL && curr2 != NULL) { temp = malloc(sizeof(TipoElementoLista)); temp->next = NULL; curr->next = temp; curr = temp; if (precede(curr1->info, curr2->info)) Copyright © 2008 - The McGraw-Hill Companies srl 52 Informatica: Arte e Mestiere, Terza edizione Stefano Ceri, Dino Mandrioli, Licia Sbattella Esercizi { temp->info = curr1->info; curr1 = curr1->next; } else { temp->info = curr2->info; curr2 = curr2->next; } } if (curr1 == NULL) copia(curr2, curr -> next); else copia(curr1, curr -> next); return lista_unione; } } void copia(TipoLista lista1, TipoLista *lista2) { TipoLista temp, curr1, curr2; *lista2 = NULL; curr1 = lista1; if (curr1 != NULL) { temp = malloc(sizeof(TipoElementoLista)); temp->next = NULL; temp->info = curr1-> info; *lista2 = temp; curr2 = temp; curr1 = curr1->next; } while (curr1 != NULL) { temp = malloc(sizeof(TipoElementoLista)); temp->next = NULL; temp->info = curr1->info; curr2->next = temp; curr2 = temp; curr1 = curr1->next; } } Punto b (versione ricorsiva). TipoLista merge (TipoLista lista1, TipoLista lista2) { TipoLista lista_unione; if (lista1 == NULL && lista2 == NULL) return NULL; if (lista1 == NULL) { lista_unione = malloc(sizeof(TipoElementoLista)); lista_unione -> info = lista2->info; lista_unione->next = merge(lista1, lista2->next); } else if (lista2 == NULL) { lista_unione = malloc(sizeof(TipoElementoLista)); lista_unione -> info = lista1->info; Copyright © 2008 - The McGraw-Hill Companies srl 53 Informatica: Arte e Mestiere, Terza edizione Stefano Ceri, Dino Mandrioli, Licia Sbattella Esercizi lista_unione->next = merge(lista1->next, lista2); } else { lista_unione = malloc(sizeof(TipoElementoLista)); if (precede(lista1->info, lista2->info)) { lista_unione -> info = lista1->info; lista_unione->next = merge(lista1->next, lista2); } else { lista_unione -> info = lista2->info; lista_unione->next = merge(lista1, lista2->next); } } return lista_unione; } Esercizio 10.5, Punto a. Si consideri il seguente programma: #include <stdio.h> #include <stdlib.h> main () { int **P; int *Q; Q = malloc(sizeof(int)); P = &Q; **P = 5; *Q = 4; printf(“il valore finale di **P è %d \n”, **P); } Si dica, spiegandone brevemente le ragioni, quale sarà l’output del programma. Punto b. Si risolva lo stesso esercizio per la seguente variazione del programma precedente: #include <stdio.h> #include <stdlib.h> typedef int *Pint; main () { int **P; int *Q; int x; x = 27; Q = malloc(sizeof(int)); P = malloc(sizeof(Pint)); *P = Q; **P = 4; Q = &x; P = &Q; *Q = 5; Copyright © 2008 - The McGraw-Hill Companies srl 54 Informatica: Arte e Mestiere, Terza edizione Esercizi Stefano Ceri, Dino Mandrioli, Licia Sbattella printf(“il valore finale di **P è %d e quello di x è %d \n”, **P, x); } Soluzione dell’Esercizo 10.5 Punto a. Dopo la malloc e l’assegnazione P = &Q in memoria si crea una situazione come indicato in figura: P Q **P = 5 produce la situazione successiva seguente: P Q 5 *Q = 4 però riscrive il valore 4 nella stessa cella dove era stato scritto 5. Perciò il risultato finale è l’output seguente: il valore finale di **P è 4 Punto b. Dopo l’esecuzione delle istruzioni: x = 27; Q = malloc(sizeof(int)); P = malloc(sizeof( Pint)); *P = Q; si è creata la situazione seguente: P Q x 27 Copyright © 2008 - The McGraw-Hill Companies srl 55 Informatica: Arte e Mestiere, Terza edizione Stefano Ceri, Dino Mandrioli, Licia Sbattella Esercizi Assegnando a Q l’indirizzo di x e a P quello di Q, si ottiene l’effetto che l’assegnamento *Q = 5 sostituisce 5 a 27 in x. Ma x è anche puntata da **P. Quindi il risultato finale è l’output seguente: il valore finale di **P è 5 e quello di x è 5 Copyright © 2008 - The McGraw-Hill Companies srl 56 Informatica: Arte e Mestiere, Terza edizione Esercizi Stefano Ceri, Dino Mandrioli, Licia Sbattella Capitolo 14 Archivi e basi di dati Esercizi SQL, Data Definition Language e Data Manipulation Language. Esercizio 14.1 Siano date le seguenti relazioni: persona (cod_fiscale, nome, eta, sesso) automobile (targa, proprietario, colore) Definire lo schema relazionale tramite il DDL SQL. Soluzione dell’esercizio 14.1 CREATE TABLE persona ( cod_fiscale: char(9), PRIMARY KEY, NOT NULL, nome: char(20), eta: integer, sesso: char(1) ) CREATE TABLE automobile ( targa(9), PRIMARY KEY, NOT NULL, proprietario: char(9), colore: char(10) ) Esercizio 14.2 Partendo dalle relazioni definite nell’esercizio precedente e riempite con i dati seguenti: Relazione: persona nome cod_fiscale Rossi RSS123456 Bianchi BIA654321 Verdi VER441611 Grandi GRA741852 eta 25 29 26 50 sesso M M F F Relazione: automobile proprietario targa RSS123456 AG125FA VER441611 FF111GG GRA741852 XX222FF RSS123456 FA143XX BIA654321 GA766AG colore rosso azzurro rosso giallo rosso Definire, usando il DML SQL, le seguenti interrogazioni: a. Estrarre il nome e l’età di tutte le persone di sesso femminile. b. Estrarre le targhe di tutte le automobili i cui proprietari hanno meno di 30 anni. c. Estrarre i nomi di tutte le persone di sesso maschile proprietarie di automobili rosse. Per ognuna di esse si disegni inoltre la tabella contenente i risultati dell’interrogazione. Copyright © 2008 - The McGraw-Hill Companies srl 57 Informatica: Arte e Mestiere, Terza edizione Esercizi Stefano Ceri, Dino Mandrioli, Licia Sbattella Soluzione dell’esercizio 14.1 SELECT nome, eta FROM persona WHERE persona.sesso = 'F’ nome Verdi Grandi eta 26 50 SELECT targa FROM persona, automobile WHERE persona.cod_fiscale = automobile.proprietario And persona.eta < 30 targa AG125FA FA143XX GA766AG FF111GG SELECT nome FROM persona, automobile WHERE persona.cod_fiscale = automobile.proprietario And persona.sesso='M' And automobile.colore = 'rosso' nome Rossi Bianchi Copyright © 2008 - The McGraw-Hill Companies srl 58 Informatica: Arte e Mestiere, Terza edizione Stefano Ceri, Dino Mandrioli, Licia Sbattella Esercizi Capitolo 18 La visione dei sistemi informatici da parte dell’utente finale Esercizi EXCEL. Esercizio 18.1 Si costruisca un archivio, usando un qualsiasi foglio elettronico, contenente informazioni relative a un insieme di dipendenti di un'azienda. Tali informazioni devono contenere, tra l'altro: - Nome e cognome della persona - Data di nascita - Data di assunzione - Retribuzione annua lorda Altre informazioni possono essere inserite a discrezione dello studente. Un ulteriore campo, il cui valore deve essere opportunamente calcolato sulla base delle informazioni precedenti, deve indicare l'ammontare annuo della pensione cui il soggetto ha eventualmente diritto, sulla base della seguente definizione: Se il soggetto ha maturato un'anzianità di servizio ≥ 35 anni (per semplicità si può calcolare tale anzianità assumendo che l'anzianità di servizio sia il periodo di tempo intercorso dalla data di assunzione alla data attuale) e < 45 anni la pensione cui ha diritto è il 60% della retribuzione annua lorda. Se il soggetto ha maturato un'anzianità di servizio ≥ 45 anni la pensione cui ha diritto è il 90% della retribuzione annua lorda (la pensione non spetta a chi ha un'anzianità inferiore a 35 anni). Soluzione dell’esercizio 18.1 Un foglio Excel contenente una possibile soluzione è fornito nel file: 6429-8_18.1.xls. Per la sua costruzione si è fatto uso • della funzione Now per indicare la data attuale; • delle funzioni logiche IF e AND per distinguere in vari casi possibili; • della funzione differenza per calcolare la differenza tra la data attuale e la data di assunzione: tale differenza è prodotta in giorni ed è stata quindi divisa per 360 secondo la convenzione commerciale che considera un anno costituito da 12 mesi di 30 giorni l’uno. Esercizio 18.2 Si risolva l'esercizio 7.5 utilizzando un foglio elettronico. In tal caso i dati di ingresso e di uscita devono trovarsi in apposite posizioni del foglio elettronico. Soluzione dell’esercizio 18.2 Un foglio Excel contenente una possibile soluzione è fornito nel file: 6429-8_18.2.xls. A mo’ di esempio una colonna contiene il calcolo della prima funzione richiesta e un’altra il calcolo della seconda. Copyright © 2008 - The McGraw-Hill Companies srl 59 Informatica: Arte e Mestiere, Terza edizione Esercizi Stefano Ceri, Dino Mandrioli, Licia Sbattella Esercizio 18.3 Si costruisca un archivio per la gestione (semplificata) di un registro IVA: - nella prima colonna si trova il numero progressivo attribuito al documento di spesa; - nella seconda colonna si trovano gli estremi del fornitore; - nella terza l'imponibile ad aliquota 10%; - nella quarta l'imposta relativa all'imponibile indicato in precedenza (cioè il 10% di quanto indicato nella terza colonna) - nella quarta e quinta rispettivamente si trovano imponibile e imposta relativi all'aliquota 20%; - nella sesta colonna si trova il totale dell'operazione (si noti che alcune fatture possono avere imponibili e imposte per entrambe le aliquote). La riga in fondo al foglio contiene i totali come suggerito dalla figura sottostante. Registro IVA Numero progressivo Dati del cliente Imponibile al 10% Imposta al 10% Imponibile al 20% Imposta al 20% Totale 1 Telecom, .... AEM, ..... Officina Pinco 10.000 1.000 234.560 46.912 292.472 200.000 20.000 1.000.000 200.000 220.000 1.200.000 Zzz vvvv tttt 2 3 .... Totali xxx yyyy Un foglio Excel contenente una possibile soluzione è fornito nel file: 6429-8_18.3.xls. Esercizio 18.4 Il signor Rossi vuole tenere sotto controllo i propri conti mensilmente. All’uopo egli desidera registrare ogni mese entrate e uscite, divise in opportune categorie, in modo da sapere se e quanto ha guadagnato o perso; inoltre egli desidera tenere sotto controllo anche il proprio conto corrente bancario, tenendo traccia di tutte le operazioni di prelievo-deposito effettuate in modo da poter sapere in ogni momento di quanto denaro egli dispone. Si costruisca mediante Framework uno strumento che soddisfi le esigenze del signor Rossi. Parte Facoltativa Si osservi che solitamente pagamenti (e incassi) possono avvenire sia in contanti che mediante operazioni bancarie (bonifici, assegni, ...). Ad esempio: lo stipendio potrebbe essere accreditato automaticamente dal datore di lavoro sul cc del signor Rossi. Alcune spese egli potrebbe effettuarle pagando in assegni, ma altre usando contante che egli preleva ogni tanto dalla banca. In questo modo le operazioni sul cc non coincidono necessariamente con le operazioni di entrata-uscita. Copyright © 2008 - The McGraw-Hill Companies srl 60 Informatica: Arte e Mestiere, Terza edizione Esercizi Stefano Ceri, Dino Mandrioli, Licia Sbattella Si organizzi allora lo strumento in modo che incassi e pagamenti effettuati tramite operazioni bancarie possano essere registrati dal signor Rossi una sola volta (ad esempio se egli paga un elettrodomestico mediante un assegno egli segna questa spesa una sola volte mediante il suo strumento che provvede automaticamente ad aggiornare sia il conto ricavi-spese che lo stato del cc bancario). Le operazioni effettuate in contante invece devono essere trattate separatamente: ad esempio se il giorno 10 il signor Rossi preleva L. 500,000 questa operazione comporta un aggiornamento del cc.ma nessuna spesa nel conto entrate-uscite; successivamente egli spende L. 250,000 al ristorante e L. 250,000 in abbigliamento pagando entrambe le volte in contanti: entrambe le operazioni vanno allora registrate come uscite nel registro entrate-uscite, ma non comportano alcun aggiornamento sul cc; chiaramente alla fine del mese I conti devono “tornare”, cioè la variazione dell’ammontare dell cc (+ la variazione dell’ammontare del contante disponibile in cassa) deve uguagliare il netto del conto entrate-uscite. Soluzione dell’esercizio 18.4 Una possibile soluzione della versione facoltativa è impostata nei fogli Excel forniti nel file: 6429-8_18.4.xls. Si noti che sono stati usati due fogli nello stesso file: uno per gestire il conto entrateuscite, chiamto ContoEc, l’altro, Banca, per gestire il conto corrente bancario. Alcune celle del foglio Banca sono calcolate automaticamente sulla base delle operazioni registrate nel ContoEc: queste sono suddivise, per ogni voce, in operazioni in contanti e operazioni effettuate tramite banca. Queste ultime sono automaticamente riportate nel foglio Banca. L’utente deve invece riportare sul foglio Banca tutte le operazioni di prelievo-deposito contanti. Si noti che, se a fine di un periodo il signor Rossi ha nel portafoglio (ammontare complessivo dei contanti) la stessa cifra che aveva all’inizio del periodo, il margine complessivo del periodo risultante nel ContoEc eguaglia la differenza tra fine-periodo e inizio-periodo del foglio Banca. NB. Per semplicità i fogli del file sono stati riempiti solo relativamente al mese di gennaio. Esercizio 18.5 Si vuole ripartire la spesa di costruzione di una strada a fondo chiuso sulla quale insistono quattro condomini di nome Primula, Violetta, Fiordaliso e Gelsomino, composti rispettivamente da 8, 6, 12 e 4 appartamenti e che distano dall’inizio della strada rispettivamente 10, 100, 250 e 300 metri. La spesa, di complessive L 50.000.000, va ripartita in base al numero di appartamenti di ogni condominio, pesato con la sua distanza dall’inizio della strada. Si costruisca un foglio elettronico che, tramite l’introduzione dei dati del problema, permetta di ottenere una ripartizione della spesa in modo analogo a quanto mostrato nella seguente tabella. Condominio Primula Violetta Fiordaliso Gelsomino D=distanza N=no.app.ti 10 100 250 300 Quote Q = N x D 8 6 12 4 80 600 3000 1200 4880 Totali Costo quota C = Costo complessivo/no. quote = Ripartizione = Q x C 819672 6147541 30737705 12295082 50000000 10245,90164 Copyright © 2008 - The McGraw-Hill Companies srl 61 Informatica: Arte e Mestiere, Terza edizione Stefano Ceri, Dino Mandrioli, Licia Sbattella Esercizi Soluzione omessa in quanto derivabile immediatamente dalla figura esplicativa. Copyright © 2008 - The McGraw-Hill Companies srl 62