Sottoprogrammi
Scrivendo un programma può essere utile suddividere il problema principale in vari sottoproblemi. In FORTRAN è possibile definire un'azione o un gruppo di azioni come FUNCTION o SUBROUTINE, contrassegnate da degli identificatori.
Le funzioni standard sono dei sottoprogrammi già pronti, che possono essere richiamati all'interno del programma principale in qualsiasi momento. Di seguito è riportato un elenco delle principali funzioni:
|
Nome funzione |
Azione |
Esempio |
|
INT |
N=INT(X) |
|
|
REAL |
X=REAL(N) |
|
|
ABS |
Restituisce il valore assoluto di un numero |
Y=ABS(X) |
|
MOD |
È l'operazione modulo (restituisce il risultato della divisione di due INTEGER) |
M=MOD(I,J) |
|
MAX |
Restituisce il massimo di almeno due numeri (gli argomenti devono essere dello stesso tipo) |
X=MAX(A,B,C,D) |
|
MIN |
Restituisce il minimo di almeno due numeri (gli argomenti devono essere dello stesso tipo) |
X=MIN(A,B,C,D) |
|
SQRT |
Restituisce la radice quadrata di un numero |
Y=SQRT(X) |
|
EXP |
Restituisce l'esponenziale di un numero |
Y=EXP(X) |
|
LOG |
Restituisce il logaritmo naturale di un numero |
Y=LOG(X) |
|
LOG10 |
Restituisce il logaritmo in base 10 di un numero |
Y=LOG10(X) |
|
COS |
Calcola il coseno (argomento in radianti) |
Y=COS(X) |
|
SIN |
Calcola il seno (argomento in radianti) |
Y=SIN(X) |
|
TAN |
Calcola la tangente (argomento in radianti) |
Y=TAN(X) |
|
ACOS |
Calcola l'arcocoseno |
Y=ACOS(X) |
|
ASIN |
Calcola l'arcoseno |
Y=ASIN(X) |
|
ATAN |
Calcola l'arcotangente |
Y=ATAN(X) |
|
ATAN2 |
Calcola l'arcotangente del rapporto di due numeri |
Z=ATAN2(X,Y) |
|
INDEX |
Restituisce la posizione iniziale di una sottostringa in una stringa |
I=INDEX(STR,SUBSTR) |
|
ICHAR |
Restituisce il codice ASCII di un carattere |
I=ICHAR(CAR) |
|
CHAR |
Restituisce un carattere a partire dal suo codice ASCII |
CAR=CHAR(I) |
|
LLE |
Verifica se una stringa è lessicamente minore o uguale ad un'altra |
BOOL=LLE(S1,S2) |
|
LGE |
Verifica se una stringa è lessicamente maggiore o uguale ad un'altra |
BOOL=LGE(S1,S2) |
|
LLT |
Verifica se una stringa è lessicamente minore di un'altra |
BOOL=LLT(S1,S2) |
|
LGT |
Verifica se una stringa è lessicamente maggiore di un'altra |
BOOL=LGT(S1,S2) |
I nomo di funzioni standard non possono essere adoperati come identificatori.
Le funzioni sono sottoprogrammi che, se necessario, possono
essere richiamati più volte all'interno di un programma per fornire dei calcoli
particolari. Le funzioni, a differenza delle funzioni
standard, vengono scritte dal programmatore stesso in quanto non è resa
disponibile dal compilatore una funzione standard
adatta allo scopo del programmatore.
Le funzioni sono dei blocchi di
istruzioni identificate da:
tipo_del_risultato FUNCTION nome_funzione(id1,id2,...,idn)
tipo_del_risultato è uno dei tipi INTEGER,
REAL, CHARACTER,
LOGICAL, e definisce il tipo di dati
restituito dalla funzione chiamata nome_funzione. È tramite
nome_funzione che si richiama la funzione all'interno del programma
principale. Gli argomenti id1,id2,...,idn sono gli
identificatori su cui la funzione opera. Esempio:
REAL FUNCTION F(N,X,Y,Z)
...............................
blocco di istruzioni del sottoprogramma
END
Le funzioni lavorano sui valori memorizzati negli argomenti e restituiscono un risultato del tipo specificato. L'elenco degli identificatori costituisce un insieme di oggetti fittizi, ai quali, quando il sottoprogramma viene richiamato, vengono rimpiazzati i valori degli argomenti dell'istruzione chiamante.
Dopo la definizione di una funzione, come di vede
dall'esempio, segue il blocco di istruzioni che costituiscono il sottoprogramma.
Tra queste vi deve essere almeno un'istruzione
di assegnazione con il nome della funzione a sinistra di = (nell'esempio ci
dovrà quindi essere F=), che assegna un valore a questa variabile e che viene
quindi restituito al programma chiamante al termine della funzione, che come
tutti i programmi deve terminare con END. Esempio:
INTEGER FUNCTION NFATT(N)
NFATT=1
DO 10 I=1,N
NFATT=NFATT*I
10 CONTINUE
END
Per richiamare una funzione nel programma principale si
adoperano istruzioni del tipo:
identificatore=nome_funzione(ID1,ID2,...,IDn)
ID1,ID2,...,IDn sono gli argomenti della funzione che
corrispondono in numero e tipo (se sono degli
array anche nelle dimensioni) a id1,id2,...,idn
rispettivamente. Per richiamare la funzione dell'esempio precedente:
N=NFATT(NUM)
Non appena nel programma princiale si incontra il nome della
funzione l'esecuzione passa alla funzione con quel nome, e ad ognuno degli
argomenti viene assegnato il valore corrispondente. Terminata l'esecuzione della
funzione si torna al programma principale. Si intende che prima di poter
richiamare una funzione dal programma principale è necessario assegnare ad ogni
variabile che compare tra gli argomenti della funzione un valore.
Le SUBROUTINE sono sottoprogrammi che, se necessario, possono
essere richiamati più volte all'interno di un programma per fornire dei calcoli
particolari. Le SUBROUTINE, come le funzioni, a differenza delle funzioni
standard, vengono scritte dal programmatore stesso in quanto non è resa
disponibile dal compilatore una funzione standard
adatta allo scopo del programmatore.
Le SUBROUTINE sono dei blocchi di
istruzioni identificate da:
SUBROUTINE
nome_sottoprogramma(id1,id2,...,idn)
nome_sottoprogramma attribuisce un nome alla SUBROUTINE, è tramite questo che si
fa riferimento al sottoprogramma all'interno del programma
principale, come avveniva per le funzioni. A differenza
delle funzioni, nome_sottoprogramma non è una
variabile passante (variabile che viene restituita al programma chiamante), per
questo non si deve specificare il tipo. Gli argomenti id1,id2,...,idn sono gli
identificatori su cui la SUBROUTINE opera. Esempio:
SUBROUTINE SUB(N,X,Y,Z)
...............................
blocco di istruzioni del sottoprogramma
END
Le SUBROUTINE lavorano sui valori memorizzati negli argomenti e possono restituire dei risultati. L'elenco degli identificatori costituisce un insieme di oggetti fittizi, ai quali, quando il sottoprogramma viene richiamato, vengono rimpiazzati i valori degli argomenti dell'istruzione chiamante.
Si è detto che le SUBROUTINE possono restituire dei
risultati, in quanto possono restituire al programma chiamante uno o
più risultati, o nessuno, a differenza delle
funzioni che restituivano sempre un solo valore. Il compito di
restituire il risultato al programma chiamante, non essendo
nome_sottoprogramma una variabile passante, è affidato a uno o più elementi id1,id2,...,idn,
o anche a nessuno, in tal caso la SUBROUTINE non restituisce alcun risultato.
Quindi sono gli identificatori che restituiscono il
risultato a trovarsi a sinistra delle istruzioni
di assegnazione all'interno del blocco di istruzioni che costituisce la
SUBROUTINE. Come tutti i programmi anche le SUBROUTINE devono essere terminate
con END. Riscriviamo il precedente esempio di funzione
come SUBROUTINE:
SUBROUTINE FATTORIALE(N,NFATT)
NFATT=1
DO 10 I=1,N
NFATT=NFATT*I
10 CONTINUE
END
FATTORIALE è il nome della SUBROUTINE. Il tipo di FATTORIALE non ha ne
importanza ne effetti, a differenza delle funzioni.
Diversamente dalla funzione NFATT, che aveva come
argomento solo N, la SUBROUTINE FATTORIALE ha come argomenti N e NFATT, e
proprio a quest'ultimo è assegnato il compito di restituire il valore al
programma chiamante.
La dichiarazione di una SUBROUTINE non causa l'esecuzione del
suo corpo, perché ciò avvenga è necessario chiamare la SUBROUTINE dal programma principale
con la seguente istruzione:
CALL
nome_sottoprogramma(ID1,ID2,...,IDn)
ID1,ID2,...,IDn sono gli argomenti della SUBROUTINE che
corrispondono in numero e tipo (se sono degli
array anche nelle dimensioni) a id1,id2,...,idn
rispettivamente, come per le funzioni. Per richiamare la
SUBROUTINE dell'esempio precedente
si usa:
CALL FATTORIALE(NUM,NRIS)
Non appena nel programma princiale si incontra il nome della
SUBROUTINE l'esecuzione passa alla SUBROUTINE con quel nome, e ad ognuno degli
argomenti viene assegnato il valore corrispondente. Terminata l'esecuzione della
SUBROUTINE si torna al programma principale. Si intende che prima di
richiamare una SUBROUTINE dal programma principale è necessario assegnare dei
valori ad ogni variabile che compare tra gli argomenti della SUBROUTINE
necessaria per i calcoli, non è necessario assegnare dei valori alle
variabili passanti.
La precedente SUBROUTINE può anche essere scritta nel
seguente modo:
SUBROUTINE FATTORIALE(N)
INTEGER APP
APP=1
DO 10 I=1,N
APP=APP*I
10 CONTINUE
N=APP
END
In questo caso la variabile N ha il compito di portare i dati all'interno
della SUBROUTINE prima e quello di restituire il risultato della SUBROUTINE al
programma chiamante. In questo caso, dopo l'esecuzione della SUBROUTINE, non si
potrà più far riferimento al precedente valore della N. La variabile APP è una
variabile locale della SUBROUTINE.
Di seguito è riportato un'altra variante della
stessa SUBROUTINE come esempio di SUBROUTINE che
non restituiscono valori al programma chiamante:
SUBROUTINE
FATTORIALE(N)
NFATT=1
DO 10 I=1,N
NFATT=NFATT*I
10 CONTINUE
PRINT*,NFATT
END
Per quanto riguarda gli ultimi due esempi visti, l'istruzione
di chiamata è la seguente:
CALL FATTORIALE(NUM)
È di seguito riportato un esempio di SUBROUTINE che
restituisce più valori:
SUBROUTINE ORDINA(X,Y)
IF (X.GT.Y) THEN
Z=X
X=Y
Y=Z
ENDIF
END
nel precedente esempio X,Y sono le variabili di ingresso-uscita, mentre Z
è una variabile di appoggio che non torna al programma chiamante.
In conclusione si noti che una SUBROUTINE può richiamare un'altra SUBROUTINE, ma non ricorsivamente (non può richiamare se stessa).
Uso degli array nelle FUNCTION e nelle SUBROUTINE
Come è già stato detto, tra gli argomenti di
SUBROUTINE e FUNCTION ci
possono essere degli array, come si vede dall'esempio
seguente:
SUBROUTINE ORDINA(X)
INTEGER X
DIMENSION X(100)
DO 10 I=1,99
DO 10 J=I+1,100
IF (X(I).GT.X(J)) THEN
NAPP=X(I)
X(I)=X(J)
X(J)=NAPP
ENDIF
10 CONTINUE
END
con il relativo comando di richiamo:
CALL ORDINA(A)
Il FORTRAN permette nei sottoprogrammi il dimensionamento
variabile degli array. Così il precedenteesempio
diventa:
SUBROUTINE ORDINA(X,N)
INTEGER X
DIMENSION X(N)
DO 10 I=1,N-1
DO 10 J=I+1,N
IF (X(I).GT.X(J)) THEN
NAPP=X(I)
X(I)=X(J)
X(J)=NAPP
ENDIF
10 CONTINUE
END
con il relativo comando di richiamo:
CALL ORDINA(A,M)
È ovvio che M (assegnato con un'istruzione
di assegnazione, o con un input) non deve
superare la dimensione di A dichiarata una volta per tutte nel programma
principale.
Segue un esempio di uso errato del dimensionamento variabile:
SUBROUTINE SOMMA(X,Y,N)
DIMENSION X(N),Y(N),Z(N)
DO 10 I=1,N
Z(I)=X(I)+Y(I)
10 CONTINUE
PRINT*,(Z(I),I=1,N)
END
l'errore è nel dimensionamento di Z, che non è una variabile passante, ma
una variabile locale della SUBROUTINE e quindi deve
essere dimensionata con una costante e non con una variabile.
È ovvio che non è neanche permesso aumentare le dimensioni di un array oltre il limite dato con l'istruzione DIMENSION nel programma principale.
Il FORTRAN permette una scrittura come la seguente:
SUBROUTINE
PRODOTTO(X,Y,Z,N)
DIMENSION X(N),Y(N),Z(*)
DO 10 I=1,N
Z(I)=0
10 CONTINUE
DO 20 I=1,N
DO 20 J=1,N
Z(I)=Z(I)+X(I)*Y(J)
20 CONTINUE
END
l'asterisco (*) sostituisce la variabile nel DIMENSION e può essere
utilizzato quando la dimensione dell'array è il
risultato delle operazioni effettuate nella SUBROUTINE.
Quanto detto per le SUBROUTINE si intende valido anche per le funzioni.