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ù?

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; 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 al 10. L'ultima riga non andrà ripetuta perché si tratta della sola impostazione del testo nella TextField. Per esprimere il fatto che deve ripetere 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
   ...                        /
}

Inizializzare una variabile per la ripetizione (d'ora in poi useremo la parola ciclo per dire ripetizione) significa darle il valore che dovrà avere appena parte la ripetizione. Nel programma che stiamo costruendo, quali sono le variabili che vanno inizializzate e a quale valore? Si inizializza n a 2 e sequenza a "1". Dato che si tratta di due inizializzazioni per semplicità inseriremo quella che ci serve per contare all'interno del ciclo e l'altra che contiene la varibile con il risultato del calcolo prima.
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 perché è lei che ci dirà quante volte abbiamo ripetuto.
Il corpo del for sarà semplicemente la costruzione di sequenza
Rimane la parte un pochino più complessa: la condizione (di ripetizione); poniamoci la domanda: Quand'è che a sequenza deve essere aggiunto il nuovo valore di n? La risposta sarà: quando n vale 2 si ripete; lo stesso quando n vale 3 e così via fino a quando n vale 10 dopodiché basta, altrimenti ci attaccheremmo anche11! 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 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);
}

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

for(n=0,a=0;a<=5;a++){
n+=a;
}
0 1 11 15

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.

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

Più difficile. Supponiamo che tu voglia mostrare tutti i divisori di un dato numero fornito dall'utente. Come si risolve? Prima di tutto si trasformi quel numero in intero. Si prende poi ogni intero a partire da 2 (tanto 1 ed il numero stesso lo sono sempre) 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;
   t_risultato.setText(sequenza);
}

Ultimo esempio molto simile (ma solo in apparenza!) al precedente: dato un numero fornito dall'utente, dire se è primo oppure no. 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 è primo!" altrimenti scriverò "Il numero non è primo". Perché tale programma è sbagliato?
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 individuaDivisori() {
   int numero, contatore;
   numero = Integer.parseInt(tNumero.getText());
   for(contatore = 2; contatore < numero; contatore++) {
      if(numero%contatore == 0) {
         t_risultato.setText("Il numero non è primo");
      } else {
         t_risultato.setText("Il numero è primo!");
      }
   }
}

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

for(n=0,a=0;a==5;a++){
n++;
}
0 1 5

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 individuaDivisori() {
   int numero, contatore;
   numero = Integer.parseInt(tNumero.getText());
   t_risultato.setText("Il numero è primo!");
   for(contatore = 2; contatore < numero; contatore++) {
      if(numero%contatore == 0) {
         t_risultato.setText("Il numero non è primo");
      }
   }
}

Ottimizziamo, per concludere, il programma: anziché andare a fare riscritture nella casella di testo, usiamo una variabile intera che conterrà il valore dell'ultimo divisore. Partirà col valore 0 che starà a significare che non ci sono divisori. Dopo il for ci chiederemo semplicemente se è 0 nel qual caso diremo che il numero è primo altrimenti non lo è.

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

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

int i,n=0;
for(i=0;i<5;i++){
	for(i=0;i<5;i++){
		tf.setText(""+n);
		n++;
	}
}
4 20 25 0