Fortran
Procedure: funzioni e subroutine
Ricerca…
Osservazioni
Funzioni e subroutine , insieme ai moduli , sono gli strumenti per suddividere un programma in unità. Ciò rende il programma più leggibile e gestibile. Ciascuna di queste unità può essere pensata come parte del codice che, idealmente, potrebbe essere compilato e testato separatamente. I programmi principali possono chiamare (o richiamare) tali sottoprogrammi (funzioni o subroutine) per eseguire un'attività.
Le funzioni e le subroutine sono diverse nel seguente senso:
- Le funzioni restituiscono un singolo oggetto e - di solito - non alterano i valori dei suoi argomenti (cioè si comportano esattamente come una funzione matematica!);
- Le subroutine di solito svolgono un compito più complicato e ordinariamente alterano i loro argomenti (se ne esiste qualcuno), così come altre variabili (ad es. Quelle dichiarate nel modulo che contiene la subroutine).
Le funzioni e le subroutine vanno sotto il nome di procedure . (Nel seguito useremo il verbo "call" come sinonimo di "invocare" anche se tecnicamente le procedure da call
sono subroutine
s, mentre le function
s appaiono come lato destro del compito o nelle espressioni.)
Sintassi della funzione
Le funzioni possono essere scritte utilizzando diversi tipi di sintassi
function name()
integer name
name = 42
end function
integer function name()
name = 42
end function
function name() result(res)
integer res
res = 42
end function
Le funzioni restituiscono valori attraverso un risultato di funzione . A meno che l'istruzione della funzione non abbia una clausola di result
, il result
della funzione ha lo stesso nome della funzione. Con result
il result
della funzione è quello dato dal result
. In ciascuno dei primi due esempi sopra il risultato della funzione è dato dal name
; nel terzo da res
.
Il risultato della funzione deve essere definito durante l'esecuzione della funzione.
Le funzioni consentono di utilizzare alcuni prefissi speciali.
Funzione pura significa che questa funzione non ha effetti collaterali:
pure real function square(x)
real, intent(in) :: x
square = x * x
end function
La funzione elementale è definita come operatore scalare, ma può essere invocata con array come argomento effettivo, nel qual caso la funzione verrà applicata a livello di elemento. A meno che non sia specificato il prefisso impure
(introdotto in Fortran 2008), una funzione elementare è anche una funzione pura .
elemental real function square(x)
real, intent(in) :: x
square = x * x
end function
Dichiarazione di ritorno
L'istruzione return
può essere utilizzata per uscire dalla funzione e dalla subroutine. A differenza di molti altri linguaggi di programmazione non è usato per impostare il valore di ritorno.
real function f(x)
real, intent(in) :: x
integer :: i
f = x
do i = 1, 10
f = sqrt(f) - 1.0
if (f < 0) then
f = -1000.
return
end if
end do
end function
Questa funzione esegue un calcolo iterativo. Se il valore di f
diventa negativo, la funzione restituisce il valore -1000.
Procedure ricorsive
In Fortran, le funzioni e le subroutine devono essere esplicitamente dichiarate come ricorsive , se devono chiamarsi di nuovo, direttamente o indirettamente. Pertanto, un'implementazione ricorsiva della serie di Fibonacci potrebbe essere simile a questa:
recursive function fibonacci(term) result(fibo)
integer, intent(in) :: term
integer :: fibo
if (term <= 1) then
fibo = 1
else
fibo = fibonacci(term-1) + fibonacci(term-2)
end if
end function fibonacci
Un altro esempio può calcolare fattoriale:
recursive function factorial(n) result(f)
integer :: f
integer, intent(in) :: n
if(n == 0) then
f = 1
else
f = n * f(n-1)
end if
end function factorial
Affinché una funzione faccia riferimento direttamente a se stessa, la sua definizione deve utilizzare il suffisso del result
. Non è possibile che una funzione sia sia recursive
che elemental
.
L'intento di argomenti fittizi
L'attributo intent
di un argomento fittizio in una subroutine o funzione dichiara l'uso previsto. La sintassi è uno dei due
intent(IN)
intent(OUT)
intent(INOUT)
Ad esempio, considera questa funzione:
real function f(x)
real, intent(IN) :: x
f = x*x
end function
L' intent(IN)
specifica che l'argomento dummy (non puntatore) x
non può mai essere definito o diventare indefinito in tutta la funzione o nella sua inizializzazione. Se un argomento fittizio puntatore ha l' intent(IN)
attributo intent(IN)
, questo vale per la sua associazione.
intent(OUT)
per un argomento fittizio senza puntatore indica che l'argomento dummy diventa indefinito in caso di invocazione del sottoprogramma (ad eccezione di qualsiasi componente di un tipo derivato con inizializzazione predefinita) e deve essere impostato durante l'esecuzione. L'argomento effettivo passato come argomento fittizio deve essere definibile: il passaggio di una costante denominata o letterale o un'espressione non è consentito.
Analogamente a prima, se un argomento fittizio puntatore è intent(OUT)
lo stato di associazione del puntatore diventa indefinito. L'argomento attuale qui deve essere una variabile puntatore.
intent(INOUT)
specifica che l'argomento attuale è definibile ed è adatto sia per l'inoltro che per il ritorno dei dati dalla procedura.
Infine, un argomento fittizio può essere senza l'attributo intent
. Tale argomento fittizio ha il suo uso limitato dall'argomento passato.
Ad esempio, considera
integer :: i = 0
call sub(i, .TRUE.)
call sub(1, .FALSE.)
end
subroutine sub(i, update)
integer i
logical, intent(in) :: update
if (update) i = i+1
end subroutine
L'argomento i
può avere alcun intent
attributo che permette sia delle chiamate subroutine del programma principale.
Fare riferimento a una procedura
Affinché una funzione o una subroutine siano utili, è necessario fare riferimento. A una subroutine viene fatto riferimento in una dichiarazione di call
call sub(...)
e una funzione all'interno di un'espressione. A differenza di molti altri linguaggi, un'espressione non costituisce un'istruzione completa, quindi un riferimento alla funzione è spesso visto in un'istruzione di assegnazione o utilizzato in altro modo:
x = func(...)
y = 1 + 2*func(...)
Esistono tre modi per designare una procedura a cui si fa riferimento:
- come il nome di un puntatore di procedura o procedura
- un componente di procedura di un oggetto di tipo derivato
- un nome vincolante alla procedura di tipo binding
Il primo può essere visto come
procedure(), pointer :: sub_ptr=>sub
call sub() ! With no argument list the parentheses are optional
call sub_ptr()
end
subroutine sub()
end subroutine
e gli ultimi due come
module mod
type t
procedure(sub), pointer, nopass :: sub_ptr=>sub
contains
procedure, nopass :: sub
end type
contains
subroutine sub()
end subroutine
end module
use mod
type(t) x
call x%sub_ptr() ! Procedure component
call x%sub() ! Binding name
end
Per una procedura con argomenti fittizi, il riferimento richiede argomenti effettivi corrispondenti, sebbene non sia possibile fornire argomenti fittizi facoltativi.
Considera la subroutine
subroutine sub(a, b, c)
integer a, b
integer, optional :: c
end subroutine
Questo può essere fatto riferimento nei seguenti due modi
call sub(1, 2, 3) ! Passing to the optional dummy c
call sub(1, 2) ! Not passing to the optional dummy c
Questo è il cosiddetto riferimento posizionale : gli argomenti reali sono associati in base alla posizione negli elenchi degli argomenti. Qui, il dummy a
è associato con 1
, b
con 2
c
(se specificato) con 3
.
In alternativa, è possibile utilizzare la referenziazione delle parole chiave quando la procedura ha un'interfaccia esplicita disponibile
call sub(a=1, b=2, c=3)
call sub(a=1, b=2)
che è lo stesso di sopra.
Tuttavia, con le parole chiave gli argomenti reali possono essere offerti in qualsiasi ordine
call sub(b=2, c=3, a=1)
call sub(b=2, a=1)
Possono essere utilizzati entrambi i riferimenti di posizione e parola chiave
call sub(1, c=3, b=2)
purché venga fornita una parola chiave per ogni argomento che segue la prima apparizione di una parola chiave
call sub(b=2, 1, 3) ! Not valid: all keywords must be specified
Il valore del riferimento alla parola chiave è particolarmente pronunciato quando ci sono più argomenti fittizi facoltativi, come mostrato di seguito se nella definizione della subroutine sopra b
erano anche facoltativi
call sub(1, c=3) ! Optional b is not passed
Gli elenchi degli argomenti per le procedure con collegamento del testo oi puntatori alle procedure del componente con un argomento passato vengono considerati separatamente.