IL MODELLO CLIENTE / SERVITORE. Servitore: un qualunque ente computazionale capace di nascondere la...
-
Upload
costantino-d-andrea -
Category
Documents
-
view
219 -
download
0
Transcript of IL MODELLO CLIENTE / SERVITORE. Servitore: un qualunque ente computazionale capace di nascondere la...
IL MODELLO CLIENTE / SERVITORE
Cliente Servitore
ambiente condiviso
Servitore
Cliente
controllo &informazione
Servitore:• un qualunque ente computazionale capace di
nascondere la propria organizzazione interna
• presentando ai clienti una precisa interfaccia per lo scambio di informazioni.
Cliente:• qualunque ente in grado di invocare uno o
più servitori per svolgere il proprio compito.
CLIENTI E SERVITORI
SERVITORI
Un servitore può• essere passivo o attivo• servire molti clienti oppure costituire la
risorsa privata di uno specifico cliente– in particolare: può servire un cliente alla volta,
in sequenza, oppure più clienti per volta, in parallelo
• trasformarsi a sua volta in cliente, invocando altri servitori o anche se stesso.
COMUNICAZIONE CLIENTE / SERVITORE
Lo scambio di informazioni tra uncliente e un servitore può avvenire
• in modo esplicito tramite le interfacce stabilite dal servitore
• in modo implicito tramite aree-dati accessibili ad entrambi.
FUNZIONI
Una funzione permette di dare un nome a una espressione rendendola parametrica
Esempi (pseudo-C):float f(){ 2 + 3 * sin(0.75); }
float f1(int x) {
2 + x * sin(0.75); }
FUNZIONI come COMPONENTI SW
Una funzione è un componentesoftware che cattura l’idea mate-matica di funzione
f : A B ... Q S molti possibili ingressi
(che non vengono modificati!) una sola uscita (il risultato)
FUNZIONI come SERVITORI
Una funzione• riceve dati di ingresso in corrispondenza ai
parametri• ha come corpo una espressione, la cui
valutazione fornisce un risultato• denota un valore in corrispondenza al suo
nome
FUNZIONI come SERVITORI
Esempio
• se x vale 1
• e f è la funzione R R
f = 3 * x2 + x - 3
• allora f(x) denota il valore 1.
FUNZIONI come SERVITORI
Una funzione è un servitore• passivo• che serve un cliente per volta• che può trasformarsi in cliente
invocando se stessa
FUNZIONI come SERVITORI
• Una funzione è un servitore dotato di nome che incapsula le istruzioni che realizzano un certo servizio.
• Per chiedere al servitore di svolgere il servizio occorre – chiamare tale servitore (per nome)– fornirgli le necessarie informazioni
Come far comunicare cliente e servitore?
COMUNICAZIONE CLIENTE/SERVITORE
Nel caso di una funzione,
cliente e servitore comunicano mediante l’interfaccia della funzione.
FUNZIONI: INTERFACCIA
• L’interfaccia di una funzione comprende– nome della funzione– lista dei parametri– tipo del valore da essa denotato
• è spesso chiamata firma (signature)• esplicita il contratto di servizio
fra cliente e servitore.
COMUNICAZIONE CLIENTE/SERVITORE
Cliente e servitore comunicano quindimediante
i parametri trasmessi dal cliente al servitore all’atto della chiamata(direzione: dal cliente al servitore)
il valore restituito dal servitore al cliente(direzione: dal servitore al cliente)
UN ESEMPIO
int max (int x, int y ){return x>y ? x : y;
}
Il simbolo max denota il nome della nuovafunzione
Le variabili intere x e y sono i parametridella nuova funzione
Il valore restituito è un intero.
FUNZIONI & INFORMATION HIDING
• La struttura interna (corpo) di una funzione è completamente inaccessibile dall’esterno.
• Così facendo si garantisce protezione dell’informazione secondo il principio del suo “nascondimento” (information hiding)
FUNZIONI IN UN PROGRAMMA C
• Un programma C è un insieme di funzioni:
<programma> ::=
{ <unità-di-traduzione> }
<unità-di-traduzione> ::= <definizione-di-funzione> | <dichiarazione-di-funzione> | …
• Il main è semplicemente la funzione, di no-me prefissato, dove inizia l’elaborazione.
DEFINIZIONE DI FUNZIONE: struttura
Una <definizione-di-funzione> è definita
dalla produzione:
<definizione-di-funzione> ::=
<tipoValore><nome>(<parametri>){ <corpo>
}
DEFINIZIONE DI FUNZIONE: parametri
Una <definizione-di-funzione> è definita
dalla produzione:
<definizione-di-funzione> ::=
<tipoValore> <nome>(< parametri >){ <corpo>
}• o una lista vuota: void• o una lista di variabili
(separate da virgole) visibili soloentro il corpo della funzione.
DEFINIZIONE DI FUNZIONE: corpo
Una <definizione-di-funzione> è definita
dalla produzione:
<definizione-di-funzione> ::=
<tipoValore> <nome>(< parametri >){ <corpo>
} <corpo> è tutto ciò che sta fra {}La forma base è
return <espressione> ;
DEFINIZIONE DI FUNZIONE: tipo
Una <definizione-di-funzione> è definita
dalla produzione:
<definizione-di-funzione> ::=
<tipoValore> <nome>(< parametri >){ <corpo>
} il tipo della funzione deve coinci-dere col tipo dell’espressione checostituisce il corpo della funzione
FUNZIONI COME COMPONENTI SOFTWARE: NASCITA E MORTE
• All’atto della chiamata, l’esecuzione del cliente viene sospesa e il controllo passa al servitore.
• Il servitore “vive” solo per il tempo necessario a svolgere il servizio.
• Al termine, il servitore “muore”,e l’esecuzione torna al cliente.
CHIAMATA DI FUNZIONE
La chiamata di funzione è un’espressionedella forma
<nomefunzione> ( <parametri-attuali> )
dove:<parametri-attuali> ::=
[ <espressione> ] { , <espressione> }
L’invocazione di una funzione da parte di un cliente costituisce un particolare tipo di espressione.
CHIAMATE DI FUNZIONE & ESPRESSIONI
<espr> ::= <term> | <espr> + <term > | <espr> - <term >
<term > ::= <fattore> | <term > * <fattore> |<term > / <fattore> | <term > % <fattore>
<fattore> ::= [ - | + ] <num ero> | ( <espr> )|<variabile> | <chiam ata di funzione>
RITORNO DA FUNZIONE
L’istruzione return provoca la restitu-zione del controllo al cliente, unitamente al valore dell’espres-sione che la segue.
Eventuali istruzioni successive alla return non saranno mai eseguite!
IL PROBLEMA DEI SIMBOLI
f(x) + g( f(x), q( x + f(y)))
Per fornire il risultato, dobbiamo conoscere:• la definizione delle funzioni f, g, q• i valori di x e y
Come conoscerli?
SIMBOLI & SIGNIFICATO
Come conoscere il significato di un simbolo? o esiste una “convenzione diffusa”,
una “cultura comune”, che associa ad un simbolo un preciso significato;
o esiste un ente di riferimento che specifica in modo esplicito il significato di un simbolo.
BINDING & ENVIRONMENT
• La conoscenza di cosa un simbolo denota viene espressa da una legame (binding) tra il simbolo e uno o più attributi.
• La collezione dei binding valida in (un certo punto di) un programma si chiama environment.
UN ESEMPIO
Sia f N->N: f(x)=x+x
Sia z = 2
f(z) + f(z) vale 8
f(2) + f(y) vale ???
g(z) vale ???
SCOPE & SCOPE RULES
• Tutte le occorrenze di un nome nel testo di un programma a cui si applica un dato binding si dicono essere entro la stessa portata o scope del binding.
• Le regole in base a cui si stabilisce la portata di un binding si dicono regole di visibilità o scope rules.
ESEMPIO
Sia f N->N: f(x)=x+x
Sia z = 2
f(z) + f(z) vale 8
f(2) + f(y) vale ???
Sia g N->N: g(x)=x*x
g(z) vale 4
f(g(z)) vale 8
DEFINIZIONE DI NUOVE FUNZIONI
• Per dare un nome alla nuova funzione,• il linguaggio deve introdurre costrutti per
estendere un environment • aggiungendo ad esso il nuovo binding
che lega il nome scelto per la funzione all’entità che realizza quella specifica funzione.
DEFINIZIONE DI NUOVE FUNZIONI
La definizione di funzione unisce la denotazione di una nuova funzione
l’attribuzione di un nome ad essa
L’environment corrente viene arricchito con uno nuovo binding tra il nome della funzione, e
la sua rappresentazione interna
FUNZIONI: IL MODELLO APPLICATIVO
1) Valutazione, nell’environment corrente, del simbolo che denota il nome della funzione;
2) Valutazione, nell’environment corrente, delle espressioni che denotano i parametri;
3) Commutazione all’environment 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 dell’environment esistente al momento della chiamata.
L’ENVIRONMENT
• La definizione di una funzione introduce un nuovo binding nell’environment di definizione della funzione – in C, il global environment
• Al momento dell’invocazione, 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;
}
L’environment 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 servitoremediante 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 all’atto della chiamata– devono corrispondere ai parametri formali
in numero, posizione e tipo
COMUNICAZIONE CLIENTE SERVITORE
Il cliente passa informazioni al servitoremediante 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 l’invocazione 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 znell’environment 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 dell’espressione 2*znell’environment 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 attuali16 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 parametriattuali 16 e 13.
Inizia l’esecuzione 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 l’espressionecondizionale nell’environment 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 lavariabile m (nell’environment del cliente)
RIASSUNTO
All’atto dell’invocazione 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ò esseretrasferito: 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
cliente servitore
z 45copia
w45
valore (copiato) di z
istanza del servitore
PASSAGGIO PER VALORE
si trasferisce una copia del valore del parametro attuale
cliente servitore
z 45copia
w45
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
cliente servitore
z 45riferimento
w
riferimento a z (indirizzo)
istanza del servitore
PASSAGGIO PER RIFERIMENTO
si trasferisce un riferimento al parametro attuale
cliente servitore
z 45riferimento
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 soloper valore (by value) si trasferisce una copia del parametro
attuale, non l’originale! 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 soloper 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-l’environment corrente (-87), vienecopiato 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 delvalore -87 e la lega al simbolo x.Poi si valuta l’espressione, che quivale 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 ymax(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 checostituiscono i parametri attuali (nel-l’environment 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 -87e 18, e li lega ai simboli x e y. Poi sivaluta l’espressione. Il risultato (18)è restituito al main chiamante.
PASSAGGIO DEI PARAMETRI IN C
Perché il C adotta sempre e solo il passaggioper 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 ilpassaggio 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 passaggioper 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 funzionipermette:• di definire nuove operazioni• di introdurre variabili per denotare
i dati in modo simbolico• di esprimere la ripetizione di una
espressione per un numero (prefissatoo 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 insiemeprescelto di primitive e delle precedentistrategie di composizione costituiscono uninsieme 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
• L’espressione condizionale riflette la consuetudine matematica di definire le funzioni per elencazione di casi.
• Esempio:
abs: N -> N
abs(x) vale x se x 0abs(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=n0
– e si può provare che, assumendola valida per n, allora vale per n+1
allora P vale per ogni nn0
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 0n! 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 0n! 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 n0,cioè anche per n<0 !!
MA COSÌ PUÒ NON TERMINARE!
LA RICORSIONE: ESEMPIO
Esempio!: N N
n! vale 1 se n 0n! 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 l’espressione che costituisce ilparametro attuale (nell’environment 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 7e la lega al simbolo n. Poi valuta l’espres-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, nell’environment di fatt,l’espressione n-1 (che vale 6), e si effettuauna 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 copiadel valore 6 e, come sopra, valuta l’espres-sione condizionale. Ciò lo porta a doverfare 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 calaogni volta, si giunge a invocare fatt conparametro 1. In questo caso, la valutazionedell’espressione 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 servitoreprecedente, che può finalmente valutarel’espressione 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ì valutarel’espressione 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 tornaal primo servitore attivato, che può quindivalutare l’espressione 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 servitorefatt, può quindi essere usato per inizializzare la variabile fz.
UN ALTRO ESEMPIO
Problema:calcolare la somma dei primi N interi
Specifica:
Considera la somma (1+2+3+...+(N-1)+N comecomposta di due termini:• (1+2+3+...+(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 (1+2+3+...+(N-1)+N comecomposta di due termini:• (1+2+3+...+(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 inun 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 l’N-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 l’N-esimo numero di Fibonacci
Codifica:unsigned fibonacci(unsigned n) { return (n<2) : n ?
fibonacci(n-1) + fibonacci(n-2);
}
UN TERZO ESEMPIO
Problema:calcolare l’N-esimo numero di Fibonacci
Codifica:unsigned fibonacci(unsigned n) { return (n<2) : n ?
fibonacci(n-1) + fibonacci(n-2);
}
Ricorsione non lineare: ogniinvocazione del servitore causadue nuove chiamate al servitore medesimo.
UNA RIFLESSIONE
Negli esempi di ricorsione visti finora
fattoriale
somma dei primi N interi
calcolo dell’N-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 parametriformali m e n, e si valuta l’espressionecondizionale.
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 lafunzione 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 lastessa cosa e invoca nuovamente lafunzione 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 chiamatacon 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(l’ultima!) 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 erestituisce 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 l’MCD è 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 ITERATIVOcalcola 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 processocomputazionale ITERATIVOè una ricorsione solo apparente
la chiamata ricorsiva è sempre l’ultima 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”)