Archivio tag: while

Realizzare un ciclo iterativo con l’istruzione repeat

Abbiamo già visto come realizzare un ciclo iterativo in R sfruttando le istruzioni for e while. Se il primo ciclo è definito da un numero predeterminato di iterazioni, il secondo è invece potenzialmente infinito e sarà la condizione logica espressa al momento del lancio a stabilire il criterio d’arresto. Di solito, il ciclo for si usa quando già prima della sua esecuzione è noto il numero di iterazioni da eseguire, mentre il ciclo while non richiede questa conoscenza: se all’interno del ciclo accade qualcosa che soddisfa la condizione d’arresto, allora il while si chiuderà.

Per comprendere il terzo ciclo, il repeat, è necessario aver ben digerito i primi due, che consiglio di andare a rivedere nel caso di dubbi (qui e qui).

Il ciclo repeat è, come il while, potenzialmente infinito. La differenza è che, rispetto al while, il repeat è ancora meno restrittivo perché non richiede di specificare alcuna condizione d’arresto; se non si presta molta attenzione, infatti, si rischia veramente di far perdere l’interprete R all’interno di un loop infinito! In questi casi, solo la brutale chiusura del programma potrà fermare il ciclo.

Utilizzare l’istruzione repeat è semplicissimo (attenzione: il codice qui sotto genera un ciclo infinito):

repeat {
    # Codice da eseguire
}

Fra i tre cicli iterativi di R, il repeat è certamente il più flessibile: l’arresto è determinato esclusivamente da quello che accade al suo interno. La vera sfida nell’uso di repeat non è tanto comprendere come funziona il comando quanto piuttosto capire come fermarlo!

Per controllare l’evolversi delle iterazioni, R mette a disposizione due istruzioni che sono next e break. La prima interrompe una iterazione per saltare direttamente alla successiva, mentre la seconda ferma tutto e chiude definitivamente il ciclo.

Per esempio, il seguente codice lancia un ciclo di iterazioni che vengono immediatamente bloccate con la conseguente uscita dal loop:

repeat {
    break
}

Ovviamente, questo non è sufficiente per utilizzare repeat in maniera proficua. Noi dobbiamo essere in grado non solo di bloccare le iterazioni ma anche di controllare quando bloccarle. Per questo motivo, le istruzioni next e break devono essere combinate con altre due istruzioni, che sono if ed else. Queste indicano a R cosa fare “se” (if) si presenta una certa situazione, e cosa fari “altrimenti” (else), nel caso in cui la condizione espressa dalla if non fosse verificata.

Dato che vedere il codice in azione è sempre più eloquente di mille spiegazioni, vediamo di utilizzare un po’ questi comandi per comprenderli meglio.

Cerchiamo di realizzare un ciclo di dieci iterazioni utilizzando l’istruzione repeat. Il ciclo non dovrà fare altro che stampare a video il numero di iterazioni eseguite, e bloccarsi al termine della decima. Ecco quindi il codice che dovremo utilizzare:


i <- 0 repeat { i <- i+1 print(i) if(i == 10) { break } } [/code]

Analizziamolo. Abbiamo inizializzato la variabile i col valore zero. Dopodiché siamo entrati nel ciclo e abbiamo aggiornato il valore di i, incrementandolo di uno. Questa operazione verrà eseguita a ogni iterazione, in modo che i conti quante iterazioni sono state eseguite sino a quel momento. Quindi, con il comando print, stampiamo a video il valore di i.

A questo punto viene la parte difficile: dobbiamo verificare a quale iterazione siamo arrivati, ovvero qual è il valore di i, perché, se siamo arrivati a 10, dobbiamo arrestare il ciclo. Per farlo ci serviamo dell’istruzione if, che verifica se i è uguale a 10.

Attenzione! if è un comando che valuta la verità di una condizione logica: se la condizione è vera, allora il codice inserito all’interno delle sue parentesi graffe verrà eseguito, altrimenti, se c’è un else consecutivo verranno eseguite le operazioni interne alla else. Se dopo la if non ci dovesse essere un else (come nel nostro caso), semplicemente l’esecuzione del codice proseguirà indisturbata. Facciamo subito un esempio:


i <- 0 repeat { i <- i+1 print(i) if(i == 10) { break } else { print("Via al prossimo ciclo!") } } [/code]

Qui sopra è stata aggiunta una condizione alternativa alla if che stampa a video una stringa nel caso i non dovesse valere 10.

Ora immaginiamo di voler saltare il secondo ciclo; vogliamo sì arrivare al decimo, ma senza eseguire il secondo. È in questi casi che entra in gioco l’istruzione next:


i <- 0 repeat { i <- i+1 if(i == 2) { next } else { print(i) if(i == 10) { break } } } [/code]

Il codice qui sopra, non appena troverà che i vale due, salterà direttamente alla terza iterazione, altrimenti proseguirà con le operazioni. Quando si iniziano ad annidare istruzioni if ed else il rischio di scordarsi la chiusura di una parentesi è sempre molto elevato, ed è per questo che ho scelto di indentare il codice.

