FUNZIONI: IL MODELLO APPLICATIVO 1) Valutazione, nellenvironment corrente, del simbolo che denota il nome della funzione; 2) Valutazione, nellenvironment.

Slides:



Advertisements
Presentazioni simili
Programmazione ad oggetti
Advertisements

Sottoprogrammi: funzioni e procedure
IL MODELLO CLIENTE / SERVITORE. Servitore: un qualunque ente computazionale capace di nascondere la propria organizzazione interna presentando ai clienti.
Procedure e funzioni A. Ferrari.
LE FUNZIONI IN C Sommario 1 Introduzione 2 Moduli di programma in C
Algoritmi e Programmazione
Fondamenti di Informatica CDL in Ingegneria Gestionale (B)- A.A CDL in Ingegneria Gestionale (B)- A.A Programmazione Ricorsiva.
Introduzione al linguaggio C
Lez. 41 Universita' di Ferrara Facolta' di Scienze Matematiche, Fisiche e Naturali Laurea Specialistica in Informatica Algoritmi Avanzati Programmazione.
La ricorsione Simulazione. Il Main /* Programma che usa una funzione ricorsiva*/ #include #define MAX_N 8 main() int valore, dato; printf(Introduci n:
Funzioni definite dall’utente
JAVASCRIPT DIFFERENZA TRA JAVASCRIPT E JAVA TAG LO SCRIPT OGGETTI LE CLASSI FUNZIONE GESTORE DI EVENTI ELEMENTI DEL LINGUAGGI è un vero e proprio linguaggio.
Prof.ssa Chiara Petrioli -- Fondamenti di programmazione, a.a. 2009/2010 Corso di Fondamenti di programmazione a.a. 2009/2010 Prof.ssa Chiara Petrioli.
DIPARTIMENTO DI ELETTRONICA E INFORMAZIONE Puntatori Marco D. Santambrogio – Ver. aggiornata al 21 Marzo 2013.
Allocazione dinamica della memoria
1 Corso di Informatica (Programmazione) Lezione 12 (19 novembre 2008) Programmazione in Java: i metodi statici.
Eliana minicozzi linguaggi L1 Lezione3.
eliana minicozzi linguaggi1a.a lezione2
Introduzione alla programmazione lll
ITERAZIONE e RICORSIONE (eseguire uno stesso calcolo ripetutamente) ITERAZIONE: ripetere piu volte una sequenza di operazioni istruzioni: for, while, do.
Il Linguaggio Macchina
Approfondimento delle classi
nome: sequenza di caratteri usata per denotare un oggetto
memoria gestita staticamente:
Esercizi FUNZIONI Passaggio di parametri per valore, variabili e tipi locali e globali, prototipo.
Esercizi Puntatori, struct con campi puntatore, puntatori a struct, rapporto tra array e puntatori. FUNZIONI Passaggio di parametri per indirizzo, passaggio.
La Programmazione Ricorsiva
Le funzioni.
Espressioni condizionali
Le funzioni a tempo di esecuzione
AN FI Array Array in Java. AN FI Array Dichiarazione di array in Java [ ]; //oppure u [] ; int a[]; int[] a; u La dimensione non è specificata.
DEFINIZIONE DI NUOVE FUNZIONI & STRATEGIE DI COMPOSIZIONE La capacità di definire nuove funzioni permette: di definire nuove operazioni di introdurre variabili.
LAPPROCCIO FUNZIONALE Obiettivo: esprimere la soluzione di un problema sotto forma di funzione. Quali funzioni primitive? Quali meccanismi di combinazione?
AN FI Un denominatoe comune Lo stile funzionale Concetti fondamentali.
FUNZIONI... Una funzione permette di dare un nome a una espressione rendendola parametrica float f(){ return * sin(0.75); } float f1(int x) { return.
Il linguaggio C Le funzioni C Language Il passaggio dei parametri
Un esempio: Calcolo della potenza n-esima di un numero reale
Prof.ssa Chiara Petrioli -- Fondamenti di programmazione, a.a. 2009/2010 Corso di Fondamenti di programmazione a.a. 2009/2010 Prof.ssa Chiara Petrioli.
Unità Didattica 3 Linguaggio C
Programmazione di Calcolatori
Lo sviluppo top down Le funzioni
Passaggio di parametri per indirizzo
1 Parte 5 Fondamenti di Programmazione. 2 Programmazione Concetti base: dati istruzioni Dati: variabili tipi Istruzioni: istruzioni base strutture di.
Ricorsione Strumento potente per definizioni matematiche
2000 Prentice Hall, Inc. All rights reserved. Capitolo 5 (Deitel) Le funzioni Indice degli argomenti Introduzione Moduli nei programmi C 5.3.
2000 Prentice Hall, Inc. All rights reserved. Attivazione di funzioni La chiamata/attivazione di funzione viene indicata citando il nome della funzione.
I metodi F. Bombi Campi e metodi Abbiamo visto che una classe può contenere – Campi – Metodi stato I campi sono utilizzati per memorizzare.
Complessità di un algoritmo
Fondamenti di Informatica II Ingegneria Informatica / Automatica (A-I) Meccanica Prof. M.T. PAZIENZA a.a – 3° ciclo.
- prof. V. Riboldi - SOTTOPROGRAMMI IN TPASCAL METODO TOP DOWN.
Prof.ssa Chiara Petrioli -- corso di programmazione 1, a.a. 2006/2007 Corso di Programmazione 1 a.a.2006/2007 Prof.ssa Chiara Petrioli Corso di Laurea.
Introduzione a Javascript
DIPARTIMENTO DI ELETTRONICA E INFORMAZIONE Funzioni e Procedure Marco D. Santambrogio – Ver. aggiornata al 3 Aprile 2015.
La ricorsione.
DIPARTIMENTO DI ELETTRONICA E INFORMAZIONE Puntatori Marco D. Santambrogio – Ver. aggiornata al 11 Ottobre 2014.
Fondamenti di Informatica II Ingegneria Informatica (A-I) Prof. M.T. PAZIENZA a.a – 3° ciclo.
Informatica 4 Funzioni. FUNZIONE: definizione MATEMATICA Relazione (o applicazione) binaria tra due insiemi A e B che associa a ogni elemento di A un.
Allievi Elettrici - AA Le funzioni ricorsive in C
1 Fabio Scotti – Università degli Studi di Milano Fabio Scotti ( ) Laboratorio di programmazione per la sicurezza Valentina Ciriani ( )
DIPARTIMENTO DI ELETTRONICA E INFORMAZIONE Puntatori Marco D. Santambrogio – Ver. aggiornata al 11 Marzo 2014.
1 Il linguaggio C Precisazioni sull’esperienza in laboratorio.
FUNZIONI: IL MODELLO A RUN-TIME 1) creazione di una nuova attivazione (istanza) del servitore 2) allocazione di memoria per i parametri e per le variabili.
STRUTTURA DI UN PROGRAMMA C In prima battuta, la struttura di un programma C è definita dalla seguente produzione: ::= { }
FUNZIONI... Una funzione permette di  dare un nome a una espressione  rendendola parametrica float f(){ return * sin(0.75); } float f1(int x) {
Informatica 4 La ricorsione. Definizione di ricorsione Ricorsione è la proprietà di quei programmi che, all’interno delle istruzioni che li compongono,
Corso di Laurea Ingegneria Informatica Fondamenti di Informatica
1 Metodo I metodi sono uno strumento che i programmatori usano per strutturare i programmi, sia per renderli più facili da capire che per permettere il.
DIPARTIMENTO DI ELETTRONICA E INFORMAZIONE Funzioni e Procedure Marco D. Santambrogio – Ver. aggiornata al 4 Aprile 2016.
10. Programmazione Ricorsiva Ing. Simona Colucci Informatica - CDL in Ingegneria Industriale- A.A
Linguaggio C: Funzioni e Puntatori Laboratorio di Programmazione Gruppo 1.
Transcript della presentazione:

FUNZIONI: IL MODELLO APPLICATIVO 1) Valutazione, nellenvironment corrente, del simbolo che denota il nome della funzione; 2) Valutazione, nellenvironment corrente, delle espressioni che denotano i parametri; 3) Commutazione allenvironment di definizione della funzione.

