Fortran
Alternativas modernas a las características históricas.
Buscar..
Tipos de variables implícitas
Cuando Fortran se desarrolló originalmente, la memoria era muy importante. Las variables y los nombres de los procedimientos podrían tener un máximo de 6 caracteres, y las variables a menudo se escribían de forma implícita . Esto significa que la primera letra del nombre de la variable determina su tipo.
- variables que comienzan con i, j, ..., n son
integer
- todo lo demás (a, b, ..., h, yo, p, ..., z) son
real
Programas como los siguientes son aceptables Fortran:
program badbadnotgood
j = 4
key = 5 ! only the first letter determines the type
x = 3.142
print*, "j = ", j, "key = ", key, "x = ", x
end program badbadnotgood
Incluso puede definir sus propias reglas implícitas con la declaración implicit
:
! all variables are real by default
implicit real (a-z)
o
! variables starting with x, y, z are complex
! variables starting with c, s are character with length of 4 bytes
! and all other letters have their default implicit type
implicit complex (x,y,z), character*4 (c,s)
La tipificación implícita ya no se considera la mejor práctica. Es muy fácil cometer un error al utilizar la escritura implícita, ya que los errores tipográficos pueden pasar desapercibidos, por ejemplo,
program oops
real :: somelongandcomplicatedname
...
call expensive_subroutine(somelongandcomplEcatedname)
end program oops
Este programa estará felizmente ejecutado y hará lo incorrecto.
Para desactivar la escritura implícita, se puede usar la instrucción implicit none
.
program much_better
implicit none
integer :: j = 4
real :: x = 3.142
print*, "j = ", j, "x = ", x
end program much_better
Si no hubiéramos usado implicit none
en el programa oops
arriba, el compilador se habría dado cuenta de inmediato y produjo un error.
Aritmética si declaración
La instrucción aritmética if
permite usar tres ramas dependiendo del resultado de una expresión aritmética
if (arith_expr) label1, label2, label3
Esta instrucción if
transfiere el flujo de control a una de las etiquetas de un código. Si el resultado de arith_expr
es negativo, label1
está involucrado, si el resultado es cero, se usa label2
y si el resultado es positivo, se aplica la última label3
. Aritmética if
requiere las tres etiquetas, pero permite la reutilización de las etiquetas, por lo tanto, esta declaración se puede simplificar a una rama dos if
.
Ejemplos:
if (N * N - N / 2) 130, 140, 130
if (X) 100, 110, 120
Ahora esta función es obsoleta con la misma funcionalidad ofrecida por el if
declaración y if-else
construcción. Por ejemplo, el fragmento.
if (X) 100, 110, 120
100 print*, "Negative"
goto 200
110 print*, "Zero"
goto 200
120 print*, "Positive"
200 continue
puede ser escrito como la construcción if-else
if (X<0) then
print*, "Negative"
else if (X==0) then
print*, "Zero"
else
print*, "Positive"
end if
Un sustituto if
de
if (X) 100, 100, 200
100 print *, "Negative or zero"
200 continue
tal vez
if (X<=0) print*, "Negative or zero"
Construcciones de OD no en bloque
La no-bloque do
construir parece
integer i
do 100, i=1, 5
100 print *, i
Es decir, donde la instrucción de terminación etiquetada no es una instrucción de continue
. Hay varias restricciones en la declaración que pueden usarse como la declaración de terminación y todo es generalmente muy confuso.
Dicha construcción no de bloque se puede reescribir en forma de bloque como
integer i
do 100 i=1,5
print *, i
100 continue
o mejor, usando una sentencia de end do
terminación,
integer i
do i=1,5
print *, i
end do
Retorno alternativo
El rendimiento alternativo es una facilidad para controlar el flujo de ejecución en el retorno desde una subrutina. A menudo se utiliza como una forma de manejo de errores:
real x
call sub(x, 1, *100, *200)
print*, "Success:", x
stop
100 print*, "Negative input value"
stop
200 print*, "Input value too large"
stop
end
subroutine sub(x, i, *, *)
real, intent(out) :: x
integer, intent(in) :: i
if (i<0) return 1
if (i>10) return 2
x = i
end subroutine
El retorno alternativo está marcado por los argumentos *
en la lista de argumentos ficticios de subrutina.
En la declaración de call
anterior *100
y *200
refieren a las declaraciones etiquetadas 100
y 200
respectivamente.
En la subrutina, las declaraciones de return
correspondientes a la devolución alternativa tienen un número. Este número no es un valor de retorno, pero denota la etiqueta proporcionada a la cual se pasa la ejecución en el retorno. En este caso, el return 1
pasa la ejecución a la declaración etiquetada 100
y el return 2
ejecución pasa a la declaración etiquetada 200
. Una declaración de return
sin adornos, o la finalización de la ejecución de subrutina sin una declaración de return
, la ejecución de Passess inmediatamente después de la instrucción de llamada.
La sintaxis alternativa de retorno es muy diferente de otras formas de asociación de argumentos y la instalación introduce un control de flujo contrario a los gustos modernos. El control de flujo más agradable se puede administrar con la devolución de un código de "estado" entero.
real x
integer status
call sub(x, 1, status)
select case (status)
case (0)
print*, "Success:", x
case (1)
print*, "Negative input value"
case (2)
print*, "Input value too large"
end select
end
subroutine sub(x, i, status)
real, intent(out) :: x
integer, intent(in) :: i
integer, intent(out) :: status
status = 0
if (i<0) then
status = 1
else if (i>10)
status = 2
else
x = i
end if
end subroutine
Forma de fuente fija
Fortran originalmente fue diseñado para un formato de formato fijo basado en una tarjeta perforada de 80 columnas:
Sí: esta es una línea del propio código del autor.
Estos fueron creados en una máquina perforadora de tarjetas, muy similar a esto:
Las imágenes son fotografías originales del autor.
El formato, como se muestra en la tarjeta de muestra ilustrada, tenía las primeras cinco columnas reservadas para las etiquetas de estados de cuenta. La primera columna se usó para denotar los comentarios mediante una letra C. La sexta columna se usó para denotar una continuación de la declaración (insertando cualquier carácter que no sea un cero '0'). Las últimas 8 columnas se usaron para la identificación y secuenciación de las tarjetas, ¡lo cual fue muy valioso si dejaste caer el mazo de cartas en el suelo! La codificación de caracteres para tarjetas perforadas tenía solo un conjunto limitado de caracteres y solo estaba en mayúsculas. Como resultado, los programas de Fortran se parecían a esto:
DIMENSION A(10) 00000001
C THIS IS A COMMENT STATEMENT TO EXPLAIN THIS EXAMPLE PROGRAM 00000002
WRITE (6,100) 00000003
100 FORMAT(169HTHIS IS A RATHER LONG STRING BEING OUTPUT WHICH GOES OVE00000004
1R MORE THAN ONE LINE, AND USES THE STATEMENT CONTINUATION MARKER IN00000005
2COLUMN 6, AND ALSO USES HOLLERITH STRING FORMAT) 00000006
STOP 00000007
END 00000008
El carácter de espacio también se ignoró en todas partes, excepto dentro de una constante de carácter Hollerith (como se muestra arriba). Esto significaba que los espacios podían aparecer dentro de palabras y constantes reservadas, o se podían perder por completo. Esto tuvo el efecto secundario de algunas afirmaciones bastante engañosas como:
DO 1 I = 1.0
es una asignación a la variable DO1I
mientras que:
DO1I = 1,0
es en realidad un bucle de DO
en la variable I
Fortran moderno ahora no requiere esta forma fija de entrada y permite la forma libre usando cualquier columna. Los comentarios ahora están indicados por un !
que también se puede añadir a una línea de declaración. Los espacios ahora no están permitidos en ningún lugar y deben usarse como separadores, como en la mayoría de los otros idiomas. El programa anterior se podría escribir en Fortran moderno como:
! This is a comment statement to explain this example program
Print *,"THIS IS A RATHER LONG STRING BEING OUTPUT WHICH no longer GOES OVER MORE THAN ONE LINE, AND does not need to USE THE STATEMENT CONTINUATION MARKER IN COLUMN 6, or the HOLLERITH STRING FORMAT"
Aunque la continuación de estilo antiguo ya no se usa, el ejemplo anterior ilustra que todavía se producirán declaraciones muy largas. Modern Fortran usa un símbolo &
al final y al comienzo de la continuación. Por ejemplo, podríamos escribir lo anterior en una forma más legible:
! This is a comment statement to explain this example program
Print *,"THIS IS A RATHER LONG STRING BEING OUTPUT WHICH still &
&GOES OVER MORE THAN ONE LINE, AND does need to USE THE STATEMENT &
&CONTINUATION notation"
Bloques comunes
En las primeras formas de Fortran, el único mecanismo para crear un almacén de variables global visible desde subrutinas y funciones es usar el mecanismo de bloque COMMON
. Esto permitió que las secuencias de variables fueran nombres y se compartieran en común.
Además de los bloques comunes con nombre, también puede haber un bloque común en blanco (sin nombre).
Un bloque común en blanco podría ser declarado como
common i, j
mientras que las variables
bloque nombradas podrían ser declaradas como
common /variables/ i, j
Como ejemplo completo, podríamos imaginar una tienda de almacenamiento dinámico utilizada por rutinas que pueden agregar y eliminar valores:
PROGRAM STACKING
COMMON /HEAP/ ICOUNT, ISTACK(1023)
ICOUNT = 0
READ *, IVAL
CALL PUSH(IVAL)
CALL POP(IVAL)
END
SUBROUTINE PUSH(IVAL)
COMMON /HEAP/ ICOUNT, ISTACK(1023)
ICOUNT = ICOUNT + 1
ISTACK(ICOUNT) = IVAL
RETURN
END
SUBROUTINE POP(IVAL)
COMMON /HEAP/ ICOUNT, ISTACK(1023)
IVAL = ISTACK(ICOUNT)
ICOUNT = ICOUNT - 1
RETURN
END
Las declaraciones comunes se pueden usar para declarar implícitamente el tipo de una variable y para especificar el atributo de dimension
. Este comportamiento solo es a menudo una fuente suficiente de confusión. Además, la asociación de almacenamiento implícita y los requisitos para definiciones repetidas en las unidades del programa hacen que el uso de bloques comunes sea propenso a errores.
Finalmente, los bloques comunes están muy restringidos en los objetos que contienen. Por ejemplo, una matriz en un bloque común debe ser de tamaño explícito; objetos asignables no pueden ocurrir; Los tipos derivados no deben tener inicialización por defecto.
En Fortran moderno, este uso compartido de variables se puede manejar mediante el uso de módulos . El ejemplo anterior se puede escribir como:
module heap
implicit none
! In Fortran 2008 all module variables are implicitly saved
integer, save :: count = 0
integer, save :: stack(1023)
end module heap
program stacking
implicit none
integer val
read *, val
call push(val)
call pop(val)
contains
subroutine push(val)
use heap, only : count, stack
integer val
count = count + 1
stack(count) = val
end subroutine push
subroutine pop(val)
use heap, only : count, stack
integer val
val = stack(count)
count = count - 1
end subroutine pop
end program stacking
Los bloques comunes nombrados y en blanco tienen comportamientos ligeramente diferentes. De nota:
- Los objetos en bloques comunes nombrados pueden definirse inicialmente; Los objetos en blanco no serán comunes.
- los objetos en bloques comunes en blanco se comportan como si el bloque común tuviera el atributo de
save
; los objetos en bloques comunes nombrados sin el atributo desave
pueden volverse indefinidos cuando el bloque no está en el alcance de una unidad de programa activa
Este último punto puede contrastarse con el comportamiento de las variables del módulo en el código moderno. Todas las variables de módulo en Fortran 2008 se guardan implícitamente y no se vuelven indefinidas cuando el módulo queda fuera del alcance. Antes de que las variables del módulo Fortran 2008, como las variables en bloques comunes con nombre, también se volvieran indefinidas cuando el módulo quedaba fuera del alcance.
Asignado GOTO
El GOTO asignado utiliza una variable entera a la que se asigna una etiqueta de declaración utilizando la instrucción ASSIGN.
100 CONTINUE
...
ASSIGN 100 TO ILABEL
...
GOTO ILABEL
El GOTO asignado es obsolescente en Fortran 90 y se elimina en Fortran 95 y posteriores. Se puede evitar en el código moderno utilizando procedimientos, procedimientos internos, indicadores de procedimientos y otras características.
GOTO computado
El GOTO computado permite la bifurcación del programa según el valor de una expresión entera.
GOTO (label_1, label_2,... label_n) scalar-integer-expression
Si scalar-integer-expression
es igual a 1, el programa continúa en la etiqueta label_1
, si es igual a 2, va a label_2
y así sucesivamente. Si es menos de 1
o mayor que n
programa continúa en la siguiente línea.
Ejemplo:
ivar = 2
...
GOTO (10, 20, 30, 40) ivar
saltará a la etiqueta de declaración 20.
Esta forma de goto
es obsolescente en Fortran 95 y versiones posteriores, siendo reemplazada por el constructo de select case
.
Especificadores de formato asignado
Antes de Fortran 95 era posible usar formatos asignados para entrada o salida. Considerar
integer i, fmt
read *, i
assign 100 to fmt
if (i<100000) assign 200 to fmt
print fmt, i
100 format ("This is a big number", I10)
200 format ("This is a small number", I6)
end
La declaración de assign
asigna una etiqueta de declaración a una variable entera. Esta variable entera se usa más tarde como el especificador de formato en la declaración de print
.
Dicha asignación de especificador de formato se eliminó en Fortran 95. En su lugar, un código más moderno puede usar alguna otra forma de control de flujo de ejecución
integer i
read *, i
if (i<100000) then
print 100, i
else
print 200, i
end if
100 format ("This is a big number", I10)
200 format ("This is a small number", I6)
end
o se puede usar una variable de carácter como el especificador de formato
character(29), target :: big_fmt='("This is a big number", I10)'
character(30), target :: small_fmt='("This is a small number", I6)'
character(:), pointer :: fmt
integer i
read *, i
fmt=>big_fmt
if (i<100000) fmt=>small_fmt
print fmt, i
end
Funciones de sentencias
Considerar el programa
implicit none
integer f, i
f(i)=i
print *, f(1)
end
Aquí f
es una función de declaración. Tiene un tipo de resultado de entero, tomando un argumento ficticio de entero. 1
Dicha función de declaración existe dentro del alcance en el que se define. En particular, tiene acceso a variables y constantes con nombre accesibles en ese ámbito.
Sin embargo, las funciones de declaración están sujetas a muchas restricciones y son potencialmente confusas (mirando a simple vista como una instrucción de asignación de elementos de matriz). Las restricciones importantes son:
- El resultado de la función y los argumentos ficticios deben ser escalares.
- Los argumentos ficticios están en el mismo ámbito que la función.
- funciones de declaración no tienen variables locales
- Las funciones de sentencias no se pueden pasar como argumentos reales.
Los beneficios principales de las funciones de sentencias son repetidos por las funciones internas.
implicit none
print *, f(1)
contains
integer function f(i)
integer i
f = i
end function
end
Las funciones internas no están sujetas a las restricciones mencionadas anteriormente, aunque quizás vale la pena señalar que un subprograma interno puede no contener un subprograma interno adicional (pero puede contener una función de declaración).
Las funciones internas tienen su propio alcance, pero también tienen una asociación de host disponible.
1 En los ejemplos de código antiguo anterior, no sería extraño ver que los argumentos ficticios de una función de declaración se escriben de forma implícita, incluso si el resultado tiene un tipo explícito.