OBIETTIVI DELLA LEZIONE
In questa lezione:
Durante la scorsa lezione, abbiamo cominciato a vedere come viene controllato il flusso di esecuzione in Java. In particolare, è stata introdotta la struttura condizionale if. E' il modo fondamentale di biforcare il flusso di esecuzione in Java. Quando il nostro programma dovrà prendere una decisione, verificherà un enunciato logico e quindi a seconda del valore di quest ultimo svolgerà un insieme di istruzioni o meno.
Vi ricordo che in Java, come in C, per raggruppare un insieme di istruzioni ed indicare che costituiscono un blocco unico, le si racchiude tra parentesi graffe:
{ istruzione1; istruzione2; istruzione3; }
Esempio: scriviamo alcune righe di codice che calcolino l'interesse annuo
sul nostro conto corrente. Se il saldo è inferiore a €2000 il tasso di interesse
sarà lo 1% altrimenti diventerà il 5%. Costruiamo per prima cosa
il diagramma a blocchi:
import javax.swing.*; class Interesse { public static void main(String[] args) { String stringaInput; double saldo, interesse; // scegliamo un double perché può avere decimali stringaInput = JOptionPane.showInputDialog("Quale è il saldo del tuo conto?"); saldo = Double.parseDouble(stringaInput); // la decisione: if(saldo > 2000.0) { // la condizione logica va tra parentesi tonde! interesse = saldo * (5.0 / 100.0); } else { interesse = saldo * (1.0 / 100.0); } JOptionPane.showMessageDialog(null, "L'interesse annuo è " + interesse); System.exit(0); // essenziale quando usiamo javax.swing } }
Da notare:
if(saldo > 2000.0) interesse = saldo * (5.0 / 100.0); else interesse = saldo * (1.0 / 100.0);
if(saldo > 2000.0); { interesse = saldo * (5.0 / 100.0); } else; { interesse = saldo * (1.0 / 100.0); }
if(saldo > 2000.0) { interesse = saldo * (5 / 100); } else { interesse = saldo * (1 / 100); }Per Java, i numeri 1, 5 e 100 sono delle costanti di tipo intero. 1/100 e 5/100 sono dunque divisioni tra interi e hanno come risultato 0. Il nostro conto corrente non darebbe interessi (meglio cambiare banca se i loro sviluppatori Java fanno questi errori).
La seconda (e ultima) struttura condizionale che vediamo è il costrutto switch - case. E' un costrutto sopravvissuto all'evoluzione della specie e deriva direttamente dalle jump-table (goto) dei linguaggi di seconda generazione. E' sopravvissuto perché in alcuni casi è molto utile! Serve quando dobbiamo confrontare una variabile di tipo intero (assolutamente non boolean, né float, né double, né long) con una serie di possibilità. Tipico esempio: scelta multipla.
import javax.swing.*; class Multipla { public static void main(String[] args) { String stringaInput; int numero; char risposta; stringaInput = JOptionPane.showInputDialog("Scegli un numero"); numero = Integer.parseInt(stringaInput); stringaInput = JOptionPane.showInputDialog("Cosa vuoi farne?\n" + "Premi R per radice quadrata,\n" + " E per il quadrato,\n" + " C per il cubo e\n" + " n o N per uscire."); risposta = stringaInput.charAt(0); switch(risposta) { // risposta è un char ma viene implicitamente trasformato in un int case 'R': // inizia qui se risposta == 'R' JOptionPane.showMessageDialog(null, "La radice quadrata è " + Math.sqrt(numero)); break; // finisce qui case 'E': // inizia qui se risposta == 'E' JOptionPane.showMessageDialog(null, "Il quadrato è " + Math.pow(numero,2.0)); break; // finisce qui case 'C': // inizia qui se risposta == 'E' JOptionPane.showMessageDialog(null, "Il cubo è " + Math.pow(numero,3.0)); break; // finisce qui case 'n': // inizia qui se risposta == 'n' case 'N': // inizia qui se risposta == 'N' JOptionPane.showMessageDialog(null, "Faccio come vuoi tu."); break; // finisce qui default: // in ogni altro caso inizia qui JOptionPane.showMessageDialog(null, "Non sai stare al gioco."); break; // finisce qui, ma il break non sarebbe stato necessario visto // che siamo alla fine del blocco e saremmo usciti comunque. } System.exit(0); // da non dimenticare! } }
Ripasso: nomi identificatori
Java è un linguaggio "case-sensitive", cioè differenzia tra lettere minuscole e maiuscole (come il C ma diversamente dal Fortran, ad es.).
Attenzione: è un errore molto comune chiamare una variabile con un nome tutto minuscolo e referenziarla poi con una iniziale maiuscola!
int saldo; Saldo = ... // SBAGLIATO! saldo è tutto minuscolo
Un altro errore comune è quello di indicare il nome di un tipo primitico con la maiuscola. Attenzione: i tipi primitivi non hanno iniziale maiuscola! Ma hanno iniziale maiuscola le pseudoclassi che contengono i metodi che agiscono sui tipi primitivi (sono pur sempre classi). Esempio chiarificatore:
Int numero; // SBAGLIATO, i tipi primitivi hanno nomi minuscoli Stringa input; // Giusto, le stringhe sono oggetti! numero = int.ParseInt(input); // Sbagliato, int è un tipo primitivo e non ha metodi! numero = Integer.ParseInt(input); // Giusto, Integer è una classe
Ripasso: concatenamento stringhe
Per concatenare due stringhe dobbiamo utilizzare l'operatore +. Rivediamo alcuni esempi.
Esempio: abbiamo le tre variabili altezza, lunghezza e larghezza e vogliamo una produrre una stringa del tipo:
"Le dimensioni della scatola sono: 13 x 10 x 2 cm (L x W x H)"
Per ottenerla:
"Le dimensioni della scatola sono: " + lunghezza + " x " + larghezza + " x " + altezza + " cm (L x W x H)";
Per costruire una stringa che, stampata, stia su più righe, ad esempio come:
Questa è una stringa su due righe!
basta inserire la sequenza di escape '\n' nel punto opportuno, cioè:
"Questa è una stringa\nsu due righe!"
In Java, le stringhe sono un oggetto e ci disinteresseremo da come vengono implementate. Sappiamo però che sono una sequenza di caratteri numerati a partire dallo 0 (come in C ma NON esiste il terminatore di stringa).
In un linguaggio di programmazione strutturato a questo punto avremmo studiato le funzioni che "permettono di operare sulle stringhe". Ma Java è un linguaggio orientato agli oggetti (OO) e in un linguaggio OO il centro del programma sono i dati stessi. Quindi non manipoleremo gli oggetti, ma più gentilemente, invieremo agli oggetti dei messaggi con delle richieste specifiche. L'oggetto che riceve il messaggio invocherà il metodo corrispondente.
Vediamo un esempio. Nel codice sopra per avere il primo carattere della stringa abbiamo scritto la seguente istruzione:
risposta = stringaInput.charAt(0);
Scomponiamola:
Altri metodi utili della classe String
NOTA BENE: se vogliamo confrontare due stringhe in una espressione logica NON dobbiamo usare l'operatore == ma il metodo equals(). Questo è valido in genere per qualunque oggetto! L'operatore == confronta il riferimento all'oggetto (Pressapoco quello che in C era il puntatore, anche se, per quanto ci riguarda, ci darà ben pochi problemi. Lo vedremo meglio più avanti).
Ripasso
In Java (come in C) l'operatore logico AND si indica con &&, OR con || e NOT con !. Le loro tabelle di verità sono come sempre:
|
|
|
Le condizioni logiche possono essere composte per creare espressioni più complesse. Ad esempio:
(a > 5) && (b < 10)
Quando si deve valutare una condizione logica complessa, Java interrompe (come il C) la valutazione non appena la condizione è univocamente determinata. Nell'esempio sopra se la prima condizione è falsa non c'è alcun bisogno di valutare la seconda.
Ricordiamo che valgono le leggi di De Morgan:
Spesso è necessario ripetere numerose volte lo stesso blocco di istruzioni e tutti i linguaggi di programmazione propongono strutture di controllo per i cicli. In questo il Java assomiglia quasi completamente al C (ad onor del vero la nuova versione 1.5 di Java introduce un nuovo tipo di ciclo, somigliante al foreach del PHP ma non presente in C).
Fondamentalmente, un ciclo è una struttura di controllo che esegue un blocco di istruzioni fino a che una condizione logica rimane vera.
Esempio: calcoliamo la somma dei numeri interi minori ed uguali del numero N (dall'algebra elementare sappiamo che la somma sarà pari a N*(N+1)/2). Per prima cosa costruiamoci un diagramma a blocchi:
Una possibile soluzione è la seguenteimport javax.swing.*; class SommaInteri { public static void main(String[] args) { String stringaInput; int numero, N, somma; stringaInput = JOptionPane.showInputDialog("Scegli il numero N"); N = Integer.parseInt(stringaInput); somma = 0; numero = 1; while(numero <= N) { somma += numero; numero++; } JOptionPane.showMessageDialog(null, "La somma è " + somma); } }
Il costrutto for è, per quanto ci riguarda, assolutamente equivalente al while. Le parti fondamentali del ciclo sono raggruppate in un costrutto più sintetico: for(condizioneIniziale; condizioneLogica; incremento). La parte centrale dell'esempio precedente andrebbe riscritta come:
somma = 0; for(numero = 1; numero <= N; numero++) { somma += numero; }
In definitiva, i due costrutti seguenti sono equivalenti:
istrInizializzazione; while(condizioneLogica) { istruzione1; istruzione2; ... istrIncremento; } |
for(istrInizializzazione; condizioneLogica; istrIncremento) { istruzione1; istruzione2; ... } |
Prediligeremo il for quando il numero di iterazioni è prefissato, il while altrimenti. Ma si potrebbe anche usare sempre uno dei due costrutti...
Gli "array" in Java sono degli oggetti. Partiamo da un esempio,
un array di 10 double:
Ecco il codice che lo produce:
class ArrayDiDouble { public static void main(String[] args) { double[] movimenti = {12.32, 0.3, 28.78, 12.45, 9.01, 1.16, 26.9, 18.04, 11.08, 5.39}; for(int i=0; i<movimenti.length; i++) System.out.println(movimenti[i]); } }
Osservazioni:
double[] movimenti;NON crea un array, ma solo un riferimento: l'oggetto è per ora completamente vuoto. E' una differenza fondamentale con i tipi di dato primitivi.
... = {12.32, 0.3, 28.78, 12.45, 9.01, 1.16, 26.9, 18.04, 11.08, 5.39};indichiamo a Java che vogliamo che crei un oggetto di tipo array di double con il contenuto specificato. Ora l'oggetto non è piu vuoto!
Se avessimo voluto che l'utente inserisse 10 dati:
import javax.swing.*; class ArrayDiDouble { public static void main(String[] args) { String stringaInput; double[] movimenti = new double[10]; for(int i=0; i<movimenti.length; i++) { stringaInput = JOptionPane.showInputDialog("Inserisci l'elemento numero " + i); movimenti[i] = Double.parseDouble(stringaInput); } for(int i=0; i<movimenti.length; i++) System.out.println(movimenti[i]); System.exit(0); } }
Da notare che l'inizializzazione è ora fatta attraverso l'istruzione:
... = new double[10];
Algoritmi da sapere: Massimo di un array
Questo non è un corso di algoritmi, ma di sicuro è necessario imparare bene alcuni algoritmi basilari. Tra questi la ricerca del massimo (o del minimo) di un array. L'idea è semplice:
Esercizio 1
Scrivere un programma che chieda all'utente di inserire tre numeri double. Il programma deve scegliere il maggiore e stamparlo a video.
Esercizio 2
Un anno bisestile ha 366 giorni. Un anno è bisestile se è divisibile per 4 (es: 1980), se non è divisibile per 100 (il 1900 non era bisestile), se è divisibile per 400 (il 2000 era bisestile). Il calendario gregoriano è stato introdotto nel 1582 e prima di esso non esistevano gli anni bisestili. Scrivere un programma che chieda all'utente un anno e che risponda indicando se quell'anno sia (stato) bisestile o meno.
Esercizio 3
Riscrivere il seguente codice, sostituendo il ciclo for con uno while
int s = 0; for (int i = 1; i <= 10; i++) s = s + 1;
Esercizio 4
Scrivere un programma che stampi a video il seguente pattern:
1 22 333 4444 55555 666666 7777777 88888888 999999999
Esercizio 5
Scrivere un programma che stampi a video il seguente pattern:
######## # ####### ## ###### ### ##### #### #### ##### ### ###### ## ####### # ########
Esercizio 6
Scrivere un programma che chieda all'utente di inserire 7 numeri double e li metta in un array. Quindi il programma deve indicare all'utente quale era il numero maggiore.
©2005 Roberto Sassi