Impariamo a prendere decisioni nel software
Ciao a tutti e bentornati! Le volte precedenti abbiamo introdotto il concetto di variabile, tentando di definire alcuni concetti basilari a riguardo.
Alcune situazioni fanno però intuire come il solo concetto di variabile non sia sufficiente a risolvere tutte le possibili situazioni che potrebbero presentarcisi davanti. Un esempio molto banale potrebbe essere quello di capire se un numero è pari o dispari. Capiamo subito come il solo concetto di variabile non sia in grado di permetterci di risolvere questo problema, perchè in fondo, programmare vuol dire risolvere problemi, no?
Durante la storia sono quindi stati sviluppati e introdotti costrutti che permettessero di variare il flusso di esecuzione del programma stesso. Cerchiamo di capire meglio.
Vediamo prima di tutto uno pseudo-codice per la risoluzione del problema.
se il numero è pari fai qualcosa altrimenti fai qualcos'altro
Ci si accorge subito che ci sono due possibili esecuzioni del programma.
La prima prevede il caso in cui il numero è pari, quindi si eseguono le istruzioni che ho indicato, in modo molto informale, con "fai qualcosa". Il secondo caso invece è quello del numero dispari, dove le azioni eseguite sono quelle denominate con "fai qualcos'altro".
La prima cosa importante da dire è che i due blocchi di istruzioni vengono eseguiti in modo esclusivo. Questo significa che, se viene eseguito un gruppo allora l'altro non viene eseguito e viceversa. Questa caratteristica risulta fondamentale proprio per il fatto che il flusso di esecuzione del programma non è più uno solo, ma può avere molteplici sviluppi. Molteplici sviluppi che comunque vanno tutti previsti.
Il costrutto if-else
Esistono diversi costrutti di decisione. Il primo e più elementare è il costrutto if-else. In Java, la sintassi per esprimerlo, è la seguente:
if(condizione è vera){ //istruzioni da eseguire se la condizione è vera } else { //istruzioni da eseguire se la condizione è falsa }
Si può facilmente notare come siano evidenziati due blocchi fondamentali. Il blocco dell'if e il blocco dell'else. Vediamo come il fulcro di tutto il costrutto sia la condizione. Questa nozione ci introduce ad un nuovo tipo di valore: il valore booleano. Fondamentalmente, condizione può assumere solo e soltanto due possibili valori: true o false.
Vedremo la prossima volta come funzionano i valori booleani.
Consideriamo ora la soluzione al problema posto prima riguardo i numeri pari e dispari.
int a = k;
if(a % 2 == 0){
System.out.println("Pari");
} else {
System.out.println("Dispari");
}
La soluzione risulta alquanto semplice, in quanto lo scheletro fondamentale del problema è quello dato dal costrutto if-else. Quello che sta al programmatore, è scegliere cosa inserire all'interno dei due blocchi.
Una piccola nota relativa alla notazione che ho usato: la k dell'inizializzazione della variabile è un modo compatto per dire che quella k può essere sostituita con un qualsiasi valore intero.
Questo esempio è l'ideale per introdurre un nuovo operatore: il %. Questo operatore ci permette di calcolare il resto di una divisione. Quando scriviamo a % b stiamo quindi calcolando il resto della divisione di a per b. Per esempio, scrivere 5 % 2 ha come risultato 1, in quanto 1 è il resto della divisione tra 5 e 2.
Il costrutto if-elseif-else
A volte può succedere ci siano una serie di condizioni che vanno verificate in maniera esclusiva. Avendo quindi, per esempio, tre condizioni c1, c2 e c3, verificarle in modo esclusivo signigica che, se c1 è vera, non si controllano nè c2 nè c3. Se c2 è vera non si controlla c3. Come poter fare questo?
Un lettore inesperto potrebbe dire che una serie di if potrebbero essere la soluzione. Si potrebbe quindi scrivere una soluzione simile alla seguente.
if(c1 vera){
//fai qualcosa
}else{
if(c2 vera){
//fai qualcos'altro
}else{
if(c3 vera){
//fai qualcos'altro ancora
}
}
}
Questa soluzione funziona senza ombra di dubbio, ma è comunque inutilmente complessa e molto poco elegante.
Giunge in nostro aiuto quindi il costrutto if-elseif-else. Vediamo la sintassi:
if(condizione vera){
}else if(altra condizione vera){
}else{
}
Vediamo quindi l'introduzione di un nuovo blocco di codice, definito dalla sezione else if. Questa sezione viene valutata solo se la condizione dell'if risulta essere falsa. Vediamo un possibile esempio.
Il problema da risolvere è il seguente: data una variabile, stamparla se rappresenta un numero positivo, stampare la stringa "zero" se il suo valore è uguale a zero e stampare il suo valore moltiplicato per due se è un valore negativo.
Una possibile soluzione è questa.
int a = k;
if(a > 0){
System.out.println(a);
}else if(a == 0){
System.out.println("zero");
}else{
System.out.println(a*2);
}
Come prima, a = k significa che k può essere sostituito con un qualsiasi valore intero.
Una domanda che può sorgere è: quanti rami else if posso aggiungere? La risposta è: quanti se ne vogliono. Generalmente, si tende a contenerne il numero, dal momento in cui il codice diventa poco leggibile e poco elegante con il crescere degli else if. Vedremo in seguito che si tende ad optare per una soluzione diversa, che tende a rendere il codice più leggibile e più chiaro, indipendentemente dal crescere delle condizioni da verificare.
Costrutto if
Lasciato per ultimo, il costrutto if è il mattoncino elementare alla base dei due precedenti mostrati prima. Il concetto è davvero semplice. Se una data condizione è vera, allora eseguo delle azioni e poi proseguo con il normale flusso del programma.
La sintassi è la seguente.
if(condizione vera){
//codice
}
Un possibile esempio è il seguente: data una variabile, stamparla e se è pari sommargli 1.
int a = k;
if(a%2 == 0){
a = a + 1;
}
System.out.println(a);
Vediamo quindi che è possibile eseguire delle operazioni solo in determinati casi. In questa maniera, si può variare il comportamento del programma a seconda della configurazione della variabili in quel dato istante.
Questa introduzione può sembrare alquanto teorica, ma successivamente vedremo alcuni esempi che ci permettono di vedere alcune situazioni di applicazione di questo concetto.
Un po' di teoria: le configurazioni delle variabili
Il concetto di configurazione di variabile è indispensabile da capire per poter cogliere a pieno il significato di alcune frasi.
Quando ci riferiamo ad una configurazione, ci stiamo riferendo all'insieme composto dalle variabili e dai valori che quelle variabili hanno in uno specifico momento dell'esecuzione del codice.
Volendo rappresentare in qualche modo una configurazione di variabili, potremmo usare una notazione come questa: { variabile1 = valoreVariabile1, variabile2 = valoreVariabile2, ... , variabilen = valoreVariabilen }.