ripetizione

eseguire più volte le stesse istruzioni

Per risolvere alcuni problemi è comodo (o indispensabile) ripetere alcune righe di codice. Esempio? dato un intero, mostrare tutti gli interi da 0 al numero indicato dall'utente. Per risolvere questo problema passiamo prima per un problema più semplice: mostrare un elenco dei primi 10 numeri naturali. La soluzione che potrei proporre potrebbe essere: "ok, dichiaro una stringa, ci inserisco i 10 numeri e ho risolto". Certo, ma se invece fossero 1.000? O 10.000? O più? Proviamo a vedere...

public void compilaElenco() {
   String sequenza = "1, 2, 3, 4, 5, 6, 7, 8, 9, 10";
   cRisposta.setText(sequenza);
}

Questo programma può essere trasformato in uno equivalente componendo la stringa sequenza ogni volta di un nuovo numero:

public void compilaElenco() {
   String sequenza = "1";
   sequenza += ", 2";
   sequenza += ", 3";
   sequenza += ", 4";
   sequenza += ", 5";
   sequenza += ", 6";
   sequenza += ", 7";
   sequenza += ", 8";
   sequenza += ", 9";
   sequenza += ", 10";
   cRisposta.setText(sequenza);
}

Certo, quest'ultimo è di gran lunga più complesso del primo. Ha però dei vantaggi nascosti che faremo venir fuori piano piano. Intanto complichiamolo ancora un po' usando un valore intero anziché il numero nella stringa:

public void compilaElenco() {
   String sequenza = 1 + "";
   sequenza += ", " + 2;
   sequenza += ", " + 3;
   sequenza += ", " + 4;
   sequenza += ", " + 5;
   sequenza += ", " + 6;
   sequenza += ", " + 7;
   sequenza += ", " + 8;
   sequenza += ", " + 9;
   sequenza += ", " + 10;
   cRisposta.setText(sequenza);
}

Bene, il più è fatto. Vorrei a questo punto sostituire quelle costanti numeriche con una variabile (la chiamerò n) che parte da 1 e verrà di volta in volta incrementata di 1.

public void compilaElenco() {
   int n = 2;
   String sequenza = "1";
   sequenza += ", " + n;
   n++;
   sequenza += ", " + n;
   n++;
   sequenza += ", " + n;
   n++;
   sequenza += ", " + n;
   n++;
   sequenza += ", " + n;
   n++;
   sequenza += ", " + n;
   n++;
   sequenza += ", " + n;
   n++;
   sequenza += ", " + n;
   n++;
   sequenza += ", " + n;
   cRisposta.setText(sequenza);
}

Il programma sarà diventato tanto più lungo ma possiamo osservare che a parte le prime 2 righe e l'ultima le altre sono 2 righe ripetute. Detto in modo diverso si desume che le righe:

