La presentazione è in caricamento. Aspetta per favore

La presentazione è in caricamento. Aspetta per favore

RICORSIONE & ITERAZIONE Riconsideriamo lesempio del Massimo Comun Divisore visto tempo addietro: m, se m=n MCD(m, n-m),se m

Presentazioni simili


Presentazione sul tema: "RICORSIONE & ITERAZIONE Riconsideriamo lesempio del Massimo Comun Divisore visto tempo addietro: m, se m=n MCD(m, n-m),se m

1 RICORSIONE & ITERAZIONE Riconsideriamo lesempio del Massimo Comun Divisore visto tempo addietro: m, se m=n MCD(m, n-m),se mn

2 RICORSIONE & ITERAZIONE Questo esempio era stato trasposto nella funzione seguente: int mcd(int m, int n){ return (m==n) : m ? (m>n) ? mcd(m-n, n) : mcd(m, n-m); } Lesempio era particolare perché il risultato veniva sintetizzato in avanti, anziché allin- dietro come nei processi ricorsivi.

3 RICORSIONE & ITERAZIONE Ogni processo computazionale che computi in avanti, per accumulo, costituisce una ITERAZIONE ossia è un processo computazionale iterativo. Ogni soluzione sintatticamente ricorsiva che dia luogo a un processo computazio- nale iterativo costituisce una ricorsione solo apparente: una ricorsione tail.

4 RICORSIONE & ITERAZIONE La caratteristica 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 è disgregato il problema fino al caso elementare.

5 IL RAGIONAMENTO ITERATIVO Si basa sulla disponibilità di una variabile, detta accumulatore, destinata a esprimere in ogni istante la soluzione corrente Si imposta identificando quelloperazione di modifica dellaccumulatore che lo porta a esprimere, dal valore relativo al passo k, il valore relativo al passo k+1.

6 ESEMPIO: CALCOLO DEL FATTORIALE Definizione: n! = 1 * 2 * 3 *… * n Detto v k = 1 * 2 * 3 *… * k: 1! = v 1 = 1 (k+1)! = v k+1 = (k+1) * v k per k 1 n! = v n per k=n

7 ESEMPIO: CALCOLO DEL FATTORIALE int fact(int n){ return factIter(n,1,1); } int factIter(int n, int v, int k){ return (k==n) ? v : factIter(n, (k+1)*v, k+1); }

8 INVARIANTI Un invariante di programma è una relazione sempre vera in un dato punto del program- ma. Esempio: double power(double b, int k){ return (k<=0) ? 1 : powerIt(b,k,b,1); } Invariante di programma: è sempre vero che qui k>0

9 INVARIANTI DI CICLO Un invariante di ciclo è una relazione sem- pre vera, in un dato punto del programma, a ogni iterazione. Identificare un invariante di ciclo è una forma di progetto. Invarianti diversi suggeriscono di norma algoritmi diversi, che quasi sempre hanno diversa efficienza.

10 PROBLEMA: CALCOLO DI b k Un approccio iterativo Posto b k = v k, si può scrivere: b 0 = v 0 = 1 per i=0 b i = v i = b * b i-1 = b * v i-1 per i>0 in particolare: b k = v k per i=k Un possibile invariante: b k = v i * b k-i

11 PROBLEMA: CALCOLO DI b k Perché b k = b k-i *v i è un invariante? Al generico passo 0

12 PROBLEMA: CALCOLO DI b k Come usarlo per progettare lalgoritmo? inizialmente, v = v 0 =1 a ogni passo si deve trasformare linvariante b k = v i * b k-i nella forma b k = v i+1 * b k-(i+1) che deve assumere al passo successivo ciò si ottiene ponendo, a ogni passo v = b * vi = i + 1

13 CALCOLO DI b k : LINVARIANTE double powerIt(double b, int k, double v, int i){ return (i==k) ? v : powerIt(b,k,v*b,i+1); } double power(double b, int k){ return (k==0) ? 1 : powerIt(b,k,1,0); } V 0 =1 i =0 V = V i+1 = V i * b i = i+1

14 PROGETTARE b k PER INVARIANTI Partendo da relazioni diverse si ottengono approcci diversi, con diversa efficienza. Un diverso invariante: k=0 b 0 = 1 k>0 k pari b k = (b 2 ) k/2 k dispari b k = b * b k-1 b*b: non richiede di saper fare potenze richiede di saper fare un prodotto