FUNZIONI: IL MODELLO APPLICATIVO 4)Chiamata della funzione; 5)Esecuzione del corpo della funzione (nel suo environment di definizione); 6)Restituzione al chiamante di –controllo –risultato con ripristino dellenvironment esistente al momento della chiamata.

LENVIRONMENT La definizione di una funzione introduce un nuovo binding nellenvironment di definizione della funzione –in C, il global environment Al momento dellinvocazione, si crea un nuovo environment –una struttura che contiene i binding dei parametri e degli identificatori dichiarati localmente alla funzione.

ESEMPIO int max (int x, int y ){ return x>y ? x : y; } Lenvironment corrente viene arricchito di un nuovo binding: max / CodiceDellaFunzione Quando max viene chiamata, si commuta a un nuovo environment, in cui sono definite le variabili intere x e y

COMUNICAZIONE CLIENTE SERVITORE Il cliente passa informazioni al servitore mediante una serie di parametri attuali. Parametri formali : –sono specificati nella dichiarazione del servitore –esplicitano il contratto fra servitore e cliente –indicano cosa il servitore si aspetta dal cliente Parametri attuali : –sono trasmessi dal cliente allatto della chiamata –devono corrispondere ai parametri formali in numero, posizione e tipo

COMUNICAZIONE CLIENTE SERVITORE Il cliente passa informazioni al servitore mediante una serie di parametri attuali. I Parametri attuali sono legati ai parametri formali al momento della chiamata, in modo dinamico. Tale legame: vale solo per linvocazione corrente vale solo per la durata della funzione.

