DEFINIZIONE DI NUOVE FUNZIONI & STRATEGIE DI COMPOSIZIONE La capacità di definire nuove funzioni permette: di definire nuove operazioni di introdurre variabili.

Slides:



Advertisements
Presentazioni simili
RICORSIONE: DEFINIZIONI RICORSIVE
Advertisements

Ricorrenze Il metodo di sostituzione Il metodo iterativo
Funzioni e procedure Ogni linguaggio di programmazione ad alto livello mette a disposizione del programmatore questi strumenti, ed il C non è da meno!
Sottoprogrammi: funzioni e procedure
IL MODELLO CLIENTE / SERVITORE. Servitore: un qualunque ente computazionale capace di nascondere la propria organizzazione interna presentando ai clienti.
Ricorsione Procedure e funzioni ricorsive. Definizioni Un oggetto si dice ricorsivo se è definito totalmente o parzialmente in termini di sé stesso La.
Procedure e funzioni A. Ferrari.
Procedure e funzioni ricorsive
Algoritmi e Programmazione
Fondamenti di Informatica CDL in Ingegneria Gestionale (B)- A.A CDL in Ingegneria Gestionale (B)- A.A Programmazione Ricorsiva.
Lez. 41 Universita' di Ferrara Facolta' di Scienze Matematiche, Fisiche e Naturali Laurea Specialistica in Informatica Algoritmi Avanzati Programmazione.
POTENZE cosa sono proprietà curiosità visualizzazione.
La ricorsione Simulazione. Il Main /* Programma che usa una funzione ricorsiva*/ #include #define MAX_N 8 main() int valore, dato; printf(Introduci n:
TERNE PITAGORICHE Obiettivi dell’esercitazione
INFORMATICA Strutture iterative
Introduzione agli algoritmi. Definizione Sistema di regole e procedure di calcolo ben definite che portano alla soluzione di un problema con un numero.
Iterazione enumerativa (for)
DIPARTIMENTO DI ELETTRONICA E INFORMAZIONE Puntatori Marco D. Santambrogio – Ver. aggiornata al 21 Marzo 2013.
Algebra di Boole ed elementi di logica
1 Corso di Informatica (Programmazione) Lezione 10 (12 novembre 2008) Programmazione in Java: espressioni booleane e controllo del flusso (selezione)
1 Corso di Laurea in Biotecnologie Informatica (Programmazione) Le stringhe di caratteri in Java Anno Accademico 2009/2010.
CORSO DI PROGRAMMAZIONE II Introduzione alla ricorsione
Introduzione alla programmazione lll
LdL - LP1 - ver. 6 - lez aa Linguaggi di programmazione I La ricorsione Prof. Luigi Di Lascio Lezione 10.
ITERAZIONE e RICORSIONE (eseguire uno stesso calcolo ripetutamente) ITERAZIONE: ripetere piu volte una sequenza di operazioni istruzioni: for, while, do.
Lezione 4: Costrutti Condizionali Prof. Raffaele Montella.
Esercizi su code Date due code in ingresso a valori interi in ordine crescente, scrivere una funzione che restituisca una terza coda che contenga i valori.
Esercizi FUNZIONI Passaggio di parametri per valore, variabili e tipi locali e globali, prototipo.
Introduzione alla Ricorsione
La Programmazione Ricorsiva
Fondamenti di Informatica Algoritmi
Il Linguaggio C.
Le funzioni.
Espressioni condizionali
Le funzioni a tempo di esecuzione
AN FI Metodologie1 Metodologie di progetto Metodologie top-down e bottom-up.
FUNZIONI: IL MODELLO APPLICATIVO 1) Valutazione, nellenvironment corrente, del simbolo che denota il nome della funzione; 2) Valutazione, nellenvironment.
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 Linguaggi di programmazione Un denominatore comune.
AN FI Un denominatoe comune Lo stile funzionale Concetti fondamentali.
RICORSIONE & ITERAZIONE
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
Programmazione di Calcolatori
Massimo Comun Divisore
Programmazione di Calcolatori
POTENZE cosa sono proprietà curiosità visualizzazione.
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
Milano, 17 Dicembre 2013 Informatica B Informatica B Matlab Laboratorio del 14/01/2014 Responsabili di laboratorio: Gianluca Durelli:
2000 Prentice Hall, Inc. All rights reserved. Attivazione di funzioni La chiamata/attivazione di funzione viene indicata citando il nome della funzione.
Complessità di un algoritmo
Ugo de'Liguoro - Informatica 2 a.a. 03/04 Lez. 2 La ricorsione Corso di Informatica 2 a.a. 2003/04 Lezione 2.
DIPARTIMENTO DI ELETTRONICA E INFORMAZIONE La Ricorsione Marco D. Santambrogio – Ver. aggiornata al 21 Maggio 2014.
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.
Allievi Elettrici - AA Le funzioni ricorsive in C
Fondamenti di Informatica 2 Ingegneria Informatica Docente: Giovanni Macchia a.a
1 Fabio Scotti – Università degli Studi di Milano Fabio Scotti ( ) Laboratorio di programmazione per la sicurezza Valentina Ciriani ( )
1 Il linguaggio C Precisazioni sull’esperienza in laboratorio.
Elementi di semantica denotazionale ed operazionale
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: ::= { }
Informatica 4 La ricorsione. Definizione di ricorsione Ricorsione è la proprietà di quei programmi che, all’interno delle istruzioni che li compongono,
Suggerimenti [1d5] SE la prima lettera della matrice (in alto a sinistra, matrice[0,0]) è diversa dalla prima lettera della parola (parola[0]) ALLORA siamo.
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
Lezione n. Parole chiave: Corso di Laurea: Insegnamento: Docente: A.A Salvatore Cuomo La ricorsione 15 Approccio ricorsivo, esercizi sulla.
Transcript della presentazione:

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)