15 PROGETTARE b k PER INVARIANTI Come usarlo per progettare lalgoritmo? a ogni passo si deve riscrivere b k in una delle due forme date ciò si ottiene ponendo, a ogni passo –se k è pari: b = b * b k = k/2 –se k è dispari: b = b k = k-1 e moltiplicando per b richiede una operazione dopo la fase di modifica di b e k soluzione ricorsiva

16 PROGETTARE b k PER INVARIANTI boolean odd(int n){ return n%2==1; } double pow(double b, int k){ return (k==0) ? 1 : odd(k) ? pow(b, k-1) * b : pow(b*b, k/2); } ricorsione non-tail (Complessità dellordine di log 2 k)

17 PROGETTARE b k PER INVARIANTI UN APPROCCIO ITERATIVO Un ulteriore invariante: b k = t * v n k=0 n=0, t=1 b k = t * v 0 = 1 k>0 –se n è pari: b k = t * (v 2 ) n/2 –se n è dispari:b k = t * v * v n-1

18 PROGETTARE b k PER INVARIANTI Progetto dellalgoritmo: a ogni passo si deve trasformare linvariante b k = t * v n in una delle due forme date ciò si ottiene ponendo: –se n è pari n = n/2,t = t,v = v 2 –se n è dispari n = n-1,t = t*v,v = v Interessante: b e k in realtà non si usano!

19 PROGETTARE b k PER INVARIANTI boolean odd(int n){return n%2==1;} double powIt(double b, int k, double t, double v, int n){ return (n==0) ? t : odd(n) ? powIt(b,k,t*v,v,n-1) : powIt(b,k,t,v*v,n/2); } Come previsto, b e k non servono! Quindi li possiamo togliere…!!

20 PROGETTARE b k PER INVARIANTI boolean odd(int n){return n%2==1;} double powIt(double t, double v, int n){ return (n==0) ? t : odd(n) ? powIt(t*v, v, n-1) : powIt(t, v*v, n/2); } double power(double b, int k){ return (k==0) ? 1 : powIt(1,b,k); }

21 ESERCIZIO: MOLTIPLICAZIONE Obiettivo: calcolare p = x* y Sfruttiamo linvariante: y = Q * B + R dove B è un intero positivo Q (quoziente) = y/B R (resto) = y%B

22 ESERCIZIO: MOLTIPLICAZIONE Obiettivo: calcolare p = x* y Sostituendo: p = x * y = x* (Q * B + R) = = x * (B * (y/B)) + x*(y%B) Caso particolare: y=0 p = x * y = x* 0 = 0

23 ESERCIZIO: MOLTIPLICAZIONE Approccio ricorsivo: si applica direttamente la relazione trovatap = x* B * (y/B) ) + x*(y%B) Ad esempio, scegliendo B=2: int MulNatR(int x, int y){ return (y==0) ? 0 : MulNatR(x*2, y/2) + x*(y%2); } Occorre fare unoperazione dopo la chiamata ricorsiva ricorsione non-tail

24 ESERCIZIO: MOLTIPLICAZIONE Approccio ricorsivo: si applica direttamente la relazione trovatap = x* B * (y/B) ) + x*(y%B) Ad esempio, scegliendo B=2: int MulNatR(int x, int y){ return (y==0) ? 0 : MulNatR(x*2, y/2) + x*(y%2); } Operazione primitiva che suppo- niamo di saper già fare (è una moltiplicazione per 0 o per 1) Operazioni primitive che supponiamo di sa- per già fare (moltiplicazione/divisione per 2)

25 ESERCIZIO: MOLTIPLICAZIONE Verso un approccio iterativo Cerchiamo un invariante di ciclo p = x * y + z Ponendo y=Q*B+R e trasformando: p = (x*B) * Q + (x*R + z) = x * y + z dove si è posto y = Q = y/B, x = x*B, z = z + x*R Caso particolare: y=0 p = z

