Title: Parte 4
1Informatica A.A. 2009/2010
- Parte 4
- Dai diagrammi di flusso alla programmazione
strutturata - le istruzioni if, for, while,
Corso A Prof. Stefano Berardi
http//www.di.unito.it/stefano Corso B Prof.
Ugo de Liguoro http//www.di.unito.it/deligu
2Corrado Bohm
Milano, 1923. Professore emerito di Teoria e
applicazione delle macchine calcolatrici presso
lUniversità di Roma La Sapienza
3Indice Parte 4 Diagrammi
- Il flusso dellesecuzione e i diagrammi di
flusso. - Il salto condizionale listruzione IF.
- Iterazione le istruzioni WHILE e FOR.
- La programmazione strutturata e il teorema di
Bohm-Jacopini. - I diagrammi di flusso venivano usati per
descrivere i programmi nellepoca in cui si
programmava in Assembly. Oggi sono ancora adatti
per spiegare programmi molto brevi.
41. Il flusso dellesecuzione
- Lesecuzione 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, lindirizzo della prossima
istruzione da eseguire) - Ogni trasformazione di stato è leffetto
dellesecuzione di unistruzione. Per es., una
assegnazione x3 oppure unistruzione JB L1 che
fa saltare lesecuzione a unistruzione lontana
di indirizzo L1 se una certa condizione è vera. - Il flusso è lordine in cui le istruzioni di un
programma vengono eseguite.
5Diagrammi di flusso
- I diagrammi di flusso sono una rappresentazione
in forma di grafo del flusso dellesecuzione di
un programma. Erano un progresso rispetto al
linguaggio assembly, perché spostavano
lattenzione dalle istruzioni del programma alla
loro relazione reciproca.
6Esempio di diagramma di flusso area di un
rettangolo
Ogni freccia rappresenta la scelta della
prossima istruzione da eseguire.
7Il programma C corrispondente
int main() double base, altezza, area cout ltlt
"base " cin gtgt base cout ltlt "altezza "
cin gtgt altezza area basealtezza cout ltlt
"area " ltlt area ltlt endl system(pause)
8Sequenza di diagrammi e Blocchi di istruzioni in
C
C1 C2 Ck
Sequenza di diagrammi
Blocco di istruzioni
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.
9Il flusso dellesecuzione 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 allaltra il C segue unidea
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 lincoveniente di essere
leggibili solo per programmi brevissimi.
10Programmazione 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.
112. Il diagramma di selezione (IF)
Listruzione IF rappresenta in C e in quasi
tutti i linguaggi il seguente diagramma di
selezione a due vie LIF fa saltare
lesecuzione allindirizzo di C se lespressione
B vale vero, e allindirizzo dellistruzione D se
B vale falso.
if (B) C else D
12Listruzione IF in C
LIF 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 lIF 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, lIF tronco.
if (ltespr.booleanagt) ltistruzione1gt else
ltistruzione2gt
13IF tronco (senza ELSE)
if (B) C
if (ltespr. booleanagt) ltistruzionegt
LIF tronco rappresenta la selezione a due vie
con una sola azione C. Se lespressione booleana
B è falsa saltiamo allistruzione successiva
senza far nulla Vediamo ora alcuni esempi di uso
di IF, IF tronco e IF annidato
14Primo esempio di IF equazione di 1 grado axb
Discussione se a 0 eq. indeterminata o
impossibile, altrimenti x b/a.
15Il programma per lequazione di 1 grado
int main() double a, b cout ltlt "a "
cin gtgt a cout ltlt "b " cin gtgt b if
(a ! 0) cout ltlt "x " ltlt b/a ltlt endl else
cout ltlt Indet. o impossibile" ltlt endl
system(pause)
16Secondo esempio di IF equazione di 2 grado, IF
annidati
Sia
- ? gt 0 due soluzioni reali
- ? 0 una soluzione reale
- ? lt 0 non ci sono soluzioni reali.
- Per scrivere questo programma dovremo utilizzare
IF scritti dentro altri IF (IFannidati), per
distinguere il secondo caso dal terzo.
17Esempio di IF diagramma equazione di 2 grado
leggi a, b, c
inizio
Deltabb4ac
Questa viene detta una decisione annidata
true
false
Deltagt0
Delta0
rad?Delta
false
true
Stampa ( b ? rad)/(2a)
Stampab/(2a)
Stampa no sol. reali
fine
18Soluzione eq. di 2 grado in C
- main() double a, b, c cin gtgt a cin gtgt b cin
gtgt c - double Delta bb - 4ac
- if (Delta gt 0)double rad sqrt(Delta)
- cout ltlt "prima sol. reale "
- ltlt (-b rad)/2a ltlt endl
- cout ltlt "seconda sol. reale "ltlt (-b -
rad)/2altltendl - else if (Delta 0)
- cout ltlt "Unica sol. reale " ltlt -b/(2a) ltlt
endl - else cout ltlt "Nessuna soluzione reale" ltlt endl
- system("pause")
sqrt radice quadrata (funzione della libreria
cmath)
Questo è un if annidato
Questo else si riferisce al secondo if Ma come
facciamo a stabilirlo?
19Quando ci sono IF annidati come accoppiamo un
if con un else?
Per definizione, la clausola else si riferisce
allultimo 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, listruzione D verrà eseguita
se B è falso listruzione C verrà eseguita se B
è vero, ma B è falso.
20If annidati un altro esempio ambiguita
Dato che la clausola else si riferisce allultimo
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 listruzione D verrà eseguita
se B è vero e B è falso, non se B è falso.
21If annidati come eliminare le ambiguita
Avvolgendo ogni sottoistruzione in parentesi
graffa ogni ambiguitá scompare
Queste parentesi sono necessarie se vogliamo
accoppiare il primo IF con lelse
if (B) if (B) C else D
Listruzione D verrà eseguita se B è falso. Le
parentesi non sono sempre necessarie come in
questo caso, peró sono sempre utili per
aggiungere chiarezza.
Esempio es. 3.14 Hubbard indovina il numero
22Come rappresentare una scelta tra n1 blocchi
È sufficiente annidare n comandi IF, ciascuno
dopo lelse dellIF precedente. Il risultato è
unistruzione composta che esegue il primo blocco
se la prima espressione booleana è vera, il
secondo blocco se la prima è falsa e la seconda è
vera, eccetera, lultimo blocco se tutte le
espressioni booleane sono false.
if (B1) ltblocco_1gt //se B1 vera else if (B2)
ltblocco_2gt //se B1 falsa e B2 vera else if (B3)
ltblocco_3gt //se B1,B2 false e B3
vera else ltblocco_(n1)gt//se B1,,Bn
false
23Un IF con più di due scelte listruzione
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 luso, 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.
24Esecuzione dello SWITCH
switch (ltespressionegt) case ltcostante_1gt
ltistruzioni_1gt break ... case ltcostante_kgt
ltistruzioni_kgt break default ltistruzionigt
Esecuzione dello SWITCH. Confrontiamo il valore
dellespressione 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.
253. Literazione il ciclo while
- Literazione è lesecuzione ripetuta di una o più
istruzioni (dette corpo delliterazione). 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 allindietro, verso
una istruzione già eseguita in precedenza. - Come primo e fondamentale esempio di ciclo
vediamo il ciclo while.
26Il diagramma di flusso del ciclo while
Un ciclo while rappresenta il seguente diagramma
di flusso
while (B) C
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 (ltespr. booleanagt) ltcorpogt
27Un programma che richiede il while la somma dei
reciproci
- Problema dato un intero n calcolare e stampare
- Soluzione utilizziamo il seguente algoritmo.
Partiamo con s0, i1. Finché iltn, eseguiamo
ss1.0/i, ii1. In questo modo, s prende i
valori 1, 11/2, 11/21/3, , mentre i prende i
valori 1, 2, 3, . Quando igtn, terminiamo e
stampiamo s, che a questo punto vale (11/21/3
1/n). - Vediamo ora come tradurre tutto con un while.
28Somma dei reciproci calcolata con un while
int main() int n cout ltlt "n " cin
gtgt n double s 0.0 int i 1
while (i lt n) s s 1.0/i i
cout ltlt "La somma dei primi " ltlt n ltlt "
reciproci " ltlt s ltlt 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 ???
29Il ruolo di i nella somma dei reciproci
Lincremento i modifica il valore di verità del
test,lazione svolta dal corpo, ed è cruciale per
il buon funzionamento del ciclo. Se lo eliminiamo
e lasciamo while (i lt 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 i1. Dunque
continuiamo a eseguire ss1.0/i con i1 quindi
continuiamo a eseguire s s 1 ne segue che s
assume i valori 0,1,2,3,4, , anziche 1,11/2,
come dovrebbe. Inoltre il test (i lt n) resta
uguale a (1 lt n) e quindi vale sempre true, il
ciclo continua per sempre.
30Un altro esempio di iterazione il ciclo for
for (ltinizializzazionegt lttestgt ltstepgt)
ltcorpogt
- Le inizializzazioni sono definizioni di
variabili o assegnazioni di valori a variabili
precedentemente dichiarate se più duna, sono
separate da virgole (e non da punti e virgole
come al solito) - Lo step o incremento di solito è lincremento di
una o più variabili se più duna, sono separate
da virgole. Lincremento serve a modificare il
valore di verità del test e, a volte, lazione
svolta dal corpo. - Nota. A differenza di quanto avviene per il for
nel Pascal, il test è unespressione booleana
arbitraria.
31Il diagramma di flusso per il ciclo for
Linizializza-zione I viene eseguita una sola
volta prima di entrare nel ciclo
for (I B S) C
I
false
B
true
Al termine di S avviene sempre un salto
allindietro, verso listruzione 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
C
Lo step S viene eseguito al termine di ogni
esecuzione del corpo C
S
32La 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(IBS) C I while(B)CS
- 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.
33Il for consente di ripetere n volte una certa
azione
Inizialmente i vale 0
for(i0,n100iltni) cout ltlt CIAO
i 0 n 100
true
false
Al termine di ogni iterazione i viene
incrementato di 1
iltn
Scrivi CIAO
Quando i vale 100 il ciclo termina
i
Il ciclo viene eseguito 100 volte, mentre i
cresce da 0 a 99. Ogni volta stampiamo un CIAO.
34Un diagramma per il calcolo della media
aritmetica m (a0 an-1)/ n
inizio
i lt n
false
leggi n
true
stampa m s/n
a i-esimo addendo ai
i 0
leggi a
s 0
fine
s s a
A partire da questo diagramma di flusso si puó
scrivere un programma in C usando un ciclo FOR
i i 1
35Il diagramma di flusso per la media (definito da
un for)
Eseguito una sola volta prima di entrare nel ciclo
for (I B S) C
i 0 s 0
true
false
iltn
Leggi a s s a
Eseguito al termine di ogni iterazione
i
36Un programma C per la media aritmetica
i è detto incremento. Se lo eliminiamo cosa
succede ???
int main()int n int i, a, s cout ltlt "Media di
n valori. n" cin gtgt n for (i 0, s 0 i lt
n i) cout ltlt "inserisci un valore"
cin gtgt a s s a cout ltlt "Media " ltlt
(double) s / (double) n ltlt endl
system("pause")
Casting necessario per avere la divisione senza
troncamento
37Il ruolo di i nella media aritmetica
Lincremento 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 lt n ) cout ltlt
"inserisci un valore" cin gtgt a s s
a allora mentre il corpo del ciclo for viene
ripetuto il valore di i non cambia, resta il
valore iniziale i0. Dunque il test (i lt n)
resta uguale (0 lt 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.
38Un ciclo meno usato listruzione dowhile
La differenza con il while è che il corpo del do
while viene eseguito almeno una volta
do ltcomandogt while (ltespr. booleanagt)
do C while (B)
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.
39Esempio stampa dei fattoriali inferiori a un
certo limite
int main() long limite cout ltlt "Inserire una
limitazione superiore " cin gtgt limite cout ltlt
"Elenco fattoriali minori di " ltlt limite ltlt
endl long f 1, i 0 / f10!i! Faremo sì
che la condizione fi! resti vera durante tutta
lesecuzione del ciclo (continua nel prossimo
lucido) /
40Esempio stampa dei fattoriali inferiori a un
certo limite
/ (continua dal lucido precedente) / do cout
ltlt i ltlt"! " ltlt f ltlt endl //stampo i! f f
f (i1) //ora f vale f(i1)i!(i1)(i1)!
i /aggiorno i ad (i1), di modo che per
la nuova i (uguale alla vecchia i1) valga la
condizione fi!./ while (f lt
limite) system("pause")
i è detto incremento. Provate a eliminarlo e
vedrete che, anche in questo caso, il ciclo
continua per sempre
414. Programmazione strutturata
- Unistruzione di jump (salto) è ad esempio
- JB ltetichettagt
- dove letichetta indica il punto del programma
a cui saltare. JB è unistruzione tipica del
linguaggio Assembly, come abbiamo visto parlando
della macchina di Von Neumann. In C
listruzione JB esiste e si scrive goto. - La programmazione strutturata, tuttavia,
proibisce luso 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.
42Programmazione strutturata
- Lesperienza 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.
43Teorema di Böhm-Jacopini
Ogni funzione Turing calcolabile è definibile in
un linguaggio di programmazione con i comandi
di Blocco di istruzioni, IF e WHILE
44Cenno di prova partiamo da un diagramma
qualunque
e ad ogni nodo del grafo associamo un numero
-
B1
C1
C2
-
-
C3
B3
B2
C5
C4
45Dopo aver numerato i nodi
usiamo una variabile contatore cp per sapere
in quale nodo ci troviamo e per decidere verso
quale nodo proseguire. A questo punto
-
B1
C1
C2
-
-
C3
B3
B2
C5
C4
46 traduciamo il nostro diagramma con un ciclo
while e tanti if
main()int cp 1//cpnodo del diagramma in cui
ci troviamo while (cp lt 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
-
B1
C1
C2
-
-
C3
B3
B2
C5
C4
47Conclusioni
- 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 listruzione 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, .
48Istruzioni di cui sconsigliamo luso
- 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.
- Unaltra istruzione che sconsigliamo è lo SWITCH.
Vedi esempi 4.16 e 4.19 dellHubbard