La presentazione è in caricamento. Aspetta per favore

La presentazione è in caricamento. Aspetta per favore

Parte 4 Dai diagrammi di flusso alla programmazione strutturata: le istruzioni if, for, while, … Informatica A.A. 2009/2010 Corso A: Prof. Stefano Berardi.

Presentazioni simili


Presentazione sul tema: "Parte 4 Dai diagrammi di flusso alla programmazione strutturata: le istruzioni if, for, while, … Informatica A.A. 2009/2010 Corso A: Prof. Stefano Berardi."— Transcript della presentazione:

1 Parte 4 Dai diagrammi di flusso alla programmazione strutturata: le istruzioni if, for, while, … Informatica A.A. 2009/2010 Corso A: Prof. Stefano Berardi Corso B: Prof. Ugo de’ Liguoro

2 “Corrado Bohm” Milano, Professore emerito di Teoria e applicazione delle macchine calcolatrici presso l’Università di Roma La Sapienza

3 Indice Parte 4: Diagrammi 1.Il flusso dell’esecuzione e i diagrammi di flusso. 2.Il salto condizionale: l’istruzione IF. 3.Iterazione: le istruzioni WHILE e FOR. 4.La programmazione strutturata e il teorema di Bohm-Jacopini. I diagrammi di flusso venivano usati per descrivere i programmi nell’epoca in cui si programmava in Assembly. Oggi sono ancora adatti per spiegare programmi molto brevi.

4 1. Il flusso dell’esecuzione L’esecuzione dei programmi C++, assembly, e in generale dei programmi detti “imperativi”, consiste in una successione di trasformazioni dello stato (delle memorie della RAM e del program counter, l’indirizzo della prossima istruzione da eseguire) Ogni trasformazione di stato è l’effetto dell’esecuzione di un’istruzione. Per es., una assegnazione x=3; oppure un’istruzione JB L1 che fa “saltare” l’esecuzione a un’istruzione lontana di indirizzo L1 se una certa condizione è vera. Il flusso è l’ordine in cui le istruzioni di un programma vengono eseguite. 4-Diagrammi

5 Diagrammi di flusso I diagrammi di flusso sono una rappresentazione in forma di grafo del flusso dell’esecuzione di un programma. Erano un progresso rispetto al linguaggio assembly, perché spostavano l’attenzione dalle istruzioni del programma alla loro relazione reciproca. inizio/fine assegnazione decisione input/output direzione del flusso 4-Diagrammi

6 Esempio di diagramma di flusso: area di un rettangolo inizio leggi base area = base*altezza leggi altezza stampa area fine Ogni freccia rappresenta la scelta della prossima istruzione da eseguire.

7 Il programma C++ corrispondente int main() {double base, altezza, area; cout << "base = "; cin >> base; cout << "altezza = "; cin >> altezza; area = base*altezza; cout << "area = " << area << endl; system(“pause”); } 4-Diagrammi

8 Sequenza di diagrammi e Blocchi di istruzioni in C++ {C1; C2; … Ck;} Blocco di istruzioni C1 C2 Ck … Più in generale, rappresentiamo una sequenza di diagrammi in C++ scrivendo le istruzioni che li rappresentano di seguito, quindi racchiudendole tra graffe, per formare una singola istruzione detta istruzione composta o blocco. Sequenza di diagrammi 4-Diagrammi

9 Il flusso dell’esecuzione in C++ Per gestire le variabili il C++ segue da vicino la nozione di indirizzo di macchina di von Neumann. Invece per la gestione dei “salti” da una istruzione all’altra il C++ segue un’idea completamente diversa, quella della programmazione strutturata. Noi spiegheremo la programmazione strutturata attraverso i diagrammi di flusso. I diagrammi di flusso spiegano come un programma funziona, ma hanno l’incoveniente di essere leggibili solo per programmi brevissimi. 4-Diagrammi

10 Programmazione strutturata La programmazione strutturata individua i diagrammi di flusso piu’ utili tra quelli con una sola entrata ed una sola uscita. Per ciascuno di essi viene introdotto una istruzione: blocchi, IF, WHILE, FOR, … del C/C++. I programmi in C++ sono costruiti con le istruzioni if, while, for cosi’ introdotte. Oggi usiamo i diagrammi di flusso per introdurre le istruzioni IF, WHILE, FOR, … e in pochi altri casi. Vediamo ora i diagrammi di flusso più importanti insieme alla loro traduzione in C++. 4-Diagrammi

