Sei sulla pagina 1di 19

7.

Diagrammi a Blocchi e Notazione Lineare Strutturata


Abbiamo visto che per esprimere un algoritmo in un linguaggio di progetto è
necessario formare le frasi usando una sintassi ben precisa. Nel capitolo degli
algoritmi abbiamo introdotto le strutture di controllo: iterazione, sequenza,
selezione. La programmazione che fa uso esclusivamente di queste strutture di
controllo si chiama Programmazione Strutturata ed è l’unica che noi
prenderemo in esame (detto in soldoni: chi tocca il ‘goto’ passa guai!)
Esamineremo ora due linguaggi di progetto per esprimere un algoritmo: uno di
questi permette di rendersi conto graficamente di quale è la successione delle
azioni eseguite dall’esecutore in quanto fa uso di frecce e quindi…basta seguire
le frecce (attenzione! bisogna stare attenti a dove vanno a finire perché
altrimenti si scrivono algoritmi sbagliati), mentre l’altro linguaggio non è grafico
ma, d’altra parte, fa sì che sia più difficile commettere errori nella scrittura
dell’algoritmo.

Il linguaggio dei Diagrammi a Blocchi Strutturati


Con questo linguaggio, chiamato anche linguaggio degli Schemi di Flusso o dei
Diagrammi di Flusso o degli Schemi a Blocchi (nei diversi libri si incontrano
questi nomi), è possibile rappresentare graficamente il percorso che l’esecutore
deve compiere per svolgere il proprio compito, ossia eseguire un insieme di
istruzioni: lo schema a blocchi rappresenta proprio l’ordine nel quale queste
istruzioni devono essere eseguite.

Il lessico
Il lessico o vocabolario di questo linguaggio è costituito dai seguenti elementi:
• blocco di inizio

INIZIO

• blocco di fine

FINE
INIZIOFIN

• blocco di istruzione

<ISTRUZIONE>

1
• blocco di test

<CONDIZIONE>

• linee di flusso

Le linee di flusso indicano il cammino tra un blocco e il successivo.

Il blocco di inizio indica il punto dal quale l’esecuzione inizia e quindi ha solo una
linea di flusso in uscita, che porta alla prima istruzione:

INIZIO

Il blocco di fine indica il punto nel quale l’esecuzione termina e quindi ha solo
una linea di flusso in entrata, che proviene dall’ultima istruzione:

FINE

2
Il blocco di istruzione specifica al suo interno un’istruzione. Ha una sola linea in
ingresso e una sola in uscita.

<ISTRUZIONE>

Un particolare blocco di istruzione è il blocco di comunicazione, il quale richiede


all’esecutore di acquisire dati dall’esterno (istruzione di input) o di fornire dei
risultati all’esterno (istruzione di output); in questo caso spesso si usano dei
blocchi a forma di parallelogramma:

Leggi(x) Scrivi(“ciao”

Il blocco di test contiene al suo interno una espressione condizionale (il cui
valore può essere VERO o FALSO) e permette all’esecutore di decidere quale
cammino dovrà percorrere in base al valore di verità dell’espressione. Pertanto
questo blocco ha una sola linea in entrata e due in uscita che sono etichettate

V F
<CONDIZIONE>

rispettivamente con i valori VERO (V) e Falso (F). Solo uno dei due cammini
possibili sarà percorso ad ogni esecuzione.

3
La sintassi
Con le regole di composizione specifichiamo quali sono i possibili modi di
aggregare i vari elementi descritti precedentemente.
Un diagramma a blocchi strutturato deve avere un unico punto di entrata e di
uscita: possiamo pertanto chiudere lo schema ottenuto in un unico blocco
istruzione e quindi un diagramma a blocchi strutturato è costituito da un blocco
di ingresso, un unico blocco istruzione e un blocco di fine.

INIZIO

<ISTRUZIONE>

FINE

Un blocco istruzione può essere:


• un’istruzione elementare, cioè una di quelle che l’esecutore sa eseguire
• un’istruzione composta (costrutto) secondo i seguenti modelli:
• una sequenza, cioè un insieme ordinato di blocchi istruzione

INIZIO

<ISTRUZIONE>

<ISTRUZIONE>

FINE

4
• una selezione, cioè l’insieme costituito da un blocco di test e dai due
blocchi istruzione fra loro alternativi. Uno dei due blocchi istruzione può
essere vuoto, e pertanto i due possibili schemi sono i seguenti:

V F
V F <condizione>
<condizione>

<ISTRUZIONE>
<ISTRUZIONE> <ISTRUZIONE>

• una iterazione o ciclo, cioè l’istruzione che permette di ripetere


l’esecuzione di un blocco istruzione fino al verificarsi di una condizione
specificata. Esistono due tipi di iterazione:
1) con la condizione che viene valutata prima dell’eventuale esecuzione del
blocco istruzione

