Fortran
Procedimientos - Funciones y subrutinas.
Buscar..
Observaciones
Las funciones y las subrutinas , junto con los módulos , son las herramientas para dividir un programa en unidades. Esto hace que el programa sea más legible y manejable. Cada una de estas unidades puede considerarse como parte del código que, idealmente, podría compilarse y probarse de forma aislada. Los programas principales pueden llamar (o invocar) a dichos subprogramas (funciones o subrutinas) para realizar una tarea.
Las funciones y las subrutinas son diferentes en el siguiente sentido:
- Las funciones devuelven un solo objeto y, generalmente, no alteran los valores de sus argumentos (es decir, ¡actúan como una función matemática);
- Las subrutinas generalmente realizan una tarea más complicada y generalmente alteran sus argumentos (si hay alguno presente), así como otras variables (por ejemplo, aquellas declaradas en el módulo que contiene la subrutina).
Las funciones y subrutinas van colectivamente bajo el nombre de procedimientos . (En lo siguiente, usaremos el verbo "call" como sinónimo de "invocar" incluso si técnicamente los procedimientos a ser call
son subroutine
, mientras que las function
s aparecen como parte derecha de la asignación o en expresiones).
Sintaxis funcional
Las funciones se pueden escribir utilizando varios tipos de sintaxis.
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
Las funciones devuelven valores a través de un resultado de función . A menos que la declaración de función tenga una cláusula de result
el result
la función tendrá el mismo nombre que la función. Con result
el result
la función es el dado por el result
. En cada uno de los dos primeros ejemplos anteriores, el resultado de la función viene dado por name
; en el tercero por res
.
El resultado de la función debe definirse durante la ejecución de la función.
Las funciones permiten utilizar algunos prefijos especiales.
Función pura significa que esta función no tiene ningún efecto secundario:
pure real function square(x)
real, intent(in) :: x
square = x * x
end function
La función elemental se define como operador escalar, pero se puede invocar con array como argumento real, en cuyo caso la función se aplicará de forma elemental. A menos que se especifique el prefijo impure
(introducido en Fortran 2008), una función elemental también es una función pura .
elemental real function square(x)
real, intent(in) :: x
square = x * x
end function
Declaración de devolución
La declaración de return
se puede utilizar para salir de la función y la subrutina. A diferencia de muchos otros lenguajes de programación, no se utiliza para establecer el valor de retorno.
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
Esta función realiza un cálculo iterativo. Si el valor de f
vuelve negativo, la función devuelve el valor -1000.
Procedimientos recursivos
En Fortran, las funciones y las subrutinas deben declararse explícitamente como recursivas , si han de llamarse a sí mismas de nuevo, directa o indirectamente. Por lo tanto, una implementación recursiva de la serie Fibonacci podría tener este aspecto:
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
Otro ejemplo es permitido calcular factorial:
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
Para que una función se refiera directamente a sí misma, su definición debe usar el sufijo de result
. No es posible que una función sea tanto recursive
como elemental
.
La intención de los argumentos ficticios
El atributo de intent
de un argumento ficticio en una subrutina o función declara su uso previsto. La sintaxis es una de
intent(IN)
intent(OUT)
intent(INOUT)
Por ejemplo, considere esta función:
real function f(x)
real, intent(IN) :: x
f = x*x
end function
La intent(IN)
especifica que el argumento ficticio (sin puntero) x
nunca se puede definir o perder su definición a lo largo de la función o su inicialización. Si un argumento ficticio de puntero tiene el atributo intent(IN)
, esto se aplica a su asociación.
intent(OUT)
para un argumento ficticio no puntero significa que el argumento ficticio se convierte en indefinido en la invocación del subprograma (a excepción de cualquier componente de un tipo derivado con inicialización predeterminada) y se debe establecer durante la ejecución. El argumento real pasado como argumento ficticio debe ser definible: no se permite pasar una constante con nombre o literal, o una expresión.
De forma similar a antes, si un argumento ficticio de puntero es intent(OUT)
el estado de asociación del puntero se vuelve indefinido. El argumento real aquí debe ser una variable de puntero.
intent(INOUT)
especifica que el argumento real es definible y es adecuado para pasar y devolver datos del procedimiento.
Finalmente, un argumento ficticio puede estar sin el atributo de intent
. Tal argumento ficticio tiene su uso limitado por el argumento real pasado.
Por ejemplo, considere
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
El argumento i
puede tener ningún atributo de intent
que permita las dos llamadas de subrutina del programa principal.
Haciendo referencia a un procedimiento
Para que una función o subrutina sea útil debe ser referenciada. Se hace referencia a una subrutina en una instrucción de call
call sub(...)
y una función dentro de una expresión. A diferencia de muchos otros idiomas, una expresión no forma una declaración completa, por lo que una referencia de función se ve a menudo en una instrucción de asignación o se usa de alguna otra manera:
x = func(...)
y = 1 + 2*func(...)
Hay tres formas de designar un procedimiento al que se hace referencia:
- como el nombre de un procedimiento o puntero de procedimiento
- un componente de procedimiento de un objeto de tipo derivado
- un nombre de enlace de procedimiento vinculado
El primero puede verse como
procedure(), pointer :: sub_ptr=>sub
call sub() ! With no argument list the parentheses are optional
call sub_ptr()
end
subroutine sub()
end subroutine
y los dos últimos como
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
Para un procedimiento con argumentos ficticios, la referencia requiere argumentos reales correspondientes, aunque es posible que no se proporcionen argumentos ficticios opcionales.
Considere la subrutina
subroutine sub(a, b, c)
integer a, b
integer, optional :: c
end subroutine
Esto puede ser referenciado de las siguientes dos maneras
call sub(1, 2, 3) ! Passing to the optional dummy c
call sub(1, 2) ! Not passing to the optional dummy c
Esto se denomina referencia de posición : los argumentos reales se asocian según la posición en las listas de argumentos. Aquí, el dummy a
está asociado con 1
, b
con 2
y c
(cuando se especifica) con 3
.
Alternativamente, la referencia de palabras clave se puede usar cuando el procedimiento tiene una interfaz explícita disponible
call sub(a=1, b=2, c=3)
call sub(a=1, b=2)
que es lo mismo que el anterior.
Sin embargo, con palabras clave los argumentos reales se pueden ofrecer en cualquier orden
call sub(b=2, c=3, a=1)
call sub(b=2, a=1)
Se pueden usar referencias posicionales y de palabras clave.
call sub(1, c=3, b=2)
siempre que se proporcione una palabra clave para cada argumento después de la primera aparición de una palabra clave
call sub(b=2, 1, 3) ! Not valid: all keywords must be specified
El valor de la referencia de palabras clave es particularmente pronunciado cuando hay múltiples argumentos ficticios opcionales, como se ve a continuación si en la definición de subrutina anterior b
también eran opcionales
call sub(1, c=3) ! Optional b is not passed
Las listas de argumentos para procedimientos vinculados a tipo o punteros de procedimiento de componente con un argumento pasado se consideran por separado.