Strutture condizionali e iterative. Array

Venerdì, 8 Ottobre 2004

OBIETTIVI DELLA LEZIONE

In questa lezione:

  1. perfezioneremo la nostra conoscenza delle strutture condizionali;
  2. impareremo a eseguire più volte blocchi di codice;
  3. vedremo nuovi dettagli sugli oggetti e un nuovo oggetto: gli array.

Strutture di controllo del flusso 2/4

Strutture condizionali: if

Nella lezione precedente è stata introdotta la struttura condizionale if. E' il modo fondamentale di programmare decisioni 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.

In Java, 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:

Una possibile classe è la seguente:

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:

Strutture condizionali: switch

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 esempio.

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!"

Confronto tra stinghe e altre funzioni

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, 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:

&& 0 1
0 0 0
1 0 1
|| 0 1
0 0 1
1 1 1
!
0 1
1 0

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:

  • NOT(A AND B) = (NOT A) OR (NOT B)
  • NOT(A OR B) = (NOT A) AND (NOT B)

Strutture di controllo del flusso 3/4

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.

Cicli: while

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 seguente
import 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);
  }
}

Cicli: for

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; consizioneLogica; 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...

Array

Un "array" è una collezione di elementi dello stesso tipo. A ciascun elemento è associata una posizione nell'array: in Java come in C le posizioni all'interno dell'array (gli "indici") partono da 0.

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:

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];

Lab

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:

        ########              #
        #######              ##
        ######              ###
        #####              ####
        ####              #####
        ###              ######
        ##              #######
        #              ########
        

Esercizio 5

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.

©2004 Roberto Sassi