26 ESERCIZIO: MOLTIPLICAZIONE Invariante di ciclo: p = x * y + z Trasformazione: p = x * y + z y = Q = y/B, x = x*B, z = z + x*R int MulNatIt(int x, int y, int z){ return(y==0) ? z : MulNatIt(x*2, y/2, z+x*(y%2)); } Operazioni primitive: supponiamo di saper già moltiplicare, dividere e modulare per 2.

27 ESERCIZIO: MOLTIPLICAZIONE Perché supponiamo di saper già moltiplicare, e dividere per 2 (trovando anche il resto) ? Perché lelaboratore è intrinsecamente capace di farlo nella propria ALU: moltiplicazione per 2 = shift a sinistra (<<) divisione per 2 = shift a destra (>>) moltiplicazione per (y%2) = 0, se y è pari (y%2 vale 0) y, se y dispari (y%2 vale 1)

28 ESERCIZIO: MOLTIPLICAZIONE Il codice finale che ne risulta: int MulNatIt(int x, int y, int z){ return (y==0) ? z : odd(y): MulNatIt(x >1, z+x) : MulNatIt(x >1, z); } boolean odd(int n){return n%2==1;} y%2 = 1 y%2 = 0 y/2 x/2

29 UNA RIFLESSIONE DI FONDO Limpostazione funzionale è sempre costruttiva. Ma si può sempre solo creare? Perché creare una versione nuova di un accumulatore ad ogni passo, quando lelaboratore di Von Neumann permette la modifica del contenuto di una cella di memoria?

30 UNA PROPOSTA È possibile riusare una stessa area dati senza bisogno di crearne una nuova ad ogni passo computazionale? Ci sono controindicazioni?

31 VARIABILI NEI LINGUAGGI IMPERATIVI Una variabile in un linguaggio imperativo non è solo un sinonimo per un dato come in matematica è unastrazione della cella di memoria associata a due diverse informazioni: il contenuto (R-value) lindirizzo a cui si trova (L-value) 3.22 x

32 ESPRESSIONI CON EFFETTI COLLATERALI Le espressioni che contengono variabili, oltre a denotare un valore, possono a volte comportare effetti collaterali sulle variabili coinvolte. Un effetto collaterale è una modifica del valore della variabile (R-value) causato da particolari operatori: operatore di assegnamento operatori di incremento e decremento

33 ASSEGNAMENTO Lassegnamento è un particolare tipo di espressione –come tale denota comunque un valore!! con un effetto collaterale: quello di cambiare il valore della variabile. Sintassi variabile = espressione Esempi di espressioni di assegnamento: j = 0k = j + 1

34 ASSEGNAMENTO Lespressione di assegnamento variabile = espressione denota il valore dell espressione ma cambia anche il valore della variabile: il nuovo valore della variabile è quello denotato dalla espressione.

35 ESEMPIO Se k valeva 2, lespressione k = 7 denota il valore 7 e cambia il valore di k, che dora in poi vale 7 (non più 2)

36 ESEMPIO Se k valeva 2, lespressione j = k+1 denota il valore 3 e cambia il valore di j, che dora in poi vale 3 (qualunque valore avesse prima) Lassegnamento è distruttivo

37 ESPRESSIONI DI ASSEGNAMENTO Il valore denotato dallespressione di assegnamento può essere usato in altre espressioni. Ad esempio, 3 + (k=7) denota il valore 10 e cambia in 7 il valore di k

38 ASSEGNAMENTO & VARIABILI Una variabile in una espressione di assegnamento: è intepretata come il suo R-value, se compare a destra del simbolo = è intepretata come il suo L-value, se compare a sinistra del simbolo = 3.22 x

39 ESEMPIO Se x valeva 2, lespressione x = x + 1 denota il valore 3 e cambia in 3 il valore di x il simbolo x a destra delloperatore = denota il valore attuale (R-value) di x, cioè 2 il simbolo x a sinistra delloperatore = denota la cella di memoria associata a x (L-value), a cui viene assegnato il valore dellespressione di destra (3) lespressione nel suo complesso denota il valore della variabile dopo la modifica, cioè 3.

40 ASSEGNAMENTO: ASSOCIATIVITÀ Come tutti gli operatori, anche loperatore di assegnamento deve avere una sua associatività k = j = 1 Prima k=j, o prima j=1 ? loperatore di assegnamento è associativo a destra: ciò consente espressioni di assegnamento multiplo

