I lucidi sono una rielaborazione e integrazione di quelli messi a disposizione dei docenti nel sito relativo al testo: Informatica: arte e mestiere 2/ed Stefano Ceri, Dino Mandrioli, Licia Sbattella Copyright 2004 - The McGraw-Hill Companies, srl Giacomo Piscitelli pag. 1/10
Programmazione ricorsiva In quasi tutti i linguaggi di programmazione moderni è ammessa la possibilità di definire funzioni/procedure ricorsive: durante l esecuzione di una funzione F è possibile chiamare la funzione F stessa. Ciò può avvenire:! direttamente: il corpo di F contiene una chiamata a F stessa.! indirettamente: F contiene una chiamata a G che a sua volta contiene una chiamata a F. Questo può sembrare strano: se pensiamo che una funzione è destinata a risolvere un sottoproblema P, una definizione ricorsiva sembra indicare che per risolvere P dobbiamo... saper risolvere P. In realtà, la programmazione ricorsiva si basa sull osservazione che per molti problemi la soluzione per un caso generico può essere ricavata sulla base della soluzione di un altro caso, generalmente più semplice, dello stesso problema. Giacomo Piscitelli pag. 2/10
Un esempio classico di algoritmo ricorsivo LA RICORSIONE IN C Individuare, in un gruppo di palline, l unica pallina di peso maggiore delle altre facendo uso di una bilancia a basculla (Per semplicità: il numero di palline sia una potenza di 3) Algoritmo Pesate: Se il gruppo di palline consiste in una sola pallina, allora essa è banalmente la pallina cercata, altrimenti procedi come segue:! Dividi il gruppo di palline in tre e confronta due dei tre sottogruppi.! Se i due gruppi risultano di peso uguale scarta entrambi, altrimenti scarta il gruppo non pesato e quello risultato di peso minore.! Applica l algoritmo Pesate al gruppo rimanente. Giacomo Piscitelli pag. 3/10
Principio di induzione ben fondata LA RICORSIONE IN C La programmazione di algoritmi di natura ricorsiva trova radici teoriche nel principio di induzione ben fondata, che può essere visto come una generalizzazione del principio di induzione sui naturali. Sia A(n) una proprietà vera sull insieme N dei numeri naturali e supponiamo che: 1. A(0) è vera 2. Per ogni k N, se è vera A(k) allora è vera A(k+1) Allora A(n) è vera per ogni n N. La soluzione di un problema viene individuata supponendo di saperlo risolvere su casi più semplici. Bisogna poi essere in grado di risolvere direttamente il problema sui casi più semplici di qualunque altro. Funzioni ricorsive sono convenienti per implementare funzioni matematiche definite in modo induttivo. Giacomo Piscitelli pag. 4/10
Esempio: Funzione fattoriale. definizione iterativa: fatt(n) = n (n - 1) (n - 2).. 2 1 " fatt(n) = 1; se n = 0 (caso base) definizione induttiva: " fatt(n) = n fatt(n-1); se n > 0 (caso induttivo) È essenziale il fatto che, applicando ripetutamente il caso induttivo, ci riconduciamo prima o poi al caso base. fatt(3)= 3 fatt(2) = 3 (2 fatt(1)) = 3 (2 (1 fatt(0))) = 3 (2 (1 1)) = 6 Giacomo Piscitelli pag. 5/10
Esempio: Funzione fattoriale. Il codice delle due diverse versioni definizione iterativa: int fatt(int n) int i,ris; ris=1; for (i=1;i<=n;i++) ris=ris*i; return ris; definizione ricorsiva: int fattric(int n) if (n == 0) return 1; else return n * fattric(n-1); Programma che usa una funzione ricorsiva. #include <stdio.h> int fattric (int); main() int x, f; scanf("%d", &x); f = fattric(x); printf("fattoriale di %d: %d\n", x, f); int fattric(int n) int ris; if (n == 0) ris = 1; else ris = n * fattric(n-1); return ris; Giacomo Piscitelli pag. 6/10
Programmazione ricorsiva Evoluzione della pila (supponendo x=3). LA RICORSIONE IN C Giacomo Piscitelli pag. 2/10
La standard library del C Sottoprogrammi di largo uso predefiniti: # Matematica # I/O # Grafica Librerie di sottoprogrammi:! Predefinite! Costruite dai programmatori applicativi Però l uso di librerie diminuisce la portabilità, a meno che anche le librerie (almeno le fondamentali) non siano standardizzate La grande forza del C: la libreria standard Giacomo Piscitelli pag. 3/10
/* Programma Concatenazione di stringhe */ #include <stdio.h> #include <string.h> #define LunghezzaArray 50 main() char PrimaStringa[LunghezzaArray], SecondaStringa[LunghezzaArray], StringaConc[2 * LunghezzaArray]; unsigned LunghezzaConc; scanf( %s, PrimaStringa); scanf( %s, SecondaStringa); if (strcmp(primastringa, SecondaStringa) <= 0) strcpy(stringaconc, PrimaStringa); strcat(stringaconc, SecondaStringa); else strcpy(stringaconc, SecondaStringa); strcat(stringaconc, PrimaStringa); LunghezzaConc = strlen(stringaconc); printf( La stringa ottenuta concatenando le due stringhe lette è %s. Essa è lunga %d caratteri\n, StringaConc, LunghezzaConc); Giacomo Piscitelli pag. 4/10
I file header Le funzioni della libreria sono disponibili in C come file di codice compilato. È compito del programmatore inserire nel programma i prototipi delle funzioni che verranno usate La libreria C comprende alcuni file, chiamati header file, che contengono i prototipi di un insieme di funzioni di libreria. #include <stdio.h> e altre #include <xxx.h> Il preprocessore copia il contenuto del file stdio.h nel programma, inserendo i prototipi delle funzioni che appartengono al gruppo di cui xxx.h è il file testata. Giacomo Piscitelli pag. 5/10