IL NOSTRO ESEMPIO Il servitore... int max (int x, int y ){ return x>y ? x : y; } … e un possibile cliente: main(){ int z = 8; int m = max(2*z,13); }

IL NOSTRO ESEMPIO Il servitore... int max (int x, int y ){ return x>y ? x : y; } … e un possibile cliente: main(){ int z = 8; int m = max(2*z,13); } 1) Valutazione del simbolo z nellenvironment corrente Si trova che z vale 8.

IL NOSTRO ESEMPIO Il servitore... int max (int x, int y ){ return x>y ? x : y; } … e un possibile cliente: main(){ int z = 8; int m = max(2*z,13); } 2) Calcolo dellespressione 2*z nellenvironment corrente Si trova che vale 16.

IL NOSTRO ESEMPIO Il servitore... int max (int x, int y ){ return x>y ? x : y; } … e un possibile cliente: main(){ int z = 8; int m = max(2*z,13); } 3) Invocazione della funzione max con parametri attuali 16 e 13. Il controllo passa al servitore.

IL NOSTRO ESEMPIO Il servitore... int max (int x, int y ){ return x>y ? x : y; } … e un possibile cliente: main(){ int z = 8; int m = max(2*z,13); } 4) I parametri formali x e y vengono legati ai parametri attuali 16 e 13. Inizia lesecuzione del servitore.

IL NOSTRO ESEMPIO Il servitore... int max (int x, int y ){ return x>y ? x : y; } … e un possibile cliente: main(){ int z = 8; int m = max(2*z,13); } 5) Viene valutata lespressione condizionale nellenvironment del servitore. Il risultato è 16.

IL NOSTRO ESEMPIO Il servitore... int max (int x, int y ){ return x>y ? x : y; } … e un possibile cliente: main(){ int z = 8; int m = max(2*z,13); } 6) Il valore così determinato (16) viene restituito al cliente. Il servitore termina e il controllo torna al cliente.

IL NOSTRO ESEMPIO Il servitore... int max (int x, int y ){ return x>y ? x : y; } … e un possibile cliente: main(){ int z = 8; int m = max(2*z,13); } 7) Il valore restituito (16) viene usato per inizializzare la variabile m (nellenvironment del cliente)