41 ASSEGNAMENTO: ASSOCIATIVITÀ Esempi k = j = 1interpretato come k = (j = 1) i = j = k = 0interpretato come i = (j = (k=0)) i = k + 5 = 6NO: k+5 non ha un L-value! Nota: anche volendo, sarebbe stato impossibile farlo associativo a sinistra, in quanto ciò avrebbe reso molte espressioni prive di significato. Ad esempio: k = j = 2interpretato come (k=j) = 2 ??? Equivarrebbe a scrivere 1 = 2 !!!!

42 INCREMENTO (++) E DECREMENTO (--) Gli operatori di incremento e decremento sono usabili in due modi come pre-operatori:++v come post-operatori:v++ prima incremento, poi uso prima uso, poi incremento

43 ESEMPI int i, j, k = 5; i = ++k /* i vale 6, k vale 6 */ i = k++ /* i vale 5, k vale 6 */ int i=4, j, k = 5; j = i + k++; /* j vale 9, k vale 6 */ j = ++k - k++; /* in cerca di guai! */

44 ATTENZIONE…!! int k = 6; j = ++k - k++; /* in cerca di guai! */ Detti x = ++k e y = k++, è certo che lespressione venga valutata come j = x - y (da sinistra a destra) è certo che alla fine k valga 8 ma non si sa se venga calcolato prima x o prima y, e qui la cosa fa molta differenza! –se prima x, poi y j = = 0 –se prima y, poi x j = = 2

45 UN ESEMPIO main() { int f, c = 20; f = 32 + c * 9 / 5; } Lespressione f = 32 + c * 9 / 5 recupera l R-value della variabile c calcola il corrispondente valore Fahrenheit e lo assegna alla variabile f (interpretata come L-value effetto collaterale) scarta il valore denotato dallespressione di assegnamento (che non viene più utilizzato) Ad esempio, se c vale 20, lespressione vale … quindi a f viene asse- gnato il valore 68. Lespressione f=68 denota ancora 68, che però viene scartato.

46 ISTRUZIONI Le istruzioni esprimono azioni che, una volta eseguite, comportano una modifica permanente dello stato interno del pro- gramma o del mondo circostante. Le strutture di controllo permettono di aggregare istruzioni semplici in istruzioni più complesse.

47 ISTRUZIONI Una istruzione C è espressa dalle seguenti produzioni: ::= ::= ; Quindi, qualsiasi espressione seguita da un punto e virgola è una istruzione semplice.

48 ESEMPI DI ISTRUZIONI SEMPLICI x = 0; y = 1;/* due istruzioni */ x = 0, y = 1;/* una istruzione */ k++; 3;/* non fa nulla */ ;/* istruz. vuota*/

49 ISTRUZIONI DI CONTROLLO Una istruzione di controllo può essere: una istruzione composta (blocco) una istruzione condizionale (selezione) una istruzione di iterazione (ciclo) come specificato dalla produzione: ::= | |

50 ISTRUZIONI DI CONTROLLO Le istruzione di controllo sono alla base della programmazione strutturata (Dijkstra, 1969). Concetti chiave: concatenazione o composizione selezione o istruzione condizionale ramifica il flusso di controllo in base al valore vero o falso di una espressione (condizione di scelta) ripetizione o iterazione esegue ripetutamente unistruzione finché rimane vera una espressione (condizione di iterazione )

51 TEOREMA DI JACOPINI-BÖHM Le strutture di concatenazione, iterazione e selezione costituiscono un insieme completo in grado di esprimere tutte le funzioni calcolabili. Dunque, luso di queste sole strutture di controllo non limita il potere espressivo. La dimostrazione del teorema è basata sulla Turing-equivalenza di un mini-linguaggio che fornisca solo tali strutture di controllo.

52 BLOCCO ::= { [ ] { } } dopo un blocco non occorre il punto e virgola (esso termina le istruzioni semplici, non separa istruzioni) Lo scope dei simboli che compaiono entro il blocco è il blocco stesso

53 ESEMPIO DI BLOCCO main() {/* INIZIO BLOCCO */ const float F1=9.0, F2=5, SH=32; int c, f, temp = 20; char scala = 'C'; c = (scala != 'F') ? temp : (F2 / F1 * (temp - SH)) ; f = (scala != 'F') ? (SH+temp*F1/F2) : temp; } /* FINE BLOCCO */

