1 Historique
FORTRAN=mathematical FORmula TRANslating system. Pourquoi un langage Fortran ?
=>Volonté de créer un langage quasi naturel pour les scientifiques et conciliant Efficacité / Performance du code généré.
Initialement, un compilateur Fortran a été créé sur les systèmes IBM. La généralisation d'un compilateur Fortran à d'autres systèmes a nécessité la création en 1966 d'un comité chargé du développement d'une norme (ANSI . American Nat. Standards Institute).
Première norme apparue : Fortran 66
Seconde norme : Fortran 77
Nécessité de moderniser le Fortran 77 en respectant les critères suivants :
o Introduire des nouveaux concepts exploités par des langages plus récents.
o Améliorer les performances en calcul scientifique
o Cette extension doit être totalement compatible avec Fortran 77
En 1991/1992, les comités ISO et ANSI créent la norme Fortran 90 (avec F77 inclus dans F90)
2 Les apports de la norme 90
Le "format libre", identificateurs, déclarations, !, &, ;
Précision des nombres. KIND, destiné à assurer une meilleure portabilité
Objets de type dérivés
DO ... END DO, SELECT CASE, WHERE
Extensions sur les tableaux : profil, manipulation, fonctions prédéfinies
Allocation dynamique de mémoire (ALLOCATABLE)
Pointeurs
Procédures et fonctions récursives
Arguments de fonctions et de procédures : OPTIONAL, INTENT, PRESENT.
Passage par mot-clé
Bloc interface, interface générique, surcharge d'opérateurs
De nouvelles fonctions intrinsèques
3 Identificateurs et formats du Fortran
Identificateurs en Fortran 77 et 90 :Un identificateur est une suite de caractères choisis parmi les lettres non accentuées, les chiffres, le blanc souligné. Le premier caractère est obligatoirement une lettre. Aucune distinction n'est faite entre les
minuscules et les majuscules. Les identificateurs sont utilisés pour nommer le programme, les variables, les procédures et fonctions et en Fortran 90 les types dérivés, modules et interfaces.
La longueur d'un identificateur est limitée à 6 caractères pour le Fortran 77 et à 31 caractères pour le Fortran 90.
Exemples :
fin_de_fichier, Compteur sont des identificateurs du Fortran 90, contrairement à 8Q et FIVE.4
Les caractéristiques du format fixe Fortran 77 :
1. Une ligne de code a la structure suivante :
o Colonnes 1 à 5 : zone étiquette. Ces colonnes peuvent contenir des espaces ou une étiquette (nombre entier compris entre 1 et 99999) permettant de se référer en cours de programme à l'instruction qui débute sur la même ligne.
o Colonne 6 : colonne de continuation. Lorsqu'une instruction ne peut être placée sur une seule ligne, cette colonne contient un caractère différent de "0". Les caractères les plus couramment utilisés sont "+", "$". Le nombre de lignes de continuation est limité à 19.
o Colonnes 7 à 72 : zone instruction. A partir de la 73ème colonne,
les caractères sont ignorés.
2. Toute ligne commenant par l'un des caractères "C", "c" ou "*" est une ligne de commentaire.
3. Le caractère blanc n'est pas sugnificatif.
Les Caractéristiques du format fixe Fortran 90 :
Il s'agit du format fixe Fortran 77 avec 2 extensions :
1. Plusieurs instructions peuvent être codées sur une même ligne en les séparant avec le caractère ";"
2. Le caractère "!" après une ou des instructions indique le début d'un commentaire. Toute ligne dont le premier caractère non blanc
commence par le caractère "!" en une colonne différente de la 6ème est une ligne de commentaire.
Ce format est principalement destiné à assurer la compatibilité entre le Fortran 77 et le Fortran 90. Les fichiers au "format fixe" porteront l'un des suffixes .f (le plus courant), .for ou .FOR pour le compilateur fortran 90 DEC-Unix.
Les caractéristiques du format libre Fortran 90 :
1. Les colonnes 1 à 6 ne sont plus réservées. Les étiquettes peuvent être placées à partir de n'importe quelle colonne.
2. Plusieurs instructions peuvent être codées sur une même ligne en les séparant avec le caractère ";" La première instruction peut être placée à partir de n'importe quelle colonne.
3. Un commentaire peut être indiqué n'importe où sur une ligne à condition qu'il soit précédé du caractère "!".
Attention . C---, c---, *--- ne sont pas des commentaires Fortran 90 en "format libre" et génèrent des erreurs de compilation.
Exemple :
i=0 ! initialisation
4. Les lignes peuvent être de longueur quelconque à concurrence de 132 caractères.
5. Les blancs sont significatifs. Les commandes ENDIF, ENDDO et GOTO peuvent également être écrite sous la forme END IF, END DO, GO TO (mais EN DIF est interdit alors que c'est autorisé en "format fixe").
6. Une ligne se terminant par le caractère "&" indique que la ligne suivante est une ligne de continuation. Si la ligne de continuation commence par le caractère "&", seuls les caractères situés juste après le "&" font partie de la ligne de continuation (pratique pour écrire des chaînes de caractères sur plusieurs lignes). Sans le caractère "&" au début de la ligne de continuation, tous les caractères, y compris les blancs et la tabulation, font partie de la ligne de continuation.
Exemple :
print *, 'Ceci est une phrase sur & &sur deux lignes'
4 Les déclarations
Forme générale d'une déclaration :
type [, liste_attributs ::] liste_objets Liste des différents types :
integer
real
double precision
complex
character
logical (valeurs prises : .true. ou .false. )
type (type_dérivé_défini_par_le_développeur)
Liste des différents attributs :
parameter : utilisé pour une constante
dimension : pour indiquer la (les) taille(s) d'un tableau
allocatable : objet dynamique
pointer : variable définie comme pointeur
target : variable cible d'un pointeur
save : variable statique
intent : vocation d'un argument muet
optional : argument muet facultatif
external / intrinsic : nature d'une procédure
Nouveauté :
Il est possible d'initialiser une variable au moment de sa déclaration (c'est d'ailleurs obligatoire si la variable possède l'attribut parameter). En Fortran 77, seule une variable de type parameter peut être initialisée au moment de sa déclaration.
Exemples de déclarations :
real x, y, z
integer, parameter :: n=5
double precision a(100)
double precision, dimension(100) :: a
integer, dimension( -1:1), parameter :: p=(/ 5, 7, 13 /) Remarque :
Les règles relatives au typage par défaut à l'intérieur d'un programme, d'une procédure ou fonction sont identiques au Fortran 77 :
déclaration implicite : INTEGER (I-N), REAL(A-H,O-Z)
déclaration non implicite : IMPLICIT NONE => toutes les variables doivent être déclarées (vivement recommandé).
5 Syntaxe des opérateurs logiques
Certains opérateurs logiques possèdent deux syntaxes : .LE. <= .LT. < .GE. >= .GT. > .EQ. == .NE. /=
En revanche, les opérateurs .AND., .OR., .NOT. n'ont pas d'équivalents nouveaux.
6 Les structures de contrôle
Les instructions conditionnelles (IF)Syntaxe 1 :
IF (expression_logique) instruction
Syntaxe 2 :
IF (expression_logique_1) THEN bloc1
ELSE IF (expression_logique_2) THEN bloc2
ELSE bloc3 END IF
Les boucles (DO)
Syntaxe 1 : [ref:] DO bloc1 IF (condition) instruction_de_sortie_de_boucle bloc2 END DO [ref]
Il s'agit d'une boucle DO dont la sortie s'effectue à l'aide d'une instruction de sortie de boucle EXIT ou GOTO.
Syntaxe 2 :
[ref:] DO variable=integer1, integer2 [,integer3] bloc
END DO [ref] ou également
[ref:] DO etiquette variable=integer1, integer2 [,integer3] bloc
etiquette CONTINUE
Syntaxe 3 :
[ref:] DO WHILE (expression_logique) bloc
END DO [ref] Remarques :
L'instruction CYCLE permet d'interrompre l'exécution d'instructions à l'intérieur d'une boucle et de continuer à l'itération suivante de la boucle.
L'instruction EXIT permet de sortir du corps d'une boucle et indique qu'il faut exécuter les instructions juste après la boucle.
Il est désormais possible de référencer des boucles par une chaîne de caractères. Ceci est particulièrement utile lorsque l'on souhaite utiliser les instructions EXIT et CYCLE à l'intérieur de boucles imbriquées.
Les instructions CYCLE et EXIT ne sont utilisables qu'à l'intérieur de boucles DO.
Les deux dernières formes de boucles, sans les références, existaient déjà en Fortran 77.
Exemple : program boucle implicit none
integer :: i, j, n, m, nb, som
integer, parameter :: som_max=150 n=10; som=0; b1 : do i=1,n read *, m do j=1,m read *, nb if (nb < 0) cycle som = som + nb
if (som > som_max) exit b1 end do
end do b1 end
Aiguillage (SELECT CASE)
Syntaxe :
[ref:] SELECT CASE (expression) CASE cas_1 bloc_1 ... CASE cas_n bloc_n CASE DEFAULT bloc_defaut
END SELECT [ref] Exemple :
integer :: flag, res ... SELECT CASE(flag) CASE (1, 2, 3) res=1 CASE (4:6) res=2 CASE DEFAULT
res=0
END SELECT
7 Types dérivés
En Fortran 90, il est désormais possible de définir des types dérivés (ou strucures de données) permettant de regrouper un ensemble de données de types hétérognes.
Exemple :
Déclaration d'un type dérivé couleur : type COULEUR
character(len=16) :: nom real, dimension(3) :: compos end type COULEUR
Dans le programme principal, la déclaration et l'initialisation d'une variable coul de type couleur se fera de la manière suivante : type(COULEUR), parameter :: coul = couleur('rouge', (/ 1.,0.,0. /)) Pour accéder aux champs nom et compos de la variable coul, on utilisera l'opérateur % :
print *,'Composantes RGB de la couleur ',coul%nom,' : ',coul%compos Caractéristiques :
chaque champ est constitué d'éléments de type intrinsèque ou de type dérivé.
un champ ne peut avoir aucun des attributs PARAMETER, ALLOCATABLE, TARGET.
un champ ne peut pas recevoir d'initialisation par défaut lors de la déclaration du type dérivé (c'est par contre possible en Fortran 95).
8 Les modules
Un module est une unité de programme permettant principalement d'encapsuler :
un ensemble de données (permet d'avoir l'équivalent des fichiers .h du C)
des définitions de types dérivés
des procédures internes et des blocs interfaces (cf chapitre suivant) Exemple : Création d'un module de données équivalent au COMMON MODULE varGlobales
integer, save :: vari real, save :: varr
END MODULE varGlobales
Les données de ce module sont accessibles dans toute unité de programme commençant par l'instruction USE varGlobales. Cet usage du module fournit une substitution aux blocs COMMON utilisés en Fortran 77.
Dans une unité de programme, il est également possible de faire appel qu'à certains objets du module à l'aide du qualificateur ONLY. Par exemple, l'accès unique à la variable vari du module varGlobales est spcifié grâce à l'instruction suivante :
USE varGlobales, ONLY : vari
Lors de l'appel d'un module, il est également possible de renommer des données de ce même module. Le renommage s'effectue à l'aide de
l'opérateur =>. Dans l'exemple précédent, on aurait ainsi pu renommer la variable vari par ma_vari de la manière suivante :
USE varGlobales, ONLY : ma_vari => vari
Quelques règles sur les modules :
Un module peut faire appel un autre module autre que lui même.
Les modules figureront dans des fichiers à part du programme
principal, des procédures et des fonctions. Plusieurs modules peuvent être définis dans un même fichier.
Compilation :
Les modules "source" doivent être compilés avant les modules, procédures et fonctions utilisant d'autres modules. Supposons que l'on ait à compiler un programme prog.f90 utilisant trois modules mod1, mod2 et mod3 regroupés dans les fichiers mod1et2.f90 et mod3.f90, où mod3 utilise mod2. La
1. Compilation préalable des sources contenant les modules : f90 -c mod1et2.f90
f90 -c mod3.f90
Ces commandes créent un fichier objet .o par fichier source et un fichier nom_module.mod par module.
2. Compilation de prog.f :
f90 -o prog pro.g mod1et2.o mod3.o
9 Précision des nombres (kind)
En Fortran 90, les types prédéfinis sont en fait des noms génériques regroupant un ensemble de types(appels aussi variantes). Ces sous-types sont accessibles à l'aide du paramètre KIND et sont à valeur entière. Ce paramètre, utilisé lors de la déclaration d'une variable, prend un
ensemble de valeurs qui sont propres au système utilisé. Ces valeurs correspondent au nombre d'octets désirés pour le codage de la variable déclarée.
Exemples :
real(kind=4) x
Sur alger, cela revient à déclarer x comme un réel simple précision (ce qui est aussi équivalent à déclarer x en tant que real*4).
real(kind=8) y
Sur alger, cela revient à déclarer x comme un réel double précision (ce qui est aussi équivalent à déclarer x en tant que real*8).
Remarque :
A chaque type correspond un sous-type par défaut, sélectionné en l'abscence du paramètre KIND (pour le type réel, il s'agit du sous-type simple
précision).
Pour les constantes, il est possible d'indiquer le sous-type désiré pour leur écriture. Pour cela, la constante sera suffixée par la valeur du sous-type, séparé par le caractère blanc souligné "_".
173_4
25.678_8
La fonction intrinsèque KIND :
Cette fonction renvoie la valeur entière correspondant au sous-type de l'argument spécifié.
Exemples :
kind(1.0d0) renvoit 8 sur alger
real(kind=kind(1.d0)) x permet de déclarer x comme un réel double précision quelle que soit la machine utilisée.
Les fonctions intrinsèques SELECTED_INT_KIND(r) et SELECTED_REAL_KIND(p, r) :
La fonction SELECTED_INT_KIND reçoit un nombre entier r en argument et retourne l'entier du sous-type integer permettant de représenter les entiers n vérifiant |n| < 10r. La fonction renvoit 1 si aucun sous-type ne convient.
La fonction SELECTED_REAL_KIND reçoit deux arguments entiers p et r (ils sont optionnels mais au moins l'un d'entre eux doit être mentionné). Elle retourne l'entier du sous-type real permettant de représenter les entiers x avec une précision au moins égale
à p chiffres décimaux et vérifiant 10-r < x < 10r (r s'appelle l'étendue
(range) de x ). Cette fonction retourne 1 si la précision demandée n'est pas disponible, 2 si l'étendue souhaitée n'est pas disponible, 3 si ni la précision ni l'étendue ne sont disponibles.
Exemple :
Sur alger, la fonction selected_int_kind retourne les valeurs suivantes :
Valeur de r
Valeur retournée par selected_int_kind 1 1 3 2 5 4 10 8 19 1
10 Les tableaux
Quelques définitions1. Le rang d'un tableau est son nombre de dimensions.
2. L'étendue d'un tableau dans une dimension donnée désigne le nombre d'éléments du tableau dans cette même dimension.
3. Le profil (shape) d'un tableau est un vecteur dont le ième élément est le nombre d'éléments du tableau dans la ième dimension.
4. Deux tableaux sont dits conformants s'ils ont le même profil.
5. La taille d'un tableau est le produit des éléments de son vecteur profil. Exemple :
integer, dimension ( -5:4, 0:2) :: x integer, dimension (0:9, -1:1) :: y
Les tableaux x et y, tous deux de rang 2, ont pour profil le vecteur (/ 10, 3 /). Ils sont donc conformants. Leur taille vaut 30.
Premières caractéristiques
1. Les tableaux de rang strictement supérieur à 7 sont interdits.
2. Tout scalaire est supposé conformant à tout tableau. Par conséquent, si a est un tableau, l'instruction a=1a un sens et signifie que tous les éléments de a sont affectés à 1.
3. Si a est un tableau de dimension 2, écrire a=1 est équivalent à écrire a(:,:)=1. De la même façon, a(i,:) désigne la ième ligne de a.
4. Tout tableau de rang 1 peut être initialisé à sa déclaration en encadrant ses valeurs d'affectation par (/ et /).
Pour les tableaux de rang au moins égal à 2, on utilisera la fonction RESHAPE.
Exemples :
character(len=1), dimension(3) :: tabc=(/ 'a', 'b', 'c' /) integer, dimension (4) :: tabi1, tabi2
tabi1 = (/ 8, 45, 6, 1 /) tabi2 = (/ (i*2, i=1,4) /)
5. La fonction intrinsèque SHAPE(array) permet d'obtenir le profil d'un tableau.
6. La fonction intrinsèque SIZE(array [,dim]) retourne la taille d'un tableau si seul ce tableau est spécifié en argument. Si un second argument, dim, est précisé, cette fonction retourne l'étendue du tableau relativement à la dimension dim.
Exemple :
integer, dimension ( 5:4, 0:2) :: x print *, size(x) ! retourne 30 print *, size(x,1) ! retourne 10 print *, size(x,2) ! retourne 3 Sections de tableaux
Une section de tableau désigne une partie de tableau.
1. Sections régulières de tableaux :
Lorsque les indices des éléments varient en suivant une suite
arithmétique, on parlera de section régulière de tableau. Pour chaque indice du tableau, cette progression arithmétique sera notée sous la forme valeur_initiale : valeur_finale : pas
Exemple :
integer, dimension (9,9) :: a integer, dimension (3,3) :: b integer, dimension (4,9) :: c ...
b = a (1:5:2, 1:9:3) ! affectation possible car les tableaux sont conformants
c = a (1:7:2, :) ! affectation possible car les tableaux sont conformants 1 1 1 2 1 3 1 4 1 5 1 6 1 7 1 8 1 9 2 1 2 2 2 3 2 4 2 5 2 6 2 7 2 8 2 9 3 1 3 2 3 3 3 4 3 5 3 6 3 7 3 8 3 9 4 1 4 2 4 3 4 4 4 5 4 6 4 7 4 8 4 9 1 1 1 4 1 7 1 1 1 2 1 3 1 4 1 5 1 6 1 7 1 8 1 9 Ainsi , si a vaut 5 1 5 2 5 3 5 4 5 5 5 6 5 7 5 8 5 9 , b vau t 3 1 3 4 3 7 et c vau t 3 1 3 2 3 3 3 4 3 5 3 6 3 7 3 8 3 9
6 1 6 2 6 3 6 4 6 5 6 6 6 7 6 8 6 9 5 1 5 4 5 7 5 1 5 2 5 3 5 4 5 5 5 6 5 7 5 8 5 9 7 1 7 2 7 3 7 4 7 5 7 6 7 7 7 8 7 9 7 1 7 2 7 3 7 4 7 5 7 6 7 7 7 8 7 9 8 1 8 2 8 3 8 4 8 5 8 6 8 7 8 8 8 9 9 1 9 2 9 3 9 4 9 5 9 6 9 7 9 8 9 9
2. Sections non régulières de tableaux :
Lorsque l'on souhaite extraire une section non régulière d'un tableau, on accède aux éléments du tableau par l'intermédiaire de vecteur d'indices. Exemple : integer, dimension (9,9) :: a integer, dimension (10,10) :: b ... b = 0 b ((/ 3, 5, 8 /), (/ 1, 5, 7 /)) = a (1:5:2, (/ 1, 5, 9 /)) b ((/ 1, 2 /), (/ 2, 3, 4 /)) = 1 11 12 13 14 15 16 17 18 19 0 1 1 1 0 0 0 0 0 0 21 22 23 24 25 26 27 28 29 0 1 1 1 0 0 0 0 0 0 31 32 33 34 35 36 37 38 39 11 0 0 0 15 0 19 0 0 0 41 42 43 44 45 46 47 48 49 0 0 0 0 0 0 0 0 0 0 Ainsi, si a vaut 51 52 53 54 55 56 57 58 59 , b vaut 31 0 0 0 35 0 39 0 0 0 61 62 63 64 65 66 67 68 69 0 0 0 0 0 0 0 0 0 0 71 72 73 74 75 76 77 78 79 0 0 0 0 0 0 0 0 0 0 81 82 83 84 85 86 87 88 89 51 0 0 0 55 0 59 0 0 0 91 92 93 94 95 96 97 98 99 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 Opérateurs définis sur les tableaux
Les opérateurs d'affectation (=), de comparaison (==, /=, < , < =, > , > =), arithmétiques (+, , *, /, **) et logiques (.and., .or., .not.) ont été définis sur les tableaux. Ces opérateurs s'appliquent sur des tableaux de profil
Remarque :
Les opérateurs arithmétiques *, ** n'opèrent pas comme des opérateurs de produit matriciel sur les vecteurs et matrices mais comme des opérateurs de produit terme terme.
Les principales fonctions intrinsèques définies sur les tableaux
RESHAPE(source, shape [,pad ] [,order]) permet de construire un
tableau de profil égal à shape à partir des éléments du
vecteur source (qui est obligatoirement un vecteur de rang 1). Si des éléments sont manquants dans source, le tableau est complété à l'aide des éléments figurant dans pad. order spécifie sous la forme d'un tableau de rang 1, l'ordre dans lequel les indices commencent à varier pour le remplissage du tableau résultat.
Exemple :
L'instruction reshape((/ (i, i=1,6) /), (/ 2, 3 /), order=(/ 2, 1 /)) permet de générer la matrice
1 2 3 4 5 6
TRANSPOSE(matrix ), où matrix est un tableau de dimension 2,
retourne la transposée de matrix.
DOT_PRODUCT(vector a, vector b) retourne le produit scalaire de
deux vecteurs, c'est à dire at.b si a et b sont de type entier ou réel,
conjugué(a)t.b si a et b sont complexes, et Somme(a
i .and. bi)
si a et b sont de type logique.
MATMUL(matrix a, matrix b) retourne le produit matriciel
de a et b, sous réserve de compatibilité des dimensions.
Soit a et b sont tous deux des matrices, soit a est un vecteur et b est une matrice, soit a est une matrice et b est un vecteur. a et b peuvent être de type quelconque.
LBOUND(array [,dim]) et UBOUND(array [,dim]) retournent les
bornes inférieures / supérieures de chacune des dimensions (ou seulement de la dimension dim) du tableau array.
Exemple :
integer, dimension( 21:2, 45:49) :: tab
lbound(tab) vaut (/ 21, 45 /) et ubound(tab) vaut (/ 2, 49 /) lbound(tab, dim=2) vaut 45 et ubound(tab, dim=1) vaut 2
ALL(mask [,dim]) applique un masque de type logique sur les
éléments du tableau et renvoit vrai si pour tous les éléments le résultat du masque est vrai. Si dim est précisé, la fonction travaille sur cet indice pour chaque valeur des autres dimensions.
Exemple :
Si A = 1 2 3 et B = 1 3 5 4 5 6 4 2 6
all(A==B) vaut .false.
all(A==B, dim=1) vaut (/ .true., .false., .false. /)
ANY(mask [,dim]) fonctionne de la même façon que la fonction
ALL à l'exception qu'elle renvoit vrai si l'un des résultats du masque est vrai.
Exemple :
En reprenant A et B de l'exemple précédent, on obtient : any(A==B) et any(A/=B) valent .true.
any(A/=B, dim=1) vaut (/ .false., .true., .true. /)
COUNT(mask [,dim]) comptabilise le nombre d'éléments pour
lesquels le résultat du masque est vrai. Exemple :
count(A==B) vaut 3
count (A/=B, dim=1) vaut (/ 0, 2, 1 /)
MINVAL(array [,dim][,mask]) et MAXVAL(array
[,dim][,mask]) retournent la plus petite / plus grande valeur du
tableau array (après un éventuel filtrage par dim et / ou mask). Exemple :
maxval(A) vaut 6
minval(A, dim=1) vaut (/ 1, 2, 3 /) minval(A,dim=2,A > 2) vaut (/ 3, 4 /)
PRODUCT(array [,dim][,mask]) et SUM(array
[,dim][,mask]) retournent le produit / la somme des valeurs des
éléments du tableau array (après un éventuel filtrage par dim et / ou mask).
Exemple :
product(A) retourne 720
sum(A,dim=1,A > 2) vaut (/ 4, 5, 9 /)
L'instruction WHERE
L'instruction WHERE permet d'effectuer des opérations sur des éléments d'un tableau sélectionnés via un filtre de type logique.
Syntaxe : WHERE (filtre) bloc1 ELSEWHERE bloc2 END WHERE
Lorsque bloc2 n'existe pas et que bloc1 se résume à une seule instruction, on peut utiliser également la forme contractée suivante :
WHERE (filtre) instruction Exemples : real, dimension(10) :: a ... where (a > 0.) a = sqrt(a) where (a > 0.) a = log(a) elsewhere a = 1. end where
11 Les tableaux dynamiques
Fortran 90 permet de créer des tableaux de manière dynamique. Pour cela, il faut tout d'abord spécifier l'attribut ALLOCATABLE lors de la dclaration d'un tableau. Les instructions ALLOCATE et DEALLOCATE, ainsi que la fonction intrinsèque ALLOCATED permettent alors de gérer les tableaux dynamiques :
ALLOCATE(array, stat=err) permet de faire l'allocation mémoire
du tableau. Si le mot clé stat= est spécifié, la variable err (de type integer) vaut 0 si l'allocation s'est déroulée sans problème et 1 sinon.
DEALLOCATE(array, stat=err) permet de libérer l'espace
ALLOCATED(array) est une fonction intrinsèque renvoyant 0 ou 1
suivant que le tableau spécifié en argument est alloué ou non. Exemple :
integer, dimension (:,:), allocatable :: a integer :: n, m, err
...
read *, n, m
if (.not. allocated(a)) then allocate(a(n,m),stat=err)
if (err /= 0) print *,'Erreur d''allocation dynamique du tableau a'; stop end if
...
deallocate(a) Remarques :
Un tableau ayant l'attribut ALLOCATABLE doit avoir été alloué avant d'être passé en argument d'une procédure.
Un tableau alloué dynamiquement dans une unité de programme et n'ayant pas l'attribut SAVE (sert pour les variables locales d'une procédure. Avant chaque sortie de procédure, la valeur courante d'une telle variable est sauvegardée) a un état indéterminé en sortie de cette unité (c'est à dire après un RETURN / END). Il ne sera pas non plus libéré automatiquement.
12 Les pointeurs
En Fortran 90 (contrairement à un langage comme le C), un pointeur désigne un alias et non une adresse sur une case mémoire. Un pointeur possède trois états :
l'état indéfini : c'est l'état qui lui est attribué à sa dclaration
l'état nul : cela signifie que le pointeur n'est alias d'aucun objet
l'état associé : le pointeur est alias d'un objet cible Quelques règles
Lors de la déclaration, on spécifie qu'une variable est un pointeur en indiquant l'attribut pointer.
Pour qu'un pointeur ait pour cible un objet c, c doit avoir
l'attribut target (inutile lorsque la cible est également un pointeur).
On indique qu'un pointeur p est alias d'une cible c à l'aide de l'opérateur => : p => c
Lorsque les opérandes de l'opérateur = sont des pointeurs, l'opération s'effectue sur les cibles et non sur les pointeurs.
Il n'est pas nécessaire qu'un pointeur soit associé avant d'être passé en argument d'une procédure (contrairement à allocatable).
Les fonctions intrinsèques NULLIFY et ASSOCIATED
NULLIFY(pointer_1 [,pointer_2] ... [pointer_n]) permet de forcer
l'état d'un ou de plusieurs pointeurs à nul.
ASSOCIATED(pointer_1 [,pointer_2] [,cible]) indique si un
pointeur est associé à une cible lorsqu'un seul pointeur est passé en argument, indique si deux pointeurs ont même cible lorsque deux pointeurs sont passés en argument et indique si un pointeur est alias d'une cible lorsqu'un pointeur et une cible sont passés en argument. Exemple :
real, pointer :: p1, p2 real, target :: c
nullify (p1, p2)
print *, associated(p1), associated (p2), associated(p1, p2) ! retourne F F F c=1.5
p1 = > c ! p1 est donc alias de c p2 = > c
print *, associated(p1), associated(p1, c), associated(p1, p2) ! retourne T T T Remarques :
Si p1 et p2 sont deux pointeurs, l'instruction p1 = > p2 est licite et signifie que p1 prend l'état de p2 (de plus il est inutile de spécifier l'attribut target à p2).
Dans l'exemple précédent, l'instruction p1 => 1.5 aurait été illicite (1.5 n'étant pas une variable).
Les pointeurs de type tableau
Deux cas peuvent se présenter : soit le pointeur est alias d'un tableau existant (ou d'une partie d'un tableau existant), soit le pointeur est alias d'une zone mémoire allouable.
Lorsque l'on souhaite qu'un pointeur soit alias d'un tableau existant, il faut lui spécifier les attributs pointer et dimension. Il n'est pas
nécessaire de préciser les bornes min et max pour chacune des dimensions; ceci est fait automatiquement lors de l'attribution du tableau cible au pointeur (cf exemple suivant).
Un pointeur de type tableau peut être alias d'une zone mémoire gérée via les commandes ALLOCATE, DEALLOCATE, ALLOCATED. Dans ce cas, l'attribut pointer est suffisant dans la liste des attributs (il est donc inutile de spécifier l'attribut ALLOCATABLE).
Exemple :
integer, dimension(10,10), target :: c integer, dimension(:), pointer :: p1 integer, dimension(:,:), pointer :: p2 c=reshape((/ (i, i=1,100) /), (/ 10, 10 /))
p1 = > c(4, 1:10:3) ! p1 est donc un vecteur de profil égal à (/ 4 /) allocate (p2(5, 10)) ! p2 est donc un tableau de profil égal à (/ 5, 10 /) p2 = reshape((/ (2*i, i=1,50) /), (/ 5, 10 /))
print *, p2 deallocate (p2)
Remarques importantes :
Une variable de type pointeur n'a pas besoin d'etre associée pour etre passée en argument d'une procédure / fonction (ceci permet d'allouer un argument de type tableau à l'intérieur d'une procédure, avantage par rapport à ALLOCATABLE).
Pour que le compilateur sache que l'argument muet est de type
pointeur, l'interface de la procédure / fonction doit etre explicite (voir chapitre suivant).
13 Interfaces de procédures
Définitions :Une interface de procédure spécifie le nom et les caractéristiques d'une procédure, à savoir le nom, le type et les attributs de chaque argument muet. Si toutes ces propriétés sont connues du programme appelant, l'interface est dite explicite. Dans le cas contraire, elle est dite implicite.
Les défauts de l'interfaçage implicite
Considérons une procédure appelée somprod permettant de calculer la somme et le produit des termes d'un vecteur et éventuellement le nombre d'éléments nuls de ce vecteur. A première vue, l'interface de cette procédure pourrait s'écrire comme suit :
subroutine somprod(vect, dim, som, prod, nbnuls) integer, dimension (dim) :: vect
integer :: dim, som, prod, nbnuls
Des erreurs non détectées à la compilation peuvent se produire si, par
exemple, dans le corps de la procédure figure une instruction comme dim=8. De même, lors de l'appel de la procédure dans le programme principal, les instructions suivantes sont erronées et ne sont pourtant pas détectées à la compilation :
real, dimension (n) :: v real :: x
...
call somprod (x, n, s, p, nb) ! Erreur de typage pour le premier argument call somprod (v, n, s, p, nb, x) ! Le dernier argument est de trop
call somprod (v, n, p, s, nb) ! Intervertion de deux arguments
call somprod (v, 10, p, s, nb) ! Argument de type constante numerique : DANGER
Ces erreurs sont dues à un interfaçage de procédure implicite ne permettant pas au compilateur de contrôler rigoureusement la cohérence des arguments d'appel avec les arguments muets.
L'interfaçage explicite
Fortran 90 permet au compilateur de mieux contrôler la cohérence des arguments. La norme prévoit en effet les possibilités suivantes :
1. Il est possible de préciser la vocation des arguments à l'aide de l'attribut INTENT. Dans la déclaration d'un argument, on indiquera alors
o intent(in) si l'argument ne doit pas être réaffecté dans la
procédure,
o intent(out) si l'argument doit être affecté dans la procédure
pour être utilisé dans le programme appelant,
o intent(inout) si la valeur de l'argument doit être modifiée dans
L'interface de la procédure somprod serait alors la suivante : subroutine somprod(vect, dim, som, prod, nbnuls) integer, dimension (dim), intent(in) :: vect
integer, intent(in) :: dim
integer, intent(out) :: som, prod, nbnuls
2. Certains arguments peuvent ne pas être utilisés lors de l'appel de la procédure à condition que cet argument ait l'attribut OPTIONAL.
Dans la procédure, il est possible de tester si cet argument est utilisé à l'appel grâce à la fonction intrinsèque PRESENT qui retourne un booléen.
Par convention, les arguments optionnels figureront en dernier dans la liste des arguments.
Pour l'interface de la procédure somprod, nbnuls recevrait donc l'attribut optional.
3. Lors de l'appel d'une procédure, il est possible de passer un argument à l'aide d'un mot-clé. Ce mot-clé est en fait le nom donné à l'argument muet de la procédure. Le passage par mot-clé est particulièrement utilisé pour les arguments optionnels. Un appel à la
fonction somprod pourrait se formuler ainsi : call somprod(v, n, s, p, nbnuls=nb)
1. L'interface d'une procédure peut être rendue explicite pour le
programme appelant en créant un module avec bloc interface ou un module avec procédure. Ces deux notions sont explicitées ci-après. Interfaçage explicite : création d'un module avec bloc interface
La première solution permettant à un programme d'avoir une visibilité complète sur l'interface d'une procédure est de créer dans ce programme ce qu'on appelle un bloc interface. Ce bloc interface regroupe l'interface complète de la procédure entre les qualificatifs interface et end interface. Exemple : Création d'un bloc interface pour la procédure somprod
interface
subroutine somprod(vect, dim, som, prod, nbnuls) integer, dimension (dim), intent(in) :: vect
integer, intent(in) :: dim
integer, intent(out), optional :: nbnuls end subroutine somprod
end interface
Les procédures externes étant susceptibles d'être appelées dans plusieurs unités de programmes, il est fortement recommandé d'inclure un bloc interface dans un module. Il suffira alors de faire appel à ce module (instruction USE) dans chacune de ces unités de programme afin que l'interface de procédure soit parfaitement connue.
Interfaçage explicite : création d'un module avec procédure
La seconde solution consiste à créer un module contenant la déclaration complète de la procédure. Ce module étant appelé dans une unité de programme, cela revient quasiment à déclarer la procédure à l'intérieur de l'unité (on parle alors de procédure interne). Ceci est possible en Fortran 90, à condition d'ajouter le mot clé CONTAINS avant la déclaration de la procédure.
Exemple : Création d'un module avec la procédure somprod module mod_somprod
contains
subroutine somprod(vect, dim, som, prod, nbnuls) integer, dimension (dim), intent(in) :: vect
integer, intent(in) :: dim
integer, intent(out) :: som, prod integer, intent(out), optional :: nbnuls som = sum(vect)
prod = product(vect)
if (present(nbnuls)) nbnuls = count(vect==0) end subroutine somprod
end module mod_somprod
Tableau en argument d'une procédure à interface explicite
Fortran 90 permet de transmettre le profil et la taille d'un tableau passé en argument d'une procédure à condition que son interface soit explicite. Dans la déclaration de la procédure et de son interface, on précise alors que la taille est inconnue (à l'aide de l'attribut dimension(:,:) pour un tableau à 2 dimensions par exemple).
Ainsi, la procédure somprod et son bloc interface pourrait être déclarées comme suit :
! Declaration de la procedure somprod subroutine somprod(vect, som, prod, nbnuls) integer, dimension (:), intent(in) :: vect integer, intent(out) :: som, prod
integer, intent(out), optional :: nbnuls som = sum(vect)
prod = product(vect)
if (present(nbnuls)) nbnuls = count(vect==0) print *,'taille du vecteur : ', size(vect)
end subroutine somprod
! Declaration du bloc interface de la procedure somprod interface
subroutine somprod(vect, som, prod, nbnuls) integer, dimension (:), intent(in) :: vect
integer, intent(out) :: som, prod integer, intent(out), optional :: nbnuls end subroutine somprod
end interface
Cas des fonctions intrinsèques
Les procédures intrinsèques du Fortran 90 possèdent toutes une interface explicite.
14 Les procédures et fonctions récursives
Le Fortran 90 permet d'écrire des procédures et fonctions récursives en les définissant sous l'une des formes suivantes :
recursive subroutine sub(arg_1, ..., arg _n)
recursive function f(n) result(res)
recursive type function f(n) result(res)
Remarques :
Le type de la variable résultat est celui de la fonction doivent être identiques.
La clause RESULT peut également être utilisée pour les fonctions non récursives.
Exemple : Ecriture de la fonction factorielle recursive integer function factorielle(n) result (fact) integer, intent(in) :: n integer :: fact if (n==0) then fact = 1 else fact = n * factorielle(n - 1) end if
15 Interface générique
Plusieurs fonctions mathématiques peuvent être utilisées à la fois sur des réels simple précision, des réels double précision et des complexes. Dans le cas de la fonction exponentielle, par exemple, exp(a) retourne une valeur de type identique à celui de a. Pratiquement, trois fonctions exponentielle sont définies et regroupées sous un nom générique (ici exp) par l'intermédiaire d'un bloc interface. Le bloc interface portera le nom de la fonction générique et regroupera l'interface de chacune des trois fonctions. A la compiltion, le compilateur choisit :-
la fonction exp si a est un réel simple précision
la fonction dexp si a est un réel double précision
la fonction cexp si a est un complexe
Cette notion existe déjà en Fortran 77 mais reste limitée aux fonctions intrinsèques. Fortran 90 permet de définir ses propres fonctions et procédures génériques.
Exercice : Créer une procédure somprod générique s'appliquant aux vecteurs entiers ou réels.
Méthode :
1. Créer une procédure isomprod pour vect de type entier et une procédure rsomprod pour vect de type réel. Placer ces deux procédures dans un même fichier.
2. Créer un bloc interface appelé somprod regroupant les interfaces de isomprod et de rsomprod. Placer ce bloc interface dans un module mod_somprod.
3. Ecrire un programme faisant appel au module mod_somprod et appelant somprod pour un vecteur entier puis pour un vecteur réel.
4. Compiler les différents fichiers et éxecuter le programme.
16 Surcharge et création d'opérateurs
Fortran 90 permet de surcharger des opérateurs comme le font certains langages orientés objets tels que C++ et Java. La définition de nouveaux opérateurs est également possible. On distinguera deux sortes d'opérateurs : les opérateurs s'appliquant sur des expressions et retournant une valeur (comme +, *) et l'opérateur d'affectation (=) qui ne retourne aucune valeur. Les opérateurs retournant une valeur
Pour la première série d'opérateurs, la surcharge s'effectue grâce à un bloc interface commençant par INTERFACE OPERATOR, suivi du symbole de l'opérateur entre parenthèses.
Il reste ensuite à spécifier l'interface complète de la fonction effectuant l'opération.
Pour la définition d'un nouvel opérateur, le principe est identique mis à part que c'est le nom de l'opérateur encadré par le caractère "." qu'il faut mettre entre parenthèses.
Exemple 1 : Interface de l'opérateur + sur les nombres entiers, réels et complexes.
interface operator(+) integer function iadd(a, b) integer, intent(in) :: a, b end function iadd
real function radd(a, b) real, intent(in) :: a, b end integer radd
complex function cadd(a, b) complex, intent(in) :: a, b end function cadd
end interface
Exemple 2 : Interface de l'opérateur .inv. d'inversion des nombres entiers interface operator (.inv.)
integer function iinv(i) integer, intent(in) :: i end function iinv
end interface Remarque :
Lors de la surcharge ou de la définition d'un tel opérateur, les arguments de la fonction doivent tous avoir l'attribut intent(in).
Surcharge de l'opérateur d'affectation =
La surcharge de l'opérateur d'affectation s'effectuera à l'aide d'une interface du type INTERFACE ASSIGNMENT(=).
L'interface de la procédure associée doit spécifier l'attribut intent(out) pour le premier argument (opérande de gauche) et l'attribut intent(in) pour le second argument (opérande de droite).
Exemple : Interface de l'opérateur = sur des données de type matrice interface assignment(=)
subroutine affect(a, b)
type(matrice), intent(out) :: a type(matrice), intent(in) :: b end subroutine affect
end interface
Exercice :
1. Définir un type matrice triangulaire sur les entiers. On s'attachera à limiter autant que possible la taille mémoire occupée par une telle matrice.
2. Créer des procédures d'allocation / désallocation et d'affichage de matrices triangulaires.
3. Surcharger les opérateurs +, =, == et définir l'opérateur de transposition .tr. sur ce type de matrices.
4. Tester ces opérateurs sur des matrices triangulaires inférieures de votre choix.
5. Pour l'ensemble de cet exercice, on s'attachera à définir une interface explicite pour toutes les procédures et fonctions.
Solution Remarques :
La définition d'un type dérivé sur les matrices triangulaires nécessite la création d'un type dérivé ligne (la commande allocate n'étant
possible que sur les tableaux). Les tableaux dynamiques étant définis à l'intérieur d'un type dérivé, ils ne peuvent avoir que l'attribut pointer (et non allocatable).
L'ordre de compilation est important. Pour compiler un fichier F2 faisant appel un module défini dans un fichier F1, il faut compiler F1 puis F2. Ainsi, dans le cas présent, l'ordre de compilation est .
f90 -c mod_mattri.f90 f90 -c mod_matutil.f90 f90 -c mod_matop.f90 f90 -c matutil.f90 f90 -c matop.f90
f90 -o TestMatTri TestMatTri.f90 mod_mattri.o mod_matutil.o mod_matop.o matutil.o matop.o
17 Rappels et nouveautés sur les entrées / sorties
Définitions Un enregistrement désigne une suite ordonnée de valeurs ou de caractères.
Un fichier est constitué d'une suite ordonnée d'enregistrements tous de même type (c'est à dire composé d'enregistrements soit tous formattés soit tous non formattés).
Les fichiers formattés sont des fichiers lisibles par un éditeur de textes.
Les fichiers non formattés sont des fichiers écrits en binaire pur.
La façon dont on accède aux enregistrements d'un fichier détermine s'il s'agit d'un fichier à accès séquentiel ou d'un fichier à accès direct.
Pour un fichier séquentiel, l'ordre des enregistrements est l'ordre dans lequel ils ont été écrits.
La longueur des enregistrements du fichier peut être de taille variable.
Pour accéder à un enregistrement du fichier, il est nécessaire d'accéder à tous les enregistrements qui le précèdent.
Un tel fichier se termine par un enregistrement de fin de fichier. En Fortran, un fichier séquentiel est par défaut formatté.
Pour un fichier à accès direct, l'accès à un enregistrement du fichier se fait par l'intermédiaire du numéro d'ordre de l'enregistrement.
Tous les enregistrements doivent avoir la même longueur. Un tel fichier ne contient pas d'enregistrement de fin de fichier. En Fortran, un tel fichier est par défaut non formatté.
Principe des entrées / sorties
En Fortran, une correspondance logique est établie entre un numéro et les périphériques clavier / écran pour la gestion des entrées / sorties sur un terminal. Pour une saisie de données (périphérique clavier), le numéro 5 est réservé. Pour l'affichage de données (périphérique écran), le numéro 6 est réservé.
La gestion des entrées / sorties dans un fichier se fait également à l'aide d'une correspondance logique entre un nombre entier positif (choisi par l'utilisateur et différent de 5 et 6) et le fichier. Ce numéro logique sera indiqué lors de l'ouverture et de la fermeture du fichier ainsi que lors de la lecture et de l'écriture de données dans le fichier. Remarque : En Fortran, on utilisera le plus souvent des fichiers séquentiels et formattés.
Les formats
De manière générale, un format indique la façon de représenter un ensemble de données. La notion de format est utilisée pour la lecture et l'écriture de nombres ou de chaînes de caractères.
Il est possible de ne pas se soucier de la représentation de ces données, auquel cas le format sera dit libre.
Dans le cas contraire, le format sera dit imposé.
Voici les différents descripteurs de format de données en Fortran 90 :
A[n] : descripteur pour les chaînes de caractères. Le descripteur A
permet de traiter les chaînes de caractères sans se soucier de leur longueur.
L[n] : descripteur pour les données de type logique. En entrée, un
champ ne contenant que des caractères blancs est considéré comme étant la valeur .false.. En sortie, la lettre T (ou F) est affichée, précédée de n-1 caractères blancs.
In, Bn, On, Zn : représentation d'un entier sur n positions (signe - à
prendre en compte pour les entiers négatifs) respectivement en base décimal, binaire, octale ou hexadécimale.
En.p : descripteur pour nombres réels sous forme de puissance de 10
(avec n >= p + 8) .
En.pEe : représentation identique à celle de En.p sauf que e positions
sont réservées pour l'exposant ( n >= p + e + 5) .
ENn.p : ce descripteur représente un nombre sous forme de puissance
de 10 de telle sorte que la puissance soit divisible par 3 et que la partie entière de sa valeur absolue (y1y2y3) soit comprise entre 1 et
999, exception faite pour la valeur 0 ( n >= p + 10) .
[+]y1y2y3.x1x2 ... xpE+c1c2 ou [+]y1y2y3.x1x2 ... xpE+c1c2c3
ENn.pEe : représentation identique à celle de ENn.p sauf
que e positions sont réservées pour l'exposant ( n >= p + e + 7) .
ESn.p : ce descripteur représente un nombre sous forme de puissance
de 10 de telle sorte que la partie entière de sa valeur absolue (y) soit comprise entre 1 et 9, exception faite pour la valeur 0 ( n >= p + 8) .
[+]y.x1x2 ... xpE+c1c2 ou [+]y.x1x2 ... xpE+c1c2c3
ESn.pEe : représentation identique à celle de ESn.p sauf
que e positions sont réservées pour l'exposant ( n >= p + e + 5) .
Fn.p :descripteur pour nombres réels sous forme fixe (p positions
après la virgule et n positions au total). Pour les nombres négatifs, on vérifiera que la relation n >= p+3 est bien respectée (une position pour le signe , une autre pour le . et au moins une pour le chiffre précédent le .).
Gn.p[Ee] : le format le plus approprié entre E et F est choisi. Plus
précisément, si la donnée d à représenter vérifie 10p <= d < 0.1, d sera
représentée au format En.p[Ee], sinon une notation avec exposant sera utilisée.
Remarques :
En écriture, si les spécifications du format ne correspondent pas, une série de caractères * est affichée.
Pour les réels, les valeurs affichées sont les valeurs arrondies. Plusieurs descripteurs de mise en page ont également été prévus :
/ : Saut d'enregistrement.
nX : Saut de colonne. A la lecture, les n positions sont ignorées. A
l'écriture, les n positions sont remplies de caractères blancs.
Tn : Tabulation. Ce descripteur est utilisé afin d'indiquer qu'une
donnée sera lue ou écrite à partir de la position n depuis le début de l'enregistrement.
write(6, 1000) X, Y ...
1000 format(T10, F10.2, T60, F10.2)
=> La valeur de X sera écrite à partir de la colonne 10 et celle de Y à partir de la colonne 60.
TLn : Tabulation arrière. Ce descripteur indique que le prochain
caractère sera lu ou écrit n positions avant la position courante du curseur.
Exemple :
print "(I2, 5X, TL4, I2)" 20, 24 =>20 24
TRn : Tabulation avant. Ce descripteur indique que le prochain
caractère sera lu ou écrit n positions après la position courante du curseur. Descripteur équivalent à nX.
$ : Descripteur de suppression de retour à la ligne (pas standard, donc
non reconnu par certains compilateurs).
'texte' ou nHtexte : Descripteur d'insertion de texte. Pour la seconde
forme, n désigne le nombre de caractères à insérer après le symbole H.
n(descripteurs) : répétition d'un ou de plusieurs descripteurs n fois.
Exemple : 3(F8.2, 3X, 2I3)
: Descripteur d'interruption de contrôle du format. Ce descipteur
permet d'interrompre l'exploitation de la liste des formats qui le succèdent lorsque le nombre d'entrées / sorties est inférieur à ce qui est attendu. Il est utilisé avec les descripteurs de répétition.
Exemple :
write (6, 1000) 10, 12 ...
1000 format(5(:,'valeur : ', I2, 1X)) => Le résultat en sortie sera : valeur : 10 valeur : 12
=>Sans le descripteur :, le résultat serait : valeur : 10 valeur : 12 valeur :
Les namelist
L'instruction NAMELIST permet d'associer un nom à une liste de variables. L'intéret est d'utiliser ce nom pour les opérations d'entrées/sorties sur ces variables.
Syntaxe de déclaration d'une namelist :
NAMELIST /nom_groupe/ liste_variables [[,] /nom_groupe/ liste_variables] Seules les variables ayant l'attribut allocatable ou pointer ne sont pas
autorisées à figurer dans une namelist.
Syntaxe de saisie des valeurs d'une namelist : &nom_groupe var=valeur [,var=valeur] ... /
Le caractère / permet d'interrompre l'instruction de lecture. Pendant
l'exécution, il est possible d'interroger le système sur la nature de la namelist à saisir :
le caractère ? permet l'affichage du nom de la namelist et de ses variables
les caractères =? permettent l'affichage du nom de la namelist à saisir, de ses variables et de leur valeur courante
Exemple :
character (len=20), dimension(2) :: nom int, dimension(3) :: mensurations
real :: poids
namelist /personne/ nom, mensurations, poids read(*, nml=personne)
open(unit=6, delim="apostrohe") write(6, nml=personne)
...
Supposons qu'à la saisie, on entre :
&personne nom(2)='dupond' mensurations=85, , 90 poids=50.2 / En sortie du programme, on aura alors :
&PERSONNE
NOM = '', 'dupond ', MENSURATIONS = 85, 0, 90, POIDS = 50.20000
/
Les entrées / sorties sur un terminal
1. Les entrées au clavier
Syntaxe au format libre :
read(5, *) liste_variables ou read *, liste_variables (forme
condensée)
read(5, nml=nom groupe) ou read(*, nml=nom
groupe) (pour les namelist)
Syntaxe au format imposé :
read(5, num fmt) liste_variables
...
num_fmt format(liste_descripteurs_de_ format)
OU
read format, liste_variables
Dans ce dernier cas, le format devra figuré entre les caractères "( et )" Exemple :
read "(I2,F10.3)", i, x 2. Les sorties sur écran
Syntaxe au format libre :
write(6, *) liste_variables ou print *, liste_variables
write(6, nml=nom_groupe) (pour les namelist)
write(6, num_fmt) liste_variables
...
num_fmt format(liste_descripteurs_de_format)
OU
print format, liste_variables
Les entrées / sorties dans un fichier
1. L'instruction OPEN
L'instruction OPEN établit la correspondance entre un numéro
logique (choisi par l'utilisateur) et un fichier (référencé par son nom). Syntaxe :
OPEN([unit=] u [liste_specifications]) où liste_specifications peut
contenir les spécifications suivantes :
o ACCESS = acc : acc spécifie le mode d'accès aux
enregistrements et vaut 'DIRECT' ou 'SEQUENTIAL' (valeur par défaut).
o ACTION = act : act peut valoir 'READ' (toute tentative
d'écriture est interdite), 'WRITE' (toute tentative de lecture est autorisée), ou 'READWRITE' (les opérations de lecture et écriture sont autorisées, valeur par dfaut).
o BLANK = blnk : blnk vaut 'NULL' ou 'ZERO' suivant que les
caractères blancs doivent être ignorés ou interprétés par des 0. La valeur par défaut est 'NULL'. Cette option n'est licite que pour les fichiers formattés.
o DELIM = del_char : del_char vaut 'APOSTROPHE',
'QUOTE' ou 'NONE' (valeur par défaut) et spécifie le caractère utilisé pour délimiter des chaînes de caractères ou des namelist.
o ERR= errs : si une erreur survient lors de l'ouverture du
fichier, l'instruction spécifiée par l'étiquette errs est exécutée.
o FILE = fln : fln est le nom du fichier à ouvrir (chaîne de
caractères).
o FORM = fm : fm est égal à 'FORMATTED' ou 'UNFORMAT
TED'.
o IOSTAT = ios : ios est une valeur de retour prenant la valeur 0 si aucune erreur ne s'est produite et une valeur strictement positive si une erreur est survenue pendant l'ouverture du fichier.
o PAD = pad_char : pad_char doit être affecté à 'YES' (valeur
par défaut) ou 'NO'. Si la valeur 'YES' est choisie, les enregistrements de longueur inférieure à celle indiquée par l'option RECL seront complétés avec des blancs.
o POSITION = pos : pos indique la position du pointeur de
fichier à l'ouverture. 'REWIND' : position en début de fichier, 'APPEND' : position en fin de fichier.
o RECL = rl : rl est une expression entière égale à la longueur
d'un enregistrement pour un fichier à accès direct.
o STATUS = sta : sta peut prendre les valeurs 'NEW' (le fichier
est créé et s'il existe déjà, une erreur survient), 'OLD' (le fichier existe déjà, sinon erreur), 'REPLACE' (si le fichier n'existe pas, il sera créé, sinon il sera détruit et un fichier de même nom sera créé), 'SCRATCH' (le fichier est effacé à sa fermeture, l'option FILE ne doit pas être utilisée) ou 'UNKNOWN' (l'existence du fichier est inconnue). 'UNKNOWN' était en fait utilisé en Fortran 77 afin d'éviter les messages d'erreur, d'où la possibilité d'utiliser la valeur 'REPLACE' en Fortran 90.-
2. L'instruction CLOSE
L'instruction CLOSE ferme la connexion entre le numéro logique indiqué et le fichier qui lui est associé.
Syntaxe :
CLOSE([UNIT=] u [,ERR = errs] [,IOSTAT = ios]) avec la même
signification des options que pour l'instruction OPEN.
3. L'instruction READ
L'instruction READ permet la lecture de données dans un fichier. Syntaxes :
READ([unit=] u, [liste_specifications]) liste_variables
READ([unit=] u, [NML=] namelist [liste_specifications]) (pour les
namelist)
Liste des options de spécification de lecture :
o ADVANCE = spec, spec valant 'YES' (valeur par défaut) ou
'NO' suivant que l'on souhaite passer à l'enregistrement suivant ou non. Cette option ne peut pas être spécifiée pour les
o END = s, où s est l'étiquette d'une instruction qui sera exécutée
en fin de lecture du fichier.
o EOR = s, où s est l'étiquette d'une instruction qui sera exécutée
en fin de lecture d'un enregistrement.
o ERR = s, où s est l'étiquette d'une instruction qui sera exécutée
en cas d'erreur.
o FMT = format, où format désigne soit un format, soit
l'étiquette d'un format.
o IOSTAT = ios, ios prenant la valeur 0 si aucune erreur n'intervient et une valeur strictement positive sinon.
o REC = rn, rn étant le numéro de l'enregistrement lire.
o SIZE = n, n étant affecté au nombre de caractères lus.
4. L'instruction WRITE
L'instruction WRITE permet l'écriture de données dans un fichier. Syntaxes :
WRITE([unit=] u, [liste_specifications]) liste_variables
WRITE([unit=] u, [NML=] namelist [liste_specifications]) (pour
les namelist)
Liste des options de spécification d'écriture :
o ADVANCE = spec, spec valant 'YES' (valeur par défaut) ou
'NO' suivant que l'on souhaite passer à l'enregistrement suivant ou non. Cette option ne peut pas être spécifiée pour les
namelist et n'existe que pour les fichiers formattés séquentiels.
o ERR = s, où s est l'étiquette d'une instruction qui sera exécutée
en cas d'erreur.
o FMT = format,où format désigne soit un format, soit
l'étiquette d'un format.
o IOSTAT = ios, ios prenant la valeur 0 si aucune erreur
n'intervient et une valeur strictement positive sinon.
o REC = rn, rn étant le numéro de l'enregistrement à lire.
5. L'instruction REWIND
d'un fichier séquentiel. Syntaxes :
REWIND([unit=] u [,ERR = s] [,IOSTAT = ios])
REWIND u
6. L'instruction BACKSPACE
L'instruction BACKSPACE positionne le curseur d'un fichier séquentiel au début de l'enregistrement précédent.
Syntaxes :
BACKSPACE([unit=] u [,ERR = s] [,IOSTAT = ios])
BACKSPACE u
7. L'instruction INQUIRE
L'instruction INQUIRE permet de récupérer des caractéristiques d'un fichier ou d'une unité. Elle admet trois formes :
inquire([unit=] u [,liste_proprietes])
inquire([file=] nom_fichier [,liste_proprietes])
inquire(iolength=len) liste_variables
Les propriétés pouvant être récupérées pour les deux premières formes sont les suivantes :
o ACCESS = acc : acc désignera le mode d'accès du fichier ou
de l'unité ('DIRECT', 'SEQUENTIAL' ou 'UNDEFINED' si la connection n'est pas établie)
o ACTION = acc : acc vaudra 'READ', 'WRITE',
'READWRITE' ou 'UNDEFINED'.
o BLANK = blnk : blnk vaudra 'NULL', 'ZERO' ou
'UNDEFINED'.
o DELIM = del_char : del_char prendra la valeur 'QUOTE',
'APOSTROPHE', 'NONE' ou 'UNDEFINED'.
o DIRECT = dir_char : dir_char prendra la valeur 'YES' si
l'accès est direct, et 'NO' sinon.
o ERR = errs : retourne l'étiquette renvoyant aux instructions
d'erreur.
o EXIST = val : retourne .true. ou .false. suivant que le fichier
o FORM = fm : retourne 'FORMATTED', 'UNFORMATTED'
ou 'UNDEFINED'.
o FORMATTED = fmt : retourne 'YES' ou 'NO' suivant que le
fichier est formatté ou non.
o IOSTAT = ios : retourne la valeur associée à une erreur de
traitement du fichier.
o NAME = nm : retourne le nom du fichier ou 'UNDEFINED' si
le fichier ou l'unité n'a pas de nom.
o NAMED = nmd : retourne 'YES ou 'NO' suivant que le fichier
possède un nom ou pas.
o NEXTREC = nr : retourne n+1, où n est le numéro du dernier
enregistrement lu ou écrit d'un fichier à accès direct. Si le fichier n'est pas connecté ou si la position est inconnue suite à une erreur, nrdevient undéfini.
o NUMBER = num : retourne le numéro de l'unité connectée. Si
l'unité n'est pas connectée, num est indéfini.
o OPENED = od : retourne .true. ou .false. suivant que le fichier
est ouvert ou pas.
o PAD = pad_char : retourne 'NO' si cette valeur a été spécifiée
lors de l'ouverture de fichier et 'YES' sinon.
o POSITION = pos_char : retourne 'APPEND', 'REWIND' ou
'UNDEFINED'.
o READ = rl : retourne 'YES' si le fichier est ouvert en lecture,
'NO' sinon et 'UNKNOWN' si le système ne parvient pas à déterminer le type d'accès aux données du fichier.
o READWRITE = rl : retourne 'YES' si le fichier est ouvert en
lecture/écriture, 'NO' sinon et 'UNKNOWN' si le système ne parvient pas à déterminer le type d'accès aux données du fichier.
o RECL = rl : retourne la longueur d'un enregistrement pour un
fichier à accès direct et la valeur maximum d'un enregistrement pour un fichier à accès séquentiel.
o SEQUENTIAL = seq : renvoit 'YES' si le mode d'accès du
fichier est séquentiel, 'NO' sinon et 'UNKNOWN' si le système ne peut pas déterminer le mode d'accès.
o UNFORMATTED = unf : renvoit 'YES', 'NO' ou
'UNKNOWN' suivant que le fichier est formatté, non formatté ou que le système ne parvient pas déterminer le formattage du fichier.
o WRITE = rl : retourne 'YES 'si le fichier est ouvert en
écriture, 'NO' sinon et 'UNKNOWN' si le système ne parvient pas à déterminer le type d'accès aux données du fichier.