Cicli Iterativi in Fortran
- I Cicli Iterativi sono cicli che si ripetono un numero specificato di volte.
- In Fortran, i cicli iterativi sono implementati usando il costrutto
DO
. - La sintassi di un ciclo iterativo
DO
include una variabile indice, un valore iniziale, un valore finale e un incremento opzionale. - La variabile indice viene aggiornata ad ogni iterazione del ciclo, e il ciclo continua finché la condizione di terminazione non è soddisfatta.
- È possibile contare in discesa usando un incremento negativo nel ciclo
DO
.
Cicli Iterativi
Nel linguaggio Fortran, un ciclo che esegue un blocco di istruzioni un numero specificato di volte è chiamato ciclo DO
iterativo o ciclo di conteggio.
I Cicli Iterativi sono conosciuti come Cicli FOR in altri linguaggi di programmazione
In altri linguaggi di programmazione, i cicli iterativi sono spesso chiamati cicli FOR. Per esempio, in C, C++, Java e JavaScript, i cicli iterativi sono chiamati cicli FOR. In Python, i cicli iterativi sono implementati usando la parola chiave for
. Anche se il nome è diverso, il concetto è lo stesso.
Il costrutto del ciclo di conteggio ha la forma che segue:
DO index = istart, iend, incr
! Corpo del ciclo
Istruzione 1
...
Istruzione n
END DO
dove index
è una variabile intera utilizzata come contatore del ciclo (nota anche come indice del ciclo). Le quantità intere istart
, iend
e incr
sono i parametri del ciclo di conteggio; controllano i valori che la variabile index
assume durante l'esecuzione. Il parametro incr
è opzionale; se manca, si assume che sia 1.
Le istruzioni tra l'istruzione DO
e l'istruzione END DO
sono note come il corpo del ciclo. Vengono eseguite ripetutamente durante ogni passaggio del ciclo DO
.
Il costrutto del ciclo di conteggio funziona come segue:
- Ognuno dei tre parametri del ciclo
DO
istart
,iend
eincr
può essere una costante, una variabile o un'espressione. Se sono variabili o espressioni, i loro valori vengono calcolati prima dell'inizio del ciclo, e i valori risultanti vengono utilizzati per controllare il ciclo. -
All'inizio dell'esecuzione del ciclo
DO
, il programma assegna il valoreistart
alla variabile di controlloindex
.Se è vera la condizione:
il programma esegue le istruzioni all'interno del corpo del ciclo.
-
Dopo che le istruzioni nel corpo del ciclo sono state eseguite, la variabile di controllo viene ricalcolata come:
Se
è ancora minore o uguale di , il programma esegue nuovamente le istruzioni all'interno del corpo. -
Il passo 2 viene ripetuto continuamente finché
. Quando questa condizione non è più vera, l'esecuzione salta alla prima istruzione che segue la fine del ciclo DO
.
Più semplicemente, bisogna interpretare le tre quantità istart
, iend
e incr
come segue:
istart
è il valore iniziale della variabile indiceindex
.iend
è il valore finale della variabile indiceindex
.incr
è l'incremento della variabile indiceindex
ad ogni iterazione. Se assente è pari a 1.
Il numero di iterazioni che il ciclo DO
effettua può essere calcolato utilizzando la seguente equazione
Esaminiamo alcuni esempi specifici per rendere più chiaro il funzionamento del ciclo di conteggio. Primo, consideriamo il seguente esempio:
DO i = 1, 10
Istruzione 1
...
Istruzione n
END DO
In questo caso, le istruzioni da 1 a n verranno eseguite 10 volte. La variabile indice i
sarà 1 la prima volta, 2 la seconda volta, e così via. La variabile indice sarà 10 nell'ultimo passaggio attraverso le istruzioni. Quando il controllo viene restituito all'istruzione DO
dopo il decimo passaggio, la variabile indice i
verrà aumentata a 11. Poiché END DO
.
Secondo, consideriamo il seguente esempio:
DO i = 1, 10, 2
Istruzione 1
...
Istruzione n
END DO
In questo caso, le istruzioni da 1 a n verranno eseguite cinque volte. La variabile indice i
sarà 1 la prima volta, 3 la seconda volta, e così via. La variabile indice sarà 9 nel quinto e ultimo passaggio attraverso le istruzioni. Quando il controllo viene restituito all'istruzione DO
dopo il quinto passaggio, la variabile indice i
verrà aumentata a 11. Poiché END DO
.
Terzo, consideriamo il seguente esempio:
DO i = 1, 10, -1
Istruzione 1
...
Istruzione n
END DO
Qui, le istruzioni da 1 a n non verranno mai eseguite, poiché vale che DO
. Invece, il controllo passerà alla prima istruzione dopo l'istruzione END DO
.
Infine, consideriamo l'esempio:
DO i = 3, -3, -2
Istruzione 1
...
Istruzione n
END DO
In questo caso, le istruzioni da 1 a n verranno eseguite quattro volte. La variabile indice i
sarà 3 la prima volta, 1 la seconda volta, −1 la terza volta e −3 la quarta volta. Quando il controllo viene restituito all'istruzione DO
dopo il quarto passaggio, la variabile indice i
verrà diminuita a −5. Poiché END DO
.
Possiamo rappresentare il flusso di controllo di un ciclo di conteggio con il diagramma di flusso mostrato nella figura che segue:
Esempio: Calcolo del Fattoriale
Per illustrare il funzionamento di un ciclo di conteggio, utilizzeremo un ciclo DO
per calcolare la funzione fattoriale. La funzione fattoriale è definita come
Il codice Fortran per calcolare N fattoriale per un valore positivo di N sarebbe
n_factorial = 1
DO i = 1, n
n_factorial = n_factorial * i
END DO
Supponiamo di voler calcolare il valore di DO
saranno istart = 1
, iend = 5
e incr = 1
. Questo ciclo verrà eseguito cinque volte, con la variabile i
che assume i valori 1, 2, 3, 4 e 5 nei cicli successivi. Il valore risultante di n_factorial
sarà
Esempio: Calcolo del Giorno dell'Anno
Il giorno dell'anno è il numero di giorni (incluso il giorno corrente) trascorsi dall'inizio di un dato anno. È un numero nell'intervallo da 1 a 365 per gli anni ordinari, e da 1 a 366 per gli anni bisestili.
Proviamo a scrivere un programma Fortran che accetta un giorno, mese e anno, e calcola il giorno dell'anno corrispondente a quella data.
Una possibile implementazione è la seguente:
PROGRAM giorno_anno
! Scopo:
! Questo programma calcola il giorno dell'anno corrispondente a una
! data specificata. Illustra l'uso dei cicli di conteggio
! e del costrutto SELECT CASE.
IMPLICIT NONE
! Dizionario dati: dichiara tipi di variabili, definizioni e unità
INTEGER :: day ! Giorno (gg)
INTEGER :: day_of_year ! Giorno dell'anno
INTEGER :: i ! Variabile indice
INTEGER :: leap_day ! Giorno extra per anno bisestile
INTEGER :: month ! Mese (mm)
INTEGER :: year ! Anno (aaaa)
! Ottieni giorno, mese e anno da convertire
WRITE (*,*) 'Questo programma calcola il giorno dell''anno data la '
WRITE (*,*) 'data corrente. Inserisci giorno(1-31), mese(1-12) '
WRITE (*,*) 'e anno in questo ordine: '
READ (*,*) day, month, year
! Controlla per anno bisestile, e aggiungi giorno extra se necessario
IF ( MOD(year,400) == 0 ) THEN
! Secoli divisibili per 400 sono bisestili
leap_day = 1
ELSE IF ( MOD(year,100) == 0 ) THEN
! Altri secoli non sono bisestili
leap_day = 0
ELSE IF ( MOD(year,4) == 0 ) THEN
! Altrimenti ogni quarto anno è bisestile
leap_day = 1
ELSE
! Tutti gli altri anni non sono bisestili
leap_day = 0
END IF
! Calcola giorno dell'anno
day_of_year = day
DO i = 1, month-1
! Aggiungi giorni nei mesi da gennaio al mese scorso
SELECT CASE (i)
CASE (1,3,5,7,8,10,12)
day_of_year = day_of_year + 31
CASE (4,6,9,11)
day_of_year = day_of_year + 30
CASE (2)
day_of_year = day_of_year + 28 + leap_day
END SELECT
END DO
! Stampa i risultati
WRITE (*,*) 'Giorno = ', day
WRITE (*,*) 'Mese = ', month
WRITE (*,*) 'Anno = ', year
WRITE (*,*) 'Giorno dell''anno = ', day_of_year
END PROGRAM giorno_anno
Per determinare il giorno dell'anno, questo programma dovrà sommare il numero di giorni in ogni mese precedente al mese corrente, più il numero di giorni trascorsi nel mese corrente. Un ciclo DO
verrà utilizzato per eseguire questa somma. Poiché il numero di giorni in ogni mese varia, è necessario determinare il numero corretto di giorni da aggiungere per ogni mese. Un costrutto SELECT CASE
verrà utilizzato per determinare il numero appropriato di giorni da aggiungere per ogni mese.
Durante un anno bisestile, un giorno extra deve essere aggiunto al giorno dell'anno per qualsiasi mese dopo febbraio. Questo giorno extra tiene conto della presenza del 29 febbraio nell'anno bisestile. Pertanto, per eseguire correttamente il calcolo del giorno dell'anno, dobbiamo determinare quali anni sono bisestili. Nel calendario gregoriano, gli anni bisestili sono determinati dalle seguenti regole:
- Gli anni divisibili per 400 sono anni bisestili.
- Gli anni divisibili per 100 ma non per 400 non sono anni bisestili.
- Tutti gli anni divisibili per 4 ma non per 100 sono anni bisestili.
- Tutti gli altri anni non sono anni bisestili.
Abbiamo utilizzato la funzione MOD
(per modulo) per determinare se un anno è divisibile per un dato numero. Se il risultato della funzione MOD
è zero, allora l'anno è divisibile.
Si noti che il programma somma il numero di giorni in ogni mese prima del mese corrente, e che utilizza un costrutto SELECT CASE
per determinare il numero di giorni in ogni mese.
Adesso proviamo il programma.
Utilizzeremo i seguenti risultati noti per testare il programma:
- L'anno 1999 non è un anno bisestile. Il 1° gennaio deve essere il giorno 1 dell'anno, e il 31 dicembre deve essere il giorno 365 dell'anno.
- L'anno 2000 è un anno bisestile. Il 1° gennaio deve essere il giorno 1 dell'anno, e il 31 dicembre deve essere il giorno 366 dell'anno.
- L'anno 2001 non è un anno bisestile. Il 1° marzo deve essere il giorno 60 dell'anno, poiché gennaio ha 31 giorni, febbraio ha 28 giorni, e questo è il primo giorno di marzo.
Se questo programma viene compilato e poi eseguito cinque volte con le date sopra indicate, i risultati sono
-
Input: 1 1 1999; Output: 1
Questo programma calcola il giorno dell'anno data la data corrente. Inserisci giorno(1-31), mese(1-12) e anno in questo ordine: 1 1 1999 Giorno = 1 Mese = 1 Anno = 1999 Giorno dell'anno = 1
-
Input: 31 12 1999; Output: 365
Questo programma calcola il giorno dell'anno data la data corrente. Inserisci giorno(1-31), mese(1-12) e anno in questo ordine: 31 12 1999 Giorno = 31 Mese = 12 Anno = 1999 Giorno dell'anno = 365
-
Input: 1 1 2000; Output: 1
Questo programma calcola il giorno dell'anno data la data corrente. Inserisci giorno(1-31), mese(1-12) e anno in questo ordine: 1 1 2000 Giorno = 1 Mese = 1 Anno = 2000 Giorno dell'anno = 1
-
Input: 31 12 2000; Output: 366
Questo programma calcola il giorno dell'anno data la data corrente. Inserisci giorno(1-31), mese(1-12) e anno in questo ordine: 31 12 2000 Giorno = 31 Mese = 12 Anno = 2000 Giorno dell'anno = 366
-
Input: 1 3 2001; Output: 60
Questo programma calcola il giorno dell'anno data la data corrente. Inserisci giorno(1-31), mese(1-12) e anno in questo ordine: 1 3 2001 Giorno = 1 Mese = 3 Anno = 2001 Giorno dell'anno = 60
Il programma fornisce le risposte corrette per le nostre date di test in tutti e cinque i casi di test.
Esempio: Analisi Statistica con Valori Positivi e Negativi
Implementiamo un algoritmo che legge un insieme di misurazioni e calcola la media e la deviazione standard del set di dati di input, quando qualsiasi valore nel set di dati può essere positivo, negativo o zero.
Per scrivere questo programma, chiederemo all'utente il numero di valori di input, e poi utilizzeremo un ciclo DO
per leggere quei valori.
L'implementazione è la seguente:
PROGRAM statistiche_valori
!
! Scopo:
! Per calcolare la media e la deviazione standard di un set di dati
! di input, dove ogni valore di input può essere positivo, negativo,
! o zero.
!
IMPLICIT NONE
! Dizionario dati: dichiara tipi di variabili, definizioni e unità
INTEGER :: i ! Indice del ciclo
INTEGER :: n = 0 ! Il numero di campioni di input.
REAL :: std_dev ! La deviazione standard dei campioni di input.
REAL :: sum_x = 0. ! La somma dei valori di input.
REAL :: sum_x2 = 0. ! La somma dei quadrati dei valori di input.
REAL :: x = 0. ! Un valore di dati di input.
REAL :: x_bar ! La media dei campioni di input.
! Richiede il numero di punti da inserire.
WRITE (*,*) 'Inserisci numero di punti: '
READ (*,*) n
! Controlla se abbiamo abbastanza dati di input.
IF ( n < 2 ) THEN
! Dati insufficienti
WRITE (*,*) 'Almeno 2 valori devono essere inseriti.'
ELSE
! Abbiamo abbastanza dati.
! Ciclo per leggere valori di input.
DO i = 1, n
! Leggi valori
WRITE (*,*) 'Inserisci numero: '
READ (*,*) x
WRITE (*,*) 'Il numero inserito è ', x
! Accumula somme.
sum_x = sum_x + x
sum_x2 = sum_x2 + x**2
END DO
! Ora calcola statistiche.
x_bar = sum_x / real(n)
std_dev = sqrt((real(n)*sum_x2 - sum_x**2) / (real(n)*real(n-1)))
! Stampa il risultato.
WRITE (*,*) 'La media di questo set di dati è:', x_bar
WRITE (*,*) 'La deviazione standard è: ', std_dev
WRITE (*,*) 'Il numero di punti dati è:', n
END IF
END PROGRAM statistiche_valori
Osservazioni sui Cicli DO
Ora che abbiamo visto esempi di un ciclo DO
di conteggio in funzione, esamineremo alcuni dettagli importanti necessari per utilizzare correttamente i cicli DO
.
-
Non è necessario indentare il corpo del ciclo
DO
come abbiamo mostrato sopra. Il compilatore Fortran riconoscerà il ciclo anche se ogni istruzione in esso inizia nella colonna 1. Tuttavia, il codice è molto più leggibile se il corpo del cicloDO
è indentato, quindi si dovrebbe sempre indentare i corpi dei cicliDO
.ConsiglioConviene sempre indentare il corpo di un ciclo
DO
Il consiglio è quello di indentare sempre il corpo di un ciclo
DO
di due o più spazi per migliorare la leggibilità del codice. -
La variabile indice di un ciclo
DO
non deve essere modificata da nessuna parte all'interno del cicloDO
. Poiché la variabile indice è utilizzata per controllare le ripetizioni nel cicloDO
, modificarla potrebbe produrre risultati inaspettati. Nel caso peggiore, modificare la variabile indice potrebbe produrre un ciclo infinito che non si completa mai. Consideriamo il seguente esempio:PROGRAM errore INTEGER :: i DO i = 1, 4 i = 2 END DO END PROGRAM errore
Se
i
viene reimpostato a 2 ogni volta attraverso il ciclo, il ciclo non finirà mai, perché la variabile indice non potrà mai essere maggiore di 4! Questo ciclo continuerà all'infinito a meno che il programma che lo contiene non venga terminato. Quasi tutti i compilatori Fortran riconosceranno questo problema, e genereranno un errore a tempo di compilazione se un programma tenta di modificare una variabile indice all'interno di un ciclo.NotaAttenzione: Cicli Infiniti
Non modificare mai il valore di una variabile indice di ciclo
DO
mentre si è all'interno del ciclo. Questo può causare un ciclo infinito che non si completa mai. -
Se il numero di iterazioni calcolato usando l'equazione vista sopra è minore o uguale a zero, le istruzioni all'interno del ciclo
DO
non vengono mai eseguite. Per esempio, le istruzioni nel seguente cicloDO
non verranno mai eseguiteDO i = 3, 2 ... END DO
poiché
-
È possibile progettare cicli
DO
di conteggio che contano in discesa oltre che in salita. Il seguente cicloDO
esegue tre volte coni
che è 3, 2 e 1 nei cicli successivi.DO i = 3, 1, -1 ... END DO
-
La variabile indice e i parametri di controllo di un ciclo
DO
dovrebbero sempre essere di tipointeger
.L'uso di variabili reali come indici di cicli
DO
e parametri di controllo di cicliDO
era una caratteristica legale ma indesiderabile di Fortran. È stata dichiarata obsoleta in Fortran 90, ed è stata completamente eliminata da Fortran 95. -
È possibile uscire da un ciclo
DO
in qualsiasi momento mentre il ciclo è in esecuzione. Se l'esecuzione del programma esce da un cicloDO
prima che altrimenti finisca, la variabile indice del ciclo mantiene il valore che aveva quando si verifica il salto. Si consideri il seguente esempio:INTEGER :: i DO i = 1, 5 ... IF (i >= 3) EXIT ... END DO WRITE (*,*) i
L'esecuzione uscirà dal ciclo
DO
e andrà all'istruzioneWRITE
al terzo passaggio attraverso il ciclo. Quando l'esecuzione arriva all'istruzioneWRITE
, la variabilei
conterrà un valore di 3. -
Se un ciclo
DO
si completa normalmente, il valore della variabile indice è indefinito quando il ciclo è completato. Nell'esempio mostrato sotto, il valore scritto dall'istruzioneWRITE
non è definito nello standard Fortran.INTEGER :: i DO i = 1, 5 ... END DO WRITE (*,*) i
Su molti computer, dopo che il ciclo è completato, la variabile indice
i
conterrà il primo valore della variabile indice a fallire il test:Nel codice sopra, il risultato conterrebbe solitamente un 6 dopo che il ciclo è finito. Tuttavia, non bisogna fare assunzioni su tale caratteristica. Poiché il valore è ufficialmente indefinito nello standard Fortran, alcuni compilatori potrebbero produrre un risultato diverso. Se il proprio codice dipende dal valore della variabile indice dopo che il ciclo è completato, si potrebbero ottenere risultati diversi quando il programma viene ricompilato su altre macchine.
NotaAttenzione: Valore Indefinito della Variabile Indice
Non dipendere mai da una variabile indice per mantenere un valore specifico dopo che un ciclo
DO
si completa normalmente.