54 ..una nota en passant... main() {/* INIZIO BLOCCO */ const float F1=9.0, F2=5, SH=32; int c, f, temp = 20; char scala = 'C'; c = (scala != 'F') ? temp : (F2 / F1 * (temp - SH)) ; f = (scala != 'F') ? (SH+temp*F1/F2) : temp; } /* FINE BLOCCO */ Il qualificatore const rende queste variabili non modificabili

55 ESEMPIO DI BLOCCHI ANNIDATI main() {/* INIZIO BLOCCO ESTERNO */ const float F1=9.0, F2=5, SH=32; int c, f, temp = 20; {/* INIZIO BLOCCO INTERNO */ char scala = C; c = (scala != 'F') ? temp : (F2 / F1 * (temp - SH)) ; f = (scala != 'F') ? (SH+temp*F1/F2) : temp; } /* FINE BLOCCO INTERNO */ } /* FINE BLOCCO ESTERNO */

56 ISTRUZIONI CONDIZIONALI ::= | la seconda non è essenziale, ma migliora lespressività. lespressione condizionale ternaria (.. ? … : …) fornisce già un mezzo per fare scelte, ma è poco leggibile in situazioni di medio/alta complessità. Listruzione di scelta fornisce un altro modo per esprimere alternative.

57 ISTRUZIONE DI SCELTA SEMPLICE ::= if [ else ] condizione verafalsa istruzione2istruzione1 Una espressione logica o relazionale, che viene valutata al momento della esecuzione dellistruzione if.

58 ISTRUZIONE DI SCELTA SEMPLICE ::= if [ else ] condizione verafalsa istruzione2istruzione1 La parte else è opzionale: se omessa, in caso di condizione falsa si passa subito allistruzione che segue lif.

59 ESEMPIO DI ISTRUZIONE if e sono ciascuna una singola istruzione Qualora occorra specificare più istruzioni, si deve quindi utilizzare un blocco. if (n > 0) { /* inizio blocco */ a = b + 5; c = (x<3) ? a : b; }/* fine blocco */ else n = b;

60 ISTRUZIONE if ANNIDATE Come caso particolare, o potrebbero essere un altro if Occorre attenzione ad associare le parti else (che sono opzionali) all if corretto if (n > 0) if (a>b) n = a; else n = b; /* riferito a if(a>b) */ if (n > 0) { if (a>b) n = a; } else n = b; /* riferito a if(n>0) */ Regola semantica: l else è sempre associato all if più interno Se ciò non soddisfa occor- re inserire esplicitamente un blocco.

61 ISTRUZIONE DI SCELTA MULTIPLA Consente di scegliere fra molte istruzioni (alternative o meno) in base al valore di una espressione di selezione. Lespressione di sele- zione deve denotare un valore numerabile (intero, carattere,…). espressione di selezione caso A istruzioni1 caso B istruzioni2 default istruzioni … break

62 ISTRUZIONE DI SCELTA MULTIPLA ::= switch ( selettore ) { case : [ break; ] case : [ break; ] … [ default : ] } Il valore dellespressione selettore viene confron- tato con le etichette dei vari casi: lesecuzione prosegue dal ramo corrispondente (se esiste). Se nessuna etichetta corri- sponde, si prosegue col il ramo default. Se neanche quello esiste, si prosegue con listruzione successiva allo switch.

63 ISTRUZIONE DI SCELTA MULTIPLA ::= switch ( selettore ) { case : [ break; ] case : [ break; ] … [ default : ] } Il valore dellespressione selettore viene confron- tato con le etichette dei vari casi: lesecuzione prosegue dal ramo corrispondente (se esiste). Le etichette sono costanti dello stesso tipo del selettore. Attenzione: denota una sequenza di istruzioni (non occorre un blocco)

64 ISTRUZIONE DI SCELTA MULTIPLA espressione di selezione caso A istruzioni1 caso B istruzioni2 default istruzioni … break I vari rami non sono mutua- mente esclusivi: imboccato un ramo, si eseguono anche tutti i rami successivi... … a meno che non ci sia il comando break a forzare esplicitamente luscita.