<condizione>

<ISTRUZIONE>

5
2) con la condizione che viene valutata dopo l’esecuzione (non eventuale ma
certa) del blocco istruzione.

<ISTRUZIONE>

<condizione>

Si può decidere che il blocco istruzione venga ripetuto quando la condizione è


vera o quando è falsa.

Per esprimere un algoritmo basta uno solo dei due tipi di iterazione precedenti, a
scelta.
Esiste anche un altro tipo di iterazione, chiamata iterazione enumerativa, che
però può essere costruita utilizzando opportunamente i tipi di iterazione già
descritti, la sequenza e la selezione. Per questo motivo ne parleremo solo quando
faremo la Notazione Lineare Strutturata.
Notate che ciascuno dei blocchi istruzione può a sua volta essere una sequenza,
una selezione o una iterazione. Osservate inoltre che con il linguaggio descritto è
possibile comporre solo tre tipi di “frasi”; queste sono tuttavia sufficienti per
esprimere tutti gli algoritmi.

6
Il linguaggio della Notazione Lineare Strutturata (NLS)

Nel linguaggio della NLS i costrutti della Programmazione Strutturata si esprimono nel
modo seguente.

Costrutto sequenza

inizio
<I1>;
<I2>;

<In>;
fine.

dove <I1>, <I2>, …, <In> sono dei blocchi istruzione, secondo il significato
che abbiamo chiarito nel paragrafo precedente.

Costrutto selezione

a due vie:

se <condizione> allora
<I1>;
altrimenti
<I2>;

a una via:

se <condizione> allora
<I1>;

Costrutti di iterazione
Quando abbiamo parlato di algoritmi, abbiamo detto che in un algoritmo possono
essere presenti blocchi di istruzioni che devono essere ripetuti più volte di
seguito durante l'esecuzione. Per esempio:
1. “Fai 10 salti”, che equivale a ripetere 10 volte l’istruzione “Fai un salto”;
2. “Leggi la poesia finché non la sai a memoria”, che equivale a ripetere
l’istruzione “Leggi la poesia” finché non la si è imparata a memoria;
3. “Fino a che ci sono soldi nella scheda telefonica, fai una telefonata”, che
equivale a verificare che ci siano soldi nella scheda e a telefonare a qualcuno
finché i soldi nella scheda non finiscono.
In tutti questi casi non è necessario che il blocco che deve essere ripetuto
compaia nell'algoritmo più volte, una per ogni volta che esso verrà eseguito.
Certe volte, come negli esempi 2 e 3, questo non è nemmeno possibile; infatti,

