Il linguaggio C Il modello a run-time

Documenti analoghi
Fondamenti di Informatica T. Linguaggio C: Stack e Ricorsione

Il linguaggio C Il modello a run-time

Fondamenti di Informatica T. Linguaggio C: Stack e Ricorsione

LA RICORSIONE LA RICORSIONE

Esempio: il fattoriale di un numero fact(n) = n! n!: Z! N n! vale 1 se n " 0 n! vale n*(n-1)! se n > 0. Codifica:

Operativamente, risolvere un problema con un approccio ricorsivo comporta

LA RICORSIONE. Una funzione matematica è definita ricorsivamente quando nella sua definizione compare un riferimento a se stessa

LA RICORSIONE! Una funzione matematica è definita ricorsivamente quando nella sua definizione compare un riferimento a se stessa

LA RICORSIONE LA RICORSIONE LA RICORSIONE: ESEMPIO LA RICORSIONE: ESEMPIO LA RICORSIONE: ESEMPIO LA RICORSIONE: ESEMPIO

Una funzione matematica è definita ricorsivamente quando nella sua definizione compare un riferimento a se stessa

Operativamente, risolvere un problema con un approccio ricorsivo comporta

FUNZIONI: IL MODELLO A RUN-TIME!

FUNZIONI: IL MODELLO A RUN-TIME

FUNZIONI: IL MODELLO A RUN-TIME

Record di Attivazione Cenni sulla ricorsione

Record di Attivazione

FUNZIONI. attribuire un nome ad un insieme di istruzioni parametrizzare l esecuzione del codice

Capitolo 19. Ricorsione

LABORATORIO DI PROGRAMMAZIONE 2 Corso di laurea in matematica. Algoritmi ricorsivi

1) Somma dei primi n numeri naturali: 3) Ricerca di un elemento el in una sequenza di interi: FONDAMENTI DI INFORMATICA II Ricorsione 2

FUNZIONI RICORSIVE PROGRAMMAZIONE RICORSIVA: Esempi di problemi ricorsivi:

INFORMATICA A. Titolo presentazione sottotitolo. Laboratorio n 6 Dott. Michele Zanella Ing. Gian Enrico Conti

Diverse politiche. La ricorsione PROGRAMMAZIONE TOP-DOWN ESEMPIO. Bottom-Up. Di solito è meglio alternare le due tecniche

Parametri Formali di una Funzione e Record di Attivazione

20. Gestione della memoria. Andrea Marongiu Paolo Valente

Ricorsione in C. slides credit Prof. Paolo Romano

Assembly V: Procedure annidate e recursive

Ricorsione. Prof. Orazio Mirabella. La ricorsione. La ricorsione consiste nella possibilità di definire una funzione mediante se stessa.

ESERCIZIO: Analisi di un programma. Dato il seguente programma C:

Ricorsione. DD cap. 5 pp KP cap. 5 pp

passaggio di vettori come parametri di funzioni/procedure. I Quando si passa un vettore come parametro ad una funzione, in

Il linguaggio C Funzioni e procedure

Laboratorio di Informatica L-A 1

INFORMATICA A. Titolo presentazione sottotitolo. Laboratorio n 5 Ing. Gian Enrico Conti Dott. Michele Zanella

Ricorsione. La ricorsione consiste nella possibilità di definire una funzione in termini di se stessa

n n 1 n = > Il calcolo del fattoriale La funzione fattoriale, molto usata nel calcolo combinatorio, è così definita

RICORSIONE. Informatica B - A.A. 2013/2014

3 aprile o Esonero: mercoledi 17 aprile ore 11:30 14:00 consulta la pag. WEB alla voce esoneri. si raccomanda la puntualita!

Corso di Fondamenti di Programmazione canale E-O. Un esempio. Funzioni ricorsive. La ricorsione

Il principio di induzione. La Ricorsione. Il fattoriale: iterativo. Il fattoriale: ricorsivo. P (0) P (n) P (n + 1) per ogni n

IEIM Esercitazione IX Puntatori, Enumerazione e Ricorsione. Alessandro A. Nacci -

