|
Costrutti
decisionali
Un
costrutto decisionale permette al programmatore di vincolare
l'esecuzione di un'istruzione (o di un blocco di istruzioni)
ad una condizione booleana. Prima di analizzare l'essenza
di tali costrutti, è bene chiarire cosa si intenda
esattamente con i termini "espressione booleana"
e "blocco di istruzioni".
Condizioni
booleane
Una
condizione booleana è un'espressione della quale
si può dire se sia vera o falsa (in inglese true
o false). Abbiamo già illustrato gli esempi più
semplici, che fanno uso degli operatori di uguaglianza,
maggiore e minore. In questa sede vale la pena di approfondire
la possibilità di combinare le condizioni booleane
utilizzando gli operatori logici AND, OR e NOT.
In
Java per l'operatore AND viene rappresentato dal carattere
"&". L'AND logico opera su due parametri,
e restituisce true solamente se entrambi sono true.
Se vogliamo che l'istruzione x = x + 1 venga eseguita
solo se il valore della variabile x è maggiore
di 10 e contemporaneamente minore di 100 (ossia compreso
tra 10 e 100) scriveremo:
if(x
>= 10 && x <= 100)
x = x + 1;
Si
noti che nell'esempio l'operatore & viene ripetuto
due volte: questa variante dell'AND, denominata short
circuit, segnala al calcolatore che può interrompere
la valutazione dell'espressione non appena sia stata
verificato il suo valore di verità, migliorando
l'efficienza di esecuzione. Ad esempio, se durante la
valutazione dell'espressione il calcolatore scopre che
il primo parametro di una AND è falso, tutta
l'espressione risulterà falsa, indipendentemente
dal valore del secondo parametro.
L'operatore
OR, rappresentato in Java con il carattere "|",
restituisce true se uno o entrambi i parametri sono
veri, mentre restituisce false solamente quando entrambi
i parametri sono falsi. Pertanto, se vogliamo che l'istruzione
x = x * 2 venga eseguita solo se x e' uguale a 7 o ad
8, dovremo scrivere
if
( x == 7 || x == 8 )
x = x * 2;
Anche
in questo caso si è fatto ricorso all'operatore
di short circuit "||" al fine di rendere più
efficiente la valutazione: se il primo parametro è
vero, l'espressione è vera indipendentemente
dal valore del secondo parametro.
L'operatore
XOR (Or Esclusivo), rappresentato in Java con il carattere
"^", restituisce true solo se uno dei due
parametri e' vero e l'altro falso; se al contrario i
parametri sono entrambi veri o entrambi falsi, l'espressione
restituisce false. Si noti che non esiste un operatore
shortcut per l'Or Esclusivo, dal momento che è
necessario valutare entrambi i parametri per fornire
un valore di verità.
Infine
l'operatore NOT (in Java il carattere "!")
permette di negare una qualunque espressione, restituendo
in tal modo un valore true se l'espressione è
false e viceversa.
Si
noti che possiamo scrivere espressioni complicate a
piacere, combinando tra loro un numero qualunque di
espressioni più semplici e ricorrendo alle parentesi
per rendere esplicite le precedenze. La seguente istruzione
azzera la variabile x se il suo valore è compreso
tra 10 e 20 o tra 30 e 40, estremi inclusi:
if((x>=10
&& x <= 20) || (x >= 30 && x <=
40))
x = 0;
Blocco
di istruzioni
Negli
esempi visti fino ad ora, l'istruzione if vincolava
l'esecuzione di una unica istruzione. Come ci si deve
comportare se si desidera fare in modo di vincolare
un numero maggiore di istruzioni? In casi come questi
si deve definire un blocco, ossia un insieme di istruzioni
racchiuso tra parentesi graffe, che vengono trattate
dal compilatore Java come se fossero un'istruzione unica.
Ecco allora che, se vogliamo azzerare le variabili x,
y e z qualora una di esse superi il valore di 100, possiamo
scrivere un frammento di codice del tipo:
if
( x >= 100 || y >= 100 || z >= 100 ) {
// se la condizione e' vera, tutte le
// seguenti istruzioni verranno eseguite
x = 0;
y = 0;
z = 0;
}
Per
convenzione, quando si apre una parentesi graffa, le
righe successive vengono indentate di un paio di spazi.
Tale convenzione è del tutto arbitraria: niente
impedisce di riscrivere il frammento di codice precedente
in questo modo:
if
( x >= 100 || y >= 100 || z >= 100 ){
x = 0; y = 0; z = 0;
}
Come
già visto in precedenza, le convenzioni di impaginazione
aiutano a rendere il codice più leggibile, e
di conseguenza più facile da correggere o da
mantenere.
Una
particolarità dei blocchi, è che al loro
interno è possibile definire delle variabili
locali. Tali variabili hanno una ciclo di vita ridotto,
che termina non appena il calcolatore esce dal blocco.
Pertanto, in un caso come il seguente:
if
( x != y ) {
int t = x;
x = y;
y = t;
}
t = 0; // ERRORE!
l'ultima
istruzione è errata perché fa riferimento
alla variabile t, definita all'interno del precedente
blocco, che al di fuori di esso ha cessato di esistere.
if
- else
Il
costrutto condizionale più usato in Java è
l'if, che può essere usato nelle due varianti
con o senza else. Vediamo di ripassarli brevemente
if
( condizioneBooleana )
istruzione;
esegue
l'istruzione se la condizione booleana è vera,
mentre prosegue senza fare niente in caso contrario.
La variante con l'else ha una forma del tipo:
if
( condizioneBooleana )
istruzione1;
else
istruzione2;
e
permette di specificare, oltre all'istruzione da eseguire
in caso di successo, anche quella da eseguire in caso
di fallimento. Come già spiegato nel paragrafo
precedente, se vogliamo che venga eseguita più
di una istruzione dobbiamo ricorrere ad un blocco:
if
( condizioneBooleana ) {
istruzione1a;
istruzione2a;
istruzione3a;
}
else {
istruzione1b;
istruzione2b;
istruzione3b;
}
if
- else annidati
Il
costrutto if può comparire anche all'interno
di un altro costrutto if, creando strutture nidificate
anche molto complesse: vediamo di analizzare alcuni
esempi. Partiamo da un frammento di codice con due if
concatenati:
if(
x >= 0 )
if( x <= 10 )
System.out.println("x è
compreso tra 0 e 10");
Il
primo di questi dice "se la variabile x è
maggiore o uguale a 0, esegui l'istruzione seguente";
l'istruzione successiva è a sua volta un if che
dice "se la variabile x è minore o uguale
a cento, esegui l'istruzione successiva": pertanto
la terza istruzione verrà eseguita solamente
se entrambe le condizioni precedenti risultano vere.
Se
a questo punto inserisco un else dopo queste istruzioni,
a quale dei due if farà riferimento? Nel linguaggio
Java, un'istruzione else fa sempre riferimento all'ultimo
if della catena (quello più interno). Per sottolineare
il concetto, si usa allineare l'else al corrispondente
if:
if(
x >= 0 )
if( x <= 10 )
System.out.println("x è
compreso tra 0 e 10");
else
System.out.println("x è
maggiore di 10");
Se
ora aggiungiamo un ulteriore else, esso farà
riferimento al primo if
if(
x >= 0 )
if( x <= 10 )
System.out.println("x è
compreso tra 0 e 10");
else // riprende l'istruzione if( x <=
10 )
System.out.println("x è
maggiore di 10");
else // riprende l'istruzione if( x >= 0 )
System.out.println("x è minore
di 0");
Come
ci dobbiamo comportare se volessimo forzare un else
a far riferimento ad un if esterno? Come possiamo rimuovere
dall'esempio precedente il primo else, facendo in modo
che quello che rimane faccia ancora riferimento all'if
più esterno? Per ottenere questo effetto dobbiamo
racchiudere l'if più interno in un blocco: in
questo modo l'if interno verrà trattato come
un'istruzione a se stante, priva di else:
if(
x >= 0 ) {
if( x <= 10 )
System.out.println("x è
compreso tra 0 e 10");
}
else // riprende l'istruzione if( x >= 0 )
System.out.println("x è minore
di 0");
E'
buona norma di programmazione evitare di ricorrere pesantemente
alla nidificazione di istruzioni if, data la confusione
che spesso ne segue. La formulazione di combinazioni
condizionali troppo complesse spesso lascia scappare
degli errori che è poi molto difficile correggere.
Con un pò di ragionamento, di non è difficile
produrre una forma più leggibile ricorrendo agli
operatori booleani. Ad esempio il frammento di codice
if(
x >= 0 )
if( x <= 10 )
System.out.println("x è
compreso tra 0 e 10");
può
tranquillamente essere sostituito dal seguente, in tutto
equivalente:
if(
x >= 0 && x <= 10 )
System.out.println("x è compreso
tra 0 e 10");
if
- else concatenati
Un
caso più semplice di combinazione condizionale
si ha quando si fa seguire ad un else una if. In questo
caso, la verità di ogni if prescinde dalla verità
di tutte le if precedenti, creando in tal modo una catena
di alternative:
if(
x <= 0 )
System.out.println("x è minore
o uguale a 0");
else if( x <= 10)
System.out.println("x è maggiore
di 0 e minore o uguale a 10");
else if ( x <= 20)
System.out.println("x è
maggiore di 10 e minore uguale a 20");
else
System.out.println("x è
maggiore di 20");
Si
noti che in questo caso l'ultimo else comprende tutti
i casi non considerati dalle precedenti istruzioni.
Il
costrutto switch - case
Il
costrutto switch permette di gestire tutte quelle situazioni
in cui dobbiamo prendere scelte diverse a seconda del
valore di un'espressione.
switch
(espressione) {
case val1:
istruzione_1a;
istruzione_2a;
....
istruzione_na;
break;
case val2:
istruzione_1b;
istruzione_2b;
....
istruzione_nb;
break;
....
default:
istruzione_1default;
istruzione_2defefault;
....
istruzione_ndefault;
break;
}
L'espressione
contenuta tra le parentesi dello switch deve essere
di tipo intero (int, byte, short o char); ogni istruzione
case lavora su di un particolare valore, e fornisce
una sequenza di istruzioni da eseguire in quella circostanza.
Tale sequenza termina usualmente con l'istruzione break,
che forza il computer a uscire dallo switch, senza verificare
i valori sucessivi. Nonostante il break sia opzionale,
il suo uso è fortemente consigliato.
Dopo
aver specificato un numero qualunque di case, si può
chiudere l'elenco specificando il blocco di default,
ovvero una sequenza di istruzioni da eseguire se non
si è verificato nessuno dei casi precedenti.
Il blocco default è opzionale, e pertanto verrà
inserito solamente nelle circostanze nelle quali risulti
necessario.
Il
seguente esempio stamperà uno specifico messaggio
se x vale 1, 2 o 3, mentre stamperà un messaggio
di default in tutti gli altri casi.
switch
(x) {
case 1:
System.out.println("x è
uguale a 1");
break;
case 2:
System.out.println("x è
uguale a 2");
break;
case 3:
System.out.println("x è
uguale a 3");
break;
default:
System.out.println("x è
diverso da 1, 2 e 3");
break;
}
Espressioni
condizionali
L'operatore
? può essere usato all'interno di espressioni
matematiche, dove una delle sotto espressioni sia vincolata
ad una particolare condizione booleana.
espressioneBooleana
? espressione1 : espressione2;
Vediamo
ad esempio un'istruzione che assegna alla variabile
y il valore assoluto di x, ossia il valore x se x è
positivo, -x se x è negativo:
y
= x < 0 ? -x : x;
Ovviamente
è sempre possibile costruire una forma equivalente
ricorrendo ad un costrutto if-else:
if
( x < 0 )
y = -x;
else
y = x;
L'uso
di una forma o di un'altra deve sempre prescindere da
criteri di leggibilità del codice risultante.
Conclusioni
Questo
mese abbiamo approfondito l'uso dei costrutti condizionali
in Java, costrutti che permettono di vincolare l'esecuzione
di un'istruzione ad una particolare condizione. Abbiamo
inoltre approfondito l'uso delle condizioni booleane
e introdotto l'uso dei blocchi. Il mese prossimo parleremo
di costrutti iterativi.
|