7
non si sa a priori quante volte si debba leggere una poesia prima di averla
imparata a memoria e quante telefonate si riescano a fare con una certa scheda
telefonica.
Consideriamo come altro esempio un algoritmo che richiede di acquisire numeri
dall'esterno e di concludere l'acquisizione quando viene inserito un numero
negativo. Dato che il progettista non sa quanti numeri non negativi verranno
inseriti durante l'esecuzione, e dato che in ogni esecuzione può avere luogo un
diverso numero di inserimenti, non è possibile scrivere nell'algoritmo una
istruzione di acquisizione per ogni azione di acquisizione che verrà eseguita.
Per risolvere queste situazioni, i linguaggi per la stesura di algoritmi mettono a
disposizione delle apposite strutture (o costrutti) che permettono di esprimere
la ripetizione di un blocco di istruzioni. Queste strutture prendono il nome di
strutture di ripetizione (oppure iterazione) o cicli (in inglese, loop). Esse si
dividono in due tipi: la ripetizione in cui non è possibile conoscere a priori il
numero di iterazioni che dovranno essere eseguite e quella in cui ciò è invece
possibile. Essenzialmente, il primo tipo è basato sui valori di verità assunti da
una particolare condizione e il secondo sull'utilizzo di un “contatore”. Insieme
alle strutture di ripetizione dei linguaggi di progetto e di programmazione che
noi trattiamo, ne presenteremo altre che esauriscono tutti i casi logicamente
possibili.

Ripetizione condizionale
Un esempio di ripetizione basata su una condizione l’abbiamo incontrato nel
paragrafo precedente (acquisizione di numeri finché non viene inserito un
numero negativo): quando non posso dire a priori quante volte un certo blocco di
istruzioni deve essere eseguito, devo usare altri elementi per fare capire
all'esecutore a quali condizioni deve ripetere l'iterazione oppure deve
concluderla. Si deve cioè trovare una condizione che determini il comportamento
dell'esecutore, cioè determini se deve eseguire il blocco di istruzioni (chiamato
corpo del ciclo) o se deve passare all’esecuzione dell’istruzione che segue il
ciclo.
Non tutte le strutture di ripetizione si comportano nello stesso modo: in alcune la
condizione viene valutata prima di eseguire il blocco, in altre dopo. Quindi il
comportamento dell'esecutore può essere uno di questi:
1. controllo la condizione; in base al suo valore di verità decido se eseguire il
blocco oppure continuare l'esecuzione dalla prima azione successiva al ciclo
(ripetizione precondizionale perché la condizione viene controllata prima
dell’eventuale esecuzione del blocco)
2. eseguo il blocco; controllo la condizione; in base al suo valore di verità decido
se eseguire ancora il blocco oppure continuare l'esecuzione dalla prima azione
successiva al ciclo (ripetizione post-condizionale perché la condizione viene
controllata dopo l’esecuzione del blocco)
A questi due tipi di iterazione corrispondono i due tipi di schemi a blocchi che
abbiamo esaminato precedentemente.
La differenza tra i due casi è notevole: nella ripetizione precondizionale il blocco
potrebbe non essere mai eseguito, mentre nella ripetizione post-condizionale il

8
blocco viene sempre eseguito almeno una volta. Infatti nel primo caso
l'esecuzione è subordinata al verificarsi di un certo valore di verità di una data
condizione, mentre nel secondo caso, la prima esecuzione non è subordinata a
nulla. L'azione di "continuare l'esecuzione a partire dalla prima azione successiva
al ciclo" in genere viene indicata come uscita dal ciclo.
Prima di esaminare in dettaglio alcuni esempi, dobbiamo specificare ancora una
cosa. Per la struttura di ripetizione si può decidere che il corpo del ciclo venga
eseguito quando la condizione ha valore VERO oppure quando la condizione ha
valore FALSO.
Abbiamo quindi quattro alternative:

CICLA PER FALSO


RIPETIZIONE POST- CONDIZIONALE
CICLA PER VERO
CICLA PER FALSO
RIPETIZIONE PRE- CONDIZIONALE
CICLA PER VERO

e queste alternative corrispondono alle seguenti sintassi:

Ripetizione post-condizionale che cicla per FALSO

In Pascal:

ripeti repeat
<blocco di istruzioni> <blocco di istruzioni>
finché <condizione>; until <condizione>;

che equivale a procedere nel seguente modo:

1. il <blocco di istruzioni> viene eseguito;


2. viene valutata la <condizione>;
3. se il valore è FALSO si torna al passo 1

