Esercitazione n. 6 Gestione dei file e ricorsione dott. Carlo Todeschini – [email protected] – Politecnico di Milano – A.A. 2010/2011 Queste slide sono distribuite con licenza Creative Commons Attribuzione-Non commerciale-Condividi allo stesso modo 2.5 Italia Problema: Che cosa succede se... (es. errori_array.c) #include <stdio.h> #define MAX_D 10 int j = 1; void pr_fun (int a[]) { int i=0; for (i=0 ; i<MAX_D+2 ; i++) a[i] = i*j; j++; } void pr (int a[MAX_D]) { int i=0; for (i=0 ; i<MAX_D+2 ; i++) printf ("%d ", a[i]); } main () { int b[MAX_D]; pr_fun (b); pr (b); printf ("\n\n"); pr_fun (b); pr (b); printf ("\n"); } Esercitazioni di Fondamenti di Informatica – Politecnico di Milano sede di Cremona – A.A. 2010/2011 – Carlo Todeschini – [email protected] 2 Problema: Che cosa succede se... (es. errori_array.c) tode@benfuter:~/esempi$ gcc -o es_01_prova_con_array es_01_prova_con_array.c tode@benfuter:~/esempi$ ./es_01_prova_con_array 0 1 2 3 4 5 6 7 8 9 10 11 0 2 4 6 8 10 12 14 16 18 20 22 Segmentation fault tode@benfuter:~/esempi$ Esercitazioni di Fondamenti di Informatica – Politecnico di Milano sede di Cremona – A.A. 2010/2011 – Carlo Todeschini – [email protected] 3 Gestione dei file: ripasso/1... Per accedere a un file (sequenza lineare di byte) è necessario definire una variabile che punti al file: FILE *fp; Nota: si utilizza la libreria standard “stdio.h”. “fp” è definito un file pointer E' possibile gestire un file in tre fasi: apertura gestione/modifica chiusura Esercitazioni di Fondamenti di Informatica – Politecnico di Milano sede di Cremona – A.A. 2010/2011 – Carlo Todeschini – [email protected] 4 Gestione dei file: ripasso/1... Apertura di un flusso di comunicazione: FILE *fopen (nomefile, modalità) modalità: r, w, a, rb, wb, ab, r+, w+, a+, rb+, wb+, ab+ “r”: “w”: “r+”: “w+”: “a”: “a+”: sola lettura sola scrittura lettura e scrittura scrittura e lettura append lettura e append (errore se non esiste) (creazione se non esiste) (errore se non esiste) (creazione se non esiste) Chiusura di un flusso di comunicazione int fclose (FILE *fp) Esercitazioni di Fondamenti di Informatica – Politecnico di Milano sede di Cremona – A.A. 2010/2011 – Carlo Todeschini – [email protected] 5 Gestione dei file: ripasso/2... Verifica se nelle precedenti operazioni di lettura/scrittura è stato riscontrato un errore: int ferror (FILE *fp) Nota: se restituisce 0 non ci sono stati errori Controlla se è stata raggiunta la fine del file: int feof (FILE *fp) Nota: se restituisce 0 non è stata ancora raggiunta la fine Esercitazioni di Fondamenti di Informatica – Politecnico di Milano sede di Cremona – A.A. 2010/2011 – Carlo Todeschini – [email protected] 6 Gestione dei file: ripasso/3... Per eseguire una operazione di scrittura formattata: int fprintf (FILE *fp, stringa di controllo, elementi) Per eseguire una operazione di lettura formattata: int fscanf (FILE *fp, stringa di controllo, indirizzo elementi) Esercitazioni di Fondamenti di Informatica – Politecnico di Milano sede di Cremona – A.A. 2010/2011 – Carlo Todeschini – [email protected] 7 Gestione dei file: ripasso/4... Per eseguire una operazione di lettura di un carattere: int fgetc (FILE *fp) Per eseguire una operazione di scrittura di un carattere: int fputc (int c, FILE *fp) Per eseguire una operazione di lettura di una stringa: char *fgets (char *s, int n, FILE *fp) Per eseguire una operazione di scrittura di una stringa: int fputs (char *s, FILE *fp) Esercitazioni di Fondamenti di Informatica – Politecnico di Milano sede di Cremona – A.A. 2010/2011 – Carlo Todeschini – [email protected] 8 Gestione dei file: ripasso/5... Per eseguire una operazione di lettura di uno o più blocchi di dati: int fread (void *ptr, dim_elemento, num_elementi, FILE *fp) Per eseguire una operazione di scrittura di uno o più blocchi di dati: int fwrite (void *ptr, dim_elemento, num_elementi, FILE *fp) Esercitazioni di Fondamenti di Informatica – Politecnico di Milano sede di Cremona – A.A. 2010/2011 – Carlo Todeschini – [email protected] 9 Gestione dei file: ripasso/6... Per eseguire un accesso diretto a un file che sposta l'indicatore di posizione di un numero “offset” di byte, in base alla direzione stabilita da “ref_point”: int fseek (FILE *fp, long offset, int ref_point) Per recuperare il valore corrente dell'indicatore di posizione del file: int ftell (FILE *fp) Per riportare all'inizio del file l'indicatore di posizione: rewind (FILE *fp) Esercitazioni di Fondamenti di Informatica – Politecnico di Milano sede di Cremona – A.A. 2010/2011 – Carlo Todeschini – [email protected] 10 Attenzione: formato dei file... Tipi di file: ➢ Testo ... ➢ Binari ... Esercitazioni di Fondamenti di Informatica – Politecnico di Milano sede di Cremona – A.A. 2010/2011 – Carlo Todeschini – [email protected] 11 C/1: Merge di file – Problema Si vuole realizzare un programma in linguaggio C in grado di: ➢ ➢ leggere due file che contengono rispettivamente il titolo e gli attori principali (anche più di uno) di una serie di film creare un file unico con, per ogni film, titolo e attori principali. Formato file “titoli”: Formato file “attori”: Il Signore degli Anelli Guerre stellari Frankenstein Jr. Viggo Mortensen Ian McKellen Elijah Wood # Mark Hamill Harrison Ford # Marty Feldman Gene Wilder # Esercitazioni di Fondamenti di Informatica – Politecnico di Milano sede di Cremona – A.A. 2010/2011 – Carlo Todeschini – [email protected] 12 C/1: Merge di file – Codice sorgente/1 /* Questo file contiene la definizione di una funzione, 'mergeBiblioData', che prende in input il nome di due file che contengono rispettivamente il titolo e gli attori principali (anche più di uno) di una serie di film, e creano un file unico con, per ogni film, titolo e attori principali. Nel file con i titoli, ad ogni riga corrisponde un film diverso (non ci devono essere righe vuote alla fine). Nel file con gli attori, le sequenze di attori sono separate da una riga che contiene il solo carattere '#' (non sono ammesse righe vuote alla fine). Esercitazioni di Fondamenti di Informatica – Politecnico di Milano sede di Cremona – A.A. 2010/2011 – Carlo Todeschini – [email protected] 13 C/1: Merge di file – Codice sorgente/2 Per esempio un file di attori può essere: Viggo Mortensen Ian McKellen Elijah Wood # Mark Hamill Harrison Ford # Marty Feldman Gene Wilder # */ La funzione 'mergeBiblioData' ritorna un valore intero, che può assumere diversi valori a seconda del risultato della funzione: - 0 la funzione ha avuto successo - 1 non è stato possibile aprire il file di input con i titoli dei film - 2 non è stato possibile aprire il file di input con gli attori - 3 non è stato possibile aprire il file di destinazione - 4 il formato dei file di input non è corretto (per esempio se i due file contengono dati di un numero diverso di film) Esercitazioni di Fondamenti di Informatica – Politecnico di Milano sede di Cremona – A.A. 2010/2011 – Carlo Todeschini – [email protected] 14 C/1: Merge di file – Codice sorgente/3 #include <stdio.h> #include <string.h> #include "userTypes.h" /* Si definisce il significato del codice ritornato dalla funzione 'mergeBiblioData' */ #define SUCCESSO 0 #define FILE_TITOLI_NON_APERTO 1 #define FILE_ATTORI_NON_APERTO 2 #define OUTPUT_NON_APERTO 3 #define FORMATO_FILE_INPUT_ERRATO 4 /* Al massimo i file di input possono avere linee lunghe MAX_LUNGH_LINEA elementi */ #define MAX_LUNGH_LINEA 80 Esercitazioni di Fondamenti di Informatica – Politecnico di Milano sede di Cremona – A.A. 2010/2011 – Carlo Todeschini – [email protected] 15 C/1: Merge di file – Codice sorgente/4 /* Il tipo corrispondente di stringa ha 2 caratteri in più: uno per il carattere '\000' di fine stringa, e uno per il carattere di 'a capo' ('\n') */ typedef char riga_input [MAX_LUNGH_LINEA+2]; int mergeBiblioData (char *nome_file_titoli, char *nome_file_attori, char *nome_file_output) { FILE *titoli, *attori, *output; riga_input titolo, attore; bool prossimo_titolo; int lstr; Esercitazioni di Fondamenti di Informatica – Politecnico di Milano sede di Cremona – A.A. 2010/2011 – Carlo Todeschini – [email protected] 16 C/1: Merge di file – Codice sorgente/5 /* Si aprono i file di input in modalità "sola lettura, file di testo"; se l'operazione di apertura del file non ha successo si ritorna un errore */ titoli = fopen (nome_file_titoli, "r"); if (titoli == NULL) return FILE_TITOLI_NON_APERTO; /* Nel caso si ritorni un errore, ci si deve prima ricordare di chiudere il file precedentemente aperto (stessa cosa quando si cerca di aprire il file di output) */ attori = fopen (nome_file_attori, "r"); if (attori == NULL) { fclose (titoli); return FILE_ATTORI_NON_APERTO; } Esercitazioni di Fondamenti di Informatica – Politecnico di Milano sede di Cremona – A.A. 2010/2011 – Carlo Todeschini – [email protected] 17 C/1: Merge di file – Codice sorgente/6 /* Si apre il file di output in modalità "scrittura, file di testo"; se l'operazione di apertura del file non ha successo si ritorna un errore */ output = fopen (nome_file_output, "w"); if (output == NULL) { fclose (attori); fclose (titoli); return OUTPUT_NON_APERTO; } Esercitazioni di Fondamenti di Informatica – Politecnico di Milano sede di Cremona – A.A. 2010/2011 – Carlo Todeschini – [email protected] 18 C/1: Merge di file – Codice sorgente/7 while (!feof(titoli)) { /* Se non si è ancora arrivati in fondo al file dei titoli, ma non ci sono più attori, si segnala un errore, perchè ci sono più titoli che gruppi di attori */ if (feof (attori)) { fclose (titoli); fclose (attori); fclose (output); return FORMATO_FILE_INPUT_ERRATO; } Esercitazioni di Fondamenti di Informatica – Politecnico di Milano sede di Cremona – A.A. 2010/2011 – Carlo Todeschini – [email protected] 19 C/1: Merge di file – Codice sorgente/8 fgets (titolo, MAX_LUNGH_LINEA+1, titoli); /* Si controlla l'ultimo carattere inserito nella stringa: se non è 'newline', si inserisce il carattere di '\n' in fondo alla stringa */ lstr = strlen (titolo); if (titolo[lstr-1] != '\n') { titolo[lstr] = '\n'; titolo[lstr+1] = '\000'; } /* Si scrive il titolo ('newline' compreso!) nel file di output */ fputs (titolo, output); Esercitazioni di Fondamenti di Informatica – Politecnico di Milano sede di Cremona – A.A. 2010/2011 – Carlo Todeschini – [email protected] 20 C/1: Merge di file – Codice sorgente/9 /* Si recuperano i nomi degli attori; se non ci sono più attori nel file vuol dire che ci sono più titoli di gruppi di attori, e si segnala un errore */ prossimo_titolo = false; while (!feof(attori) && !prossimo_titolo) { fgets (attore, MAX_LUNGH_LINEA+1, attori); /* Se il primo carattere della riga è '#' si passa al prossimo titolo, cioè sono finiti gli attori per il film corrente */ if (attore[0] == '#') { prossimo_titolo = true; } Esercitazioni di Fondamenti di Informatica – Politecnico di Milano sede di Cremona – A.A. 2010/2011 – Carlo Todeschini – [email protected] 21 C/1: Merge di file – Codice sorgente/10 else { /* Se serve, si aggiunge un '\n' in fondo alla riga letta */ lstr = strlen (attore); if (attore[lstr-1] != '\n') { attore[lstr] = '\n'; attore[lstr+1] = '\000'; } } } fputs (attore, output); } fprintf (output, "#\n"); fclose (titoli); fclose (output); Esercitazioni di Fondamenti di Informatica – Politecnico di Milano sede di Cremona – A.A. 2010/2011 – Carlo Todeschini – [email protected] 22 C/1: Merge di file – Codice sorgente/11 /* Se si è giunti in fondo al file dei titoli, ma ci sono ancora attori, si segnala un errore, perchè ci sono più gruppi di attori che titoli di film */ if (!feof(attori)) { fclose (attori); return FORMATO_FILE_INPUT_ERRATO; } } fclose (attori); return SUCCESSO; Esercitazioni di Fondamenti di Informatica – Politecnico di Milano sede di Cremona – A.A. 2010/2011 – Carlo Todeschini – [email protected] 23 C/1: Merge di file – Codice sorgente/12 main () { int ris; ris = mergeBiblioData ("es_02_mergeFile_titoliFilm", "es_02_mergeFile_attoriFilm", "es_02_mergeFile_datiFilm"); Esercitazioni di Fondamenti di Informatica – Politecnico di Milano sede di Cremona – A.A. 2010/2011 – Carlo Todeschini – [email protected] 24 C/1: Merge di file – Codice sorgente/13 } switch (ris) { case SUCCESSO: printf ("Operazione avvenuta con successo\n"); break; case FILE_TITOLI_NON_APERTO: printf ("Errore nell'apertura del file con i titoli\n"); break; case FILE_ATTORI_NON_APERTO: printf ("Errore nell'apertura del file con gli attori\n"); break; case OUTPUT_NON_APERTO: printf ("Errore nell'apertura del file di output\n"); break; case FORMATO_FILE_INPUT_ERRATO: printf ("I file di input hanno un formato errato\n"); break; default: printf ("Codice ritornato non riconosciuto!\n"); } Esercitazioni di Fondamenti di Informatica – Politecnico di Milano sede di Cremona – A.A. 2010/2011 – Carlo Todeschini – [email protected] 25 C/2: Controllo parentesi – Problema Si vuole realizzare un programma in linguaggio C in grado di leggere un file di testo in codice C, e di controllare che per ogni parentesi graffa e tonda aperta ce ne sia una chiusa corrispondente (vengono verificate anche le parentesi all'interno dei commenti). Una parentesi graffa non può apparire mentre ci sono parentesi tonde aperte. Esercizio da svolgere in autonomia: modificare il programma in modo tale che le parentesi all'interno dei commenti vengano saltate (per questo occorre riconoscere le sequenze di caratteri che marcano l'inizio e la fine di un commento...) Esercitazioni di Fondamenti di Informatica – Politecnico di Milano sede di Cremona – A.A. 2010/2011 – Carlo Todeschini – [email protected] 26 C/2: Controllo parentesi – Codice sorgente/1 /* Questo file contiene la definizione di una funzione, 'controllaParentesi', che prende in input il nome di un file che contiene codice C, e controlla che per ogni parentesi graffa e tonda aperta ce ne sia una chiusa corrispondente (vengono verificate anche le parentesi all'interno dei commenti). Una parentesi graffa non può apparire mentre ci sono parentesi tonde aperte. La funzione ritorna un intero, che può assumere i seguenti significati: - 0 tutto OK - 1 sono presenti delle parentesi graffe aperte - 2 sono presenti delle parentesi tonde aperte - 3 sono presenti delle parentesi graffe chiuse e mai aperte - 4 sono presenti delle parentesi tonde chiuse e mai aperte - 5 sono presenti graffe aperte o chiuse ma ci sono ancora delle tonde da chiudere - 6 impossibile aprire il file */ Esercitazioni di Fondamenti di Informatica – Politecnico di Milano sede di Cremona – A.A. 2010/2011 – Carlo Todeschini – [email protected] 27 C/2: Controllo parentesi – Codice sorgente/2 #include <stdio.h> #define #define #define #define #define #define #define OK 0 GRAFFE_APERTE 1 TONDE_APERTE 2 GRAFFE_NON_APERTE 3 TONDE_NON_APERTE 4 GRAFFE_CON_TONDE 5 FILE_NON_APERTO 6 Esercitazioni di Fondamenti di Informatica – Politecnico di Milano sede di Cremona – A.A. 2010/2011 – Carlo Todeschini – [email protected] 28 C/2: Controllo parentesi – Codice sorgente/3 int controllaParentesi (char *nome_file_C) { FILE *file; char curr; int graffe_aperte = 0, tonde_aperte = 0, ris = OK; /* Si apre il file di input in modalità sola lettura, file di testo; se l'operazione di apertura del file non ha successo si ritorna un errore. */ file = fopen (nome_file_C, "r"); if (file == NULL) return FILE_NON_APERTO; Esercitazioni di Fondamenti di Informatica – Politecnico di Milano sede di Cremona – A.A. 2010/2011 – Carlo Todeschini – [email protected] 29 C/2: Controllo parentesi – Codice sorgente/4 while (ris == OK && !feof(file)) { curr = fgetc (file); if (curr == '{') { if (tonde_aperte > 0) ris = GRAFFE_CON_TONDE; else graffe_aperte++; } else if (curr == '(') tonde_aperte++; else if (curr == '}') Esercitazioni di Fondamenti di Informatica – Politecnico di Milano sede di Cremona – A.A. 2010/2011 – Carlo Todeschini – [email protected] 30 C/2: Controllo parentesi – Codice sorgente/5 { } if (graffe_aperte > 0) { if (tonde_aperte > 0) { ris = GRAFFE_CON_TONDE; } else { graffe_aperte--; } } else ris = GRAFFE_NON_APERTE; Esercitazioni di Fondamenti di Informatica – Politecnico di Milano sede di Cremona – A.A. 2010/2011 – Carlo Todeschini – [email protected] 31 C/2: Controllo parentesi – Codice sorgente/6 } else if (curr == ')') { if (tonde_aperte > 0) tonde_aperte--; else ris = TONDE_NON_APERTE; } if (ris ris = else if ris = } == OK && graffe_aperte > 0) GRAFFE_APERTE; (ris == OK && tonde_aperte > 0) TONDE_APERTE; fclose (file); return ris; Esercitazioni di Fondamenti di Informatica – Politecnico di Milano sede di Cremona – A.A. 2010/2011 – Carlo Todeschini – [email protected] 32 C/2: Controllo parentesi – Codice sorgente/7 main () { int ris; ris = controllaParentesi ("es_03_controlloParentesiTest.c"); switch (ris) { case OK: printf ("Operazione avvenuta con successo\n"); break; case GRAFFE_APERTE: printf ("Ci sono delle parentesi graffe aperte\n"); break; case TONDE_APERTE: printf ("Ci sono delle parentesi tonde aperte\n"); break; Esercitazioni di Fondamenti di Informatica – Politecnico di Milano sede di Cremona – A.A. 2010/2011 – Carlo Todeschini – [email protected] 33 C/2: Controllo parentesi – Codice sorgente/8 } case GRAFFE_NON_APERTE: printf ("Ci sono delle parentesi graffe chiuse e mai aperte\n"); break; case TONDE_NON_APERTE: printf ("Ci sono delle parentesi tonde chiuse e mai aperte\n"); break; case GRAFFE_CON_TONDE: printf ("Ci sono delle parentesi graffe aperte o chiuse con parentesi tonde da chiudere\n"); break; case FILE_NON_APERTO: printf ("Non è stato possibile aprire il file\n"); break; default: printf ("Codice ritornato incomprensibile!\n"); } Esercitazioni di Fondamenti di Informatica – Politecnico di Milano sede di Cremona – A.A. 2010/2011 – Carlo Todeschini – [email protected] 34 Programmazione ricorsiva: ripasso/1... Dal libro di testo: “Si parte dal presupposto che per moltissimi problemi la soluzione per un caso generico può essere ricavata sulla base della soluzione di un altro caso, generalmente più semplice, dello stesso problema” Esercitazioni di Fondamenti di Informatica – Politecnico di Milano sede di Cremona – A.A. 2010/2011 – Carlo Todeschini – [email protected] 35 Programmazione ricorsiva: ripasso/2... Un sottoprogramma può dunque richiamare se stesso! String reversal Il classico esempio: la successione dei numeri di Fibonacci f0 = 0 f1 = 1 Per n>1, fn = fn-1 + fn-2 Esercitazioni di Fondamenti di Informatica – Politecnico di Milano sede di Cremona – A.A. 2010/2011 – Carlo Todeschini – [email protected] 36 Programmazione ricorsiva: ripasso/3... #include <stdio.h> int calcolaFibonacci (int numero); main () { int numeroDaCalcolare; printf ("Inserire numero: "); scanf ("%d", &numeroDaCalcolare); printf ("Numero di Fibonacci f(%d) = %d\n", numeroDaCalcolare, calcolaFibonacci (numeroDaCalcolare)); } int calcolaFibonacci (int numero) { if (numero == 0) return 0; else if (numero == 1) return 1; else return (calcolaFibonacci (numero-1) + calcolaFibonacci (numero-2)); } Esercitazioni di Fondamenti di Informatica – Politecnico di Milano sede di Cremona – A.A. 2010/2011 – Carlo Todeschini – [email protected] 37 C/4: Palindrome – Problema Si vuole realizzare un programma in linguaggio C in grado di verificare se una parola inserita dall'utente è o meno palindroma. “Il palindromo è una serie di simboli leggibile, senza cambiamento di significato, in ordine invertito.” Es: ama anna effe ereggere Esercitazioni di Fondamenti di Informatica – Politecnico di Milano sede di Cremona – A.A. 2010/2011 – Carlo Todeschini – [email protected] 38 C/4: Palindrome – Codice sorgente/1 /* Questo programma prende una stringa e determina se è palindroma, cioè se è uguale sia che la si legga a partire dall'inizio, sia che la si legga a partire dal fondo. Per esempio, la parola RADAR è palindroma. Per verificare se una stringa è palindroma si usa tipicamente un algoritmo ricorsivo, che funziona nel modo che segue: 1. se la stringa da verificare è di lunghezza 1 o 0, STOP: è palindroma 2. confrontare il primo carattere con l'ultimo nella stringa: se sono diversi STOP, la stringa NON E' palindroma 3. se il primo e l'ultimo carattere sono uguali, ripetere l'algoritmo sulla stringa ottenuta eliminando il primo e l'ultimo carattere */ Esercitazioni di Fondamenti di Informatica – Politecnico di Milano sede di Cremona – A.A. 2010/2011 – Carlo Todeschini – [email protected] 39 C/4: Palindrome – Codice sorgente/2 #include <stdio.h> #include <string.h> #include "userTypes.h" #define MAX_EL 100 Esercitazioni di Fondamenti di Informatica – Politecnico di Milano sede di Cremona – A.A. 2010/2011 – Carlo Todeschini – [email protected] 40 C/4: Palindrome – Codice sorgente/3 bool palindroma (char *s, int n_el) { /* Caso di base: se la stringa ha solo 1 elemento o se è la stringa vuota, è palindroma */ if (n_el < 2) { return true; } } /* Passo ricorsivo */ if (s[0] == s[n_el-1]) { return palindroma(s+1, n_el-2); } else { return false; } Esercitazioni di Fondamenti di Informatica – Politecnico di Milano sede di Cremona – A.A. 2010/2011 – Carlo Todeschini – [email protected] 41 C/4: Palindrome – Codice sorgente/4 main () { char parola[MAX_EL+1]; bool palindroma (char *s, int n_el); } printf ("Inserire la parola da controllare: "); scanf ("%s", parola); if (palindroma (parola, strlen(parola))) { printf ("La parola \"%s\" è palindroma\n", parola); } else { printf ("La parola \"%s\" NON è palindroma\n", parola); } Esercitazioni di Fondamenti di Informatica – Politecnico di Milano sede di Cremona – A.A. 2010/2011 – Carlo Todeschini – [email protected] 42 C/4: Palindrome – Compito Il programma realizzato è in grado di riconoscere solo parole palindrome come ad esempio “ama”, “anna”, “effe”, “ereggere”. Da svolgere autonomamente: Modificare tale programma in modo che sia in grado leggere frasi palindrome come ad esempio “Alle carte t'alleni nella tetra cella”, “E' corta e atroce” (nota: non devono essere considerati segni di punteggiatura, accenti e spazi; cercare in rete frasi palindrome da verificare...) Esercitazioni di Fondamenti di Informatica – Politecnico di Milano sede di Cremona – A.A. 2010/2011 – Carlo Todeschini – [email protected] 43 C/5: Binary search – Problema Si vuole realizzare un programma in linguaggio C che acquisisca una sequenza di interi, e li inserisca in modo ordinato (in ordine crescente) in un array di elementi. Viene data poi la possibilità all'utente di inserire un valore da cercare nell'array. La ricerca viene fatta con l'algoritmo di ricerca binaria (quello dell'elenco telefonico...). Esercitazioni di Fondamenti di Informatica – Politecnico di Milano sede di Cremona – A.A. 2010/2011 – Carlo Todeschini – [email protected] 44 C/5: Binary search – Codice sorgente/1 /* Questo programma prende una sequenza di interi, e li inserisce in modo ordinato (in ordine crescente) in un array di elementi. Viene chiesto all'utente di inserire un valore da cercare nell'array. La ricerca viene fatta con l'algoritmo di ricerca binaria (quello dell'elenco telefonico...). */ Gli scopi della funzione definita in questo programma sono di mostrare come in realtà gli array siano SEMPRE passati per indirizzo (funzione inser_ord), e fare un esempio di funzione ricorsiva (binary_search) #include <stdio.h> #include "userTypes.h" #define MAX_EL 100 Esercitazioni di Fondamenti di Informatica – Politecnico di Milano sede di Cremona – A.A. 2010/2011 – Carlo Todeschini – [email protected] 45 C/5: Binary search – Codice sorgente/2 /* la funzione prende un array di interi a (il quale ha le prime n_el caselle occupate) e inserisce in modo ordinato un nuovo intero v, se v ancora non esiste nell'array. Se l'operazione va a buon fine viene restituito l'indice in cui viene inserito il nuovo elemento e il numero di elementi nell'array viene incrementato di 1, altrimenti viene restituito -1 (una variante rispetto all'uso di un tipo bool apposito). Si tratta l'array con la sintassi degli array, quindi si riferiscono gli elementi con le parentesi quadre. */ Esercitazioni di Fondamenti di Informatica – Politecnico di Milano sede di Cremona – A.A. 2010/2011 – Carlo Todeschini – [email protected] 46 C/5: Binary search – Codice sorgente/3 int inser_ord (int v, int a[MAX_EL], int *n_el) { /* possibile intestazione anche per la funzione: int inser_ord (int v, int a[], int *n_el) int inser_ord (int v, int *a, int *n_el) */ int j, i=0; while (i<*n_el && a[i] < v) { i++; } /* Se il valore esiste già si ritorna -1 senza modificare l'array */ if (i<*n_el && a[i] == v) { return -1; } Esercitazioni di Fondamenti di Informatica – Politecnico di Milano sede di Cremona – A.A. 2010/2011 – Carlo Todeschini – [email protected] 47 C/5: Binary search – Codice sorgente/4 else { /* Prima di tutto si spostano tutti gli elementi nell'array in avanti di un posto */ for (j=*n_el; j>i ; j--) { a[j] = a[j-1]; } a[i] = v; *n_el = *n_el+1; /* Attenzione perchè se si scrive *n_el++ non è corretto perchè viene incrementato l'indirizzo contenuto in n_el, invece che il contenuto della cella in cui indirizzo è in n_el */ return i; } Esercitazioni di Fondamenti di Informatica – Politecnico di Milano sede di Cremona – A.A. 2010/2011 – Carlo Todeschini – [email protected] 48 C/5: Binary search – Codice sorgente/5 /* Notare come in realtà in questo programma non si faccia per niente uso della costante 'MAX_EL'. In effetti, per il C, scrivere che un parametro in ingresso è "int a[MAX_EL]", oppure è "int a[]", oppure ancora (ed è la scrittura più comune) è "int *a" non cambia niente, le tre scritture sono del tutto equivalenti. Non è quindi possibile verificare che l'array passato sia effettivamente della lunghezza MAX_EL (si potrebbe tranquillamente chiamare la funzione passando un array di MAX_EL*2 elementi, e il compilatore non segnalerebbe nessun errore), ne è possibile verificare che nell'array ci sia ancora spazio: è la funzione chiamante che deve preoccuparsi di passare un array che abbia ancora delle caselle libere per inserire l'elemento nuovo */ } Esercitazioni di Fondamenti di Informatica – Politecnico di Milano sede di Cremona – A.A. 2010/2011 – Carlo Todeschini – [email protected] 49 C/5: Binary search – Codice sorgente/6 /* In questa procedura, che stampa i primi n_el elementi di un array a di interi, si tratta l'array come un puntatore (perchè un array in realtà è un puntatore). Invece di fare riferimento agli elementi con la notazione con le parentesi quadre, si utilizza la notazione dei puntatori (con gli '*') */ void stampa_array (int *a, int n_el) { int i; for (i=0 ; i<n_el ; i++) { printf ("%d ", *(a+i)); } printf ("\n"); } Esercitazioni di Fondamenti di Informatica – Politecnico di Milano sede di Cremona – A.A. 2010/2011 – Carlo Todeschini – [email protected] 50 C/5: Binary search – Codice sorgente/7 /* Questa funzione prende in ingresso un array ordinato di interi (insieme al numero di caselle occupate dell'array), e un valore, e ritorna true o false a seconda che il valore passato sia presente nell'array o no. L'algoritmo che viene usato è tipicamente ricorsivo: 1. Passo di base: se l'insieme in cui cercare è vuoto STOP, l'elemento NON esiste; 2. Passo ricorsivo: si prende l'elemento di mezzo dell'insieme; se l'elemento è uguale a quello cercato STOP, l'elemento è stato trovato; se l'elemento di mezzo è MAGGIORE di quello cercato, applicare la ricerca binaria alla prima metà dell'insieme, altrimenti applicare la ricerca binaria alla seconda metà dell'insieme. */ Esercitazioni di Fondamenti di Informatica – Politecnico di Milano sede di Cremona – A.A. 2010/2011 – Carlo Todeschini – [email protected] 51 C/5: Binary search – Codice sorgente/8 bool binary_search (int v, int *a, int n_el) { /* Caso di base: se l'insieme è vuoto, l'elemento cercato non esiste */ if (n_el == 0) { return false; } /* Passo ricorsivo */ if (a[n_el/2] == v) { return true; } else if (a[n_el/2] > v) { return binary_search (v, a, n_el/2); } else { return binary_search (v, a+n_el/2+1, n_el/2-1+n_el%2); Esercitazioni di Fondamenti di Informatica – Politecnico di Milano sede di Cremona – A.A. 2010/2011 – Carlo Todeschini – [email protected] 52 C/5: Binary search – Codice sorgente/9 /* La parte più complessa è definire correttamente in C la forma delle due metà dell'insieme di partenza. Nel caso in cui si debba applicare la ricerca binaria alla SECONDA metà dell'insieme di partenza, si deve passare (ricorsivamente) alla funzione "binary_search" la seconda metà dell'array a. Per fare questo si passa l'indirizzo della casella dell'array successiva a quella che si trova a metà array (inizio della seconda metà dell'array a), e si specifica che la lunghezza di questa parte dell'array è n_el/2-1+n_el%2 Infatti, supponendo che n_el sia 8, e quindi che gli indici delle caselle nell'array a vadano da 0 a 7: 0 1 2 3 4 5 6 7 a[n_el/2] (ovvero l'elemento di indice 4, che è il quinto) è l'elemento di mezzo che viene confrontato con v. Se v è maggiore di a[n_el/2] (quindi si trova nella seconda metà), si effettua la ricerca binaria sull'insieme [5 6 7], che è in effetti la parte di array che inizia dall'indice n_el/2+1, ed è lunga n_el/2-1+n_el%2, cioe' 4-1+0 = 3 */ } } Esercitazioni di Fondamenti di Informatica – Politecnico di Milano sede di Cremona – A.A. 2010/2011 – Carlo Todeschini – [email protected] 53 C/5: Binary search – Codice sorgente/10 main() { int dati[MAX_EL], dato_corr; int num_el=0; int inser_ord (int v, int a[MAX_EL], int *n_el); bool binary_search (int v, int *a, int n_el); void stampa_array (int *a, int n_el); do { printf ("Inserire elemento (0 per terminare, max %d elementi): ", MAX_EL); scanf ("%d", &dato_corr); Esercitazioni di Fondamenti di Informatica – Politecnico di Milano sede di Cremona – A.A. 2010/2011 – Carlo Todeschini – [email protected] 54 C/5: Binary search – Codice sorgente/11 /* Qui si inserisce anche lo 0! */ inser_ord (dato_corr, dati, &num_el); /* num_el viene passato per indirizzo, viene incrementato solo se l'elemento non esiste già nell'array. */ Notare come sia possibile non assegnare il valore ritornato da una funzione, il quale in questo caso viene ignorato printf ("Nuova situazione dell'array:\n"); stampa_array (dati, num_el); } while (dato_corr != 0 && num_el<MAX_EL); Esercitazioni di Fondamenti di Informatica – Politecnico di Milano sede di Cremona – A.A. 2010/2011 – Carlo Todeschini – [email protected] 55 C/5: Binary search – Codice sorgente/12 do { printf ("\nInserire elemento da cercare (0 per terminare): "); scanf ("%d", &dato_corr); if (binary_search (dato_corr, dati, num_el) == true) { printf ("\nIl numero %d è presente nell'array\n", dato_corr); } else { printf ("\nIl numero %d NON è presente nell'array\n", dato_corr); } } while (dato_corr != 0); } Esercitazioni di Fondamenti di Informatica – Politecnico di Milano sede di Cremona – A.A. 2010/2011 – Carlo Todeschini – [email protected] 56 C/compito: La torre di Hanoi – Problema Si vuole realizzare un programma in linguaggio C in grado di giocare al gioco della “torre di Hanoi” (v. libro di testo). Da Wikipedia: “La Torre di Hanoi è un rompicapo matematico composto da tre paletti e un certo numero di dischi di grandezza decrescente, che possono essere infilati in uno qualsiasi dei paletti. Il gioco inizia con tutti i dischi incolonnati su un paletto in ordine decrescente, in modo da formare un cono. Lo scopo del gioco è portare tutti dischi sull'ultimo paletto, potendo spostare solo un disco alla volta e potendo mettere un disco solo su un altro disco più grande, mai su uno più piccolo.” Tower of Hanoi Esercitazioni di Fondamenti di Informatica – Politecnico di Milano sede di Cremona – A.A. 2010/2011 – Carlo Todeschini – [email protected] 57 C/compito: La torre di Hanoi – Buone vacanze Buon lavoro ;-) Esercitazioni di Fondamenti di Informatica – Politecnico di Milano sede di Cremona – A.A. 2010/2011 – Carlo Todeschini – [email protected] 58