Fondamenti di Programmazione

La Ricorsione. Carla Binucci e Walter Didimo

FUNZIONI FUNZIONI COME COMPONENTI SW

FUNZIONI FUNZIONI COME COMPONENTI SW

Corso di Fondamenti di Informatica. La ricorsione

Introduzione alla Ricorsione

La ricorsione. Ver Claudio Fornaro - Corso di programmazione in C

Perché il linguaggio C?

FUNZIONI COME COMPONENTI SW FUNZIONI COME COMPONENTI SW FUNZIONI MODELLO CLIENTE/SERVITORE

Programmazione ricorsiva.

Programmazione a Oggetti e JAVA. Prof. B.Buttarazzi A.A. 2012/2013

FUNZIONI. Esempi (pseudo-c): dare un nome a una espressione rendere tale espressione parametrica. float f(){ * sin(0.75); } float f1(int x) {

Scope delle variabili e passaggio parametri. Danilo Ardagna Politecnico di Milano

Linguaggio C: le funzioni. Visibilità variabili e passaggio parametri

Funzioni, Stack e Visibilità delle Variabili in C

Le procedure ricorsive Come eseguire un programma. Architettura degli Elaboratori e delle Reti, Turno I

Una funzione riceve dati di ingresso in corrispondenza ai parametri

Esercizio 2 (punti 7) Dato il seguente programma C: #include <stdio.h> int swap(int * nome, int length);

Assembly MIPS: le procedure

FUNZIONI COME COMPONENTI SW FUNZIONI COME COMPONENTI SW FUNZIONI MODELLO CLIENTE/SERVITORE

Corso di Informatica Modulo T3 2 Ambiente locale e globale

FUNZIONI. Esempi (pseudo-c): dare un nome a una espressione rendere tale espressione parametrica. float f(){ * sin(0.75); } float f1(int x) {

Procedure: esempi. Esempi: ricorsione fattoriale Fibonacci ordinamenti

Programmi in Assembly

Laboratorio di Informatica I

Programmazione ricorsiva: cenni

Esempi (pseudo-c):! float f(){ * sin(0.75); } float f1(int x) { 2 + x * sin(0.75); } Esempio

Sommario. Tipo stringa: string. Tipo stringa: stile C. Tipo stringa Tipo booleano

Informatica per Statistica Riassunto della lezioni del 14/11/2012 e 16/11/2012

Programmazione ricorsiva

ELEMENTI DI INFORMATICA L-B. Ing. Claudia Chiusoli

INFORMATICA. Strutture iterative

Parte 2. Ricorsione. [M.C.Escher Drawing hands, 1948] - AA. 2012/13 2.1

Gestione della memoria

Passaggio dei parametri

Le procedure. L insieme delle istruzioni (4) Prima della chiamata di una procedura. Le procedure (2) Architetture dei Calcolatori (lettere A-I)

Ricorsione. Stefano Ferrari. Università degli Studi di Milano Programmazione. anno accademico

Funzioni in C. Informatica Generale - Funzioni in C Versione 1.0, aa p.1/25

La gestione della memoria. Gestione della memoria. Uso della Memoria RAM

Ricorsione. quindi si può definire n! mediante i seguenti due punti:

Funzioni e Ricorsione

Funzioni, Stack e Visibilità delle Variabili in C

Allocazione dinamica della memoria

Istruzioni di Ciclo. Unità 4. Domenico Daniele Bloisi

int main(){ int numero; /* numero di cui voglio calcolare il fattoriale */ int fatt; /* memorizzo il fattoriale di numero */ int somma=0;

Lo sviluppo di un semplice programma e la dimostrazione della sua correttezza

Spazio di indirizzamento virtuale

definire definire una una funzione in termini di se stessa compare una chiamata alla funzione stessa identificare un caso base

definire definire una una funzione in termini di se stessa compare una chiamata alla funzione stessa identificare un caso base

Corso di Informatica

RICORSIONE RICORSIONE

LA RICORSIONE IN C. CdL Ingegneria Informatica n.o. Anno Accademico 2006/07 Fondamenti di Informatica I corso A Giacomo Piscitelli pag.

Esercitazione 6. Array

Esercitazione 11. Liste semplici

Transcript:

3 Il linguaggio C Il modello a run-time Puntatore al codice Return address Link dinamico Parametro Parametro 2... Variabile locale Variabile locale 2... B A A 3 FUNZIONI: IL MODELLO A RUN-TIME Ogni volta che viene invocata una funzione: si crea di una nuova attivazione (istanza) del servitore viene allocata la memoria per i parametri e per le variabili locali si effettua il passaggio dei parametri si trasferisce il controllo al servitore si esegue il codice della funzione

3 Record di Attivazione Al momento dell invocazione: viene creata dinamicamente una struttura dati che contiene i binding dei parametri e degli identificatori definiti localmente alla funzione detta RECORD DI ATTIVAZIONE. È il mondo della funzione : contiene tutto ciò che serve per la chiamata alla quale è associato: i parametri formali le variabili locali Record di Attivazione l indirizzo di ritorno (Return address ) che indica il punto a cui tornare (nel codice del cliente) al termine della funzione, per permettere al cliente di proseguire una volta che la funzione termina. un collegamento al record di attivazione del cliente (Link Dinamico DL), per sapere dove finisce il record di attivazione corrente (utile per la gestione della memoria) l'indirizzo del codice della funzione (puntatore alla prima istruzione) 3 2

Record di Attivazione 3 Puntatore al codice Return address Link dinamico Parametro Parametro 2... Variabile locale Variabile locale 2... Dimensione del record (non fissa) Record di Attivazione Rappresenta il mondo della funzione : nasce e muore con essa è creato al momento della invocazione di una funzione permane per tutto il tempo in cui la funzione è in esecuzione è distrutto (deallocato) al termine dell esecuzione della funzione stessa. Ad ogni chiamata di funzione viene creato un nuovo record, specifico per quella chiamata di quella funzione La dimensione del record di attivazione varia da una funzione all altra per una data funzione, è fissa e calcolabile a priori 3 3

Record di Attivazione 3 Funzioni che chiamano altre funzioni danno luogo a una sequenza di record di attivazione allocati secondo l ordine delle chiamate deallocati in ordine inverso La sequenza dei link dinamici costituisce la cosiddetta catena dinamica, che rappresenta la storia delle attivazioni ( chi ha chiamato chi ) L area di memoria in cui vengono allocati i record di attivazione viene gestita come una pila : STACK Stack È una struttura dati gestita a tempo di esecuzione con politica LIFO (Last In, First Out - l ultimo a entrare è il primo a uscire) nella quale ogni elemento è un record di attivazione. La gestione dello stack avviene mediante due operazioni: Attivaz.3 Attivaz.2 Attivaz. push: aggiunta di un elemento (in cima alla pila) pop: prelievo di un elemento (dalla cima della pila) 3 4

Stack L'ordine di collocazione dei record di attivazione nello stack indica la cronologia delle chiamate: 3 Pop Push att 2 att Record di Attivazione Normalmente lo STACK dei record di attivazione si disegna nel modo seguente: 3 sequenza attivazioni Quindi, se la funzione A chiama la funzione B, lo stack evolve nel modo seguente A chiama B e B poi B finisce passa il e il controllo A controllo a B A torna ad A A 5

3 Record di Attivazione Il valore di ritorno calcolato dalla funzione può essere restituito al cliente in due modi: inserendo un apposito slot nel record di attivazione il cliente deve copiarsi il risultato da qualche parte prima che il record venga distrutto tramite un registro della CPU soluzione più semplice ed efficiente, privilegiata ovunque possibile. Esempio: chiamate annidate Programma: int R(int A) { return A+; int Q(int x) { return R(x); int P(void) { int a=0; return Q(a); () { int x = P(); Sequenza chiamate: S.O. P() Q() R() 6

Esempio: chiamate annidate Sequenza chiamate: S.O. P() Q() R() R sequenza attivazioni Q P Esempio: parametri di tipo puntatore void scambia(int* a, int* b) { int t; t = *a; *a = *b; *b = t; (){ int y = 5, x = 33; scambia(&x, &y); 7

Esempio: parametri di tipo puntatore Caso del passaggio per riferimento: Indirizzo di x e y Modificati dalla procedura DL a α b β t 33 x 33 5 y 5 33 α β (parte del) record di attivazione della funzione scambia (parte del) record di attivazione della funzione Spazio di indirizzamento La memoria allocata a ogni programma in esecuzione è suddivisa in varie parti (segmenti), secondo lo schema seguente: 3 code segment data segment code segment: contiene il codice eseguibile del programma data segment: contiene le variabili globali e static heap: contiene le variabili dinamiche stack: è l'area dove vengono allocati i record di attivazione heap stack Code segment e data segment sono di dimensione fissata staticamente (a tempo di compilazione). La dimensione dell'area associata a stack + heap è fissata staticamente: man mano che lo stack cresce, diminuisce l'area a disposizione dell'heap, e viceversa. 8

3 Il linguaggio C La ricorsione fact() fact(0) fact() fact(0) 3*2=6 2*=2 *= La ricorsione Una funzione matematica è definita ricorsivamente quando nella sua definizione compare un riferimento a se stessa La ricorsione consiste nella possibilità di definire una funzione mediante se stessa. È basata sul principio di induzione matematica: se una proprietà P vale per n=n 0 CASO BASE e si può provare che, assumendola valida per n, allora vale per n+ allora P vale per ogni n n 0 9

La ricorsione Operativamente, risolvere un problema con un approccio ricorsivo comporta di identifie un caso base (n=n0) in cui la soluzione sia nota di riuscire a esprimere la soluzione al caso generico n in termini dello stesso problema in uno o più casi più semplici (n-, n-2, etc). La ricorsione: esempio Esempio: il fattoriale di un numero naturale fact(n) = n! n!: N N n! vale se n == 0 n! vale n*(n-)! se n > 0 0

Ricorsione in C In C è possibile definire funzioni ricorsive: Il corpo di ogni funzione ricorsiva contiene almeno una chiamata alla funzione stessa. 3 Esempio: definizione in C della funzione ricorsiva fattoriale. int fact(int n) { if (n==0) return ; else return n*fact(n-); Ricorsione in C: esempio Servitore & Cliente: fact è sia servitore che cliente (di se stessa): int fact(int n) { if (n==0) return ; else return n*fact(n-); () { int fz, z = 5; fz = fact(z-2);

Ricorsione in C: esempio Servitore & Cliente: int fact(int n) { if (n==0) return ; else return n*fact(n-); (){ int fz, z = 5; fz = fact(z-2); Si valuta l espressione che costituisce il parametro attuale (nell environment del ) e si trasmette alla funzione fatt una copia del valore così ottenuto (3). Ricorsione in C: esempio Servitore & Cliente: int fact(int n) { if (n==0) return ; else return n*fact(n-); (){ int fz, z = 5; fz = fact(z-2); La funzione fact lega il parametro n a 3. Essendo 3 positivo si passa al ramo else. Per calcolare il risultato della funzione e necessario effettuare una nuova chiamata di funzione (fact(n-)). 2

Ricorsione in C: esempio Servitore & Cliente: int fact(int n) { if (n==0) return ; else return n*fact(n-); (){ int fz, z = 5; fz = fact(z-2); La funzione fact lega il parametro n a 3. Essendo 3 positivo si passa al ramo else. Per calcolare il risultato della funzione e necessario effettuare una nuova chiamata di funzione (fact(n-)). n- nell environment di fact vale 2 quindi viene chiamata Ricorsione in C: esempio Servitore & Cliente: int fact(int n) { if (n==0) return ; else return n*fact(n-); (){ int fz, z = 5; fz = fact(z-2); Il nuovo servitore lega il parametro n a 2. Essendo 2 positivo si passa al ramo else. Per calcolare il risultato della funzione e necessario effettuare una nuova chiamata di funzione. n- nell environment di fact vale quindi viene chiamata fact() 3

Ricorsione in C: esempio Servitore & Cliente: int fact(int n) { if (n==0) return ; else return n*fact(n-); (){ int fz, z = 5; fz = fact(z-2); Il nuovo servitore lega il parametro n a. Essendo positivo si passa al ramo else. Per calcolare il risultato della funzione e necessario effettuare una nuova chiamata di funzione. n- nell environment di fact vale 0 quindi viene chiamata fact(0) Ricorsione in C: esempio Servitore & Cliente: int fact(int n) { if (n==0) return ; else return n*fact(n-); (){ int fz, z = 5; fz = fact(z-2); Il nuovo servitore lega il parametro n a 0. La condizione n <=0 e vera e la funzione fact(0) torna come risultato e termina. 4

Ricorsione in C: esempio Servitore & Cliente: int fact(int n) { if (n==0) return ; else return n*fact(n-); (){ int fz, z = 5; fz = fact(z-2); Il controllo torna al servitore precedente fact() che puo valutare l espressione n * (valutando n nel suo environment dove vale ) ottenendo come risultato e terminando. Ricorsione in C: esempio Servitore & Cliente: int fact(int n) { if (n==0) return ; else return n*fact(n-); (){ int fz, z = 5; fz = fact(z-2); Il controllo torna al servitore precedente che puo valutare l espressione n * (valutando n nel suo environment dove vale 2) ottenendo come risultato 2 e terminando. 5

Ricorsione in C: esempio Servitore & Cliente: int fact(int n) { if (n==0) return ; else return n*fact(n-); (){ int fz, z = 5; fz = fact(z-2); Il controllo torna al servitore precedente che puo valutare l espressione n * 2 (valutando n nel suo environment dove vale 3) ottenendo come risultato 6 e terminando. IL CONTROLLO PASSA AL MAIN CHE ASSEGNA A fz IL VALORE 6 Ricorsione in C: esempio fact() fact(0) fact() fact(0) 3*2=6 2*=2 *= fact() fact(0) Cliente di Cliente di Servitore del Cliente di fact() Servitore di Cliente di fact(0) Servitore di Servitore di fact() 6

Cosa succede nello stack? int fact(int n) { if (n==0) return ; else return n*fact(n-); (){ int fz, z = 5; fz = fact(z-2); NOTA: Anche il ()e una funzione Seguiamo l'evoluzione dello stack durante l'esecuzione: Cosa succede nello stack? Situazione iniziale Il () chiama chiama chiama fact() fact() chiama fact(0) fact(0) fact() fact() 7

Il () chiama Cosa succede nello stack? chiama chiama fact() fact() chiama fact(0) fact(0) termina restituendo il valore. Il controllo torna a fact() fact() fact(0) fact() fact(0) fact() chiama chiama fact() fact() chiama fact(0) fact(0) termina restituendo il valore. Il controllo torna a fact() fact() effettua la moltiplicazione e termina restituendo il valore. Il controllo torna a fact() fact(0) fact() fact(0) fact() fact() 8

chiama fact() fact() chiama fact(0) fact(0) termina restituendo il valore. Il controllo torna a fact() fact() effettua la moltiplicazione e termina restituendo il valore. Il controllo torna a effettua la moltiplicazione e termina restituendo il valore 2. Il controllo torna a fact() fact(0) fact() fact(0) fact() fact() 2 fact(0) termina restituendo il valore. Il controllo torna a fact() fact() effettua la moltiplicazione e termina restituendo il valore. Il controllo torna a effettua la moltiplicazione e termina restituendo il valore 2. Il controllo torna a fact(6) effettua la moltiplicazione e termina restituendo il valore 6. Il controllo torna al. fact(0) fact() fact() 2 6 9

Esempio: somma dei primi N naturali Problema: calcolare la somma dei primi N naturali Specifica: Considera la somma +2+3+...+(N-)+N come composta di due termini: Il primo termine non è altro che lo (+2+3+...+(N-)) stesso problema in un caso più semplice: calcolare la somma dei N Valore noto primi N- interi Esiste un caso banale ovvio: CASO BASE la somma fino a vale. Esempio: somma dei primi N naturali Problema: calcolare la somma dei primi N naturali Algoritmo ricorsivo: Somma: N -> N Somma(n) vale se n == Somma(n) vale n+somma(n-) se n > 0 20

Esempio: somma dei primi N naturali Codifica: int sommafinoa(int n) { if (n==) return ; else return sommafinoa(n-)+n; Esempio: somma dei primi N naturali #include<stdio.h> int sommafinoa(int n); () { int dato; printf("\ndammi un intero positivo: "); scanf("%d", &dato); if (dato>0) printf("\nrisultato: %d", sommafinoa(dato)); else printf("errore!"); int sommafinoa(int n) { if (n==) return ; else return sommafinoa(n-)+n; Esercizio: seguire l'evoluzione dello stack nel caso in cui dato=4. 2

Esempio: il numero di Fibonacci Problema: calcolare l N-esimo numero di Fibonacci 0, se n=0 fib (n) =, se n= fib(n-) + fib(n-2), altrimenti Codifica: unsigned fibonacci(unsigned n) { if (n<2) return n; else return fibonacci(n-)+fibonacci(n-2); Esempio: il numero di Fibonacci Ricorsione non lineare: una invocazione del servitore causa due nuove chiamate al servitore medesimo. 22

Esempio: il numero di Fibonacci Valore di ritorno: 2 fib(2) fib(2) +fib(0) +0 fib()+fib(0) fib(3) fib(2)+fib() + +fib() = 2 fib(0) fib() fib() 0 fib(0) 0 fib() fib() Osservazioni Negli esempi visti finora si inizia a sintetizzare il risultato solo dopo che si sono aperte tutte le chiamate, a ritroso, mentre le chiamate si chiudono. Le chiamate ricorsive decompongono via via il problema, ma non calcolano nulla Il risultato viene sintetizzato a partire dalla fine, perché prima occorre arrivare al caso base il caso base fornisce il valore di partenza poi si sintetizzano, a ritroso, i successivi risultati parziali. Processo di calcolo effettivamente ricorsivo 23

Esempio fact() fact(0) fact() fact(0) 3*2=6 2*=2 *= PASSI: ) chiama passandogli il controllo, 2) calcola il fattoriale di 2 e termina restituendo 2 3) riprende il controllo ed effettua la moltiplicazione 3*2 4) termina anche e torna il controllo al Calcolo iterativo del fattoriale Il fattoriale può essere anche calcolato mediante un'algoritmo iterativo: int fact(int n){ int i; int F=; /*inizializzazione del fattoriale*/ for (i=2;i <= n; i++) F=F*i; return F; DIFFERENZA CON LA VERSIONE RICORSIVA: ad ogni passo viene accumulato un risultato intermedio 24

Calcolo iterativo del fattoriale int fact(int n){ int i; int F=; /*inizializzazione del fattoriale*/ for (i=2;i <= n; i++) F=F*i; return F; La variabile F accumula risultati intermedi: se n = 3 inizialmente F= poi al primo ciclo for i=2 F assume il valore 2. Infine all ultimo ciclo for i=3 F assume il valore 6. Al primo passo F accumula il fattoriale di Al secondo passo F accumula il fattoriale di 2 Al i-esimo passo F accumula il fattoriale di i Processo computazionale iterativo Nell'esempio precedente (ciclo for) il risultato viene sintetizzato in avanti L'esecuzione di un algoritmo di calcolo che computi in avanti, per accumulo, è un processo computazionale iterativo. La atteristica fondamentale di un processo computazionale iterativo è che a ogni passo è disponibile un risultato parziale dopo k passi, si ha a disposizione il risultato parziale relativo al caso k questo non è vero nei processi computazionali ricorsivi, in cui nulla è disponibile finché non si è giunti fino al caso elementare. 25

Processo computazionale iterativo Un processo computazionale iterativo si può realizzare anche tramite funzioni ricorsive Si basa sulla disponibilità di una variabile, detta accumulatore, destinata a esprimere in ogni istante la soluzione corrente Si imposta identificando quell operazione di modifica dell accumulatore che lo porta a esprimere, dal valore relativo al passo k, il valore relativo al passo k+. Esempio: calcolo iterativo del fattoriale Definizione: Detto: n! = * 2 * 3 * * n! = v = v k = * 2 * 3 * * k: (k+)! = v k+ = (k+)* v k per k n! = v n per k=n 26

Esempio: calcolo iterativo del fattoriale Inizializzazione dell accumulatore: int fact(int n){ corrisponde al fattoriale di return factit(n,,); Contatore del passo int factit(int n, int F, int i){ if (i < n) Ad ogni chiamata ricorsiva {F = (i+)*f; viene accumulato un i = i+; risultato intermedio in F return factit(n,f,i); return F; Accumulatore del risultato parziale Esempio: calcolo iterativo del fattoriale factit(3,,) factit(3,2,2) factit(3,6,3) factit(3,,) factit(3,2,2) factit(3,6,3) 6 6 6 Al passo i-esimo viene calcolato il fattoriale di i. Quando i = n l attivazione della funzione corrispondente calcola il fattoriale di n. NOTA: ciascuna funzione che effettua una chiamata ricorsiva si sospende, aspetta la terminazione del servitore e poi termina, cioè NON EFFETTUA ALTRE OPEZIONI DOPO. [diversamente, nel caso del fattoriale ricorsivo vero e proprio, dopo la fine del servitore veniva effettuata una moltiplicazione] 27

Esempio: calcolo iterativo del fattoriale Confrontiamo la realizzazione mediante la funzione factit con una funzione iterativa (senza ricorsione) : int factit(int n, int F, int i) { if (i < n) { F = (i+)*f; i = i+; return factit(n,f,i); return F; int fact(int n) { int i; int F=, i=2; while(i <= n) { F=F*i; i++; return F; 3 Fattoriale: Iterazione e Ricorsione il ciclo diventa un if con, in fondo, la chiamata ricorsiva (che realizza l'iterazione successiva) while (condizione) { <corpo del ciclo> if (condizione) { <corpo del ciclo> <chiamata ricorsiva> 28

3 Fattoriale: Iterazione e Ricorsione La soluzione ricorsiva individuata per il fattoriale è sintatticamente ricorsiva ma dà luogo a un processo computazionale ITETIVO Ricorsione apparente, detta RICORSIONE TAIL Il risultato viene sintetizzato in avanti ogni passo decompone e calcola e porta in avanti il nuovo risultato parziale quando le chiamate si chiudono non si fa altro che riportare indietro, fino al cliente, il risultato ottenuto. 3 Una ricorsione che realizza un processo computazionale ITETIVO è una ricorsione apparente la chiamata ricorsiva è sempre l ultima istruzione i calcoli sono fatti prima la chiamata serve solo, dopo averli fatti, per proseguire la computazione Ricorsione tail questa forma di ricorsione si chiama RICORSIONE TAIL ( ricorsione in coda ) 29

Esempio Scrivere una versione ricorsiva dell'algoritmo di ordinamento di un vettore: Soluzione: #include <stdio.h> #define n 5 void leggi(int*, int); void stampa(int*, int); void ordina (int*, int); void scambia (int*, int*); () {int i, a[n]; leggi(a, n); ordina(a, n); stampa(a, n); Definizione tail-ricorsiva della fuzione ordina: void ordina(int *V, int N) {int j, max, tmp; if (N==) return; else { for( max=n-,j=0; j<n; j++) if (V[j]>V[max]) max=j; if (max<n-) scambia( &V[N-], &V[max] ); ordina(v, N-); /* chiamata tail-ricorsiva*/ 30

void leggi(int a[], int dim) {int i; printf ("Scrivi %d interi\n", dim); for (i = 0; i < dim; i++) scanf ("%d", &a[i]); void stampa(int a[], int dim) {int i; printf ("Vettore:\n"); for (i = 0; i < dim; i++) printf ("%d\t", a[i]); Esercizio Scrivere una funzione ricorsiva che, data una sequenza di atteri (terminata dal attere '.') stampi i atteri della sequenza in ordine inverso. La funzione non deve utilizzare stringhe. Ad esempio: " parola " " alorap " funzione 3

Esercizio Osservazione: l'estrazione (pop) dei record di attivazione dallo stack avviene sempre in ordine inverso rispetto all'ordine di inserimento (push). associamo ogni attere letto a una nuova chiamata ricorsiva della funzione Soluzione: void (char ); { char c; if (!= '.') { scanf("%c", &c); (c); printf("%c", ); else return; ogni record di attivazione nello stack memorizza un singolo attere letto (push); in fase di pop, i atteri vengono stampati nella sequenza inversa Soluzione #include <stdio.h> #include <string.h> void (char ); () { char k; printf("\nintrodurre una sequenza terminata da.:\t"); scanf("%c", &k); (k); void (char ) { char c; if (!= '.') { scanf("%c", &c); (c); printf("%c", ); else return; 32

Stack Codice S.O.... (void) {... (k); void (char ); { if (!= '.') {... (c); printf("%c", ); else return; Standard Input: "uno." Stack Codice 'u' S.O.... (void) {... (getchar()); (k); void (char ); { if (!= '.') {... (getchar()); (c); printf("%c", putchar(); ); else return; Standard Input: "uno." 33

Stack Codice 'n' 'u' int... f(int a, int b) {static (void) int X=0; if { (!(a b))... return ; else (getchar()); (k); return X=f(a-2,b-) + X; void (char ); { if (!= '.') () {... { (getchar()); printf("%d\n", (c); f(4,2)); printf("%c", putchar(); ); else return; S.O. Standard Input: "uno." Stack Codice 'o' 'n' 'u' int... f(int a, int b) {static (void) int X=0; if { (!(a b))... return ; else (getchar()); (k); return X=f(a-2,b-) + X; void (char ); { if (!= '.') () {... { (getchar()); printf("%d\n", (c); f(4,2)); printf("%c", putchar(); ); else return; S.O. Standard Input: "uno." 34

'.' 'o' 'n' 'u' Stack Codice int... f(int a, int b) {static (void) int X=0; if { (!(a b))... return ; else (getchar()); (k); return X=f(a-2,b-) + X; void (char ); { if (!= '.') () {... { (getchar()); printf("%d\n", (c); f(4,2)); printf("%c", putchar(); ); else return; S.O. Standard Input: "uno." '.' 'o' 'n' 'u' Stack Codice int... f(int a, int b) {static (void) int X=0; if { (!(a b))... return ; else (getchar()); (k); return X=f(a-2,b-) + X; void (char ); { if (!= '.') () {... { (getchar()); printf("%d\n", (c); f(4,2)); printf("%c", putchar(); ); else return; S.O. Standard Input: "uno." 35

Stack Codice 'o' 'n' 'u' int... f(int a, int b) {static (void) int X=0; if { (!(a b))... return ; else (getchar()); (k); return X=f(a-2,b-) + X; void (char ); { if (!= '.') () {... { (getchar()); printf("%d\n", (c); f(4,2)); printf("%c", putchar(); ); else return; S.O. Standard output: "o" Standard Input: "uno." Stack Codice 'n' 'u' int... f(int a, int b) {static (void) int X=0; if { (!(a b))... return ; else (getchar()); (k); return X=f(a-2,b-) + X; void (char ); { if (!= '.') () {... { (getchar()); printf("%d\n", (c); f(4,2)); printf("%c", putchar(); ); else return; S.O. Standard output: "on" Standard Input: "uno." 36

Stack Codice 'u' int... f(int a, int b) {static (void) int X=0; if { (!(a b))... return ; else (getchar()); (k); return X=f(a-2,b-) + X; void (char ); { if (!= '.') () {... { (getchar()); printf("%d\n", (c); f(4,2)); printf("%c", putchar(); ); else return; S.O. Standard output: "onu" Standard Input: "uno." Stack Codice int... f(int a, int b) {static (void) int X=0; if { (!(a b))... return ; else (getchar()); (k); return X=f(a-2,b-) + X; void (char ); { if (!= '.') () {... { (getchar()); printf("%d\n", (c); f(4,2)); printf("%c", putchar(); ); else return; S.O. Standard output: "onu" Standard Input: "uno." 37