Ripetizione post-condizionale che cicla per VERO

In C:

ripeti do
<blocco di istruzioni> <blocco di istruzioni>
mentre <condizione>; while (<condizione>);

9
che equivale a procedere nel seguente modo:

1. il blocco viene eseguito;


2. viene valutata la condizione;
3. se il valore è VERO si torna al passo 1

Ripetizione pre-condizionale che cicla per VERO

mentre <condizione> esegui


<blocco di istruzioni>;

In Pascal:

while <condizione> do
<blocco di istruzioni>;

In C:

while (<condizione>)
<blocco di istruzioni>;

che equivale a procedere nel seguente modo:

1. viene valutata la condizione;


2. se il valore è VERO si esegue il blocco e si torna al passo 1

Ripetizione pre-condizionale che cicla per FALSO

mentre non <condizione> esegui


<blocco di istruzioni>;

che equivale a procedere nel seguente modo:

1. viene valutata la condizione


2. se il valore è FALSO si esegue il blocco e si torna al passo 1

Come avete notato, non tutte le strutture di ripetizione sono disponibili in tutti i
linguaggi di programmazione: come approfondiremo nei seguenti capitoli, in
Pascal c'è la ripetizione post-condizionale che cicla per FALSO e quella pre-
condizionale che cicla per VERO, in C/C++ la ripetizione post-condizionale che
cicla per VERO e quella pre-condizionale che cicla per VERO.

10
Riprendiamo gli esempi precedenti:
1. “Fai 10 salti”: questa non è una ripetizione condizionale perché so
esattamente quante volte devo eseguire l’istruzione “Fai un salto” (10 volte)
2. “Leggi la poesia finché non la sai a memoria”. Può essere riscritta nel
seguente modo:

ripeti
Leggi la poesia
finché l’hai imparata a memoria;

che è una ripetizione postcondizionale: si deve leggere la poesia almeno una


volta.
3. “Fino a che ci sono soldi nella scheda telefonica, fai una telefonata”. Può
essere riscritta nel seguente modo:

mentre ci sono soldi nella scheda telefonica esegui


fai una telefonata;

che è una ripetizione precondizionale: se nella scheda non ci sono soldi non
viene effettuata neanche una telefonata.
È importante capire che ogni azione può essere espressa indifferentemente
usando una qualunque delle strutture presentate, anche se in certi casi vedremo
che può essere più naturale usarne una piuttosto che un'altra.

Esempio
Riprendiamo ora l'esempio iniziale dell'acquisizione dei numeri (acquisire numeri
dall'esterno e concludere l'acquisizione quando viene inserito un numero
negativo) e analizziamo come possiamo esprimere l'algoritmo con i quattro tipi di
ripetizione che abbiamo visto, ricordando che il comportamento che ci
attendiamo dall'esecutore è il seguente:

1. inizio
2. chiedo un numero;
3. se il numero non è negativo torno al passo 2;
4. fine

Le soluzioni possibili sono le seguenti:

Ripetizione post-condizionale che cicla per FALSO


ripeti
AcquisisciNumero(X);
finché X<0;

Ripetizione post-condizionale che cicla per VERO


ripeti

11
AcquisisciNumero(X);
mentre X>=0;

Ripetizione pre-condizionale che cicla per VERO


AcquisisciNumero(X);
mentre X >=0 esegui
AcquisisciNumero(X);

Ripetizione pre-condizionale che cicla per FALSO


AcquisisciNumero(X);
mentre non X<0 esegui
AcquisisciNumero(X);