11 2. Il diagramma di selezione (IF) L’istruzione IF rappresenta in C++ e in quasi tutti i linguaggi il seguente “diagramma di selezione a due vie” L’IF fa “saltare” l’esecuzione all’indirizzo di C se l’espressione B vale vero, e all’indirizzo dell’istruzione D se B vale falso. if (B) C; else D; B CD truefalse

12 L’istruzione IF in C++ if ( ) ; else ; L’IF viene tradotto nei compilatori nel linguaggio Assembly con un salto condizionato, che fa eseguire come prossima istruzione C oppure D a seconda se B è vero oppure falso. Noi utilizzeremo l’IF solo come istruzione primitiva, con la seguente sintassi: Al posto di istruzione1 e istruzione2 possiamo scrivere una qualunque istruzione o blocco di istruzioni del C++, compresi altri IF: in questo caso si parla di IF annidati. Il C++ consente un secondo tipo di IF, l’IF tronco.

13 IF “tronco” (senza ELSE) if (B) C; if ( ) ; B C truefalse L’IF tronco rappresenta la selezione a due vie con una sola azione C. Se l’espressione booleana B è falsa saltiamo all’istruzione successiva senza far nulla Vediamo ora alcuni esempi di uso di IF, IF tronco e IF annidato

14 Primo esempio di IF: equazione di 1° grado ax=b Discussione: se a = 0 eq. indeterminata o impossibile, altrimenti x = b/a. inizio leggi a, b a  0 truefalse stampa b/astampa “indet. o imposs.” fine 4-Diagrammi