RIASSUNTO Allatto dellinvocazione di una funzione: si crea una nuova attivazione (istanza) del servitore si alloca la memoria per i parametri (e le eventuali variabili locali) si trasferiscono i parametri al servitore si trasferisce il controllo al servitore si esegue il codice della funzione.

PASSAGGIO DEI PARAMETRI In generale, un parametro può essere trasferito: per valore o copia (by value) si trasferisce il valore del parametro attuale per riferimento (by reference) si trasferisce un riferimento al parametro attuale

PASSAGGIO PER VALORE si trasferisce una copia del valore del parametro attuale cliente z 45

PASSAGGIO PER VALORE si trasferisce una copia del valore del parametro attuale clienteservitore z 45 copia w 45 valore (copiato) di z istanza del servitore

PASSAGGIO PER VALORE si trasferisce una copia del valore del parametro attuale clienteservitore z 45 copia w 45 valore (copiato) di z istanza del servitore Ogni azione fatta su w è strettamente locale al servitore

PASSAGGIO PER RIFERIMENTO si trasferisce un riferimento al parametro attuale cliente z 45

PASSAGGIO PER RIFERIMENTO si trasferisce un riferimento al parametro attuale clienteservitore z 45 riferimento w riferimento a z (indirizzo) istanza del servitore

PASSAGGIO PER RIFERIMENTO si trasferisce un riferimento al parametro attuale clienteservitore z 45 riferimento w riferimento a z (indirizzo) istanza del servitore Ogni azione fatta su w è in realtà fatta sulla variabile z del cliente!

PASSAGGIO DEI PARAMETRI IN C In C, i parametri sono trasferiti sempre e solo per valore (by value) si trasferisce una copia del parametro attuale, non loriginale! tale copia è strettamente privata e locale a quel servitore il servitore potrebbe quindi alterare il valore ricevuto, senza che ciò abbia alcun impatto sul cliente

PASSAGGIO DEI PARAMETRI IN C In C, i parametri sono trasferiti sempre e solo per valore (by value) Conseguenza: è impossibile usare un parametro per trasferire informazioni verso il cliente per trasferire (una) informazione al cliente si sfrutta il valore di ritorno della funzione

ESEMPIO: VALORE ASSOLUTO Definizione formale: |x|: N N |x| vale x se x 0 |x| vale -x se x 0 Codifica sotto forma di funzione C: int valAss(int x) { return (x<0) ? -x : x; }

ESEMPIO: VALORE ASSOLUTO Servitore & Cliente: int valAss(int x) { return (x<0) ? -x : x; } main(){ int z = -87; int absz = valAss(z); int abs4 = valAss(4); }

ESEMPIO: VALORE ASSOLUTO Servitore & Cliente: int valAss(int x) { return (x<0) ? -x : x; } main(){ int z = -87; int absz = valAss(z); int abs4 = valAss(4); } Quando valAss(z) viene chiamata, il valore attuale di z, valutato nel- lenvironment corrente (-87), viene copiato e passato a valAss.

ESEMPIO: VALORE ASSOLUTO Servitore & Cliente: int valAss(int x) { return (x<0) ? -x : x; } main(){ int z = -87; int absz = valAss(z); int abs4 = valAss(4); } valAss riceve quindi una copia del valore -87 e la lega al simbolo x. Poi si valuta lespressione, che qui vale 87, e si restituisce questo valore.

ESEMPIO: VALORE ASSOLUTO Servitore & Cliente: int valAss(int x) { return (x<0) ? -x : x; } main(){ int z = -87; int absz = valAss(z); int abs4 = valAss(4); } Il valore restituito (87) viene usato per inizializzare la variabile absz.

ESEMPIO: MASSIMO DI DUE NUMERI Definizione formale: max: N N N max(x,y) vale x se x y max(x,y) vale y se x < y Codifica sotto forma di funzione C: int max(int x, int y) { return (x<y) ? y : x; }

ESEMPIO: MASSIMO DI DUE NUMERI Servitore & Cliente: int max(int x, int y) { return (x<y) ? y : x; } main(){ int z = -87, y = 12; int m = max(z,18); int n = max(4*y,z+100); }