Analizziamole in parallelo.
Cosa cambia tra la ripetizione pre-condizionale e post-condizionale?
Siccome noi vogliamo essere sicuri che l'esecutore chieda all'esterno almeno un
numero, la ripetizione post-condizionale è la scelta più naturale in quanto
prevede che il blocco di istruzioni, AcquisisciNumero(X), venga eseguito almeno
una volta. Se invece si vuole usare la ripetizione pre-condizionale, siamo costretti
a inserire esplicitamente l'istruzione AcquisisciNumero(X) prima del ciclo
perché altrimenti non avrebbe senso controllare il valore della variabile X: essa
infatti dovrebbe contenere il valore inserito dall’esterno, ma come facciamo a
controllare il valore inserito dall’esterno prima che sia stato inserito? Una
alternativa sarebbe quella di sostituire l’istruzione di acquisizione con
l'assegnamento alla variabile X di un valore che “forzi” l'esecuzione del blocco, e
cioè in questo caso non negativo.
Cosa cambia tra i cicli che ciclano per vero e quelli che ciclano per falso?
Cambia la condizione che deve essere usata per ottenere un dato
comportamento. Infatti, se so che un ciclo esegue il blocco in presenza di un
valore VERO per la condizione, devo prevedere in esso una condizione di
continuazione, cioè una condizione che vale VERO nelle situazioni in cui il blocco
deve essere eseguito. Se so che un ciclo esegue il blocco in presenza di un
valore FALSO per la condizione, devo prevedere in esso una condizione di
terminazione, cioè una condizione che vale VERO nelle situazioni in cui
l’esecuzione del ciclo deve essere terminata. Per ottenere lo stesso
comportamento nei due casi, devo usare nei due cicli condizioni complementari,
cioè che siano una la negazione dell’altra.
Infatti, nelle due precedenti ripetizioni post-condizionali le due condizioni sono
rispettivamente X<0 e X>=0 (quando una è falsa l’altra è vera), e nelle due
ripetizioni pre-condizionali le condizioni sono rispettivamente X>=0 e X<0.

Sottolineiamo che nel nostro esempio il corpo del ciclo è costituito da una singola
istruzione, ma in generale ci dobbiamo aspettare che esso sia un blocco che
contiene diverse istruzioni.

12
Esempio
Supponiamo di dovere visualizzare i numeri interi da 0 a 9. Dobbiamo usare una
variabile Cont che indica il numero che deve essere visualizzato e pertanto è una
sorta di contatore. Per risolvere il problema dobbiamo quindi far partire Cont
dal valore 0, visualizzare il suo valore, incrementarlo, e così via fino a
visualizzare il valore 9 come ultimo output. Scriviamo l'algoritmo utilizzando la
ripetizione post-condizionale (per esempio, quella che che cicla per VERO):

Cont  0;
ripeti
Scrivi(Cont);
Cont  Cont+1;
mentre Cont<10;

Notate che nella ripetizione postcondizionata non c'è bisogno di specificare


esplicitamente quale blocco di istruzioni deve essere ripetuto, perchè è quello
compreso tra le parole chiave ripeti e finché o mentre. Invece usando la
ripetizione precondizionale c'è bisogno di delimitare il blocco che deve essere
ripetuto: per questo si usano le parole chiave già introdotte inizio e fine e
quindi si scrive:

Cont  0;
mentre Cont<10 esegui
inizio
Scrivi(Cont);
Cont  Cont+1;
fine;

Inoltre, è importante notare che all'interno del corpo del ciclo ci deve essere
qualche istruzione che è legata in qualche modo alla condizione del ciclo stesso;
nell'ultimo esempio l'istruzione Cont  Cont+1; cambia il valore di Cont, che è
la variabile che compare nella condizione del ciclo (Cont < 10 ). Se così non fosse,
il ciclo potrebbe essere infinito oppure non essere eseguito neanche una volta,
secondo il tipo del ciclo e i valori iniziali delle variabili che compaiono nella sua
condizione.
Per esempio, come si comporta un esecutore che esegue il seguente ciclo?

X  0;
ripeti
x  X+1;
mentre A>0;

Se prima di eseguire il ciclo il valore di A è positivo, il corpo del ciclo viene


eseguito infinite volte, perché A non subisce nessuna modifica e quindi rimane