65 ISTRUZIONI DI ITERAZIONE ::= | | Per il Teorema di Jacopini-Böhm, una struttura di controllo iterativa sarebbe sufficiente: averne di più migliora lespressività del linguaggio. Le istruzioni di iterazione: hanno un solo punto di ingresso e un solo punto di uscita nel flusso del programma perciò possono essere interpretate come una singola azione in una computazione sequenziale.

66 ISTRUZIONE while ::= while( ) condizione vera falsa istruzione Listruzione viene ripetuta per tutto il tempo in cui la condi- zione rimane vera. Se la condizione è falsa, litera- zione non viene eseguita neppure una volta. In generale, non è noto quante volte listruzione sarà ripetuta.

67 ISTRUZIONE while ::= while( ) condizione vera falsa istruzione Prima o poi, direttamente o indirettamente, listruzione deve modificare la condizione: altrimenti, literazione durerà per sempre! Perciò, quasi sempre istruzione è un blocco, al cui interno si modifica qualche variabile che compare nella condizione.

68 ISTRUZIONE do...while ::= do while( ); È una variazione sul tema della precedente: la condizione viene verificata dopo aver eseguito listruzione. Se la condizione è falsa, litera- zione viene comunque ese- guita almeno una volta. condizione vera falsa istruzione particolarmente adatta alle verifiche dopo un input

69 ISTRUZIONE for È una evoluzione dellistruzione while rispetto a cui mira a eliminare alcune frequenti sorgenti di errore: mancanza delle necessarie inizializza- zioni delle variabili mancanza della fase di modifica del ciclo (rischio di ciclo senza fine)

70 ISTRUZIONE for ::= for( ; ; ) condizione vera falsa istruzione espr-inizializzazione espr-modifica Struttura del while

71 ISTRUZIONE for ::= for( ; ; ) condizione vera falsa istruzione espr-inizializzazione espr-modifica Espressione di inizia- lizzazione: valutata una e una sola volta prima di iniziare litera- zione.

72 ISTRUZIONE for ::= for( ; ; ) Condizione: valutata a ogni interazione, per decidere se proseguire (come in un while) condizione vera falsa istruzione espr-inizializzazione espr-modifica Condizione: valutata a ogni interazione, per decidere se proseguire (come in un while) Se manca si assume vera!

73 ISTRUZIONE for ::= for( ; ; ) Condizione: valutata a ogni interazione, per decidere se proseguire (come in un while) condizione vera falsa istruzione espr-inizializzazione espr-modifica Espressione di modifica: valutata a ogni interazione, dopo aver eseguito listru- zione.

74 UN ESEMPIO Il solito problema: calcolo del fattoriale Dallapproccio ricorsivo…...allapproccio sintatticamente ricorsivo, ma computazionalmente iterativo (ricorsione tail)......allapproccio iterativo tramite istruzioni di iterazione.

75 IL FATTORIALE ITERATIVO TRAMITE ESPRESSIONE CONDIZIONALE... int factIter(int n, int i, int v){ /* inizialmente, v = 1 */ /* invariante di ciclo: v = i! */ return (i==n) ? v : factIter(n,i+1,(i+1)*v); } Chiamata: factIter(n,0,1)

76 ... IL FATTORIALE ITERATIVO TRAMITE ISTRUZIONE CONDIZIONALE... int factIter(int n, int i, int v){ /* inizialmente, v = 1 */ /* invariante di ciclo: v = i! */ if (i==n) return v; else return factIter(n,i+1,(i+1)*v); } Chiamata: factIter(n,0,1)