ESEMPIO: MASSIMO DI DUE NUMERI Servitore & Cliente: int max(int x, int y) { return (x<y) ? y : x; } main(){ int z = -87, y = 12; int m = max(z,18); int n = max(4*y,z+100); } Si valutano le due espressioni che costituiscono i parametri attuali (nel- lenvironment del main) e si trasmette alla funzione copia dei valori ottenuti.

ESEMPIO: MASSIMO DI DUE NUMERI Servitore & Cliente: int max(int x, int y) { return (x<y) ? y : x; } main(){ int z = -87, y = 12; int m = max(z,18); int n = max(4*y,z+100); } La funzione riceve copia dei valori -87 e 18, e li lega ai simboli x e y. Poi si valuta lespressione. Il risultato (18) è restituito al main chiamante.

PASSAGGIO DEI PARAMETRI IN C Perché il C adotta sempre e solo il passaggio per valore (by value)? è sicuro: le variabili del chiamante e del chiamato sono completamente disac- coppiate consente di ragionare per componenti e servizi: la struttura interna dei singoli componenti è irrilevante

PASSAGGIO DEI PARAMETRI IN C Limiti: consente di restituire al cliente solo valori di tipo (relativamente) semplice non consente di restituire collezioni di valori non consente di scrivere componenti software il cui scopo sia diverso dal calcolo di una espressione

PASSAGGIO DEI PARAMETRI Molti linguaggi mettono a disposizione il passaggio per riferimento (by reference) non si trasferisce una copia del valore del parametro attuale si trasferisce un riferimento al parametro, in modo da dare al servitore accesso di- retto al parametro in possesso del cliente il servitore accede e modifica direttamente il dato del cliente.

PASSAGGIO DEI PARAMETRI IN C Il C non supporta direttamente il passaggio per riferimento è una grave mancanza! il C lo fornisce indirettamente solo per alcuni tipi di dati ergo, occorre costruirselo quando serve. (vedremo dei casi) Il C++ e Java invece lo forniscono.

DEFINIZIONE DI NUOVE FUNZIONI & STRATEGIE DI COMPOSIZIONE La capacità di definire nuove funzioni permette: di definire nuove operazioni di introdurre variabili per denotare i dati in modo simbolico di esprimere la ripetizione di una espressione per un numero (prefissato o meno) di volte.

STRATEGIE DI COMPOSIZIONE Tre grandi approcci: 1) la composizione di funzioni; 2) le espressioni condizionali; 3) la ricorsione. Le funzioni definibili in termini di un insieme prescelto di primitive e delle precedenti strategie di composizione costituiscono un insieme detto delle funzioni ricorsive generali.

1) COMPOSIZIONE DI FUNZIONI I parametri in una chiamata di funzione pos- sono consistere/comprendere altre funzioni Es: f(x) + g(f(x), q(x + f(y))) x1 = f(x) x2 = f(x) (mossa evitabile da un automa intelligente ) x3 = f(y) x4 = x + x3 x5 = q( x4 ) x6 = g( x2,x5 ) x7 = x1 + x6

2) ESPRESSIONE CONDIZIONALE Lespressione condizionale riflette la consuetudine matematica di definire le funzioni per elencazione di casi. Esempio: abs: N -> N abs(x) vale x se x 0 abs(x) vale -x se x 0

3) LA RICORSIONE La ricorsione consiste nella pos- sibilità di definire una funzione in termini di se stessa. È basata sul principio di induzione matematica: –se una proprietà P vale per n=n 0 –e si può provare che, assumendola valida per n, allora vale per n+1 allora P vale per ogni n n 0

LA RICORSIONE Operativamente, risolvere un problema con un approccio ricorsivo comporta –di identificare un caso base la cui soluzione sia ovvia –di riuscire a esprimere la soluzione al caso generico n in termini dello stesso problema in uno o più casi più semplici (n-1, n-2, etc).