13
positivo; se invece il valore iniziale di A fosse nullo o negativo, il corpo del ciclo
verrebbe eseguito solo una volta, perché dopo il primo assegnamento l’esecutore
valuta la condizione A>0, trova che vale FALSO e quindi esce dal ciclo.

Ancora, ricordate che le condizioni che si usano nelle strutture di ripetizione


possono essere proposizioni composte, in cui cioè compaiono i connettivi logici
AND, OR e NOT.
Per esempio, come si comporta un esecutore istruito con il seguente algoritmo?

ripeti
Acquisici(X);
mentre X>=1 AND X<=10;

Ripetizione enumerativa
In certi casi si conosce a priori il numero delle iterazioni che devono essere
eseguite: in uno degli esempi precedenti (visualizzare i numeri da 0 a 9)
sapevamo che dovevamo ripetere dieci volte le azioni: visualizzare un valore e
incrementarlo.
Esiste una struttura di ripetizione che funziona esattamente in questo modo:
utilizza una variabile contatore che viene inizializzata ad un valore particolare e
successivamente incrementata fino ad un valore massimo. Per ogni valore che il
contatore assume viene ripetuta l’esecuzione di un blocco di istruzioni.
L'incremento ha un valore arbitrario e viene chiamato step e deve essere
specificato dal progettista insieme ai valori iniziale e finale del contatore. In
questo tipo di ciclo non viene indicata esplicitamente nessuna condizione; infatti
si esce dal ciclo quando il valore del contatore supera il valore massimo previsto.
La sintassi è la seguente:

per <contatore> da <min> a <max> con step <val> esegui


<blocco>;

dove <min> e <max> sono delle espressioni dello stesso tipo di <contatore>, che è
una variabile di tipo enumerativo, cioè una variabile che può assumere valori per
ognuno dei quali è definito un unico valore precedente e un unico valore
successivo. Per esempio, una variabile intera è di tipo enumerativo (potete dire
qual è il numero che precede il valore 5?) mentre una variabile decimale, cioè
con virgola, non lo è (potete dire il valore decimale successivo a 5,1?
Chiaramente, non 5,2 e neanche 5,11!).
Il valore <val> è l’incremento del contatore ad ogni esecuzione di <blocco>.

L’esecuzione di un ciclo enumerativo così espresso corrisponde alle seguenti


azioni:
1. inizio
2. <contatore> viene inizializzato con il valore <min>;
3. se <contatore> > <max> allora vai alla 7
4. esegui <blocco>

14
5. incrementa <contatore> di un valore <val>
6. vai alla 3
7. fine

Alternativamente il valore dello step può essere negativo e quindi il contatore


viene in effetti decrementato ad ogni esecuzione del blocco:

per <contatore> da <max> a <min> con step <val> esegui


<blocco>;

Attenzione: In questo caso il valore iniziale del contatore deve essere maggiore o
uguale a quello finale perché il blocco venga eseguito almeno una volta. Il
modello di comportamento dell’esecutore diventa il seguente:

1. inizio
2. <contatore> viene inizializzato con il valore <max>;
3. se <contatore> < <min> allora vai alla 7
4. esegui <blocco>
5. decrementa <contatore> del valore assoluto di <val>
6. vai alla 3
7. fine

Si vedrà che in certi linguaggi di programmazione esistono dei costrutti nei quali
è definito a priori uno step unitario e quindi il suo valore non deve essere
specificato dal progettista.
Notate che il valore del contatore viene aggiornato automaticamente, e NON è
una buona regola di programmazione inserire all'interno del blocco una
istruzione che ne cambi il valore (per esempio con una operazione di
assegnamento o di input), per quanto alcuni compilatori lo permettano. Se si ha
la necessità di modificare il valore del contatore all'interno del corpo del ciclo,
significa che è meglio usare un altro tipo di ripetizione.
Vediamo come si può usare un ciclo enumerativo per visualizzare tutti i numeri
compresi tra 0 e 9; si scriverà:

per I da 0 a 9 con step 1 esegui


Scrivi(I);

e la successione delle azioni è la seguente:

