Il controllo del flusso Dai diagrammi di flusso alla programmazione strutturata Il flusso • L’esecuzione di un programma (imperativo) consiste in una successione di trasformazioni dello stato • Ogni trasformazione è l’effetto dell’esecuzione di un’istruzione • Il flusso è l’ordine in cui le istruzioni di un programma vengono eseguite. Diagrammi di flusso • I diagrammi di flusso sono una rappresentazione in forma di grafo del flusso determinato da un programma inizio/fine input/output assegnazione direzione del flusso decisione Diagramma per la media aritmetica inizio false leggi n i < n true i = 0 m = 0 leggi k m = m + k i = i + 1 stampa m/n fine Diagrammazione strutturata • L’uso indiscriminato dei diagrammi di flusso può produrre schemi caotici • La diagrammazione strutturata individua e compone sottodiagrammi con una sola entrata ed una sola uscita • A ciascuno di essi si attribuisce un costrutto programmativo (ossia un modo per rappresentarlo in un programma C/C++ ad es.) Sequenza Se un Ci consta di più istruzioni queste sono racchiuse tra graffe C1; C1 C2; C2 … … Ck; Ck sequenza Nota: in C/C++ “;” non è un operatore di composizione, ma un semplice terminatore. Esempio: area di un rettangolo inizio leggi base leggi altezza area = base*altezza stampa area fine Esempio: area di un rettangolo int main() { double base, altezza, area; cout << "base = "; cin >> base; cout << "altezza = "; cin >> altezza; area = base*altezza; cout << "area = " << area << endl; return 0; } inizio leggi base leggi altezza area = base*altezza stampa area fine Selezione if (B) true B false C; C else D D; if (<espr. booleana>) <comando>; else <comando>; selezione a due vie Selezione if (B) true B false C; C if (<espr. booleana>) <comando>; selezione a due vie Esempio: equazione di 1° grado ax = b Discussione: se a = 0 non esiste soluzione, altr. x = b/a. inizio leggi a, b true a ≠ 0 stampa b/a false stampa “no sol.” fine Esempio: equazione di 1° grado int main() { inizio double a, b; cout << "a = "; cin >> a; cout << "b = "; leggi a, b true a ! 0 stampa b/a cin >> b; false stampa “no sol.” fine if (a != 0) cout << "x = " << b/a << endl; else cout << "Non esiste soluzione" << endl; return 0; } Esempio: equazione di 2° grado 2 ax + bx + c = 0 " = b 2 ! 4ac • Δ > 0: due soluzioni reali • Δ = 0: una soluzione reale • Δ < 0: non ci sono soluzioni reali. "b± ! 2a b ! 2a Esempio: equazione di 2° grado inizio leggi a, b, c true Delta = b*b – 4*a*c Delta > 0 radDelta = √Delta Delta = 0 true stampa (– b ± radDelta)/(2*a) Decisione annidata false stampa –b/(2*a) fine false stampa “no sol. reali” Esempio: equazione di 2° grado double Delta = b*b - 4*a*c; Radice quadrata (funzione di libreria) if (Delta > 0) { double radDelta = sqrt(Delta); cout << "prima sol. reale = " << (-b + radDelta)/2*a << endl; cout << "seconda sol. reale = " << (-b - radDelta)/2*a << endl; } else if (Delta == 0) cout << "Unica sol. reale = " << -b/(2*a) << endl; else cout << "Nessuna soluzione reale" << endl; Si riferisce al secondo if If annidati La clausola else si riferisce all’ultimo if non accoppiato con un else precedente: if (B) if (B’) C; else C’; else D; L’istruzione D verrà eseguita se B è falso; L’istruzione C’ verrà eseguita se B è vero, ma B’ è falso. If annidati La clausola else si riferisce all’ultimo if non accoppiato con un else precedente: if (B) if (B’) C; else D; L’istruzione D verrà eseguita se B è vero e B’ è falso, non se B è falso (come l’indentazione suggerirebbe). If annidati La clausola else si riferisce all’ultimo if non accoppiato con un else precedente: if (B) { if (B’) C; } else D; L’istruzione D verrà eseguita se B è falso. le parentesi sono necessarie L’iterazione: while • L’iterazione è l’esecuzione ripetuta di una o più istruzioni (corpo), controllata da un test. while (B) C; true B false C while (<espr. booleana>) <comando>; Esempio: somma dei reciproci • Dato un intero n calcolare: n 1 1 1 1 s = ! = 1+ + +L+ 2 3 n i =1 i Esempio: somma dei reciproci int main() { int n; cout << "n = "; cin >> n; double s = 0.0; int i = 0; while (i < n) { Scrivendo: s = s + 1/i; la divisione 1/i è intera, dunque troncata della parte frazionaria i++; s = s + 1.0/i; } cout << "La somma dei primi " << n << " reciproci = " << s << endl; return 0; } L’iterazione: for for ([<iniz..>]; [<test>]; [<step>]) <comando> Le inizializzazioni sono definizioni di variabili o assegnazioni di valori a variabili precedentemente dichiarate; se più d’una, separate da virgole. Lo step è l’incremento di una o più variabili; se più d’uno, separati da virgole. Nota: a differenza dal Pascal, il test è un’espressione booleana arbitraria. L’iterazione: for for (I; B; S) Eseguito una sola volta prima di entrare nel ciclo I C; true B false C S Eseguito al termine di ogni iterazione L’iterazione: for • In C/C++ iterazione while ed iterazione for sono interdefinibili: for(I;B;S) C; = {I; while(B){C;S;}} while(B) C; = for(;B;) c; Esempio: media aritmetica int main() { int n; cout << "Media di n valori, n = "; cin >> n; int i, k, m = 0; for (i = 0; i < n; i++) { cout << "valore " << i+1 << " = "; cin >> k; m = m + k; } Casting necessario per la divisione senza troncamento cout << "Media = " << (float) m/(float) n << endl; return 0; } L’iterazione: do while do <comando>; while (<espr. booleana>); Il corpo dell’iterazione viene eseguito almeno una volta do C C; while (B); true B false Nota: il Pascal ha do … until, che esce però quando il test è vero. Esempio: fattoriali int main() { long limite; cout << "Inserire un intero positivo: "; cin >> limite; cout << "Numeri fattoriali < " << limite << endl; long f = 1, i = 1; do { cout << " " << f; f = f * i; i++; } while (f < limite); return 0; } Programmazione strutturata • Un’ istruzione di salto è ad esempio: goto <etichetta> dove l’etichetta indica il punto del programma a cui saltare. • La programmazione strutturata proibisce l’uso di istruzioni di salto, e si basa soltanto su sequenza, selezione ed iterazione per controllare il flusso. Programmazione strutturata • Se si parte da un diagramma non strutturato sarà difficile scrivere un programma strutturato. E’ possibile trasformare un diagramma arbitrario in uno equivalente (e quindi in un programma) strutturato? Si: teorema di Böhm-Jacopini 1966 Teorema di Böhm-Jacopini Ogni funzione Turing calcolabile è programmabile in un linguaggio di programmazione che controlli il flusso soltanto con sequenziazione, selezione ed iterazione. Per mostrare l’idea della prova (senza introdurre le funzioni Turing-calcolabili) assumiamo che tutti gli algoritmi si possano descrivere con diagrammi di flusso, e mostriamo come un diagramma qualsiasi si possa tradurre in un programma strutturato. Teorema di Böhm-Jacopini Ad ogni nodo del grafo associamo un numero 1 + 2 3 - B1 C1 + C4 B2 4 C2 - + 5 8 C5 10 B3 9 6 C3 7 Teorema di Böhm-Jacopini 1 Quindi usiamo una variabile contatore per sapere come proseguire dopo ogni istruzione/decisione + 2 3 - B1 C1 + C4 B2 4 C2 - + 5 8 C5 10 B3 9 6 C3 7 Teorema di Böhm-Jacopini 1 int cp = 1; // cp = cont. programma + while (cp < 10) { if (cp == 1) cp = 2; 2 3 - B1 C1 4 C2 else if (cp == 2) { if (B1) cp = 3; else cp = 4;} else if (cp == 3) {C1; cp = 5;} else if (cp == 4) {C2; cp = 6;} else if (cp == 5) { if (B2) cp = 8; else cp = 4;} else if (cp == 6) { if (B3) cp = 9; else cp = 7;} else if (cp == 7) {C3; cp = 2;} else if (cp == 8) {C4; cp = 3;} else if (cp == 9) {C5; cp = 10} } + C4 B2 - + 5 8 C5 10 B3 9 6 C3 7 Selezione a più vie: Switch switch (<espressione>) { case <costante_1>: <istruzioni>; break; ... case <costante_k>: <istruzioni>; break; [default: <istruzioni>;] } Esecuzione: si confronta il valore dell’espressione con le costanti; la prima che coincide col valore causa l’esecuzione della sequenza di istruzioni relativa; l’istruzione break forza l’uscita dal blocco (tra parentesi {…}); si eseguono le istruzioni che seguono default (se presente) quando nessuna delle costanti eguagli il valore. Teorema di Böhm-Jacopini int cp = 1; // cp = cont. programma while (cp < 10) switch (cp) {case 1: cp = 2; break; case 2: if (B1) cp = 3; else cp = 4; break; case 3: C1; cp = 5; break; case 4: C2; cp = 6; break; case 5: if (B2) cp = 8; else cp = 4; break; case 6: if (B3) cp = 9; else cp = 7; break; case 7: C3; cp = 2; break; case 8: C4; cp = 3; break; case 9: C5; cp = 10; break; } Equivalente al precedente, con l’uso di switch Conclusioni e … • Per il controllo del flusso sono sufficienti i costrutti programmativi della programmazione strutturata (i.e. senza salti). • La dimostrazione del teorema di BöhmJacopini non dà alcuna informazione su come trasformare (rendendolo chiaro) un diagramma caotico in uno strutturato. • Per questo motivo non useremo i diagrammi di flusso, ma scriveremo direttamente il codice (o lo pseudo-codice) in forma strutturata. … deprecazioni Il C/C++ comprende istruzioni di salto di cui però non faremo uso: • goto • break (salvo se usato con switch) • continue • exit.
© Copyright 2024 Paperzz