77 ...IL FATTORIALE ITERATIVO TRAMITE ISTRUZIONE DI ITERAZIONE: while int fact(int n){ int v=1; /* inizialmente, v = 1 */ int i=0; /* inizialmente, i = 0 */ while (i

78 ...IL FATTORIALE ITERATIVO TRAMITE ISTRUZIONE DI ITERAZIONE: do...while int fact(int n){ int v=1; /* inizialmente, v = 1 */ int i=0; /* inizialmente, i = 0 */ do { /* invariante: v = i! */ v = (i+1)*v; i = i+1; } while (i

79 ...IL FATTORIALE ITERATIVO TRAMITE ISTRUZIONE DI ITERAZIONE: for int fact(int n){ int v=1; /* inizialmente, v = 1 */ int i; for (i=0;i

80 ITERAZIONE & RICORSIONE TAIL Poiché la ricorsione tail dà luogo a un processo computazionale di tipo iterativo, deve essere possibile trasformare un ciclo in ricorsione tail e viceversa. COME FARLO? il corpo del ciclo rimane immutato il ciclo diventa un if con, in fondo, la chiamata tail-ricorsiva.

81 ITERAZIONE & RICORSIONE TAIL il corpo del ciclo rimane immutato il ciclo diventa un if con, in fondo, la chiamata tail-ricorsiva. Naturalmente, può essere necessario aggiungere nuovi parametri nellintestazione della funzione tail- ricorsiva, per portare avanti le variabili di stato.

82 ESEMPIO: Massimo Comun Divisore La soluzione tail-ricorsiva già vista... int mcd(int m, int n){ if (m!=n)if (m>n) return mcd(m-n, n); else return mcd(m, n-m); else return m; } … opportunamente riscritta... int mcd(int m, int n){ if (m!=n) { if (m>n) m=m-n; else n=n-m; return mcd(m,n); } else return m; }

83 ESEMPIO: Massimo Comun Divisore … opportunamente riscritta... int mcd(int m, int n){ if (m!=n) { if (m>n) m=m-n; else n=n-m; return mcd(m,n); } else return m; } … traslata in ciclo: int mcd(int m, int n){ while (m!=n) if (m>n) m=m-n; else n=n-m; return m; }

84 ESERCIZIO 1 Dati tre valori a b c che rappresentano le lunghezze di tre segmenti, valutare se posso-no essere i tre lati di un triangolo, e se sì deci-derne il tipo (scaleno, isoscele, equilatero). Vincolo: deve essere c < (a+b) Rappresentazione delle informazioni: la variabile booleana triangolo indica se i tre seg- menti possono costituire un triangolo le variabili booleane scaleno, isoscele e equil indicano il tipo di triangolo.

85 ESERCIZIO 1 Specifica: se a+b>c triangolo = vero se a=b=c { equil=isoscele=vero scaleno=falso } altrimenti se a=b o b=c o a=c { isoscele=vero; equil=scaleno=falso } altrimenti { scaleno=vero; equil=isoscele=falso } altrimenti triangolo = falso

86 ESERCIZIO 1 main (){ float a=1.5, b=3.0, c=4.0; int triangolo, scaleno, isoscele, equil; triangolo = (a+b>c); if (triangolo) { if (a==b && b==c) { equil=isoscele=1; scaleno=0; } else if (a==b || b==c || a==c) { isoscele=1; scaleno=equil=0;} else { scaleno=1; isoscele=equil=0;} }

87 ESERCIZIO 1 main (){ float a=1.5, b=3.0, c=4.0; int triangolo, scaleno, isoscele, equil; triangolo = (a+b>c); if (triangolo) { if (a==b && b==c) { equil=isoscele=1; scaleno=0; } else if (a==b || b==c || a==c) { isoscele=1; scaleno=equil=0;} else { scaleno=1; isoscele=equil=0;} } Attenzione! Una espressione come a==b==c sarebbe stata formalmente lecita, ma avreb- be avuto tuttaltro significato! Non si può usare listruzione di scelta multipla (switch) perché le condizioni da verificare sono uguaglianze fra variabili, non semplici confronti con etichette predefinite.

88 ESERCIZIO 2 Dati due valori positivi X e Y, calcolarne la divisione intera X/Y come sequenza di sottrazioni, ottenendo quoziente e resto. Invariante di ciclo: X = Q * Y + R, con R 0 inizialmente, Q=0, R=X (R>Y) a ogni passo, Q=Q+1, R=R-Y (R>Y) alla fine, X = Q (n) * Y + R (n) (0

89 ESERCIZIO 2 Specifica: sia Q il quoziente, inizialmente pari a 0 sia R il resto, inizialmente pari a X while (R Y) incrementare il quoziente Q decrementare R di una quantità Y Codifica main(){ int x = 20, y = 3, q, r; for (q=0, r=x; r>=y; q++, r=r-y); } Notare luso di una espressione concatenata per concatenare due assegnamenti e inizializzare così due variabili. Idem per le operazioni di modifica

90 OPERATORI DI ASSEGNAMENTO COMPATTI Il C introduce una forma particolare di assegnamento che ingloba anche unoperazione aritmetica o bit a bit: l-espr = è quasi equivalente a l-espr = l-espr dove indica un operatore fra +, –, *, /, %, >>, <<, &, ^, |

91 OPERATORI DI ASSEGNAMENTO COMPATTI Perché quasi equivalente ? nel primo caso, l-espr viene valutata una sola volta nel secondo, invece, viene valutata due volte Quindi, le due forme sono equivalenti solo se la valutazione di l-espr non comporta effetti collaterali

92 OPERATORI DI ASSEGNAMENTO COMPATTI Esempi k += j equivale a k = k + j k *= a + b equivale a k = k*(a+b)*/ v[i++] *= n non equivale a v[i++] = v[i++]*n */

93 ESERCIZIO 3 Dati tre valori a, b, c, rappresentanti i coeffi- cienti di unequazione di secondo grado a x 2 + b x + c = 0, calcolarne le radici (reali). Specifica: Calcolare il valore delta = b 2 - 4ac Se delta 0 calcolare d = delta calcolare le due radici x1, x2 = - (b d) / 2a altrimenti (radici complesse: halt)

94 ESERCIZIO 3 #include main (){ float a=1.0, b=2.0, c=-15.0; float delta, d, x1, x2; delta = b*b-4*a*c; if (delta>=0){ d = sqrt(delta); x1 = -(b+d)/(2*a); x2 = -(b-d)/(2*a); } Direttiva al preprocessore: include la libreria matematica (fornisce la funzione sqrt )

95 ESERCIZIO 4 Scrivere una funzione che verifichi se un naturale N è primo. Specifica di I° livello (Crivello di Eratostene): Occorre provare a dividere N per tutti i numeri K N: se nessuno risulta essere un divisore, allora N è primo Specifica di II° livello: Se N è 1, 2 o 3, allora è primo senzaltro. Altrimenti, se è un numero pari, non è primo. Se invece N è dispari e >3, occorre tentare tutti i possibili divisori da 3 in avanti, fino a N.

96 ESERCIZIO 4 #include int isPrime(int n) { int max,i; if (n>=1 && n<=3) return true; /* 1,2,3 ok */ if (n%2==0) return false; /* numeri pari no */ max = sqrt(n); for(i=3; i<=max; i+=2) if (n%i==0) return false; return true; }

97 ESERCIZIO 5 Scrivere una funzione radice che calcoli la radice quadrata (intera) di un naturale N. Specifica di I° livello: int radice(int n); restituisce il massimo intero X tale che X*X N Specifica di II° livello: Considera un naturale X dopo laltro a partire da 1, e calcolane il quadrato X*X: fermati appena tale quadrato supera N. Il precedente numero considerato (X-1) è il risultato.

98 ESERCIZIO 5 int radice(int n) { int x; for(x=0; x*x <= n; x++); return x-1; } Il corpo del ciclo è vuoto: in effetti, lelaborazione consiste solo nellincrementare x per un opportuno numero di volte.

99 ESERCIZIO 6 Scrivere una funzione che, dato un carattere C, restituisca il corrispondente maiuscolo. Specifica di I° livello: char maiuscolo(char c); restituisce il maiuscolo di C Specifica di II° livello: Se C non è una lettera minuscola, restituiscilo tale e quale. Altrimenti, per calcolare il corrispondente maiu- scolo, sfrutta l ordinamento della codifica dei caratteri: – ogni carattere è associato a un intero – le lettere da A a Z sono in sequenza – le lettere da a a z sono in sequenza

100 ESERCIZIO 6 char maiuscolo(char c) { if (c 'z') return c; else return c – 'a' + 'A'; } Aritmetica fra caratteri: possibile perché le operazioni vengono svolte sul corrispondente codice (ASCII o UNICODE) Attenzione! Una espressione come 'a


Scaricare ppt "RICORSIONE & ITERAZIONE Riconsideriamo lesempio del Massimo Comun Divisore visto tempo addietro: m, se m=n MCD(m, n-m),se m

Presentazioni simili


Annunci Google