1. inizio
2. il contatore I viene inizializzato a 0;
3. la condizione I<=9 è vera (e quindi il blocco verrà eseguito)
4. il valore di I viene visualizzato;
5. il contatore I viene incrementato automaticamente e assume il valore 1;
6. la condizione I<=9 è vera (e quindi il blocco verrà eseguito)
7. il valore di I viene visualizzato;

15
8. il contatore I viene incrementato automaticamente e assume il valore 2;

35.il contatore I viene incrementato automaticamente e assume il valore 9;
36.la condizione I<=9 è vera (e quindi il blocco verrà eseguito)
37.il valore di I viene visualizzato;
38.il contatore I viene incrementato automaticamente e assume il valore 10;
39.la condizione I<=9 è falsa (e quindi il blocco non verrà eseguito)
40.fine

Se invece si vogliono visualizzare solo i numeri pari nello stesso intervallo, si può
scrivere:

per I da 0 a 9 con step 2 esegui


Scrivi(I);

che è lo stesso ciclo di prima tranne per quanto riguarda lo step.


Vedremo nella parte dedicata ai cicli nei vari linguaggi di programmazione che
alcuni di essi (per esempio il C) mettono a disposizione un costrutto enumerativo
molto più flessibile di quello ora presentato.

Teorema di Böhm-Jacopini

Fin qui abbiamo presentato i seguenti costrutti:


• sequenza
• selezione
• iterazione

Il Teorema di Böhm-Jacopini ci assicura che la sequenza, la selezione e un unico


tipo di ripetizione condizionale (uno qualunque) sono sufficienti per esprimere
qualunque algoritmo. In altre parole, non è necessario che un linguaggio di
programmazione metta a disposizione tutti i tipi di iterazione, ma basta che
fornisca uno dei seguenti insiemi di costrutti:
• sequenza, selezione, iterazione pre-condizionata
• sequenza, selezione, iterazione post-condizionata
anche se, in effetti, i linguaggi esistenti mettono a disposizione tutti (o quasi) i
costrutti di ripetizione esaminati. Potremmo però pensare di disporre di un
linguaggio che fornisca solo i costrutti sequenza, selezione e un solo tipo di
ripetizione condizionale.
Invece, se un linguaggio li fornisce tutti, possiamo pensare che alcuni siano
primitivi e altri derivati, nel senso che quelli derivati possono essere espressi
con una combinazione opportuna di quelli primitivi.
Per esempio, supponendo di considerare primitivo il costrutto di iterazione post-
condizionale che cicla per FALSO, possiamo ottenere un comporta me nt o equivalente
al costrutto di iterazione pre- condizionale che cicla per VERO:

16
mentre <condizione> esegui
<blocco>;

scrivendo:

se <condizione> allora
ripeti
<blocco>
finché NOT <condizione>;

Ricordate infatti che il ripeti…finché prevede che il blocco venga eseguito almeno
una volta e quindi per evitare di eseguirlo se la condizione del mentre è falsa, si deve
far precedere il ripeti da un costrutto condizionale. Notate inoltre che la condizione
del ripeti è la complementare di quella del mentre, come abbiamo già detto.
Per esempio, consideriamo il ciclo:

mentre A>0 esegui


Scrivi(A);

Se è A>0 il valore di A verrà visualizzato infinite volte; se invece è A<=0, non sarà mai
visualizzato.
Se scrivessimo:

ripeti
Scrivi(A);
finché A<=0;
cioè se non usassimo il costrutto condizionale, il valore di A verrebbe visualizzato
comunq u e almeno una volta: una sola volta se A<=0 e infinite volte se A>0.
Se invece prima di eseguire il ripeti…finché testiamo il valore di A, si ottiene il
comporta me nt o voluto.

L’enunciato del teorema, di cui tralasciamo la dimostrazione (che esiste!), è il seguente:


Teorema di Böhm - Jacopini : Ogni algoritmo può essere espresso utilizzando solo le
seguenti strutture:
• sequen za
• selezione
• ripetizione di un unico tipo tra pre - condizionata oppure post - condizionata