public void compilaElenco() {
   int n = 2;
   String sequenza = "1";

vanno eseguite una sola volta; mentre le righe

sequenza += ", " + n;
   n++;

vanno ripetute 9 volte in quanto l'1 è già stato fatto; queste due righe si dovranno ripetere per tutti gli interi a partire dal 2 fino ad arrivare al 10. Un lettore attento avrà notato che in effetti nel programma l'istruzione n++ in fondo manca ma volendocela mettere non cambierebbe asolutamente nulla. L'ultima riga non andrà ripetuta perché si tratta della sola impostazione del testo nella TextField.

Il ciclo for

Per esprimere il fatto che un gruppo di comandi va ripetuto si usa l'istruzione for con la seguente sintassi:

for(inizializzazione delle variabili ; condizione ; incremento) {
   istruzione 1 da ripetere;  \
   istruzione 2 da ripetere;   \
   istruzione 3 da ripetere;   /  Corpo del for
   ...                        /
}

Vediamo una per una tutte le parti che compongono questa istruzione: inizializzare una variabile vuol semplicemente dire darle un valore iniziale, per la ripetizione (d'ora in poi useremo la parola ciclo per dire ripetizione) significa darle il valore che dovrà avere appena prima che inizi la parte che andrà ripetuta. Nel programma che stiamo costruendo quali sono le variabili che vanno inizializzate e a quale valore? Si inizializza n a 2 (perché come dicevamo questo è il primo numero da cui vogliamo iniziare a ripetere le stesse operazioni) e sequenza a "1" (visto che il numero uno deve comparire nel risultato). Dato che si tratta di due inizializzazioni per semplicità inseriremo quella che ci serve per contare (la variabile n) all'interno del ciclo e l'altra che contiene la variabile con il risultato del calcolo prima del ciclo stesso.

Nella parte incremento bisogna indicare come cambiano le variabili che riguardano la ripetizione ogni volta che si arriva in fondo al ciclo. In questo caso la variabile da cambiare sarà n: andrà incrementata di uno perché è lei che tiene il conto quante volte abbiamo ripetuto il corpo del ciclo.

Il corpo del for sarà semplicemente la costruzione di sequenza.

Rimane la parte un pochino più complessa: la condizione (di ripetizione). Per poterla scrivere dobbiamo porci la domanda: quand'è che a sequenza deve essere concatenato il nuovo valore di n? La risposta è: quando n vale 2 va fatto; lo stesso quando n vale 3 e così via fino a quando n vale 10 dopodiché basta, altrimenti ci attaccheremmo anche 11! In definitiva diciamo che si ripete tutte le volte che n<=10. Ok, abbiamo detto tutto quindi produciamo finalmente il codice:

sequenza = "1";
for(n = 2; n <= 10; n++) {
   sequenza += ", " + n;
}

e quindi la funzione completa apparirà come:

public void compilaElenco() {
   int n;
   String sequenza;
   sequenza = "1";
   for(n = 2; n <= 10; n++) {
      sequenza += ", " + n;
   }
   cRisposta.setText(sequenza);
}

Certo, non è corto come la prima versione che abbiamo proposto, ma se anziché 10 elementi ne volessi 10.000, cosa cambia in quello e in questo?

Veniamo allora al nostro primo quesito. Al momento della redazione del programma non abbiamo idea di quale sia il numero che l'utente vuole. Lo si chiederà allora con una TextField e lo si userà come estremo. Ultima considerazione: n non partirà da 1 ma da 0.

public void compilaElenco() {
   int n, massimo;
   String sequenza;
   massimo = Integer.parseInt(tNumero.getText());
   sequenza = "0";
   for(n = 1; n <= massimo; n++) {
      sequenza += ", " + n;
   }
   cRisposta.setText(sequenza);
}
L'istruzione for è composta da 4 parti: una inizializzazione, una condizione, un incremento e un corpo secondo la sintassi:
for( inizializzazione ; condizione ; incremento ){
  corpo;
}
L'inizializzazione viene eseguita per prima e una sola volta, poi viene controllata la condizione e se è vera si esegue il corpo altrimenti si prosegue con l'istruzione successiva al for, alla fine del corpo si esegue l'incremento e si torna a controllare la condizione per procedere allo stesso modo di prima.

Quanto varrà la variabile n alla fine del seguente frammento di programma?

n=0;
for(a=0;a<=5;a++){
  n+=a;
}
0 no, vengono aggiunti i valori di a che non è sempre zero 1 no, a non assume soltanto il valore 1 11 no, vengono sommati tutti i valori da 1 a 5 compresi gli estremi 15 giusto

Scrivi un programma che contiene una casella di testo, un pulsante e una etichetta (cioè un oggetto di tipo Label) per stampare il risultato.

Alla pressione del pulsante dovrà visualizzare nell'etichetta la tabellina del numero inserito dall'utente.

Scrivi un programma che dato un capitale iniziale, un tasso di interesse e un numero di anni calcola il capitale alla fine del periodo. Tieni conto del fatto che dopo un anno il capitale sarà formato dal capitale iniziale e dagli interessi del primo anno. Questo meccanismo si chiama interesse composto. Facciamo un esempio: se all'inizio metto 1000€ e il tasso è del 10% dopo un anno avrò 1100€, se passa un anno ancora avrò il 10% di interessi su 1100€ e quindi il capitale a fine del secondo anno sarà di 1210€

Scrivi un programma calcola il valore di n! (n fattoriale). n!=1*2*...*n

Divisori

Più difficile. Supponiamo che tu voglia mostrare tutti i divisori di un dato numero fornito dall'utente. Come si risolve? Prima di tutto si trasforma quel numero in intero. Si prende poi ogni intero a partire da 2 (1 ed il numero stesso lo sono sempre quindi non ci interessano) fino ad arrivare al precedente del numero dato e si verifica per ciascuno se è divisibile. In altri termini si verifica se il resto della divisione intera tra il numero dato ed il numero in questione dà resto uguale a zero. Se lo è, lo si aggiunge alla stringa della sequenza dei divisori altrimenti... niente:

public void individuaDivisori() {
   int numero, contatore;
   String sequenza;
   numero = Integer.parseInt(tNumero.getText());
   sequenza = "1";
   for(contatore = 2; contatore < numero; contatore++) {
      if(numero%contatore == 0) {
         sequenza += ", " + contatore;
      }
   }
   sequenza += ", " + numero;
   tRisultato.setText(sequenza);
}

Numero primo

Ultimo esempio molto simile (ma solo in apparenza!) al precedente: dato un numero fornito dall'utente, dire se è primo oppure no.

Primo tentativo

Un modo di procedere sbagliato può essere questo: prendo ogni numero (col for) da 2 a numero-1. Mi chiedo per ciascuno se il resto è 0, nel qual caso scriverò "Il numero non è primo!" altrimenti scriverò "Il numero è primo". Perché tale programma è sbagliato?
Perché il risultato sarà sempre che il numero è primo.

Il problema è che una volta visto che il numero è non primo in quanto hai trovato un divisore dovresti interrompere il ciclo e non continuare altrimenti il tuo messaggio di non primo sulla TextField verrà sovrascritto con un messaggio di numero primo. Chi sarà che scrive per l'ultima volta? Questa domanda te la propongo perché solo l'ultimo messaggio scritto dal programma sarà quello presente nella TextField e quindi visibile dall'utente. Bene, l'ultima if la farai con numero-1 che mai è divisore di numero, da qui il problema:

public void numeroPrimo() {
   int numero, contatore;
   numero = Integer.parseInt(tNumero.getText());
   for(contatore = 2; contatore < numero; contatore++) {
      if(numero%contatore == 0) {
         tRisultato.setText("Il numero non è primo");
      } else {
         tRisultato.setText("Il numero è primo!");
      }
   }
}

Quanto varrà la variabile n alla fine del seguente frammento di programma?

n=0; 
for(a=0;a==5;a++){
  n++;
}
0 giusto, il corpo del ciclo non viene mai eseguito 1 no, verifica meglio se si entra nel ciclo 5 no, verifica meglio se si entra nel ciclo

Seconda soluzione

Una soluzione può essere quella di andare a scrivere nella casella di testo prima di iniziare il for. Ci scriveremo che non abbiamo trovato divisori, quindi è primo. Poi nella if (senza else) se è divisore sovrascriveremo il messaggio dicendo che ci sono divisori, altrimenti rimarrà quello:

public void numeroPrimo() {
   int numero, contatore;
   numero = Integer.parseInt(tNumero.getText());
   tRisultato.setText("Il numero è primo!");
   for(contatore = 2; contatore < numero; contatore++) {
      if(numero%contatore == 0) {
         tRisultato.setText("Il numero non è primo");
      }
   }
}

Domanda: e se invece fuori dal ciclo scrivessi "non primo" e poi dentro al ciclo mettessi una condizione per scrivere quando è primo? tipo numero%contatore!=0... funzionerebbe?

Ultima soluzione

Ottimizziamo, per concludere, il programma: anziché andare a fare riscritture nella casella di testo, usiamo una variabile che indicherà se abbiamo trovato o meno un divisore (non ci importa quale, se ne abbiamo trovato uno qualsiasi tanto basta a dire che il numero non è primo). All'inizio dichiareremo che il divisore non è stato trovato. Dopo il for ci chiederemo semplicemente se abbiamo trovato un divisore (non qual caso il numero non è primo) o meno (in questo caso è primo).

Visto che la variabile deve indicare semplicemente trovato/non trovato la scelta giusta è di usare un boolean e di dare un nome comprensibile alla variabile.

public void numeroPrimo() {
  int numero, contatore;
  boolean divisoreTrovato;
  numero = Integer.parseInt(tNumero.getText());
  divisoreTrovato = false;
  for(contatore = 2; contatore < numero; contatore++) {
    if(numero%contatore == 0) {
      divisoreTrovato = true;
    }
  }
  if( divisoreTrovato ) {
    eRisultato.setText("Il numero non è primo");
  } else {
    eRisultato.setText("Il numero è primo!");
  }
}

Cosa si poteva scrivere nella condizione dell'if al posto di divisoreTrovato?

divisoreTrovato==true giusto, ma non lo facciamo perché... è troppo lungo niente la condizione è sbagliata no, la condizione è giusta divisoreTrovato==1è una variabile booleana non posso confrontarla con 1

Cosa apparirà nella casella alla fine di questo frammento di programma?

for(i=0;i<5;i++){
  tf.setText(""+i);
}
0 no, vorrebbe dire che il ciclo viene eseguito una sola volta 4 giusto 5 no, quando i vale 5 il corpo del ciclo non viene eseguito