15 Il programma per l’equazione di 1° grado int main() { double a, b; cout << "a = "; cin >> a; cout << "b = "; cin >> b; if (a != 0) cout << "x = " << b/a << endl; else cout << “Indet. o impossibile" << endl; system(“pause”);} 4-Diagrammi

16 Secondo esempio di IF: equazione di 2° grado, IF “annidati” 1.  > 0: due soluzioni reali 2.  = 0: una soluzione reale 3.  < 0: non ci sono soluzioni reali. Per scrivere questo programma dovremo utilizzare IF scritti dentro altri IF (IF“annidati”), per distinguere il secondo caso dal terzo. Sia:

17 Esempio di IF: diagramma equazione di 2° grado inizio leggi a, b, c Delta=b*b–4*a*c Delta>0 Stampa: (– b  rad)/(2*a) true rad=  Delta Delta=0 Stampa:–b/(2*a) Stampa: “no sol. reali” fine true false Questa viene detta una decisione annidata 4-Diagrammi

18 Soluzione eq. di 2° grado in C++ main(){ double a, b, c; cin >> a; cin >> b; cin >> c; double Delta = b*b - 4*a*c; if (Delta > 0){double rad= sqrt(Delta); cout << "prima sol. reale = " << (-b + rad)/2*a << endl; cout << "seconda sol. reale = "<< (-b - rad)/2*a<

19 Quando ci sono IF “annidati” come accoppiamo un if con un else? Per definizione, la clausola else si riferisce all’ultimo if non accoppiato con un else precedente: if (B) if (B’) C; else C’; else D; Il secondo “else” viene accoppiato con il primo IF. In questo caso, l’istruzione D verrà eseguita se B è falso; l’istruzione C’ verrà eseguita se B è vero, ma B’ è falso. 4-Diagrammi

20 If “annidati”: un altro esempio ambiguita’ Dato che la clausola else si riferisce all’ultimo if non accoppiato con un else precedente, se manca un solo else (se un IF è tronco) tutto cambia: if (B) if (B’) C; else D; Questa volta “else” viene accoppiato con il secondo IF. Dunque l’istruzione D verrà eseguita se B è vero e B’ è falso, non se B è falso. 4-Diagrammi

21 If annidati: come eliminare le ambiguita’ Avvolgendo ogni sottoistruzione in parentesi graffa ogni ambiguitá scompare: if (B) { if (B’) C; } else D; L’istruzione D verrà eseguita se B è falso. Le parentesi non sono sempre necessarie come in questo caso, peró sono sempre utili per aggiungere chiarezza. Queste parentesi sono necessarie se vogliamo accoppiare il primo IF con l’else Esempio: es Hubbard: ``indovina il numero’’

22 Come rappresentare una scelta tra n+1 blocchi if (B1) ;//se B1 vera else if (B2) ;//se B1 falsa e B2 vera else if (B3) ;//se B1,B2 false e B3 vera ………… else ;//se B1,…,Bn false È sufficiente annidare n comandi IF, ciascuno dopo l’else dell’IF precedente. Il risultato è un’istruzione composta che esegue il primo blocco se la prima espressione booleana è vera, il secondo blocco se la prima è falsa e la seconda è vera, eccetera, l’ultimo blocco se tutte le espressioni booleane sono false.

23 Un IF con più di due scelte: l’istruzione SWITCH(sconsigliata) Un tempo per rappresentare la scelta tra più di due alternative veniva usata una istruzione chiamata SWITCH. Per completezza nel prossimo lucido spieghiamo il funzionamento dello SWITCH. Tuttavia ne sconsigliamo l’uso, sia per ragioni semplicità di scrittura, sia perché è facile sbagliarsi a usarlo. Al posto dello SWITCH, usate un IF annidato come spiegato nel lucido precedente.

24 Esecuzione dello SWITCH switch ( ) { case : ; break;... case : ; break; default: ; } Esecuzione dello SWITCH. Confrontiamo il valore dell’espressione con le costanti. Trovata la prima che coincide col valore eseguiamo le istruzioni relative. Arrivati al break usciamo dal blocco (le parentesi {…}). Quando non troviamo una costante uguale al valore, eseguiamo le istruzioni che seguono il “default”.

25 3. L’iterazione: il ciclo while L’iterazione è l’esecuzione ripetuta di una o più istruzioni (dette “corpo” dell’iterazione). Un diagramma iterativo viene detto un ciclo. Un ciclo è controllata dal valore di verità di un certo test. Viene tradotto dal compilatore in linguaggio Assembly mediante un “salto” condizionato JB (ovvero mediante una assegnazione del program counter) che va “all’indietro”, verso una istruzione già eseguita in precedenza. Come primo e fondamentale esempio di ciclo vediamo il ciclo while.

26 Il diagramma di flusso del ciclo while while (B) C; B C truefalse Il corpo puo’ essere un qualunque blocco di istruzioni C++. Finché B è vero, il while esegue il corpo C e poi di nuovo B. La prima volta che B è falso il while termina: dunque se B è sempre vero il while non termina mai. La sintassi del while è while ( ) ; Un ciclo while rappresenta il seguente diagramma di flusso:

27 Un programma che richiede il while: la somma dei reciproci Problema: dato un intero n calcolare e stampare: 4-Diagrammi Soluzione: utilizziamo il seguente algoritmo. Partiamo con s=0, i=1. Finché i n, terminiamo e stampiamo s, che a questo punto vale (1+1/2+1/3 + … 1/n). Vediamo ora come tradurre tutto con un while.

28 Somma dei reciproci calcolata con un while int main() { int n; cout > n; double s = 0.0; int i = 1; while (i <= n) {s = s + 1.0/i; i++;} cout << "La somma dei primi " << n << " reciproci = " << s << endl; system("pause");} Scrivendo invece: s = s + 1/i; la divisione 1/i è intera, dunque viente arrotondata: per es. 1/2 diventa 0 ++i è detto incremento. Cosa succede se lo eliminiamo ???

29 Il ruolo di ++i nella “somma dei reciproci” L’incremento ++i modifica il valore di verità del test,l’azione svolta dal corpo, ed è cruciale per il buon funzionamento del ciclo. Se lo eliminiamo e lasciamo: while (i <= n) {s = s + 1.0/i;} allora, mentre ripetiamo il corpo {s = s + 1.0/i;} del ciclo while, il valore di i non cambia. Resta il valore iniziale i=1. Dunque continuiamo a eseguire s=s+1.0/i con i=1: quindi continuiamo a eseguire s = s + 1 ; ne segue che s assume i valori 0,1,2,3,4, …, anziche’ 1,1+1/2, … come dovrebbe. Inoltre il test (i <= n) resta uguale a (1 <= n) e quindi vale sempre true, il ciclo continua per sempre.

30 Un altro esempio di iterazione: il ciclo for for ([ ]; [ ]; [ ]) Le inizializzazioni sono definizioni di variabili o assegnazioni di valori a variabili precedentemente dichiarate; se più d’una, sono separate da virgole (e non da punti e virgole come al solito) Lo step o incremento di solito è l’incremento di una o più variabili; se più d’una, sono separate da virgole. L’incremento serve a modificare il valore di verità del test e, a volte, l’azione svolta dal corpo. Nota. A differenza di quanto avviene per il for nel Pascal, il test è un’espressione booleana arbitraria. 4-Diagrammi

31 Il diagramma di flusso per il ciclo for for (I; B; S) C; I B C S true false L’inizializza- zione I viene eseguita una sola volta prima di entrare nel ciclo Lo step S viene eseguito al termine di ogni esecuzione del corpo C Al termine di S avviene sempre un salto all’indietro, verso l’istruzione che prima calcola B, poi a seconda del valore di B sceglie se ripetere il corpo del for o saltare alla prima istruzione dopo il for

32 La relazione tra while e for A differenza di quanto succede in altri linguaggi, in C++ il ciclo while ed il ciclo for sono interdefinibili, usando le seguenti equazioni: for(I;B;S) C; = {I; while(B){C;S;}} while(B) C; = for(;B;) C; Ne segue che la differenza tra i due cicli è solo di stile: di solito il for è più chiaro, perché mette in evidenza la parte di inizializzazione e incremento. Come capita per il while, il for non termina mai se il test B resta sempre vero. Vediamo ora come usare il for per ripetere n volte la stessa azione. 4-Diagrammi

33 Il for consente di ripetere n volte una certa azione for(i=0,n=100;i

34 Un diagramma per il calcolo della media aritmetica m = (a 0 + … a n-1 )/ n inizio leggi n i = 0 i < n s = 0 leggi a s = s + a stampa m = s/n fine i = i + 1 true false a = i-esimo addendo a i A partire da questo diagramma di flusso si puó scrivere un programma in C++ usando un ciclo FOR

35 Il diagramma di flusso per la media (definito da un for ) for (I; B; S) C; i = 0 s = 0 i

36 Un programma C++ per la media aritmetica int main(){int n; int i, a, s; cout > n; for (i = 0, s = 0; i < n; i++) { cout << "inserisci un valore="; cin >> a; s = s + a;} cout << "Media = " << (double) s / (double) n << endl; system("pause");} “Casting” necessario per avere la divisione senza troncamento ++i è detto incremento. Se lo eliminiamo cosa succede ??? 4-Diagrammi

37 Il ruolo di ++i nella “media aritmetica” L’incremento ++i serve a modificare il valore di verità del test, ed è cruciale per il buon funzionamento del ciclo. Se lo eliminiamo e lasciamo: for (i = 0, s = 0; i < n; ) {cout << "inserisci un valore="; cin >> a; s = s + a;} allora mentre il corpo del ciclo for viene ripetuto il valore di i non cambia, resta il valore iniziale i=0. Dunque il test (i < n) resta uguale (0 < n) e quindi vale true. Il ciclo for continua per sempre. In generale, se dimentico di inserire gli incrementi il ciclo (for o while) non termina. 4-Diagrammi

38 Un ciclo meno usato: l’istruzione do…while do C; while (B); do ; while ( ); B C truefalse La differenza con il while è che il corpo del do while viene eseguito almeno una volta Vi chiediamo di saper riconoscere questa istruzione, ma vi consigliamo di usare solo while e for. Il Pascal ha una istruzione simile: do … until, che esce però quando il test è vero. 4-Diagrammi

39 Esempio: stampa dei fattoriali inferiori a un certo limite int main(){ long limite; cout << "Inserire una limitazione superiore: "; cin >> limite; cout << "Elenco fattoriali minori di " << limite << endl; long f = 1, i = 0; /* f=1=0!=i! Faremo sì che la condizione f=i! resti vera durante tutta l’esecuzione del ciclo (continua nel prossimo lucido) */ 4-Diagrammi

40 Esempio: stampa dei fattoriali inferiori a un certo limite /* (continua dal lucido precedente) */ do {cout << i <<"! = " << f << endl; //stampo i! = f f = f * (i+1); //ora f vale: f*(i+1)=i!*(i+1)=(i+1)! i++; /*aggiorno i ad (i+1), di modo che per la nuova i (uguale alla vecchia i+1) valga la condizione f=i!.*/ } while (f < limite); system("pause");} ++i è detto incremento. Provate a eliminarlo e vedrete che, anche in questo caso, il ciclo continua per sempre 4-Diagrammi

41 4. Programmazione strutturata Un’istruzione di “jump” (salto) è ad esempio: JB dove l’etichetta indica il punto del programma a cui saltare. JB è un’istruzione tipica del linguaggio Assembly, come abbiamo visto parlando della macchina di Von Neumann. In C++ l’istruzione JB esiste e si scrive goto. La programmazione strutturata, tuttavia, proibisce l’uso delle istruzioni di salto anche se disponibili, e si basa soltanto su sequenza (blocchi), selezione (IF) ed iterazione (WHILE, FOR, ecc.) per controllare il flusso del programma.

42 Programmazione strutturata L’esperienza mostra che si parte da un diagramma non strutturato (cioè non combinazione di diagrammi per if e while) è difficile trasformarlo in un programma strutturato equivalente. Ma almeno in linea di principio, è sempre possibile trasformare un diagramma qualunque in uno equivalente, che pero’ corrisponda a un programma strutturato, cioè scritto con solo if e while? La risposta è si’ per il Teorema di Böhm-Jacopini del 1966 In altre parole: if e while sono in linea di principio sufficienti a scrivere qualunque programma. 4-Diagrammi

43 Teorema di Böhm-Jacopini Ogni funzione Turing calcolabile è definibile in un linguaggio di programmazione con i comandi di: Blocco di istruzioni, IF e WHILE Ogni funzione Turing calcolabile è definibile in un linguaggio di programmazione con i comandi di: Blocco di istruzioni, IF e WHILE Ma cosa sono le funzioni Turing calcolabili? Per mostrare l’idea della prova senza definire 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 con solo blocchi, if, while.

44 Cenno di prova: partiamo da un diagramma qualunque … B1 C1C2 C5 C3 B3B2 C … e ad ogni nodo del “grafo” associamo un numero 4-Diagrammi

45 Dopo aver numerato i nodi … 4-Diagrammi … usiamo una variabile contatore cp per sapere in quale nodo ci troviamo e per decidere verso quale nodo proseguire. A questo punto … B1 C1C2 C5 C3 B3B2 C

46 main(){int cp = 1;//cp=nodo del diagramma in cui ci troviamo while (cp < 10) { if (cp == 1) cp = 2; 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}}} … traduciamo il nostro diagramma con un ciclo while e tanti if: 4-Diagrammi B1 C1C2 C5 C3 B3B2 C

47 Conclusioni In base al Teorema di Böhm-Jacopini, per il controllo del flusso sono sufficienti i costrutti programmativi della programmazione strutturata (blocchi, if, while, senza l’istruzione goto). Attenzione però: la dimostrazione del teorema di Böhm-Jacopini non ci dà alcuna informazione su come trasformare un diagramma caotico in un programma strutturato leggibile. Per questo motivo non useremo i diagrammi di flusso, ma scriveremo direttamente il codice in forma strutturata, con if, while, for, …. 4-Diagrammi

48 Istruzioni di cui sconsigliamo l’uso Il C/C++ comprende, oltre al goto, altre istruzioni di salto che erano tipiche dei più antichi linguaggi Assembly. Noi però non ne faremo uso, perche’ i “salti” producono facilmente errori e programmi poco leggibili. Solo per completezza, elenchiamo qui le istruzioni di salto del C++: goto break continue exit. Un’altra istruzione che sconsigliamo è lo SWITCH. Vedi esempi 4.16 e 4.19 dell’Hubbard


Scaricare ppt "Parte 4 Dai diagrammi di flusso alla programmazione strutturata: le istruzioni if, for, while, … Informatica A.A. 2009/2010 Corso A: Prof. Stefano Berardi."

Presentazioni simili


Annunci Google