LA RICORSIONE: ESEMPIO Esempio !: N N n! vale 1 se n 0 n! vale n*(n-1)! se n > 0 Codifica: int fact(int n) { return n==0 ? 1 : n*fact(n-1); }

LA RICORSIONE: ESEMPIO Esempio !: N N n! vale 1 se n 0 n! vale n*(n-1)! se n > 0 Codifica: int fact(int n) { return n==0 ? 1 : n*fact(n-1); } Attenzione: la codifica non corrisponde alla specifica!! Il 2° caso si applica per n 0, cioè anche per n<0 !! MA COSÌ PUÒ NON TERMINARE!

LA RICORSIONE: ESEMPIO Esempio !: N N n! vale 1 se n 0 n! vale n*(n-1)! se n > 0 Codifica: int fact(int n) { /* return n==0 ? 1 : n*fact(n-1); */ return n>0 ? n*fact(n-1) : 1; } Nuova codifica

ESEMPIO: FATTORIALE Servitore & Cliente: int fatt(int n) { return (n>0) ? n*fatt(n-1) : 1; } main(){ int z = 5; int fz = fatt(z+2); int f6 = fatt(6); }

ESEMPIO: FATTORIALE Servitore & Cliente: int fatt(int n) { return (n>0) ? n*fatt(n-1) : 1; } main(){ int z = 5; int fz = fatt(z+2); int f6 = fatt(6); } Si valuta lespressione che costituisce il parametro attuale (nellenvironment del main) e si trasmette alla funzione fatt una copia del valore così ottenuto (7).

ESEMPIO: FATTORIALE Servitore & Cliente: int fatt(int n) { return (n>0) ? n*fatt(n-1) : 1; } main(){ int z = 5; int fz = fatt(z+2); int f6 = fatt(6); } La funzione riceve una copia del valore 7 e la lega al simbolo n. Poi valuta lespres- sione condizionale: ciò impone di valutare una espressione che contiene una nuova chiamata di funzione.

ESEMPIO: FATTORIALE Servitore & Cliente: int fatt(int n) { return (n>0) ? n*fatt(n-1) : 1; } main(){ int z = 5; int fz = fatt(z+2); int f6 = fatt(6); } Si valuta quindi, nellenvironment di fatt, lespressione n-1 (che vale 6), e si effettua una nuova chiamata al servitore fatt, pas- sandogli una copia del valore 6.

ESEMPIO: FATTORIALE Servitore & Cliente: int fatt(int n) { return (n>0) ? n*fatt(n-1) : 1; } main(){ int z = 5; int fz = fatt(z+2); int f6 = fatt(6); } Il (nuovo) servitore riceve quindi una copia del valore 6 e, come sopra, valuta lespres- sione condizionale. Ciò lo porta a dover fare una nuova chiamata passando 5.

ESEMPIO: FATTORIALE Servitore & Cliente: int fatt(int n) { return (n>0) ? n*fatt(n-1) : 1; } main(){ int z = 5; int fz = fatt(z+2); int f6 = fatt(6); } … la cosa prosegue, servitore dopo servitore.....

ESEMPIO: FATTORIALE Servitore & Cliente: int fatt(int n) { return (n>0) ? n*fatt(n-1) : 1; } main(){ int z = 5; int fz = fatt(z+2); int f6 = fatt(6); } Prima o poi, dato che il valore passato cala ogni volta, si giunge a invocare fatt con parametro 1. In questo caso, la valutazione dellespressione dà come risultato 1.

ESEMPIO: FATTORIALE Servitore & Cliente: int fatt(int n) { return (n>0) ? n*fatt(n-1) : 1; } main(){ int z = 5; int fz = fatt(z+2); int f6 = fatt(6); } Ciò chiude la sequenza di chiamate ricorsive. Il controllo torna al servitore precedente, che può finalmente valutare lespressione n*1 (valutando n nel suo environment, dove vale 2) ottenendo 2.

ESEMPIO: FATTORIALE Servitore & Cliente: int fatt(int n) { return (n>0) ? n*fatt(n-1) : 1; } main(){ int z = 5; int fz = fatt(z+2); int f6 = fatt(6); } Il valore 2 viene restituito al servitore pre- cedente, che a sua volta può così valutare lespressione n*2 (valutando n nel suo environment, dove vale 3) ottenendo 6.

