Fortran
Des alternatives modernes aux caractéristiques historiques
Recherche…
Types de variables implicites
Lorsque Fortran a été développé à l’origine, la mémoire était très coûteuse. Les variables et les noms de procédure peuvent comporter 6 caractères au maximum et les variables sont souvent implicitement saisies . Cela signifie que la première lettre du nom de la variable détermine son type.
- les variables commençant par i, j, ..., n sont des
integer
- tout le reste (a, b, ..., h et o, p, ..., z) est
real
Des programmes comme le suivant sont acceptables 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
Vous pouvez même définir vos propres règles implicites avec la déclaration implicit
:
! all variables are real by default
implicit real (a-z)
ou
! 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)
Le typage implicite n'est plus considéré comme la meilleure pratique. Il est très facile de faire une erreur en utilisant le typage implicite, car les fautes de frappe peuvent passer inaperçues, par exemple
program oops
real :: somelongandcomplicatedname
...
call expensive_subroutine(somelongandcomplEcatedname)
end program oops
Ce programme se déroulera avec plaisir et fera la mauvaise chose.
Pour désactiver le typage implicite, l'instruction implicit none
peut être utilisée.
program much_better
implicit none
integer :: j = 4
real :: x = 3.142
print*, "j = ", j, "x = ", x
end program much_better
Si nous avions utilisé implicit none
dans le programme oops
ci-dessus, le compilateur aurait remarqué immédiatement et produit une erreur.
Arithmétique si déclaration
Arithmétique if
instruction permet d'utiliser trois branches en fonction du résultat d'une expression arithmétique
if (arith_expr) label1, label2, label3
Cette instruction if
transfère le flux de contrôle à l'une des étiquettes d'un code. Si le résultat de arith_expr
est négatif, label1
est impliqué, si le résultat est nul, label2
est utilisé et si le résultat est positif, la dernière label3
est appliquée. if
arithmétique requiert les trois étiquettes mais permet de réutiliser les étiquettes, cette instruction peut être simplifiée en deux branches if
.
Exemples:
if (N * N - N / 2) 130, 140, 130
if (X) 100, 110, 120
Maintenant, cette fonctionnalité est obsolète avec la même fonctionnalité offerte par la construction if
et if-else
. Par exemple, le fragment
if (X) 100, 110, 120
100 print*, "Negative"
goto 200
110 print*, "Zero"
goto 200
120 print*, "Positive"
200 continue
peut être écrit comme la construction if-else
if (X<0) then
print*, "Negative"
else if (X==0) then
print*, "Zero"
else
print*, "Positive"
end if
Un remplacement de déclaration if
pour
if (X) 100, 100, 200
100 print *, "Negative or zero"
200 continue
peut être
if (X<=0) print*, "Negative or zero"
Constructions DO non bloquantes
Le non-bloc do
construire ressemble
integer i
do 100, i=1, 5
100 print *, i
C'est-à-dire que l'instruction de terminaison étiquetée n'est pas une instruction continue
. Il y a diverses restrictions sur l'instruction qui peut être utilisée comme instruction de terminaison et le tout est généralement très déroutant.
Une telle construction non-bloc peut être réécrite sous forme de bloc comme
integer i
do 100 i=1,5
print *, i
100 continue
ou mieux, en utilisant une déclaration de end do
terminaison,
integer i
do i=1,5
print *, i
end do
Retour alternatif
Le retour alternatif est une fonction permettant de contrôler le flux d'exécution à la sortie d'un sous-programme. Il est souvent utilisé comme une forme de gestion des erreurs:
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
Le retour alternatif est marqué par les arguments *
dans la liste d'arguments factices de la sous-routine.
Dans la déclaration d' call
ci-dessus *100
et *200
réfèrent aux déclarations étiquetées 100
et 200
respectivement.
Dans la sous-routine elle-même, les instructions de return
correspondant à une déclaration alternative ont un numéro. Ce nombre n'est pas une valeur de retour, mais indique l'étiquette fournie à laquelle l'exécution est transmise lors du retour. Dans ce cas, return 1
transmet l'exécution à l'instruction libellée 100
et return 2
passe l'exécution à l'instruction intitulée 200
. Une instruction de return
sans ornement, ou l'exécution d'une exécution de sous-routine sans instruction de return
, passe l'exécution immédiatement après l'instruction d'appel.
La syntaxe de retour alternative est très différente des autres formes d'association d'arguments et la fonctionnalité introduit le contrôle de flux contrairement aux goûts modernes. Un contrôle de flux plus agréable peut être géré avec le retour d'un code entier "status".
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
Formulaire source fixe
Fortran était à l'origine conçu pour un format de format fixe basé sur une carte perforée de 80 colonnes:
Oui: il s'agit d'une ligne du code de l'auteur
Celles-ci ont été créées sur une machine à perforer les cartes, comme ceci:
Les images sont des photographies originales de l'auteur
Le format, comme indiqué sur la carte illustrée illustrée, comportait les cinq premières colonnes réservées aux étiquettes de relevé. La première colonne a été utilisée pour désigner les commentaires par une lettre C. La sixième colonne a été utilisée pour désigner une suite d'instruction (en insérant un caractère autre qu'un zéro 0). Les 8 dernières colonnes ont été utilisées pour l'identification et le séquencement des cartes, ce qui était très utile si vous jetiez votre jeu de cartes par terre! Le codage des caractères pour les cartes perforées ne comportait qu'un ensemble limité de caractères et était uniquement en majuscules. En conséquence, les programmes Fortran ressemblaient à ceci:
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
Le caractère d'espace a également été ignoré partout, sauf dans une constante de caractère Hollerith (comme indiqué ci-dessus). Cela signifiait que des espaces pouvaient apparaître à l'intérieur des mots réservés et des constantes, ou manquer complètement. Cela a eu l'effet secondaire de certaines déclarations plutôt trompeuses telles que:
DO 1 I = 1.0
est une affectation à la variable DO1I
alors que:
DO1I = 1,0
est en fait une boucle DO
sur la variable I
Fortran moderne n'a pas besoin maintenant de cette forme fixe d'entrée et permet la forme libre en utilisant n'importe quelle colonne. Les commentaires sont maintenant indiqués par un !
qui peut également être ajouté à une ligne de relevé. Les espaces ne sont plus autorisés nulle part et doivent être utilisés comme séparateurs, comme dans la plupart des autres langues. Le programme ci-dessus pourrait être écrit dans Fortran moderne comme:
! 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"
Bien que l’ancienne suite ne soit plus utilisée, l’exemple ci-dessus montre que des déclarations très longues se produiront toujours. Le Fortran moderne utilise un symbole &
à la fin et au début de la suite. Par exemple, nous pourrions écrire ce qui précède sous une forme plus lisible:
! 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"
Blocs communs
Dans les premières formes de Fortran, le seul mécanisme permettant de créer un stockage de variables global visible à partir de sous-programmes et de fonctions consiste à utiliser le mécanisme de bloc COMMON
. Cela permettait aux séquences de variables d'être des noms et partagées en commun.
En plus des blocs communs nommés, il peut également y avoir un bloc commun vide (sans nom).
Un bloc commun vide pourrait être déclaré comme
common i, j
alors que les variables
bloc nommées pourraient être déclarées comme
common /variables/ i, j
Comme exemple complet, nous pourrions imaginer un magasin de tas qui est utilisé par les routines qui peuvent ajouter et supprimer des valeurs:
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
Les instructions communes peuvent être utilisées pour déclarer implicitement le type d'une variable et spécifier l'attribut de dimension
. Ce comportement seul est souvent une source de confusion suffisante. De plus, l'association de stockage implicite et les exigences pour des définitions répétées entre les unités de programme rendent l'utilisation des blocs communs sujettes aux erreurs.
Enfin, les blocs communs sont très restreints dans les objets qu’ils contiennent. Par exemple, un tableau dans un bloc commun doit être de taille explicite; les objets attribuables peuvent ne pas se produire; les types dérivés ne doivent pas avoir d'initialisation par défaut.
Dans Fortran moderne, ce partage de variables peut être géré par l'utilisation de modules . L'exemple ci-dessus peut être écrit comme suit:
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
Les blocs communs nommés et vides ont des comportements légèrement différents. À noter:
- les objets des blocs communs nommés peuvent être définis initialement; les objets en blanc communs ne doivent pas être
- les objets dans les blocs communs vides se comportent comme si le bloc commun avait l'attribut
save
; les objets dans des blocs communs nommés sans l'attributsave
peuvent ne plus être définis lorsque le bloc ne fait pas partie d'une unité de programme active
Ce dernier point peut être opposé au comportement des variables de module dans le code moderne. Toutes les variables de module dans Fortran 2008 sont implicitement enregistrées et ne deviennent pas indéfinies lorsque le module est hors de portée. Avant Fortran 2008, les variables de module, comme les variables dans les blocs communs nommés, deviendraient également indéfinies lorsque le module serait hors de portée.
GOTO attribué
GOTO assigné utilise une variable entière à laquelle une étiquette est affectée à l'aide de l'instruction ASSIGN.
100 CONTINUE
...
ASSIGN 100 TO ILABEL
...
GOTO ILABEL
Assigné GOTO est obsolète dans Fortran 90 et supprimé dans Fortran 95 et versions ultérieures. Il peut être évité dans le code moderne en utilisant des procédures, des procédures internes, des pointeurs de procédure et d’autres fonctionnalités.
GOTO calculé
GOTO calculé permet le branchement du programme en fonction de la valeur d'une expression entière.
GOTO (label_1, label_2,... label_n) scalar-integer-expression
Si scalar-integer-expression
est égal à 1, le programme continue à l'étiquette d'étiquette label_1
, s'il est égal à 2, il passe à label_2
et ainsi de suite. Si elle est inférieure à 1
ou plus grand que n
programme se poursuit sur la ligne suivante.
Exemple:
ivar = 2
...
GOTO (10, 20, 30, 40) ivar
sautera à l'étiquette d'étiquette 20.
Cette forme de goto
est obsolète dans Fortran 95 et les versions ultérieures, étant remplacée par la construction de select case
sélectifs.
Spécificateurs de format assignés
Avant Fortran 95, il était possible d'utiliser des formats assignés pour l'entrée ou la sortie. Considérer
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
L'instruction assign
assigne une étiquette à une variable entière. Cette variable entière est utilisée ultérieurement comme spécificateur de format dans l'instruction print
.
Une telle attribution de spécificateur de format a été supprimée dans Fortran 95. Au lieu de cela, un code plus moderne peut utiliser une autre forme de contrôle de flux d'exécution.
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
ou une variable de caractère peut être utilisée comme spécificateur de format
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
Fonctions de déclaration
Considérez le programme
implicit none
integer f, i
f(i)=i
print *, f(1)
end
Ici f
est une fonction de déclaration. Il a un type de résultat entier, prenant un argument factice entier. 1
Une telle fonction d'instruction existe dans le cadre dans lequel elle est définie. En particulier, il a accès aux variables et aux constantes nommées accessibles dans cette étendue.
Cependant, les fonctions d'instruction sont soumises à de nombreuses restrictions et peuvent être source de confusion (en regardant un regard décontracté comme une instruction d'affectation d'éléments de tableau). Les restrictions importantes sont les suivantes:
- le résultat de la fonction et les arguments factices doivent être scalaires
- les arguments factices ont la même portée que la fonction
- les fonctions d'instruction n'ont pas de variables locales
- les fonctions d'instruction ne peuvent pas être passées comme arguments réels
Les principaux avantages des fonctions de relevé sont répétés par les fonctions internes
implicit none
print *, f(1)
contains
integer function f(i)
integer i
f = i
end function
end
Les fonctions internes ne sont pas soumises aux restrictions mentionnées ci-dessus, mais il est peut-être utile de noter qu’un sous-programme interne ne peut contenir d’autres sous-programmes internes (mais il peut contenir une fonction de relevé).
Les fonctions internes ont leur propre portée mais disposent également d'une association hôte.
1 Dans les vrais anciens exemples de code, il ne serait pas inhabituel de voir implicitement les arguments factices d'une fonction d'instruction, même si le résultat a un type explicite.