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.