ESEMPIO: FATTORIALE Servitore & Cliente: int fatt(int n) { return (n>0) ? n*fatt(n-1) : 1; } main(){ int z = 5; int fz = fatt(z+2); int f6 = fatt(6); } … la cosa prosegue...

ESEMPIO: FATTORIALE Servitore & Cliente: int fatt(int n) { return (n>0) ? n*fatt(n-1) : 1; } main(){ int z = 5; int fz = fatt(z+2); int f6 = fatt(6); } Prima o poi, a forza di retrocedere, si torna al primo servitore attivato, che può quindi valutare lespressione n*720 (valutando n nel suo environment, dove vale 7), giun- gendo così a trovare il valore 5040.

ESEMPIO: FATTORIALE Servitore & Cliente: int fatt(int n) { return (n>0) ? n*fatt(n-1) : 1; } main(){ int z = 5; int fz = fatt(z+2); int f6 = fatt(6); } Il valore 5040, restituito dal servitore fatt, può quindi essere usato per inizializzare la variabile fz.

UN ALTRO ESEMPIO Problema: calcolare la somma dei primi N interi Specifica: Considera la somma ( (N-1)+N come composta di due termini: ( (N-1)) N Esiste un caso banale assolutamente ovvio: la somma fino a 1 vale 1.

UN ALTRO ESEMPIO Problema: calcolare la somma dei primi N interi Specifica: Considera la somma ( (N-1)+N come composta di due termini: ( (N-1)) N Esiste un caso banale assolutamente ovvio: la somma fino a 1 vale 1. Il primo termine non è altro che la soluzione allo stesso problema in un caso più semplice Il secondo termine è un valore già noto.

UN ALTRO ESEMPIO Problema: calcolare la somma dei primi N interi Codifica: int sommaFinoA(int n){ return (n==1) ? 1 : sommaFinoA(n-1) + n; }

UN TERZO ESEMPIO Problema: calcolare lN-esimo numero di Fibonacci 0, se n=0 fib(n-1) + fib(n-2), altrimenti fib (n) =1, se n=1

UN TERZO ESEMPIO Problema: calcolare lN-esimo numero di Fibonacci Codifica: unsigned fibonacci(unsigned n) { return (n<2) : n ? fibonacci(n-1) + fibonacci(n-2); }

UN TERZO ESEMPIO Problema: calcolare lN-esimo numero di Fibonacci Codifica: unsigned fibonacci(unsigned n) { return (n<2) : n ? fibonacci(n-1) + fibonacci(n-2); } Ricorsione non lineare: ogni invocazione del servitore causa due nuove chiamate al servitore medesimo.

UNA RIFLESSIONE Negli esempi di ricorsione visti finora fattoriale somma dei primi N interi calcolo dellN-esimo numero di Fibonacci si inizia a sintetizzare il risultato solo dopo che si sono aperte tutte le chiamate, a ritroso, mentre le chiamate si chiudono.

UNA RIFLESSIONE 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 banale: il caso banale fornisce il valore di partenza poi, e solo poi, si sintetizzano, a ritroso, i successivi risultati parziali.

UNA RIFLESSIONE Ciò indica che tali soluzioni sono sintatticamente ricorsive e danno luogo a un processo computa- zionale effettivamente ricorsivo.

UN ESEMPIO DIVERSO Problema: trovare il Massimo Comun Divisore tra N e M m, se m=n MCD(m, n-m),se m<n MCD(m, n) =MCD(m-n, n),se m>n

UN ESEMPIO DIVERSO Problema: calcolare il Massimo Comun Divisore tra N e M Codifica: int mcd(int m, int n){ return (m==n) : m ? (m>n) ? mcd(m-n, n) : mcd(m, n-m); }

UN ESEMPIO DIVERSO Servitore & Cliente: int mcd(int m, int n){ return (m==n) : m ? (m>n) ? mcd(m-n, n) : mcd(m, n-m); } main(){ int m = mcd(36,15); }

UN ESEMPIO DIVERSO Servitore & Cliente: int mcd(int m, int n){ return (m==n) : m ? (m>n) ? mcd(m-n, n) : mcd(m, n-m); } main(){ int m = mcd(36,15); } Si valutano i parametri attuali e si invoca la funzione con parametri 36 e 15

UN ESEMPIO DIVERSO Servitore & Cliente: int mcd(int m, int n){ return (m==n) : m ? (m>n) ? mcd(m-n, n) : mcd(m, n-m); } main(){ int m = mcd(36,15); } Si legano i parametri 36 e 15 ai parametri formali m e n, e si valuta lespressione condizionale.

UN ESEMPIO DIVERSO Servitore & Cliente: int mcd(int m, int n){ return (m==n) : m ? (m>n) ? mcd(m-n, n) : mcd(m, n-m); } main(){ int m = mcd(36,15); } Poiché 36 15, si invoca nuovamente la funzione con parametri m-n (21) e n (15).

UN ESEMPIO DIVERSO Servitore & Cliente: int mcd(int m, int n){ return (m==n) : m ? (m>n) ? mcd(m-n, n) : mcd(m, n-m); } main(){ int m = mcd(36,15); } Il nuovo servitore, poiché 21 15, fa la stessa cosa e invoca nuovamente la funzione con parametri m-n (6) e n (15).

UN ESEMPIO DIVERSO Servitore & Cliente: int mcd(int m, int n){ return (m==n) : m ? (m>n) ? mcd(m-n, n) : mcd(m, n-m); } main(){ int m = mcd(36,15); } Il nuovo servitore, poiché 6 15, invoca un ulteriore servitore con parametri m (6) e n-m (9).

UN ESEMPIO DIVERSO Servitore & Cliente: int mcd(int m, int n){ return (m==n) : m ? (m>n) ? mcd(m-n, n) : mcd(m, n-m); } main(){ int m = mcd(36,15); } Poiché 6 9, si ha una nuova chiamata con parametri m (6) e n-m (3).

UN ESEMPIO DIVERSO Servitore & Cliente: int mcd(int m, int n){ return (m==n) : m ? (m>n) ? mcd(m-n, n) : mcd(m, n-m); } main(){ int m = mcd(36,15); } Poiché 6 3, si ha una nuova invocazione (lultima!) con parametri m-n (3) e n (3).

UN ESEMPIO DIVERSO Servitore & Cliente: int mcd(int m, int n){ return (m==n) : m ? (m>n) ? mcd(m-n, n) : mcd(m, n-m); } main(){ int m = mcd(36,15); } Poiché 3 = 3, il servitore termina e restituisce come risultato 3.

UN ESEMPIO DIVERSO Perché questo esempio è diverso ? il risultato viene sintetizzato via via che le chiamate si aprono, in avanti quando le chiamate si chiudono non si fa altro che riportare indietro, fino al cliente, il risultato ottenuto.

UNA RIFLESSIONE La soluzione ricorsiva individuata per lMCD è sintatticamente ricorsiva... … ma dà luogo a un processo computa- zionale diverso dal precedente: un processo computazionale ITERATIVO Il risultato viene sintetizzato in avanti ogni passo decompone e calcola e porta in avanti il nuovo risultato parziale

UNA RIFLESSIONE Ogni processo computazionale ITERATIVO calcola a ogni passo un risultato parziale dopo k passi, si ha a disposizione il risultato parziale relativo al caso k questo non è vero nei processi computa- zionali ricorsivi là, finché non si sono aperte tutte le chiamate, non è disponibile nessun risultato!

RICORSIONE TAIL Una ricorsione che realizza un processo computazionale ITERATIVO è una ricorsione solo apparente la chiamata ricorsiva è sempre lultima istruzione i calcoli sono fatti prima la chiamata serve solo, dopo averli fatti, per proseguire la computazione questa forma di ricorsione si chiama RICORSIONE TAIL (ricorsione in coda)