Cicli while

I cicli while sono la forma più generica di ciclo. La struttura generale è questa:

while( condizione ) {
  istruzione1;
  istruzione2;
  ...
}

Quando questo blocco viene eseguito, si compiono i seguenti passi:

  1. si verifica se la condizione è vera;
  2. se lo è, si eseguono in sequenza le istruzioni istruzione1, istruzione2, ecc., altrimenti si esce dal ciclo
  3. si verifica di nuovo la condizione,
  4. se è verificata si eseguono di nuovo le istruzioni;
  5. ...

La frase "si esce dal ciclo" indica che si passa ad eseguire la prima istruzione che segue il blocco, ossia la prima istruzione che viene dopo il }. In italiano, il ciclo while si potrebbe tradurre come: "finchè la condizione è verificata, continua ad eseguire le istruzioni".

È facile far vedere che ogni ciclo for si può tradurre in un ciclo while:

for(istruz1; condiz; istruz2) {           istruz1;
  A;                                -->   while(condiz) {
}                                           A;
                                            istruz2;
                                          }

D'altra parte, i cicli in cui una variabile deve venire assegnata a valori crescenti o decrescenti in un intervallo si codificano in maniera più naturale usando cicli for: il codice risulta in questo modo più breve da scrivere e più facile da capire. D'altra parte, i cicli while sono più conveniente negli altri casi di iterazione di istruzioni ma non c'è una variabile che si incrementa o decrementa.

Esempio: serie di Fibonacci

Si vogliono i valori inferiori a 10000 della serie di Fibonacci. La serie di Fibonacci è definita in questo modo: i primi due elementi f0 e f1 valgono 1. Gli elementi successivi sono determinati con la formula:

fi=fi-1+fi-2

Il problema si risolve facimente usando due variabili penultimo e ultimo in cui si memorizzano gli ultimi due valori trovati della serie. All'inizio, queste due variabili hanno valore 1, dato che i primi due elementi della serie valgono 1 per definizione. Per determinare il successivo elemento della serie, è sufficiente sommare queste due variabili. A questo punto, la variabile ultimo deve assumere il valore dell'ultimo elemento della serie, e quindi il valore della somma, mentre penultimo deve diventare il valore del penultimo elemento trovato, per cui deve prendere il valore che aveva ultimo in precedenza. Il programma Fibonacci.java risolve il problema in questo modo.

/*
  La serie di Fibonacci per valori minori di 10000.
*/

class Fibonacci {
  public static void main(String[] args) {
    int penultimo, ultimo, nuovo;
    int limite=10000;

    penultimo=1;
    ultimo=1;

    System.out.println(penultimo);

    while( ultimo < limite ) {
      System.out.println(ultimo);

      nuovo=penultimo+ultimo;
      penultimo=ultimo;
      ultimo=nuovo;
    }
  }
}

Il programma funziona nel modo seguente. Ad ogni passo, penultimo e ultimo sono gli ultimi due valori trovati della serie, e si stampa ultimo. Si calcola il nuovo valore della serie, che è la somma di questi due, e lo si memorizza nella variabile nuovo. A questo punto, penultimo e ultimo sono diventati il terz'ultimo e il penultimo elemento della serie, per cui è necessario cambiarli per far sí ritornino ad essere il penultimo e l'ultimo. Questo significa che penultimo prende il valore di ultimo, mentre ultimo prende il valore di nuovo. Dal momento che questa sequenza di passi si trova all'interno di un ciclo while, viene eseguita fino a che la condizione è verificata. In questo caso, si continua fino a che l'ultimo valore trovato non supera il limite imposto.

Ultima nota: dal momento che a venire stampato è sempre il valore di ultimo dopo questo cambiamento, nelle stampe si perde il primo elemento della serie. Per questa ragione è stata messa l'istruzione System.out.println(penultimo); prima della esecuzione del ciclo.

Esempio: zero di una funzione

È noto che una funzione, se è continua in un intervallo [a,b], ha valore positivo in a e negativo in b, allora è nulla in almeno un punto dell'intervallo. Si vuole un programma che calcola un intervallo sufficientemente piccolo in cui la funzione ha (almeno) uno zero. Siano quindi dati la funzione, i valori (reali) di a e b, e un terzo valore e, e si vuole un intervallo [c,d] che sia contenuto in [a,b], che contenga un punto x tale che f(x)=0, e la dimensione di questo intervallo sia minore di e, ossia d-c <= e.

Un possibile meccanismo risolutivo è il seguente: si parte con c,d pari agli estremi dell'intervallo dato. Poi si assegna x=(c+d)/2, e si valuta f(x). In altre parole, si fissa x al punto esattamente in mezzo all'intervallo, e si valuta f nel punto. Dal momento che f(c) e f(d) hanno segni diversi, almeno uno dei due ha un segno uguale a quello di f(x). Nel caso in cui f(c) e f(x) hanno lo stesso segno, si assegna c=x. Nel caso in cui sono f(d) e f(x) ad avere lo stesso segno, si assegna d=x. Alla fine di questo passo, la proprietà che f(c) e f(d) hanno segni diversi continua a valere. D'altra parte, la differenza fra d e c si è dimezzata.

Il programma Nullo.java contiene il programma Java che ripete questi passi fino a che la ampiezza dell'intervallo non diventa minore o uguale del numero dato e.

/*
  Trova un punto in cui una funzione f(x) ha un
  valore sufficientemente vicino allo zero.
  Siano a e b due valori tali che f(a) ha segno
  opposto a f(b). Si assume che la funzione sia
  continua.
*/

class Nullo {
  public static void main(String[] args) {
    double a=0,b=10;
    double c,d;
    double e=0.01;
    double x,f,f1,f2;

    f1=a*a-5*a-2;
    f2=b*b-5*b-2;

    if( f1*f2 >0 ) {
      System.out.println("La funzione non ha segno diverso negli estremi");
      System.out.println(f1+" "+f2);
    }
    else {
      c=a;
      d=b;
  
      while( d-c>e ) {
        x=(c+d)/2;
        System.out.println("Intervallo: ["+c+","+d+"], Medio: "+x);
  
        f=x*x-5*x-2;
        f1=c*c-5*c-2;
        f2=d*d-5*d-2;
  
        if( f*f2 < 0 ) {
          c=x;
        }
        else if( f*f1 < 0 ) {
          d=x; 
        }
        else {
          c=x;
          d=x;
          break;
        }
      }
  
      System.out.println("Trovato intervallo ["+c+","+d+"]");
    }
  }
}

Usiamo la condizione f*f1>0 per decidere se due numeri hanno lo stesso segno. Una delle prime istruzioni del programma è la verifica se effettivamente la funzione ha segno differente pre a e per b. Se il segno risulta uguale, si stampa un messaggio di errore e basta. In caso contrario, si esegue il resto della procedura.