ESERCIZI
Scrivere gli algoritmi che risolvono i problemi descritti, utilizzando i costrutti
della Notazione Lineare Strutturata
1. Risolvere una equazione di primo grado della quale si acquisiscono i
coefficienti dall'esterno

17
2. Risolvere una equazione di secondo grado della quale si acquisiscono i
coefficienti dall'esterno
3. Scrivere un algoritmo che disegna un rettangolo composto da asterischi, di
base e altezza variabile.
4. Scrivere un algoritmo che calcola l'eta` di una persona prendendo in input
l'anno corrente e la data di nascita di una persona.
5. Dato un numero naturale n, trovare la somma dei primi n numeri naturali.
6. Dati due numeri naturali x e y, calcolare la potenza x^y utilizzando
l'operazione di moltiplicazione.
7. Dati due numeri naturali a e n, stampare tutti i multipli di a minori o uguali a
n*a.
8. Dato un numero naturale n e un carattere c, visualizzare sullo schermo una
riga che contiene n volte il carattere c.
9. Visualizzare i numeri pari compresi tra due numeri interi acquisiti
dall'esterno
10.Visualizzare i numeri dispari compresi tra due numeri interi acquisiti
dall'esterno
11.Determinare qual è la più piccola potenza di 2 maggiore di un numero
positivo acquisisto dall'esterno
12.Simulare una calcolatrice che esegue le operazioni aritmetiche (+ - * /) fra
due numeri interi senza utilizzare gli operatori di moltiplicazione e divisione
13.Calcolare il quoziente e il resto di una divisione di interi tramite sottrazioni
successive
14.Indovinare un numero pensato dall'utente compreso tra 1 e 100, facendo in
modo che dopo ogni tentativo l'utente risponda "troppo alto" oppure "troppo
basso"
15.Visualizzare la tavola pitagorica
16.Visualizzare i numeri da 1 a 10 usando tutti i diversi tipi di ciclo
17.Visualizzare i numeri compresi tra due numeri acquisiti dall'esterno
18.Sommare i numeri compresi tra due numeri acquisiti dall'esterno
19.Calcolare il fattoriale di un numero acquisito dall'esterno
20.Acquisire dall'esterno tre numeri: il primo pari, il secondo maggiore del
primo, il terzo compreso tra i primi due. Contare quanti multipli del primo
numero sono compresi tra gli altri due.
21.Scrivere un algoritmoche realizzi il seguente gioco: due giocatori possono
dire, uno alla volta, un numero maggiore di quante unità si vuole, da 1 a 10,
rispetto al precedente. Chi dice prima 100 vince.

Comprensione e correzione di un algoritmo in modo che esegua un certo


compito dichiarato: correggete gli errori (sintattici e logici) dei seguenti
algoritmi, tenendo conto di quale dovrebbe essere il loro compito, spiegato nel
testo:
1. “Scrivere l’algoritmo che consenta di calcolare e visualizzare il risultato
dell'operazione AA dove A è un numero intero >0.”

Algoritmo da correggere:

18
riserva A, Pot, Contatore: intero;
inizio
ripeti
Leggi(A)
finché A<=0;
Pot  A;
Contatore  0;
ripeti
Pot  A*A;
Contatore  Contatore+1
finché Contatore > A;
Scrivi(Pot);
fine.

Previsione del comportamento di un algoritmo espresso correttamente: dire qual


è il risultato dell’esecuzione seguente pezzo di programma:

riserva B: intero;
inizio
B  7;
se B<0 allora
Scrivi('numero negativo')
altrimenti
se B mod 2=0 allora
inizio
Scrivi(B);
vai a capo;
fine;
altrimenti
inizio
Scrivi (B div 2);
vai a capo;
fine;
Scrivi(B);
fine.

Sostituite a B  7 l'assegnamento B  4 e rispondete nuovamente alla


domanda.

19

Potrebbero piacerti anche