Si faccia attenzione che, quando il corpo dell’istruzione if contiene un’unica riga di codice, le parentesi graffe possono anche essere omesse (lo stesso vale per else).


i <- 0 repeat { i <- i+1 if(i == 2) { next } else { print(i) if(i == 10) break } } [/code]

A questo punto non mi resta che augurarvi buon divertimento!

Il ciclo iterativo while

Qualche tempo fa abbiamo parlato dei cicli iterativi e in particolare del ciclo for. I cicli sono una componente fondamentale di un linguaggio di programmazione e consentono di ripetere per un certo numero di volte una sequenza di istruzioni. Definita una variabile contatore e definiti n valori che tale variabile dovrà assumere, Il ciclo for ripete un blocco di istruzioni, aggiornando ciclo dopo ciclo il valore della variabile contatore finché questa non avrà assunto tutti i possibili valori.

Il ciclo while invece ha una logica un po’ diversa. Potenzialmente, un ciclo while potrebbe anche essere infinito perché non è richiesta alcuna variabile contatore: sarà il programmatore a stabile se è opportuno utilizzarla o meno. L’unica cosa che richiede il ciclo while è una condizione logica: finché questa sarà soddisfatta, il codice continuerà a essere ripetuto.


i <- 0 while(i == 0) print(i) [/code]

Nell’esempio qui sopra è stata creata una variabile i che contiene il valore 0. La condizione logica espressa nel ciclo while impone che l’istruzione prosegua nelle iterazioni finché i sarà uguale a 0. Dato che all’interno del ciclo i non viene in alcun modo alterata, l’istruzione while andrà avanti all’infinito rendendo necessario l’arresto forzato di R.

Se invece volessimo realizzare solo 100 iterazioni, dovremmo fare in modo che, all’interno del ciclo, i venga di volta in volta aggiornata, diventando così un vero e proprio contatore: quanto i varrà 100, il ciclo si chiuderà.


i <- 0 while(i < 100) { i <- i+1 print(i) } [/code]

Il ciclo qui sopra quindi non è infinito ma realizza 100 iterazioni. La potenza dell’istruzione while sta però proprio nel fatto di togliere al programmatore l’onere di stabilire in partenza il numero di iterazioni (per quello esiste già il ciclo for!). Può capitare infatti che il programmatore non abbia un’idea precisa di quante iterazioni occorrano per raggiungere un certo risultato; in questi casi sarà il codice a dover “capire” quando è giunta l’ora di concludere il ciclo.

Proviamo a fare un esempio pratico. Dobbiamo creare un ciclo while che, dato un certo numero x, a ogni iterazione tolga a x la sua radice quadrata; il ciclo si dovrà fermare quando x sarà inferiore a 1. Di seguito è riportato il codice per x = 100.


x <- 100 while(x >= 1)
x <- x-sqrt(x) [/code]

Dopo aver definito il valore x, il ciclo while viene avviato; esso parte (e prosegue) solo se x è maggiore di 1. Nel corso delle iterazioni x viene di volta in volta aggiornato, sottraendogli la sua radice quadrata. Il ciclo si chiuderà quando la condizione logica in apertura restituirà FALSE, cosa che capiterà soltanto quando x sarà minore di 1.

Possiamo aggiungere un vincolo al ciclo, facendo in modo che il numero di iterazioni non superi mai un certo valore; questa è una strategia che viene utilizzata spesso negli algoritmi di ottimizzazione, i quali a un certo punto devono concludersi anche se la soluzione del problema di ottimizzazione non è stata individuata. Nel nostro caso possiamo imporre che il ciclo non ecceda le 10 iterazioni. Per fare questo avremo bisogno di una variabile contatore che, aggiornata iterazione dopo iterazione, tenga traccia del numero di cicli eseguiti.


x <- 100 i <- 0 while(x >= 1 & i < 10) { i <- i+1 x <- x-sqrt(x) } [/code]

Il ciclo riportato qui sopra terminerà ben prima che la variabile x sia diventata inferiore a 1. Infatti, quando il contatore i avrà raggiunto il valore 10, il ciclo si dovrà in ogni caso arrestare e questo a prescindere dal valore di x. Vale però anche il contrario: il ciclo potrà fermarsi ben prima delle 10 iterazioni se x sarà giunto a un valore inferiore a 1. Difatti, perché la condizione x >= 1 & i < 10 risulti vera, x deve essere maggiore o uguale a 1 e contemporaneamente i minore di 10. Quando anche solo una delle due variabili non dovesse rispettare la condizione posta, il ciclo terminerà.

Il ciclo while è un’istruzione fondamentale quando si utilizza R per creare del codice “intelligente”, cioè scritto in modo che sia in grado di valutare autonomamente delle condizioni e decidere quanto spingersi nella soluzione di un problema. Forse nell’uso quotidiano di R è poco applicato (io di solito preferisco il for) ma ci sono casi in cui questa istruzione diventa davvero imprescindibile.