Sunteți pe pagina 1din 509

Machine Translated by Google

Machine Translated by Google

MATEMATICĂ NUMERICALĂ I CALCUL TIIN IFIC

Editori de serie

¨
AM STUART E. SULI
Machine Translated by Google

MATEMATICĂ NUMERICALĂ I CALCUL TIIN IFIC

Cărțile din seria


Monografii marcate cu asterisc ( ) au apărut în seria „Monografii în analiză numerică”, care este continuată
de seria actuală.

Pentru o listă completă de titluri, vă


rugăm să vizitați http://www.oup.co.uk/academic/science/maths/series/nmsc

JH Wilkinson: Problema algebrică cu valori proprii I.


Duff, A. Erisman și J. Reid: Metode directe pentru matrici rare MJ Baines:
Elemente finite în mișcare JD Pryce: Soluție numerică a problemelor Sturm-
Liouville

C. Schwab: metodele elementelor finite p- și hp-: teorie și aplicații în mecanica solidelor și fluidelor JW Jerome:
Modelare și calcul pentru aplicații în matematică, știință și inginerie A. Quarteroni și A. Valli: Metode de
descompunere în domeniul diferențialelor parțiale ecuații G. Em Karniadakis și SJ Sherwin: Metode de elemente
spectrale/hp pentru CFD I. Babuˇska și T. Strouboulis: Metoda elementelor finite și fiabilitatea acesteia B.
Mohammadi și O. Pironneau: Optimizarea formei aplicate pentru fluide S. Succi: The lattice Ecuația Boltzmann:
pentru dinamica fluidelor și dincolo de P. Monk: Metode cu elemente finite pentru ecuațiile lui Maxwell A.
Bellen și M. Zennaro: Metode numerice pentru ecuații diferențiale de întârziere J. Modersitzki: Metode numerice
pentru înregistrarea imaginilor M. Feistauer, J. Felcman și I. Straˇskraba: Metode matematice i de calcul
pentru

debit compresibil
W. Gautschi: Polinoame ortogonale: calcul și aproximare MK Ng: Metode
iterative pentru sistemele Toeplitz M. Metcalf, J. Reid și M. Cohen: Fortran
95/2003 au explicat G. Em Karniadakis și S. Sherwin: Spectral/hp element
methods pentru dinamica fluidelor computaționale,
a doua editie
DA Bini, G. Latouche și B. Meini: Metode numerice pentru lanțuri Markov structurate H. Elman, D.
Silvester și A. Wathen: Elemente finite și soluții iterative rapide: cu aplicații
în dinamica fluidelor incompresibile
M. Chu și G. Golub: Probleme cu valori proprii inverse: teorie, algoritmi și aplicații J.-F. Gerbeau, C.
Le Bris și T. Leli`evre: Metode matematice pentru magnetohidrodinamica
metale lichide
G. Allaire și A. Craig: Analiză și optimizare numerică: o introducere în matematică
modelare și simulare numerică
K. Urban: Metode Wavelet pentru ecuații diferențiale parțiale eliptice B.
Mohammadi și O. Pironneau: Optimizarea formei aplicate pentru fluide, ediția a doua K. B¨ohmer:
Metode numerice pentru ecuații diferențiale eliptice neliniare: o sinopsis M. Metcalf, J. Reid , și M.
Cohen: Modern Fortran Explained
Machine Translated by Google

Fortran modern explicat

Michael Metcalf
Fost CERN, Geneva, Elveția

John Reid
Asociații JKR, Oxfordshire

și

Malcolm Cohen
Grupul de algoritmi numerici, Oxfordshire

1
Machine Translated by Google

Strada Great 3ox2


Clarendon, Oxford 6dp

Oxford University Press este un departament al Universității din Oxford.


Promovează obiectivul de excelență al Universității în cercetare, burse și educație prin
publicarea în întreaga lume în
Oxford New York
Auckland Cape Town Dar es Salaam Hong Kong Karachi
Kuala Lumpur Madrid Melbourne Mexico City Nairobi
New Delhi Shanghai Taipei Toronto
Cu birouri în
Argentina Austria Brazilia Chile Republica Cehă Franța Grecia
Guatemala Ungaria Italia Japonia Polonia Portugalia Singapore
Coreea de Sud Elveția Thailanda Turcia Ucraina Vietnam
Oxford este o marcă înregistrată a Oxford University Press în Marea
Britanie și în anumite alte țări
Publicat în Statele Unite de Oxford
University Press Inc., New York c Michael Metcalf,
John Reid și Malcolm Cohen 2011 Drepturile morale ale autorilor au
fost afirmate Dreptul bazei de date Oxford University Press
(producător)
Prima ediție publicată în 1987 ca Fortran 8x Explained
Ediția a doua publicată în 1989
A treia ediție publicată în 1990 ca Fortran 90 Explained
Ediția a patra publicată în 1996 ca Fortran 90/95 Explained
Ediția a cincea publicată în 1999
Ediția a șasea publicată în 2004 ca Fortran 95/2003 Explained
Această ediție a apărut în 2011
Toate drepturile rezervate. Nicio parte a acestei publicații nu poate fi reprodusă, stocată într-
un sistem de recuperare sau transmisă, sub nicio formă sau prin orice mijloc, fără permisiunea
prealabilă în scris a Oxford University Press, sau așa cum este permis în mod expres de lege sau
în condițiile convenite cu organizarea adecvată a drepturilor de reprografie. Întrebările privind reproducerea
în afara domeniului de aplicare a celor de mai sus trebuie trimise la Departamentul de Drepturi,
Oxford University Press, la adresa de mai sus.

Nu trebuie să distribuiți această carte în nicio altă legătură sau copertă și


trebuie să impuneți aceeași condiție oricărui dobânditor
British Library Cataloging in Publication Data
Date disponibile

Catalogarea Bibliotecii Congresului în date de publicare


Număr de control al Bibliotecii Congresului: 2010941705
Tipărit în Marea Britanie pe
hârtie fără acid de
CPI Antony Rowe, Chippenham, Wiltshire

ISBN 978–0–
19–960141–7 (Hbk)
978–0–
19– 960142– 4 (Pbk)

1 3 5 7 9 10 8 6 4 2
Machine Translated by Google

Prefa ă

Fortran rămâne unul dintre principalele limbaje folosite în domeniile programării științifice, numerice și
inginerești, iar o serie de revizuiri ale standardului care definesc versiunile succesive ale limbajului și-a
sporit progresiv puterea și l-a menținut competitiv cu mai multe generații de rivali.

Începând cu 1978, comitetul tehnic responsabil de dezvoltarea standardelor Fortran, X3J3 (acum
PL22.3, dar încă denumit informal J3), s-a străduit să producă o nouă versiune modernă a limbajului,
foarte necesară, Fortran 90. Scopul său a fost să „ promovează portabilitatea, fiabilitatea, mentenabilitatea
și execuția eficientă. . . pe o varietate de sisteme de calcul”.
Acest standard a fost publicat în 1991, iar lucrările au început în 1993 la o revizuire minoră, cunoscută
sub numele de Fortran 95. Ulterior, și cu același scop, o nouă actualizare majoră a limbii a fost pregătită
de J3 și de comitetul internațional, WG5. Această revizuire, care includea caracteristici de programare
orientată pe obiecte, este acum cunoscută sub numele de Fortran 2003. Aceasta a fost urmată acum de
o nouă revizuire, Fortran 2008, și, încă o dată, pare oportun să se pregătească o descriere informală
definitivă a limbajului pe care îl defineste. Aceasta continuă seria de ediții ale acestei cărți – cele două
ediții ale Fortran 8x Explained care descriau cele două versiuni ale standardului (1987 și 1989), Fortran 90
Explained care descriu standardul Fortran 90 (1990), două ediții Fortran 90/ 95 S-a explicat că includeau
și Fortran 95 (1996 și 1999) și Fortran 95/2003 (2004), cu capitolele sale adăugate despre Fortran 2003. În
acel efort final, un al treilea coautor a fost binevenit.

În această carte, un capitol inițial prezintă fundalul lucrării privind noile standarde, iar cele nouă
capitole următoare descriu Fortran 95 (mai puțin caracteristicile sale învechite și caracteristicile
redundante ale Fortran 77 a căror utilizare o depreciem) într-un mod adecvat atât pentru a înțelege
implicațiile caracteristicilor sale și pentru scrierea de programe. Includem extensiile de matrice alocabile
care au fost publicate inițial ca Raport tehnic ISO și fac acum parte din Fortran 2003, deoarece au fost
implementate în compilatoarele Fortran 95 de mulți ani.
Se presupun anumite cunoștințe de concepte de programare. Pentru a reduce numărul de referințe
forward și, de asemenea, pentru a permite, cât mai repede posibil, să fie scrise programe utile pe baza
materialului deja absorbit, ordinea de prezentare nu urmează întotdeauna cea a standardului. În special,
am ales să amânăm la anexe descrierea caracteristicilor care sunt oficial etichetate ca redundante (unele
dintre ele au fost șterse din standardul Fortran 95) și a altor caracteristici a căror utilizare o depreciem.
Ele pot fi întâlnite în programele vechi, dar nu sunt necesare în programele noi.

Capitolul 11 descrie o altă parte a Fortran 2003 care a fost definită inițial de un raport tehnic ISO.
Aceasta este urmată, în capitolele 12 până la 17, de descrieri ale celorlalte caracteristici
Machine Translated by Google

vi Prefa ă

definit de standardul Fortran 2003. Capitolul 18 descrie o parte a Fortran 2008 care a fost definită inițial
de un raport tehnic ISO și alte două capitole descriu celelalte caracteristici noi ale Fortran 2008. Structura
cărții permite astfel cititorului să distingă clar între Fortran 95 (plus extensii de matrice alocabile). ),
Fortran 2003 și noile caracteristici Fortran 2008. Rețineți că, în afară de un număr mic de ștergeri, fiecare
dintre limbile Fortran 77, Fortran 90, Fortran 95, Fortran 2003 și Fortran 2008 este un subset al
succesorului său.

Pentru a face din carte o lucrare de referință completă, se încheie cu șapte anexe.
Acestea conțin, succesiv, o listă a procedurilor intrinseci, o descriere a diferitelor caracteristici a căror
utilizare o depreciem și nu o descriem în corpul cărții, o descriere a caracteristicilor învechite și șterse,
sfaturi pentru evitarea cascadelor de compilare, un exemplu extins care ilustrează utilizarea orientării
obiectelor, un glosar al termenilor Fortran și soluții pentru majoritatea exercițiilor.

Sperăm că această carte, oferind descrieri complete ale Fortran 95, Fortran 2003 și Fortran 2008, va
continua rolul util pe care l-au jucat edițiile anterioare pentru versiunile corespunzătoare ale standardului
și că va servi drept referință pe termen lung. lucrează pentru limbajul modern de programare Fortran.

Malcolm Cohen dorește să mulțumească Grupului de algoritmi numerici (NAG) pentru încurajarea sa
agement în timpul scrierii acestei cărți.
Machine Translated by Google

Convenții utilizate în această carte

Textul afișat Fortran este setat în fontul mașinii de scris:

întreg :: i, j

iar o linie constând din două puncte indică liniile omise:

sortarea subrutinei
:
sfârșitul sortării subrutinei

Termenii informali BNF sunt cu caractere cursive:

if (scalar-logic-expr) action-stmt

Parantezele pătrate în cursive indică elemente opționale:

se încheie dacă [nume]

iar o elipsă reprezintă un număr arbitrar de elemente repetate:

[case selector [nume]] bloc] ...

Litera italica b semnifică un caracter gol.


Corectările oricăror erori semnificative detectate în această carte vor fi disponibile în
fișierele edits.ps și edits.pdf la ftp://ftp.numerical.rl.ac.uk/pub/MRandC.
Machine Translated by Google

Această pagină a fost lăsată goală în mod intenționat


Machine Translated by Google

Cuprins

1 De unde Fortran? 1
1.1 Introducere ................................... 1
1.2 Istoria timpurie a lui Fortran ............................. 2
1.3 Unitatea pentru standardul Fortran 90 ...................... 3 1.4 Evoluția limbajului ................ ...............
4
1.5 Fortran 95 .................................... 4
1.6 Extensii la Fortran 95 ............................ 5
1.7 Fortran 2003 ................................... 6
1.8 Fortran 2008 ................................... 7
1.9 Conformitate .................................. 7

2 Elemente de limbaj 2.1 9


Introducere ................................... 9 2.2 Setul de caractere Fortran 2.3 Jetoane 2.4
Forma sursă 2.5 Conceptul de .............................
tip . 9
. . . . . . . ... . . . . . . . . ..... . . . . . . . . . . . . . . . 10
. . . . ... . . . . . . . . ..... . . . . . . . . ... . . . .
. ... . . . . . . . . ..... . . . . . . . . . . . . . . 11 .
2.6 Constante literale de tip intrinsec . . . . ..... . . . . . . . . . . . . . . . 13
2.6.1 Constante literale întregi . . . . . ..... . . . . . . . . . . . . . . . 14
2.6.2 Constante literale reale . . . . . . ..... . . . . . . . . . . . . . . 14 .
2.6.3 Constante literale complexe . . . . . . . . . . . . . . . . . . . . . . . . 15
2.6.4 Constante literale cu caractere . . . . . . . . . . . . . . . . . . . . . . . 17
2.6.5 Constante literale logice 2.7 . . . . . . . . . . . . . . . . . . . . . . . 17 .
Nume . . . . . . . ... . . . . . . . . ..... . . . . . . . . . . . . . . 19 .
2.8 Variabile scalare de tip intrinsec . . . . ..... . . . . . . . . . . . . . . 20 .
2.9 Tipuri de date derivate . . . . . . . . . . . ..... . . . . . . . . . . . . . . . 20
2.10 Matrice de tip intrinsec . . . . . . . . . ..... . . . . . . . . . . . . . . 21 .
2.11 Subșiruri de caractere . . . . . . . . . . . ..... . . . . . . . . . . . . . . 23 .
2.12 Obiecte și subobiecte . . . . . . . . . ..... . . . . . . . . . . . . . . . 26
2.13 Indicatori . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
2.14 Rezumat . . . . . ... . . . . . . . . ..... . . . . . . . . . . . . . . 27 . 29

3 Expresii și teme 3.1 Introducere


. . . . ... . . . . . . . . ..... . . . . . . . . ... . . . 33 . 33
Machine Translated by Google

x Cuprins

3.2 Expresii numerice scalare . . . . . . . ... . . . . . . . . . . ... . . . . 34 .


3.3 Variabile definite și nedefinite . . . . ... . . . . . . . . . . ... . . . 37 . 38

3.4 Atribuire numerică scalară . . . . . . . ... . . . . . . . . . . ... . . .

3.5 Operatori relaționali scalari . . . . . . . ... . . . . . . . . . . ... . . . . 38

3.6 Expresii logice scalare și atribuiri . . . . . . . . . . . ... . . . . 39

3.7 Expresii de caractere scalare și atribuiri . . . . . . . . . . ... . . . . 41

3.8 Constructori de structură și operatori definiți scalari . . . . . . . ... . . . . 42

3.9 Atribuții scalare definite . . . . . . . ... . . . . . . . . . . ... . . . . 45 .

3.10 Expresii matrice . ... . . . . . . . . ... . . . . . . . . . . ... . . . 46 .

3.11 Alocarea matricei . ... . . . . . . . . ... . . . . . . . . . . ... . . . 48 .

3.12 Indicatori în expresii și sarcini . . . . . . . . . . . . . ... . . . 48 .

3.13 Instrucțiunea nullify 3.14 .. . . . . . . . . ... . . . . . . . . . . ... . . . 51 . 51

Rezumat . . . . . ... . . . . . . . . ... . . . . . . . . . . ... . . .

4 Construcții de control 4.1 55


Introducere 4.2 . . . . ... . . . . . . . . ... . . . . . . . . . . ... . . . . 55 .
Construcția și instrucțiunea if 4.3 Construcția . . . . . ... . . . . . . . . . . ... . . . 55 .
. . . . go to
caz 4.4 Construcția do 4.5 Instrucția . . . . . . . . ... . . . . . . . . . . ... . . . 57 .
4.6 Rezumat . . ... . . . . . . . . ... . . . . . . . . . . ... . . . 59 .
... . . . . . . . . ... . . . . . . . . . . ... . . . 62 . 63
. . . . ... . . . . . . . . ... . . . . . . . . . . ... . . .

5 Unități de program și proceduri 5.1 67


Introducere . . . . ... . . . . . . . . ... . . . . . . . . . . ... . . . . 67

5.2 Programul principal . . . . . . . . . . . . . . ... . . . . . . . . . . ... . . . . 68 .

5.3 Instrucțiunea stop 5.4 ... . . . . . . . . ... . . . . . . . . . . ... . . . 69 .

Subprograme externe . . . . . . . . . . ... . . . . . . . . . . ... . . . 69 .


5.5 Module 5.6 . . . . . . ... . . . . . . . . ... . . . . . . . . . . ... . . . 70 .

Subprograme interne . . . . . . . . . . ... . . . . . . . . . . ... . . . 73 .

5.7 Argumente ale procedurilor . . . . . . . . ... . . . . . . . . . . ... . . . 73 .

5.7.1 Argumente pointer . . . . . . . ... . . . . . . . . . . ... . . . 75 .

5.7.2 Restricții privind argumentele reale . . . . . . . . . . . . . ... . . . 76 . 76

5.7.3 Argumente cu atributul țintă 5.8 Instrucțiunea .. . . . . . . . . . . ... . . . 77 77 .


. . Funcții
return 5.9 Intenția argumentului 5.10 . . . . . . . . ... . . . . . . . . . . ... . . . .
. . ... . . . . . . . . ... . . . . . . . . . . ... . . . .
. . . . . ... . . . . . . . . ... . . . . . . . . . . ... . . . 78 .
5.10.1 Efecte secundare interzise 5.11 . . . . . ... . . . . . . . . . . ... . . . 80 . 80

Interfețe explicite și implicite 5.12 Proceduri . . . . . ... . . . . . . . . . . ... . . .

ca argumente . . . . . . . . ... . . . . . . . . . . ... . . . . 82

5.13 Cuvânt cheie și argumente opționale . . . . ... . . . . . . . . . . ... . . . . 83

5.14 Domeniul de aplicare . . ... . . . . . . . . ... . . . . . . . . . . ... . . . . 85 .

al etichetelor 5.15 Domeniul . . . . . . . . . . . . . ... . . . . . . . . . . ... . . . 85 .


de aplicare al numelor 5.16 . . . . . . . . . . . . . ... . . . . . . . . . . ... . . . 88 .
Recursie directă 5.17 Recursie . . . . . . . . . . . . ... . . . . . . . . . . ... . . . 89 . 90

indirectă 5.18 Supraîncărcare și interfețe generice . . . ... . . . . . . . . . . ... . . .


Machine Translated by Google

Cuprins xi

5.19 Lungimea caracterului presupus . . . . . . . . . . . . . . . . . . . . . ... . . . . 93 .


5.20 Subrutinele și instrucțiunile de funcție 5.21 . . ... . . . . . . . . ... . . . 95 . 96

Rezumat . . . . . ... . . . . . . . . . . ... . . . . . . . . ... . . .

6 Caracteristici ale
matricei 6.1 . . . . ... . . . . . . . . . . ... . . . . . . . . ... . . . 99 .
...
Introducere 6.2 Matrice de dimensiune .zero
. .. . . . . . . . . . . . . . . . . . . ... . . . 99 .

6.3 Matrice de formă asumată . . . . . . . . . . . ... . . . . . . . . ... . . . 99 .

6.4 Obiecte automate . ... . . . . . . . . . . ... . . . . . . . . ... . . . 100 .


6.5 Alocarea datelor 6.5.1 . ... . . . . . . . . . . ... . . . . . . . . ... . . . 100 .
Atributul alocabil 6.5.2 Instrucțiunea . . . . . . ... . . . . . . . . ... . . . 102 .
alocare 6.5.3 Instrucțiunea deallocate . . . . . . . ... . . . . . . . . ... . . . 102 . 103
6.5.4 Argumente fictive alocabile. . . . . . . ... . . . . . . . . ... . . . . 104
. . ... . . . . . . . . ... . . . . 105
6.5.5 Funcții alocabile . . . . . . . . ... . . . . . . . . ... . . . . 105

6.5.6 Componente alocabile . . . . . . ... . . . . . . . . ... . . . . 106 .

6.5.7 Matrice alocabile vs. pointeri . . . ... . . . . . . . . ... . . . 109 .

6.6 Operații și atribuții elementare . . . ... . . . . . . . . ... . . . 110 .

6.7 Funcții cu valori matrice 6.8 . . . . . . . . . . . ... . . . . . . . . ... . . . 110


Instrucțiunea și constructul where 6.9 Instrucțiunea. . . . . . . . . . . . . . . . ... . . . . 111
și constructul forall 6.10 Proceduri pure . . . . . . ... . . . . . . . . ... . . . . 114
... . . . . . . . . . . ... . . . . . . . . ... . . . . 117 .

6.11 Proceduri elementare . . . . . . . . . . . . ... . . . . . . . . ... . . . 118 .

6.12 Elemente matrice . . ... . . . . . . . . . . ... . . . . . . . . ... . . . 119 .

6.13 Subobiecte matrice . ... . . . . . . . . . . ... . . . . . . . . ... . . . 120 .

6.14 Matrice de pointeri . ... . . . . . . . . . . ... . . . . . . . . ... . . . 123


6.15 Pointerii ca alias 6.16 . ... . . . . . . . . . . ... . . . . . . . . ... . . . . 124 .

Constructori de matrice 6.17 ... . . . . . . . . . . ... . . . . . . . . ... . . . 125 .

Matrice de masca . . . . ... . . . . . . . . . . ... . . . . . . . . ... . . . 126

6.18 Rezumat . . . . . ... . . . . . . . . . . ... . . . . . . . . ... . . . . 127

7 Declarații de specificații 7.1 133 .


Introducere 7.2 Tastare . . . . . . . . . . . . . . . . . ... . . . . . . . . ... . . . 133 .

implicită . . ... . . . . . . . . . . ... . . . . . . . . ... . . . 133 .

7.3 Declararea entităților de forme diferite . . . . ... . . . . . . . . ... . . . 134 .

7.4 Constante denumite și expresii constante . ... . . . . . . . . ... . . . 134 .


7.5 Valori inițiale pentru variabile . . . . . . . . . . ... . . . . . . . . ... . . . 137 .

7.5.1 Inițializarea în declarațiile de tip declarație 7.5.2 Declarația . . . . . . . . . . . . . 137


de date . . . . . . . . . ... . . . . . . . . ... . . . . 138
7.5.3 Inițializarea pointerului și funcția null . . . . . . . . ... . . . . 140

7.5.4 Inițializarea implicită a componentelor . . . . . . . . . . . ... . . . . 141

7.6 Atributele publice și private 7.7 Instrucțiunile . . . . . . . . . . . . . . . . . ... . . . . 142

pointer, target și alocabile 7.8 Intenția și instrucțiunile opționale . . . . . . . . . . ... . . . . 144

7.9 Atributul salvare . . . . . ... . . . . . . . . ... . . . . 144 .


. ... . . . . . . . . . . ... . . . . . . . . ... . . . 145
Machine Translated by Google

xii Cuprins

7.10 Instrucțiunea de utilizare . . . . . . . . . . . . ... . . . . . . . . . . ... . . . . 146 .

7.11 Definiții de tip derivat 7.12 . . . . . . . . ... . . . . . . . . . . ... . . . 149 .

Instrucțiunea de declarație de tip 7.13 . . . . . ... . . . . . . . . . . ... . . . 150 .

Specificarea parametrilor de tip și tip . ... . . . . . . . . . . ... . . . 152 .

7.14 Expresii de specificație . . . . . . . . ... . . . . . . . . . . ... . . . 153 .

7.14.1 Funcții de specificare 7.15 . . . . . ... . . . . . . . . . . ... . . . 153 .


. . .. . . . . . . . . .
Instrucțiunea listei de nume 7.16 Rezumat . . . . . . . . . . ... . . . 155 .
. . . . ... . . . . . . . . ... . . . . . . . . . . ... . . . 156

8 Proceduri intrinseci 8.1 161 .


Introducere . . . . ... . . . . . . . . ... . . . . . . . . . . ... . . . 161 .

8.1.1 Apeluri de cuvinte . . . . . . . . . ... . . . . . . . . . . ... . . . 161 .

cheie 8.1.2 Categorii de proceduri intrinseci . . . . . . . . . . . . ... . . . 162 .


8.1.3 Instrucțiunea intrinsecă 8.1.4 . . . . . ... . . . . . . . . . . ... . . . 162 .
. . . . . . . . ...
Intenții de argument 8.2 Funcții de . . . . . . . . . . ... . . . 162 .

interogare pentru orice tip. . . . . ... . . . . . . . . . . ... . . . 162 .


8.3 Funcții numerice elementare 8.3.1 . . . . . . ... . . . . . . . . . . ... . . . 163 .

Funcții elementare care pot converti . . . . . . . . . . ... . . . 163 .


8.3.2 Funcții elementare care nu convertesc . . . . . . . . . ... . . . 164 .
8.4 Funcții matematice elementare 8.5 Caractere . . . ... . . . . . . . . . . ... . . . 165 .

elementare și funcții logice 8.5.1 Conversii caracter- .. . . . . . . . . . . ... . . . 166 .

întreg . ... . . . . . . . . . . ... . . . 166 .

8.5.2 Funcții de comparație lexicală 8.5.3 . ... . . . . . . . . . . ... . . . 167 .

Funcții elementare de manipulare a șirurilor 8.5.4 . . . . . . . . . . . ... . . . 167 .

Conversie logică . . . . . . . ... . . . . . . . . . . ... . . . 168 .

8.6 Funcții non-elementale de gestionare a șirurilor ... . . . . . . . . . . ... . . . 168 .

8.6.1 Funcția de interogare pentru gestionarea ... . . . . . . . . . . ... . . . 168 .


. . . . . . . ...
șirurilor 8.6.2 Funcții transformaționale de manipulare a șirurilor . . . 168 .

8.7 Funcții numerice de interogare și manipulare 8.7.1 . . . . . . . . . . . ... . . . 168

Modele pentru date întregi și reale 8.7.2 Funcții de . . . . . . . . . . . . . ... . . . . 168


. pentru
interogare numerică 8.7.3 Funcții elementare . . ... . . . . . . . . . . ... . . . . 169 .

manipularea reale 8.7.4 Funcții de transformare pentru . . . . . . . . . ... . . . 170


valori tip. . . . . . . ... . . . . 171

8.8 Proceduri de manipulare a biților . . . . . . ... . . . . . . . . . . ... . . . . 171

8.8.1 Funcția de interogare . . . . . . . . ... . . . . . . . . . . ... . . . . 172


8.8.2 Funcții elementare 8.8.3 . . . . . . ... . . . . . . . . . . ... . . . . 172 .
Subrutină elementară 8.9 Funcția . . . . . . ... . . . . . . . . . . ... . . . 173 .
. ...
de transfer 8.10 Funcții de multiplicare . . . . . . . . ... . . . . . . . . . . ... . . . 173

vectorială și matrice 8.11 Funcții de transformare care .. . . . . . . . . . . ... . . . . 174 .

reduc tablourile . . . . . . . . . . ... . . . 175 .

8.11.1 Cazul cu un singur argument . . . . . . . . . . . . . . . . . . . ... . . . 175 .

8.11.2 Argument opțional dim 8.11.3 . . . . . . . . . . . . . . . . . . ... . . . 175 .

Mască de argument opțional 8.12 . . . . ... . . . . . . . . . . ... . . . 176 .

Funcții de interogare matrice . . . . . . . . . ... . . . . . . . . . . ... . . . 176 .


8.12.1 Starea alocării . . . . . . . . ... . . . . . . . . . . ... . . . 176
Machine Translated by Google

Cuprins xiii

8.12.2 Limite, formă și dimensiune . . . . ... . . . . . . . . . . ... . . . . 176

8.13 Funcții de construcție și manipulare a matricei 8.13.1 . . . . . . . . . ... . . . . 177


. . . . și . . . . . . . . . . . . .
Funcția elementară de îmbinare 8.13.2 Împachetarea . . . . 177

dezambalarea matricelor . ... . . . . . . . . . . ... . . . . 177

8.13.3 Reformarea unei matrice . . . . . . . ... . . . . . . . . . . ... . . . . 177

8.13.4 Funcția de transformare pentru replicare . . . . . . . . ... . . . . 178 .

8.13.5 Funcții de deplasare a matricei . . . . . . . . . . . . . . . . . ... . . . 178 .

8.13.6 Transpunerea matricei . . . . . . . . ... . . . . . . . . . . ... . . . 179 .

8.14 Funcții de transformare pentru locație geometrică 8.15 Funcție . . . . . . . . . . . . . . 179 .

de transformare pentru disocierea pointerului 8.16 Subrutine . . . . . . . ... . . . 179 .


intrinseci non-elementale . . ... . . . . . . . . . . ... . . . 180 .
8.16.1 Ceas în timp real . . . . . . . . . ... . . . . . . . . . . ... . . . 180 .
8.16.2 Timp CPU 8.16.3 . . . . . . . . . . . . ... . . . . . . . . . . ... . . . 181 .
Numere aleatorii 8.17 Rezumat .. . . . . . . . . . . . . . . . . . . . . ... . . . 181 .
. . . . ... . . . . . . . . ... . . . . . . . . . . ... . . . 182

9 Transferul de date 185 .


9.1 Introducere 9.2 . . . . ... . . . . . . . . ... . . . . . . . . . . ... . . . 185 .
Conversia numerelor . . . . . . . . . . . ... . . . . . . . . . . ... . . . 185 .
9.3 Liste I/O 9.4 . . . . . . ... . . . . . . . . ... . . . . . . . . . . ... . . . 186 .
Definirea formatului 9.5 . ... . . . . . . . . ... . . . . . . . . . . ... . . . 188 .
Numerele unităților 9.6 . . . ... . . . . . . . . ... . . . . . . . . . . ... . . . 190 .
. . . . ...
Fișiere interne 9.7 Intrare . . . . . . . . ... . . . . . . . . . . ... . . . 191 .

formatată . . ... . . . . . . . . ... . . . . . . . . . . ... . . . 193 .

9.8 Ieșire formatată . ... . . . . . . . . ... . . . . . . . . . . ... . . . 194 .


9.9 I/E direc ionate pe listă . . . . . . . . . . . . . ... . . . . . . . . . . ... . . . 195 .
9.10 I/O liste de nume . . . . . . . . . . . . . . . ... . . . . . . . . . . ... . . . 197 .

9.11 I/O fără avansare . . . . . . . . . . . ... . . . . . . . . . . ... . . . 198

9.12 Editarea descriptorilor . . . . . . . . . . . . . ... . . . . . . . . . . ... . . . . 200

9.12.1 Numărări repetate . . . . . . . . . . ... . . . . . . . . . . ... . . . . 200

9.12.2 Descriptori de editare a datelor . . . . . . . . . . . . . . . . . . . ... . . . . 201

9.12.3 Descriptor de editare a șirului de caractere . . . . . . . . . . . . . . ... . . . . 205 .

9.12.4 Descriptori de editare de control . . . . . . . . . . . . . . . . . . ... . . . 205 .


9.13 I/O neformatat . . ... . . . . . . . . ... . . . . . . . . . . ... . . . 208 .
9.14 Fișiere cu acces direct . ... . . . . . . . . ... . . . . . . . . . . ... . . . 209 .
9.15 Execuția unei instrucțiuni de transfer de date . ... . . . . . . . . . . ... . . . 210

9.16 Rezumat . . . . . ... . . . . . . . . ... . . . . . . . . . . ... . . . . 211

10 Operații pe fișiere externe 10.1 213


Introducere . . . . ... . . . . . . . . ... . . . . . . . . . . ... . . . . 213

10.2 Declarații de poziționare pentru fișiere secvențiale .. . . . . . . . . . . ... . . . . 214


.
10.2.1 Declarația backspace 10.2.2 Declarația . . . . . . . . . . . . . . . . ... . . . . 214
. Declarații
rewind 10.2.3 Declarația endfile 10.2.4 . . . . . ... . . . . . . . . . . ... . . . . 214 .
de transfer de date . . . . . . ... . . . . . . . . . . ... . . . 215 .
. . . . ... . . . . . . . . . . ... . . . 215
Machine Translated by Google

xiv Cuprins

10.3 Declarația deschisă 10.4 ... . . . . . . . . ... . . . . . . . . . . ... . . . . 216


Declarația de închidere 10.5 ... . . . . . . . . ... . . . . . . . . . . ... . . . . 218 .

Declarația de întrebare 10.6 .. . . . . . . . . ... . . . . . . . . . . ... . . . 219

Rezumat . . . . . ... . . . . . . . . ... . . . . . . . . . . ... . . . . 222

11 Gestionarea excepțiilor în virgulă mobilă 223 .


11.1 Introducere . . . . ... . . . . . . . . ... . . . . . . . . . . ... . . . 223
11.2 Standardul IEEE 11.3 ... . . . . . . . . ... . . . . . . . . . . ... . . . . 224
Accesul la funcții 11.4 Steaguri .. . . . . . . . . ... . . . . . . . . . . ... . . . . 225

Fortran . ... . . . . . . . . ... . . . . . . . . . . ... . . . . 227

11.5 Oprire . . . . . . ... . . . . . . . . ... . . . . . . . . . . ... . . . . 228 .

11.6 Modul de rotunjire 11.7 ... . . . . . . . . ... . . . . . . . . . . ... . . . 228 .

Modul underflow (numai Fortran 2003) . . . . . . . . . . . . . ... . . . 229

11.8 Modulul ieee_exceptions . . . . . . ... . . . . . . . . . . ... . . . . 229 .

11.8.1 Tipuri derivate . . . . . . . . . . ... . . . . . . . . . . ... . . . 229 .

11.8.2 Funcții de interogare pentru excepțiile IEEE . . . . . . . . . . ... . . . 230

11.8.3 Subprograme pentru steaguri și moduri de oprire 11.8.4 . . . . . . . . . . . . . . 230 .

Subprograme pentru întreaga stare de virgulă mobilă 11.9 Modulul . . ... . . . 231 .
ieee_arithmetic . . . . . . ... . . . . . . . . . . ... . . . 232

11.9.1 Tipuri derivate . . . . . . . . . . ... . . . . . . . . . . ... . . . . 232

11.9.2 Funcții de interogare pentru aritmetica IEEE . . . . . . . . . . ... . . . . 232 .


11.9.3 Funcții elementare . . . . . . ... . . . . . . . . . . ... . . . 234 .
11.9.4 Subrutine non-elementale 11.9.5 . . . ... . . . . . . . . . . ... . . . 235
Funcția de transformare pentru valoarea kind . . . . . . . . ... . . . . 236 .

11.10 Exemple . . . . . ... . . . . . . . . ... . . . . . . . . . . ... . . . 237

11.10.1 Produs punctual . . . . . . . . . . ... . . . . . . . . . . ... . . . . 237

11.10.2 Apelarea procedurilor alternative . ... . . . . . . . . . . ... . . . . 237 .

11.10.3 Apelarea unui cod alternativ în linie ... . . . . . . . . . . ... . . . 238

11.10.4 Funcție de ipotenuză de încredere . ... . . . . . . . . . . ... . . . . 238


11.10.5 Acces la valorile aritmetice IEEE . . . . . . . . . . . . ... . . . . 239

12 Interoperabilitatea cu C 12.1 243


Introducere 12.2 . . . . ... . . . . . . . . ... . . . . . . . . . . ... . . . . 243 .

Interoperabilitatea tipurilor intrinseci . . . . ... . . . . . . . . . . ... . . . 243 .

12.3 Interoperabilitate cu tipuri de pointer C . . ... . . . . . . . . . . ... . . . 245

12.4 Interoperabilitatea tipurilor derivate . . . . ... . . . . . . . . . . ... . . . . 246

12.5 Interoperabilitatea variabilelor . . . . . . . ... . . . . . . . . . . ... . . . . 247


12.6 Atributul valoare ... . . . . . . . . ... . . . . . . . . . . ... . . . . 248

12.7 Interoperabilitatea procedurilor . . . . . . ... . . . . . . . . . . ... . . . . 249 .

12.8 Interoperabilitatea datelor globale . . . . . ... . . . . . . . . . . ... . . . 250 .

12.9 Invocarea unei funcții C din Fortran 12.10 . . . ... . . . . . . . . . . ... . . . 251

Invocarea Fortran din C . . . . . . . . ... . . . . . . . . . . ... . . . . 252 .


12.11 Enumerări . . ... . . . . . . . . ... . . . . . . . . . . ... . . . 253
Machine Translated by Google

Cuprins xv

13 Parametri de tip și indicatori de procedură 13.1 255 .


Introducere 13.2 Parametri. . de
. .tip. amânat
.. . . . . . . . . ..... . . . . . . . . ... . . . 255 .
. . . . . . . ..... . . . . . . . . ... . . . 255 .

13.3 Interogare tip parametru . . . . . . . . . ..... . . . . . . . . ... . . . 256 .

13.4 Tipuri derivate parametrizate . . . . . . ..... . . . . . . . . ... . . . 256

13.4.1 Definirea unui tip derivat parametrizat . . . . . . . . . . ... . . . . 256 .

13.4.2 Parametri de tip asumat și amânat . . . . . . . . . ... . . . 258 .

13.4.3 Valori implicite ale parametrilor de tip . . . . . . . . . . . . . . ... . . . 258 .

13.4.4 Interogarea parametrului tip derivat . . . . . . . . . . . . . ... . . . 259 .


13.5 Interfețe abstracte 13.6 ... . . . . . . . . ..... . . . . . . . . ... . . . 259 .

Indicatori de procedură . . . . . . . . . . . ..... . . . . . . . . ... . . . 261 .

13.6.1 Variabile indicator de procedură . . . ..... . . . . . . . . ... . . . 261 .

13.6.2 Componentele indicatorului de procedură . . . . . . . . . . . . . . ... . . . 261 .

13.6.3 Atributul de trecere . . . . . . . . ..... . . . . . . . . ... . . . 262

14 Programare orientată pe obiecte 14.1 265 .


Introducere 14.2 Extensie
. .de. tip
. 14.2.1
... . . . . . . . . ..... . . . . . . . . ... . . . 265 .
. .tip. .. .. .
Extensie de tip și parametri de . . . . . . . . ..... . . . . . . . . ... . . . 265 .
. . . . . . . . ... . . . 267 .

14.3 Entită i polimorfe 14.3.1 .. . . . . . . . . ..... . . . . . . . . ... . . . 267 .

Stabilirea tipului dinamic . ..... . . . . . . . . ... . . . 268 .

14.3.2 Limitări ale utilizării unei variabile polimorfe . . . . ... . . . 269 .

14.3.3 Rețele polimorfe și scalari 14.3.4 Entități ..... . . . . . . . . ... . . . 269 .

polimorfe nelimitate 14.3.5 Entități polimorfe și ..... . . . . . . . . ... . . . 269 .

rezoluție generică . . . . . . . ... . . . 270


14.4 Construcția asociată 14.5 . . . . . . . . . ..... . . . . . . . . ... . . . . 271

Construcția tip select 14.6 Proceduri . . . . . . . . ..... . . . . . . . . ... . . . . 272

legate de tip . . . . . . . . . ..... . . . . . . . . ... . . . . 274

14.6.1 Proceduri specifice legate de tip . . . . . . . . . . . . . ... . . . . 274

14.6.2 Proceduri generice legate de tip . . . . . . . . . . . . . ... . . . . 277

14.6.3 Extinderea tipului și procedurile legate de tip . . . . . . . ... . . . . 279 .

14.7 Legături amânate și tipuri de abstract . . ..... . . . . . . . . ... . . . 280 .


14.8 Finalizare . . . . ... . . . . . . . . ..... . . . . . . . . ... . . . 281 .

14.8.1 Extensie de tip și subrutine finale 14.9 Exemplu . . . . . . . . . . . ... . . . 284 .

de încapsulare a procedurii . . . . ..... . . . . . . . . ... . . . 284 .

14.10 Funcții de interogare tip . . . . . . . . . ..... . . . . . . . . ... . . . 286

15 Stabilirea și mutarea datelor 15.1 289


Introducere . . . . ... . . . . . . . . ..... . . . . . . . . ... . . . . 289

15.2 Accesibilitatea componentelor mixte . . . . . ..... . . . . . . . . ... . . . . 289 .


15.3 Constructori de structură .. . . . . . . . . ..... . . . . . . . . ... . . . 289 .
15.4 Instrucțiunea alocare . . . . . . . . . ..... . . . . . . . . ... . . . 291 .

15.4.1 Alocare tip tip și parametri de tip amânat . . . . . ... . . . 291 .

15.4.2 Variabile polimorfe și alocare tipizată 15.4.3 Alocare sursă . . . . . . . . . . . . . 292 .


. . . . . . . ..... . . . . . . . . ... . . . 292
Machine Translated by Google

xvi Cuprins

15.5 Entită i alocabile 15.5.1 ... . . . . . . . . ... . . . . . . . . . . ... . . . . 293 .


Scalari alocabili 15.5.2 Atribuire . . . . . . . ... . . . . . . . . . . ... . . . 294 .

la o matrice alocabilă . . . . . . . . . . . . ... . . . 294 .

15.5.3 Transferarea unei alocări 15.6 . . . ... . . . . . . . . . . ... . . . 295

Atribuire pointer . . . . . . . . . . . ... . . . . . . . . . . ... . . . . 296 .


15.7 Mai mult control al accesului de la un modul . ... . . . . . . . . . . ... . . . 296 .

15.8 Redenumirea operatorilor pe instrucțiunea use .. . . . . . . . . . . ... . . . 297 .

15.9 Sintaxa constructorului de matrice .. . . . . . . . . . . . . . . . . . . . ... . . . 297 .

15.10 Specificație și expresii constante . . . . . . . . . . . . . ... . . . 298

16 Îmbunătățiri diverse 16.1 Introducere 16.2 301 .


. . . volatil
Intenția pointerului 16.3 Atributul . ... . . . . . . . . ... . . . . . . . . . . ... . . . 301
. . . ... . . . . . . . . ... . . . . . . . . . . ... . . . . 301
.. . . . . . . . . ... . . . . . . . . . . ... . . . . 301
16.3.1 Semantică volatilă . . . . . . . ... . . . . . . . . . . ... . . . . 302

16.3.2 Scoping volatil . . . . . . . . . ... . . . . . . . . . . ... . . . . 303 .

16.3.3 Argumente volatile . . . . . . . ... . . . . . . . . . . ... . . . 304 .

16.4 Instrucțiunea de import .. . . . . . . . . ... . . . . . . . . . . ... . . . 304 .


16.5 Module intrinseci 16.6 . ... . . . . . . . . ... . . . . . . . . . . ... . . . 306 .

Accesul la mediul de calcul . ... . . . . . . . . . . ... . . . 307 .


16.6.1 Variabile de mediu . . . . . ... . . . . . . . . . . ... . . . 307 .

16.6.2 Informa ii despre invocarea programului . . . . . . . . ... . . . 308 .

16.7 Suport pentru internaționalizare 16.7.1 . . . . . . . . . . . . . . . . . . ... . . . 308 .


. . de
Seturi de caractere 16.7.2 Setul . . . . . . . . ... . . . . . . . . . . ... . . . 309 .
caractere ASCII 16.7.3 Setul de . . . . . . . ... . . . . . . . . . . ... . . . 309 .
caractere ISO 10646 16.7.4 Fișiere . . . . ... . . . . . . . . . . ... . . . 310 .
. . . pentru
UTF-8 16.7.5 Virgulă zecimală . . . . . . . . ... . . . . . . . . . . ... . . . 310 .

intrare/ieșire . . . . . . . . . . . . . ... . . . 311

16.8 Lungimile numelor și declarațiilor 16.9 . . . . ... . . . . . . . . . . ... . . . . 312

Constante binare, octale și hexazecimale 16.10 Alte ... . . . . . . . . . . ... . . . . 312

modificări ale procedurilor intrinseci . ... . . . . . . . . . . ... . . . . 313

16.11 Preluare mesaj de eroare . . . . . . . . ... . . . . . . . . . . ... . . . . 314 .

16.12 Constante complexe îmbunătățite . . . . . ... . . . . . . . . . . ... . . . 314 .


. . . . . . . ...
16.13 Extensii bloc de interfață 16.14 Entități . . . . . . . . . . ... . . . 314 .

publice de tip privat . . . . . ... . . . . . . . . . . ... . . . 315

17 Îmbunătățiri de intrare/ieșire 17.1 317 .


Introducere 17.2 Intrare/ieșire
. . . .de. . . . . . . . . . . ... . . . . . . . . . . ... . . . 317

tip derivat non-implicit . ... . . . . . . . . . . ... . . . . 317

17.3 Intrare/ieșire asincronă . . . . . . . ... . . . . . . . . . . ... . . . . 320

17.4 Atributul asincron 17.5 Intrarea și . . . . . . . ... . . . . . . . . . . ... . . . . 322 .

ieșirea valorilor excepționale IEEE. . . . . . . . . . . ... . . . 323 .

17.6 Intrare/ieșire acces stream . . . . . . . ... . . . . . . . . . . ... . . . 323 .

17.7 Intrare/ieșire recursive . . . . . . . . . ... . . . . . . . . . . ... . . . 324 .


17.8 Instrucțiunea de flush ... . . . . . . . . ... . . . . . . . . . . ... . . . 324
Machine Translated by Google

Cuprins xvii

17.9 Virgulă după un descriptor de editare P . . . . . . . . . . . . . . . . . . . ... . . 324 .

17.10 Specificatorul iomsg= . . . . . . . . . . . ..... . . . . . . . . ... . 325 .

17.11 Specificatorul rotund= . ... . . . . . . . . ..... . . . . . . . . ... . 325 .

17.12 Semnul= specificatorul . . ... . . . . . . . . ..... . . . . . . . . ... . 325 .

17.13 Parametrii de tip fel ai specificatorilor întregi și logici . . . . . . . ... . 325

17.14 Mai mulți specificatori în instrucțiunile de citire și .... . . . . . . . . ... . . 326 .

scriere 17.15 Funcții intrinseci pentru testarea stării I/O .. . . . . . . . . . . . . . ... . 326 .

17.16 Unele îmbunătățiri ale declarațiilor de întrebare . . ..... . . . . . . . . ... . 326 .


17.17 Îmbunătățiri ale listei de nume . . . . . . . . . . . . . . . . . . . . . . . ... . 327

18 Facilități îmbunătățite ale modulelor 329 .


. . . . . .. . . .
18.1 Introducere 18.2 Submodule . . . . . . . . ..... . . . . . . . . ... . 329
. . . . . ... . . . . . . . . ..... . . . . . . . . ... . . 330

18.2.1 Proceduri separate ale modulelor . . . . ..... . . . . . . . . ... . . 330


18.2.2 Submodule de submodule . . . . ..... . . . . . . . . ... . . 331
18.2.3 Entități submodule 18.2.4 . . . . . . . . . ..... . . . . . . . . ... . . 331 .
Submodule și asociere de utilizare . . ..... . . . . . . . . ... . 332 .

18.3 Avantajele submodulelor . . . . . . . ..... . . . . . . . . ... . 332

19 Coarrays 19.1 333 .


Introducere 19.2 . . . . . . ... . . . . . . . . ..... . . . . . . . . ... . 333 .

Referințarea imaginilor . . ... . . . . . . . . ..... . . . . . . . . ... . 334 .

19.3 Proprietățile coarrays . . . . . . . . . ..... . . . . . . . . ... . 335

19.4 Accesarea coarrays . . ... . . . . . . . . ..... . . . . . . . . ... . . 336

19.5 Instrucțiunea sync all 19.6 ... . . . . . . . . ..... . . . . . . . . ... . . 337 .

Coarrays în proceduri . . . . . . . . . . . ..... . . . . . . . . ... . 338 .

19.7 Coarrays alocabile . ... . . . . . . . . ..... . . . . . . . . ... . 340 .

19.8 Coarrays cu componente alocabile sau pointer . . . . . . . . . . ... . 341 .

19.8.1 Componente de date . . . . . . . . . . ..... . . . . . . . . ... . 341 .

19.8.2 Componentele indicatorului de procedură . . . . . . . . . . . . . . . . ... . 342 .

19.9 Componente Coarray . ... . . . . . . . . ..... . . . . . . . . ... . 342 .

19.10 Referiri la subobiecte polimorfe . . ..... . . . . . . . . ... . 343 .

19.11 Atribute volatile și asincrone 19.12 . . . ..... . . . . . . . . ... . 343 .

Interoperabilitate . . . ... . . . . . . . . ..... . . . . . . . . ... . 343 .

19.13 Sincronizare . . . ... . . . . . . . . ..... . . . . . . . . ... . 343 .

19.13.1 Segmente de execuție . . . . . . . . ..... . . . . . . . . ... . 343 .

19.13.2 Declarația de sincronizare a . . . . ..... . . . . . . . . ... . 344 .


imaginilor 19.13.3 Declarațiile de blocare și . . ..... . . . . . . . . ... . 345
deblocare 19.13.4 Secțiuni critice
.. . . . . . . . . ..... . . . . . . . . ... . . 347

19.13.5 Instrucțiunea memoriei de sincronizare și subrutinele atomice . . . . ... . . 347

19.13.6 Specificatorii stat= și errmsg= în instrucțiunile de sincronizare 19.13.7 . . . 348 .


. . . programului
Instrucțiunile de control al imaginii 19.14 Terminarea ..... .19.15
. . Intrare/ieșire
. . . . . . . .. . 348 .
. ... . . . . . . . . ..... . . . . . . . . ... . 348 .
. . . . ... . . . . . . . . ..... . . . . . . . . ... . 349 .

19.16 Proceduri intrinseci . ... . . . . . . . . ..... . . . . . . . . ... . 351


Machine Translated by Google

xviii Cuprins

19.16.1 Funcții de interogare . . . . . . . . ... . . . . . . . . . . ... . . . . 351 .


19.16.2 Funcții de transformare . . ... . . . . . . . . . . ... . . . 351

20 Alte îmbunătățiri Fortran 2008 353 .

20.1 Comodități sintactice banale . . . . . ... . . . . . . . . . . ... . . . 353 .

20.1.1 Matrice cu formă implicită . . . . . . ... . . . . . . . . . . ... . . . 353 .

20.1.2 Bucle-do implicite în instrucțiunile de date . . . . . . . . . . . ... . . . 353 .

20.1.3 Proceduri legate de tip . . . . . ... . . . . . . . . . . ... . . . 354 .


20.1.4 Constructori de structură . . . . . ... . . . . . . . . . . ... . . . 354 .
20.1.5 Punct virgulă . . . . . . . . . . . ... . . . . . . . . . . ... . . . 355 .

20.1.6 Instrucțiunea stop 20.1.7 . . . . . . . ... . . . . . . . . . . ... . . . 355 .

Ieșire din aproape orice construcție 20.2 . ... . . . . . . . . . . ... . . . 355 .

Modificări de limitare . . . . . . . . . . . ... . . . . . . . . . . ... . . . 356 .

20.2.1 Suport pentru numere întregi pe 64 de biți . . . . . . ... . . . . . . . . . . ... . . . 356 .

20.2.2 Rangul maxim al matricei . . . . . . ... . . . . . . . . . . ... . . . 356 .

20.3 Expresivitatea datelor . . . . . . . . . . . ... . . . . . . . . . . ... . . . 356 .

20.3.1 Componente alocabile de tip recursiv . . . . . . . . ... . . . 356 .

20.3.2 Asocierea inițială a pointerului . . . . ... . . . . . . . . . . ... . . . 358 .


20.4 Caracteristici orientate spre performanță . . . . . ... . . . . . . . . . . ... . . . 359 .
20.4.1 Construcția do concurrent 20.4.2 . . ... . . . . . . . . . . ... . . . 359 .

Atributul contiguu 20.4.3 Designatori de . . . . . . . . . . . . . . . . . ... . . . 361 .

matrice pur și simplu contigui . . . . . . . . . . . ... . . . 364 .

20.5 Expresivitatea computațională . . . . . ... . . . . . . . . . . ... . . . 365 .

20.5.1 Accesarea unor părți ale variabilelor complexe . . . . . . . . . . ... . . . 365 .

20.5.2 Funcții pointer care denotă variabile . . . . . . . . . . . ... . . . 366 .


20.5.3 Construcția bloc 20.5.4 . . . . . . . ... . . . . . . . . . . ... . . . 366 .

Proceduri elementare impure . . ... . . . . . . . . . . ... . . . 368

20.5.5 Procedurile interne ca argumente reale . . . . . . . . . ... . . . . 370

20.5.6 Specificarea tipului unei variabile de index forall . . . . . . ... . . . . 370


20.5.7 Rezoluție generică . . . . . . . ... . . . . . . . . . . ... . . . . 371

20.6 Utilizarea și calculul datelor . . . . . . ... . . . . . . . . . . ... . . . . 372


20.6.1 Îmbunătățiri ale instrucțiunii de alocare 20.6.2 . . . . . . . . . ... . . . . 372 .
. . . . ale
Realocare automată 20.6.3 Restricții elementare . ... . . . . . . . . . . ... . . . 373 .

subprogramelor 20.7 Intrare/ieșire . .. . . . . . . . . . . ... . . . 373 .


. . . ... . . . . . . . . ... . . . . . . . . . . ... . . . 374 .

20.7.1 Intrare/ieșire recursive . . . . . ... . . . . . . . . . . ... . . . 374 .

20.7.2 Specificatorul newunit= . . . . . ... . . . . . . . . . . ... . . . 374 .

20.7.3 Scrierea valorilor separate prin virgulă . . . . . . . . . . . . . ... . . . 375 .

20.8 Proceduri intrinseci . . . . . . . . . . . ... . . . . . . . . . . ... . . . 376 .


20.9 Funcții matematice intrinseci 20.9.1 . . . . ... . . . . . . . . . . ... . . . 376 .

Modificări ale funcțiilor trigonometrice 20.9.2 Noi . . . . . . . . . . . ... . . . 376 .

funcții trigonometice hiperbolice 20.9.3 Noi funcții . . . . . . . . . ... . . . 376 .

matematice speciale 20.9.4 Norme euclidiene 20.10 . . . . . . . . . . . ... . . . 377 .


Manipularea biților . . . . . . . . . ... . . . . . . . . . . ... . . . 378 .
... . . . . . . . . ... . . . . . . . . . . ... . . . 378
Machine Translated by Google

Cuprins xix

20.10.1 Comparație biți (fără semnă) . . . . . . . . . . . . . ... . . . . 378 .

20.10.2 Deplasare dublă lățime . . . . . ..... . . . . . . . . ... . . . 379 .


20.10.3 Reduceri pe biți 20.10.4 . . . . . . . . . . . . . . . . . . . . ... . . . 379

Numărarea biților 20.10.5 . . . . . . . . . . ..... . . . . . . . . ... . . . . 380

Producerea măștilor de biți . . . . . . ..... . . . . . . . . ... . . . . 380


.. . . . . . . . . .....
20.10.6 Îmbinarea biților 20.10.7 . . . . . . . . ... . . . . 381 .

Operații suplimentare de schimbare . . . ..... . . . . . . . . ... . . . 381 .

20.11 Diverse proceduri intrinseci . . ..... . . . . . . . . ... . . . 382 .

20.11.1 Proceduri care suportă coarrays . . . . . . . . . . . . . ... . . . 382 .

20.11.2 Executarea unui alt program . . ..... . . . . . . . . ... . . . 382 .

20.11.3 Compararea caracterelor . . . . . ..... . . . . . . . . ... . . . 383 .

20.11.4 Căutare matrice . . . . . . . . ..... . . . . . . . . ... . . . 383 .

20.11.5 Paritate logică . . . . . . . . . ..... . . . . . . . . ... . . . 383 .

20.11.6 Suport aritmetic zecimal . . ..... . . . . . . . . ... . . . 384 .

20.11.7 Dimensiunea unui obiect din memorie . . . . . . . . . . . . . . . ... . . . 384 .


20.12 Adăugiri la modulul iso_fortran_env .... . . . . . . . . ... . . . 385 .

20.12.1 Informații de compilare 20.12.2 . . . . ..... . . . . . . . . ... . . . 385 .


Nume pentru tipuri comune 20.12.3 . . . ..... . . . . . . . . ... . . . 385 .

Matrice de tip . . . . . . . . . . . ..... . . . . . . . . ... . . . 386 .

20.12.4 Facilități de sprijin Coarray . . . ..... . . . . . . . . ... . . . 386

20.13 Modificări la alte module intrinseci standard .. . . . . . . . . ... . . . . 387

20.13.1 Modulul iso_c_binding 20.13.2 . . . ..... . . . . . . . . ... . . . . 387 .


Modulul ieee_arithmetic . . ..... . . . . . . . . ... . . . 387 .

20.14 Programe și proceduri . . . . . . . ..... . . . . . . . . ... . . . 388 .


20.14.1 Entități de modul salvate . . . . . ..... . . . . . . . . ... . . . 388 .

20.14.2 Direcționare automată cu pointer . . ..... . . . . . . . . ... . . . 388 .

20.14.3 Indicarea argumentelor absente . . ..... . . . . . . . . ... . . . 389

A Proceduri intrinseci 393

B Caracteristici depreciate B.1 399 .


Introducere B.2 Asocierea. . . . ... . . . . . . . . ..... . . . . . . . . ... . . . 399 .

de stocare B.2.1 Unități de ... . . . . . . . . ..... . . . . . . . . ... . . . 399 .

stocare B.2.2 Declarația .. . . . . . . . . ..... . . . . . . . . ... . . . 399 .

de echivalență B.2.3 Blocul comun B.2.4 . . . ..... . . . . . . . . ... . . . 400 .


. . . . . . . .....
Unitatea de program de date bloc B.2.5 . . . . . . . . ... . . . 402 .

Coarrays și asociere de stocare . . ..... . . . . . . . . ... . . . 404 .


..... . . . . . . . . ... . . . 405 .

B.3 Dezacord între formă și lungimea caracterului . . . . . . . . . . . . . ... . . . 405


B.4 Linia include . . ... . . . . . . . . ..... . . . . . . . . ... . . . . 407

B.5 Alte forme de control al buclei . . . . . . . ..... . . . . . . . . ... . . . . 407 .


B.5.1 Construcția do etichetată B.5.2 . . . . ..... . . . . . . . . ... . . . 407 .
Construcția do while . . . . . . . . . . ..... . . . . . . . . ... . . . 408 .

B.6 Precizie dublă reală B.7 .. . . . . . . . . ..... . . . . . . . . ... . . . 408 .

Declarațiile de dimensiune, codimensiune și parametri . . . . . ... . . . 409


Machine Translated by Google

xx Fortran modern explicat

B.8 Denumiri specifice ale procedurilor intrinseci . ... . . . . . . . . . . ... . . . . 410


B.9 Mapare non-implicit pentru tastarea implicită . . . . . . . . . . . . . ... . . . . 412 .
B.10 Caracteristici depreciate Fortran 2008 . . . . ... . . . . . . . . . . ... . . . 413 .
B.10.1 Instrucțiunea memoriei de sincronizare și subrutinele atomice . . ... . . . 413 .
B.10.2 Componente de tip c_ptr sau c_funptr . . . . . . . . ... . . . 416 .
B.10.3 Declarații de tip B.10.4 . . . . . . . . . . . . . . . . . . . . . ... . . . 416
Redundant conține instrucțiunea B.10.5 . ... . . . . . . . . . . ... . . . . 417
. .la .atan2
Instrucțiunea finală B.10.6 Referirea . . . . ... . . . . . . . . . . ... . . . . 417 .
cu numele atan. . . . . . . . . . ... . . . 418

C Caracteristici învechite 419 .


C.1 Învechit în Fortran 95 . . . . . . . . ... . . . . . . . . . . ... . . . 419
C.1.1 Formă sursă fixă C.1.2 . . . . . . . ... . . . . . . . . . . ... . . . . 419
. . . . . . . . . ...
Calculat merge la C.1.3 Caracter . . . . . . . . . . ... . . . . 420 .
de specificare a lungimii caracterului* C.1.4 Instrucțiuni . . . . . . . . ... . . . 420 .
. . . . . . . . . . . ...
de date între executabile C.1.5 Funcții de instrucțiuni C.1.6 . . . 420
Lungimea caracterului presupus a.rezultatelor
. . . . . funcției
. ... . . . . . . . . . . ... . . . . 421
C.1.7 Aritmetică if C.1.8 Terminare do-loop partajată C.1.9 . . . . . . ... . . . . 422
. . .în.Fortran
Retur alternativ C.2 Caracteristică învechită . . . . 2008:
. . . . . . . . . . ... . . . . 422
. . în
Declarație de intrare C.3 Caracteristică ștearsă . Fortran
... . 2003:
. . . . . . . . . ... . . . . 423 .
. . . . . șterse
Controlul transportului C.4 Caracteristici . . . în
. .Fortran
.. . 95
. . . . . . . . . ... . . . 423
. . . . . . ... . . . . 424
. . . . . . . . ... . . . . 426
. . . . ... . . . . . . . . . . ... . . . . 427

D Evitarea cascadelor de compilare 429

E Exemplu de listă orientată pe obiecte 433

F Termenii Fortran 441

G Soluții la exerciții 453

Index 475
Machine Translated by Google

1. De unde Fortran?

1.1 Introducere

Această carte se referă la limbajele de programare Fortran 95, Fortan 2003 și Fortran 2008, prezentând
o descriere rezonabil de concisă a fiecăruia. Forma aleasă pentru prezentarea sa este cea a unui manual
destinat utilizării în predarea sau învățarea limbii.
Descrierea Fortran 95 ocupă capitolele 2 până la 10 și apendicele B și C. Includem extensiile de
matrice alocabile care au fost publicate inițial ca Raport tehnic ISO, deoarece au fost implementate în
compilatoarele Fortran 95 de mulți ani și fac acum parte din Fortran. 2003. Aceste capitole sunt scrise în
așa fel încât programele simple să poată fi deja codificate după ce au fost citite primele trei (pe elemente
de limbaj, expresii și atribuții și control). Pot fi scrise succesiv programe mai complexe pe măsură ce
informațiile din fiecare capitol următor sunt absorbite. Capitolul 5 descrie conceptul important al
modulului și numeroasele aspecte ale procedurilor, Capitolul 6 completează descrierea caracteristicilor
puternice ale matricei, Capitolul 7 ia în considerare detaliile privind specificarea obiectelor de date și a
tipurilor derivate, iar Capitolul 8 detaliază procedurile intrinseci. Capitolele 9 și 10 acoperă toate
caracteristicile de intrare/ieșire într-o manieră astfel încât cititorul să poată aborda și această zonă mai
dificilă caracteristică după caracteristică, dar întotdeauna cu un subset util deja acoperit. În Anexele B și
C, descriem caracteristicile care sunt redundante în limbaj.

Cele din apendicele B fac încă parte integral din standard, dar utilizarea lor este depreciată de noi, în
timp ce cele din apendicele C sunt desemnate ca învechite de standard.
Capitolul 11 descrie o extensie oficială a Fortran 95.
Fortran 2003 conține tot Fortran 95, inclusiv extensiile capitolului 11, iar capitolele 12 până la 18
descriu caracteristicile sale suplimentare. Capitolul 12 tratează interoperabilitatea cu limbajul de
programare C, Capitolul 13 cu tipurile de date parametrizate și pointerii de procedură, Capitolul 14 cu
programarea orientată pe obiecte și Capitolul 15 cu stabilirea și manipularea datelor. Capitolul 16
acoperă unele îmbunătățiri diverse, în timp ce capitolul 17 se ocupă de îmbunătățiri în zona de intrare/
ieșire și Capitolul 18 cu submodule, în mod oficial o extensie.

Fortran 2008, la rândul său, conține întregul Fortran 2003, inclusiv extensiile capitolului 18, cu o
adăugare, coarrays, care este importantă pentru procesarea paralelă, precum și o serie de îmbunătățiri
mai mici. Acestea sunt descrise în capitolele 19 și, respectiv, 20.
Acest capitol introductiv are sarcina de a pune scena pentru cei care urmează. Secțiunea 1.2 prezintă
istoria timpurie a Fortranului, începând cu introducerea sa în urmă cu peste cincizeci de ani. Secțiunea
1.3 continuă cu dezvoltarea standardului Fortran 90, rezumă importantul acestuia
Machine Translated by Google

2 Fortran modern explicat

caracteristici noi și prezintă modul în care sunt dezvoltate standardele; Secțiunea 1.4 analizează mecanismul care
a fost adoptat pentru a permite limbajului să evolueze. Secțiunile 1.5 până la 1.8 iau în considerare dezvoltarea
Fortran 95 și extensiile sale, apoi a Fortran 2003 și Fortran 2008. Secțiunea finală ia în considerare cerințele privind
programele și procesoarele pentru conformitatea cu standardul.

1.2 Istoria timpurie a lui Fortran

Programarea în primele zile ale calculului a fost plictisitoare la extrem. Programatorii aveau nevoie de cunoștințe
detaliate despre instrucțiunile, registrele și alte aspecte ale unității centrale de procesare (CPU) a computerului
pentru care scriau cod. Codul sursă în sine a fost scris într-o notație numerică, așa-numitul cod octal. De-a lungul
timpului au fost introduse codurile mnemonice, o formă de codificare cunoscută sub numele de cod de mașină
sau de asamblare.
Aceste coduri au fost traduse în cuvinte de instrucțiuni prin programe cunoscute sub numele de asamblatori.
În anii 1950 a devenit din ce în ce mai evident că această formă de programare era foarte incomod, deși permitea
folosirea procesorului într-un mod foarte eficient.
Aceste dificultăți au determinat o echipă condusă de John Backus de la IBM să dezvolte unul dintre cele mai
vechi limbaje de nivel înalt, Fortran. Scopul lor a fost să producă un limbaj care să fie simplu de înțeles, dar
aproape la fel de eficient în execuție ca limbajul de asamblare. În asta au reușit dincolo de visele lor cele mai
sălbatice. Limbajul a fost într-adevăr simplu de învățat, deoarece a fost posibil să se scrie formule matematice
aproape așa cum sunt scrise de obicei în textele matematice. (De fapt, numele Fortran este o contracție a
traducerii formulei.) Acest lucru a permis ca programele de lucru să fie scrise mai rapid decât înainte, pentru doar
o mică pierdere de eficiență, deoarece s-a acordat multă atenție generării de cod obiect rapid.

Dar Fortran a fost atât revoluționar, cât și inovator. Programatorii au fost scutiți de povara obositoare a utilizării
limbajului de asamblare și s-au putut concentra mai mult asupra problemei în cauză. Poate mai important, însă, a
fost faptul că computerele au devenit accesibile oricărui om de știință sau inginer dornic să dedice puțin efort
pentru a dobândi cunoștințe de lucru despre Fortran; nu mai era necesar să fii expert în calculatoare pentru a
putea scrie programe de aplicație.

Fortran s-a răspândit rapid, deoarece a îndeplinit o nevoie reală. Inevitabil, s-au dezvoltat dialectele limbajului,
ceea ce a dus la probleme în schimbul de programe între computere și astfel, în 1966, Asociația Americană de
Standarde (mai târziu Institutul Național American de Standarde, ANSI) a scos la iveală primul standard pentru un
limbaj de programare, acum cunoscut sub numele de Fortran 66.

Fortran a adus cu el alte câteva progrese. A fost, de exemplu, un limbaj care a rămas aproape și a exploatat
hardware-ul disponibil, mai degrabă decât un concept abstract.
De asemenea, a adus cu sine și posibilitatea programatorilor de a controla alocarea spațiului de stocare într-un
mod simplu, o caracteristică care era foarte necesară în acele timpuri de început ale amintirilor mici.
Proliferarea dialectelor a rămas o problemă după publicarea standardului din 1966.
A existat o implementare pe scară largă în compilatoare a caracteristicilor care erau esențiale pentru programele
la scară largă, dar care au fost ignorate de standard. Diferiți compilatori au implementat astfel de facilități în
moduri diferite. Aceste dificultăți au fost parțial rezolvate de
Machine Translated by Google

De unde Fortran? 3

publicarea unui nou standard, în 1978, cunoscut sub numele de Fortran 77, care includea câteva
caracteristici noi care se bazau pe extensii de furnizor sau pre-procesoare.

1.3 Unitatea pentru standardul Fortran 90

După treizeci de ani de existență, Fortran era departe de a fi singurul limbaj de programare disponibil pe
majoritatea computerelor, dar superioritatea lui Fortran fusese întotdeauna în domeniul aplicațiilor
numerice, științifice, ingineriei și tehnice și așa, pentru ca acesta să fie dezvoltat corespunzător. până în
prezent, comitetul tehnic acreditat ANSI X3J3 (cunoscut ulterior ca J3 și acum oficial ca PL22.3), care
lucrează ca organism de dezvoltare pentru comitetul ISO ISO/IEC JTC1/SC22/WG5, a pregătit din nou un
nou standard, anterior cunoscut ca Fortran 8x și acum ca Fortran 90. Vom folosi abrevierile J3 și WG5
pentru aceste două comitete.

J3 în sine este un organism compus din reprezentanți ai vânzătorilor de hardware și software,


utilizatori și mediul academic. Acum este acreditat la NCITS (National Council for Information Technology
Standards). J3 acționează ca organism de dezvoltare pentru grupul internațional corespunzător, WG5,
format din experți internaționali responsabili cu recomandarea ca un proiect de standard să devină un
standard internațional. J3 menține alte contacte strânse cu comunitatea internațională, primind membri
străini, inclusiv autorii prezenți de-a lungul mai multor ani.

Care au fost justificările pentru a continua revizuirea definiției limbajului Fortran?


Pe lângă standardizarea extensiilor furnizorilor, a existat necesitatea modernizării acestuia ca răspuns la
evoluțiile în proiectarea limbajului care au fost exploatate în alte limbi, cum ar fi APL, Algol 68, Pascal,
Ada, C și C++. Aici, J3 s-ar putea baza pe beneficiile evidente ale unor concepte precum ascunderea
datelor. În aceeași ordine de idei a fost necesitatea de a începe să ofere o alternativă la asocierea
periculoasă de stocare, de a elimina rigiditatea formei sursă demodate și de a îmbunătăți în continuare
regularitatea limbajului, precum și de a crește siguranța programării în limbajului și pentru a înăspri
cerințele de conformitate. Pentru a păstra investiția vastă în codurile Fortran 77, întregul Fortran 77 a
fost păstrat ca subset.
Cu toate acestea, spre deosebire de standardul anterior, care a rezultat aproape în întregime dintr-un
efort de a standardiza practicile existente, standardul Fortran 90 a fost mult mai mult o dezvoltare a
limbajului, introducând caracteristici care erau noi pentru Fortran, dar se bazau pe experiența în alte
limbi.
Principalele caracteristici ale Fortran 90 au fost, în primul rând, limbajul matricei și tipurile de date
abstracte. Primul este construit pe operațiuni și atribuiri întregi matrice, secțiuni de matrice, proceduri
intrinseci pentru matrice și stocare dinamică. A fost conceput pentru optimizare. Acesta din urmă este
construit pe module și proceduri de module, tipuri de date derivate, supraîncărcare operator și interfețe
generice, împreună cu pointeri. De asemenea, importante au fost noile facilități pentru calcul numeric,
inclusiv un set de funcții de interogare numerică, parametrizarea tipurilor intrinseci, noi constructe de
control – select case și noi forme de do, proceduri interne și recursive, argumente opționale și de cuvinte
cheie, I/ îmbunătățit. O facilitati si multe proceduri intrinseci noi. Nu în ultimul rând au fost noua formă
de sursă gratuită, un stil îmbunătățit de specificații orientate pe atribute, declarația implicită none și un
mecanism de identificare a caracteristicilor redundante pentru eliminarea ulterioară din
Machine Translated by Google

4 Fortran modern explicat

limba. Cerința compilatorilor de a putea identifica, de exemplu, extensiile de sintaxă și de a raporta motivul
pentru care un program a fost respins, este de asemenea semnificativă. Limbajul rezultat nu a fost doar un
instrument mult mai puternic decât predecesorul său, ci și unul mai sigur și mai fiabil. Asocierea de
depozitare, cu pericolele asociate, nu a fost desființată, ci a devenit inutilă. Într-adevăr, experiența a arătat
că compilatorii au detectat erori mult mai frecvent decât înainte, rezultând un ciclu de dezvoltare mai rapid.
Sintaxa matricei și recursiunea au permis, de asemenea, scrierea unui cod destul de compact, un ajutor
suplimentar pentru programarea sigură.

1.4 Evoluția limbajului


Procedurile în care funcționează J3 necesită o perioadă de notificare înainte ca orice caracteristică existentă
să fie eliminată din limbă. Aceasta înseamnă, în practică, un minim de un ciclu de revizuire, care pentru
Fortran este de cel puțin cinci ani. Necesitatea de a elimina caracteristici este evidentă: dacă singura acțiune
a comitetului este de a adăuga noi caracteristici, limbajul va deveni grotesc de mare, cu multe elemente
suprapuse și redundante. Soluția adoptată în final de J3 a fost publicarea ca anexă la un standard a unui
set de două liste care să arate care articole au fost eliminate sau care sunt candidate pentru o eventuală
eliminare.
O listă conține caracteristicile șterse, cele care au fost eliminate. Deoarece Fortran 90 conținea întregul
Fortran 77, această listă a fost goală pentru Fortran 90, dar nu a fost pentru Fortran 95.

A doua listă conține caracteristicile învechite, cele considerate a fi depășite și redundante și care sunt
candidate pentru ștergere în următoarea revizuire. Caracteristicile învechite Fortran 95 sunt descrise în
Anexa C.
Pentru Fortran 2003, nu au existat caracteristici noi învechite și nici una dintre caracteristicile învechite
Fortran 95 nu a fost ștearsă. În Fortran 2008, declarația de intrare a fost făcută caducă.

Caracteristicile învechite care au fost șterse din Fortran 95 sunt încă acceptate de majoritatea
compilatorilor, din cauza cererii de programe vechi încercate și testate pentru a continua să funcționeze.
Astfel, conceptul de uzură nu funcționează într-adevăr așa cum s-a intenționat, dar cel puțin dă un semnal
clar că anumite caracteristici sunt depășite și ar trebui evitate în programele noi și nu trebuie predate noilor
programatori.

1.5 Fortran 95

După publicarea standardului Fortran 90 în 1991, au mai avut loc două dezvoltări semnificative în ceea ce
privește limbajul Fortran. Prima a fost funcționarea continuă a celor două comitete de standarde Fortran,
J3 și WG5, iar a doua a fost înființarea Forumului Fortran de înaltă performanță (HPFF).

La începutul deliberărilor lor, comitetele de standarde au decis asupra unei strategii prin care o revizuire
minoră a Fortran 90 să fie pregătită până la mijlocul anilor 1990 și o revizuire majoră până în jurul anului
2000. Prima revizuire, Fortran 95, este subiectul prima parte a acestei cărți.
HPFF a fost înființat într-un efort de a defini un set de extensii pentru Fortran, astfel încât să fie posibil
să se scrie cod portabil atunci când se utilizează computere paralele pentru gestionarea problemelor care
implică seturi mari de date care pot fi reprezentate prin grile obișnuite. Această versiune de Fortran
Machine Translated by Google

De unde Fortran? 5

urma să fie cunoscut ca Fortran de înaltă performanță (HPF) și s-a decis rapid, având în vedere
caracteristicile matrice ale Fortran 90, că acesta, și nu Fortran 77, ar trebui să fie limbajul său de bază.
Forma finală a HPF1 a fost a unui superset de Fortran 90, principalele extensii fiind sub formă de directive
care iau forma liniilor de comentarii Fortran 90, și sunt astfel recunoscute ca directive doar de un
procesor HPF. Cu toate acestea, a devenit necesar să se adauge și o sintaxă suplimentară, deoarece nu
toate caracteristicile dorite puteau fi găzduite sub forma unor astfel de directive.

Activitatea J3 și WG5 a continuat în același timp cu cea a HPFF, iar organismele au colaborat strâns.
Era evident că, pentru a evita dezvoltarea dialectelor divergente din Fortran, ar fi de dorit să se includă
noua sintaxă definită de HPFF în Fortran 95 și, într-adevăr, caracteristicile HPF sunt cele mai semnificative
caracteristici noi. Dincolo de aceasta, au fost făcute un număr mic de alte modificări urgente, dar minore,
în limbaj, în principal bazate pe experiența cu utilizarea Fortran 90.

Fortran 95 a fost compatibil invers cu Fortran 90, în afară de o modificare minoră în definiția semnului
(Secțiunea 8.3.2) și ștergerea unor caracteristici Fortran 77 declarate învechite în Fortran 90. Cu toate
acestea, au existat două proceduri intrinseci noi, nule și cpu_time, care ar putea fi, de asemenea, nume
de proceduri externe dintr-un program Fortran 90 existent.

Detaliile Fortran 95 au fost finalizate în 1995, iar noul standard ISO a fost înlocuit
Fortran 90, a fost adoptat în 1997, în urma voturilor de succes, ca ISO/IEC 1539-1: 1997.

1.6 Extensii la Fortran 95

La scurt timp după publicarea Fortran 90, a fost dezvoltat un standard auxiliar pentru corzi de lungime
variabilă. O minoritate a considerat că acest lucru ar fi trebuit să facă parte din Fortran 90, dar au fost
mulțumiți de această alternativă. Standardul auxiliar definește interfața și semantica pentru un modul
care oferă facilități pentru manipularea șirurilor de caractere de lungime arbitrară și variabilă dinamic. A
fost revizuit pentru Fortran 95 ca ISO/IEC 1539-2: 2000(E). O anexă face referire la o posibilă implementare2
în Fortran 95, ceea ce a demonstrat fezabilitatea acesteia. Intenția a fost ca vânzătorii să ofere
caracteristici echivalente care să se execute mai eficient, dar, de fapt, acest lucru nu s-a întâmplat
niciodată.
Mai mult, în 1995, WG5 a decis că aceste trei caracteristici:

i) gestionarea excepțiilor în virgulă mobilă;

ii) permiterea matricelor alocabile ca componente de structură, argumente fictive și funcție


rezultate; și

iii) interoperabilitate cu C,

au fost atât de urgent necesare în Fortran încât a înființat organisme de dezvoltare pentru a dezvolta
„Rapoarte tehnice de tip 2”. Intenția a fost ca materialul acestor rapoarte tehnice să fie integrat în
următoarea revizuire a standardului Fortran, în afară de eventualele defecte constatate în teren. A fost
în esență o facilitate de testare beta pentru o caracteristică de limbă. În caz, primele două

1Manualul Fortran de înaltă performanță, C. Koebel și colab., MIT Press, Cambridge, MA,
1994. 2ftp://ftp.nag.co.uk/sc22wg5/ISO_VARYING_STRING/
Machine Translated by Google

6 Fortran modern explicat

au fost finalizate, iar primul este subiectul capitolului 11. Detaliile celui de-al doilea au fost încorporate în capitolele
anterioare, deoarece a fost implementat pe scară largă în compilatoarele Fortran 95.
Au fost întâmpinate dificultăți cu al treilea, astfel încât mecanismul de raportare a fost abandonat pentru
interoperabilitatea cu C, dar a fost ulterior inclus în Fortran 2003 (vezi capitolul 13).
Un alt standard auxiliar, ISO/IEC 1539-3: 1999(E), a fost dezvoltat pentru a satisface nevoia programatorilor de
a menține mai multe versiuni de cod pentru a permite diferite sisteme și aplicații diferite. Păstrarea mai multor
copii ale codului sursă este predispusă la erori. Este mult mai bine să mențineți un cod principal din care poate fi
selectată oricare dintre versiuni. Acest standard este pentru o formă foarte simplă de compilare condiționată, care
selectează unele dintre liniile Fortran din sursă și omite restul sau le convertește în comentarii. Procesul este
controlat de „linii coco” din sursă, care sunt, de asemenea, omise sau convertite în comentarii. Acest standard
auxiliar a avut un succes redus.

1.7 Fortran 2003

Următoarea revizuire completă a limbii a fost publicată în noiembrie 2004 și este cunoscută sub numele de
Fortran 2003, deoarece detaliile au fost finalizate în 2003. Este subiectul părții de mijloc a acestei cărți.
Spre deosebire de Fortran 95, a fost o revizuire majoră, principalele sale caracteristici noi fiind:

• Îmbunătățiri ale tipurilor derivate: tipuri derivate parametrizate, control îmbunătățit al


accesibilitate, constructori de structuri îmbunătățiți și finalizatori.

• Suport de programare orientată pe obiecte: extensie de tip și moștenire, polimorfism,


alocare dinamică de tip și proceduri legate de tip.

• Îmbunătățiri în manipularea datelor: componente alocabile, parametri de tip amânat, atribut volatil,
specificații explicite de tip în constructori de matrice și instrucțiuni de alocare, îmbunătățiri ale pointerului,
expresii de inițializare extinse (denumite acum expresii constante) și proceduri intrinseci îmbunătățite.

• Îmbunătățiri de intrare/ieșire: transfer asincron, acces la flux, operațiuni de transfer specificate de utilizator
pentru tipurile derivate, control specificat de utilizator al rotunjirii în timpul conversiilor de format,
constante denumite pentru unitățile preconectate, instrucțiunea de curățare, regularizarea cuvintelor
cheie și accesul la mesaje de eroare .

• Indicatori de procedură.

• Suport pentru excepții IEC 60559 (IEEE 754).

• Interoperabilitate cu limbajul de programare C.

• Suport pentru utilizare internațională: acces la caractere de 4 octeți ISO 10646 și alegere
virgulă sau virgulă în I/O formatat numeric.

• Integrare îmbunătățită cu sistemul de operare gazdă: acces la argumentele liniei de comandă, variabilele
de mediu și mesajele de eroare ale procesorului.

Fortran 2003 a fost lent să fie implementat complet în compilatoare. Standardul a fost completat de un alt Raport
tehnic, publicat în februarie 2005, care definește modul în care utilizarea modulelor poate fi îmbunătățită prin
utilizarea submodulelor (vezi Capitolul 12).
Machine Translated by Google

De unde Fortran? 7

1.8 Fortran 2008

În ciuda faptului că compilatoarele compatibile cu Fortran 2003 au întârziat să apară, comitetele de


standardizare au considerat de cuviință să se apuce de un alt standard, Fortran 2008. Cea mai importantă
caracteristică nouă este destinată procesării paralele – adăugarea de manipulare coaarray. facilită i.
Mai mult, sunt introduse forma concomitentă de control al buclei și atributul contiguu, iar alte
caracteristici noi includ: extensia submodulului la Fortran 2003, diverse îmbunătățiri ale datelor, acces
îmbunătățit la obiectele de date, îmbunătățiri la I/O și controlul execuției și proceduri mai intrinseci, în
special pentru procesarea biților. Fortran 2008 a fost publicat în octombrie 2010.

1.9 Conformitate

Standardele se ocupă aproape exclusiv de regulile pentru programe, mai degrabă decât pentru
procesoare. Un procesor este obligat să accepte un program conform standardului și să-l interpreteze
conform standardului, sub rezerva limitelor pe care procesorul le poate impune asupra dimensiunii și
complexității programului. Procesorului i se permite să accepte o sintaxă suplimentară și să interpreteze
relații care nu sunt specificate în standard, cu condiția ca acestea să nu intre în conflict cu standardul. În
multe locuri în această carte spunem „... nu este permis”. Prin aceasta ne referim la faptul că nu este
permis într-un program care respectă standardele. O implementare o poate permite totuși ca o extensie.
Desigur, programatorul trebuie să evite astfel de extensii de sintaxă dacă se dorește portabilitatea.

Interpretarea unora dintre sintaxa standard este dependentă de procesor, adică poate varia de la
procesor la procesor. De exemplu, setul de caractere permis în șirurile de caractere este dependent de
procesor. Trebuie avut grijă ori de câte ori este utilizată o caracteristică dependentă de procesor, în cazul
în care aceasta duce la neportabilitatea programului la un procesor dorit.
Un dezavantaj al standardului Fortran 77 a fost că nu a făcut nicio declarație cu privire la solicitarea
procesoarelor să furnizeze un mijloc de a detecta orice abatere de la sintaxa permisă de către un
program, atâta timp cât acea abatere nu intra în conflict cu regulile de sintaxă definite de standard. Noile
standarde sunt scrise într-un stil diferit de cel vechi. Regulile de sintaxă sunt exprimate într-o formă de
BNF cu constrângeri asociate, iar semantica este descrisă de text.
Acest stil semi-formal nu este folosit în această carte, așa că un exemplu, din Fortran 95, este poate util:

R609 subșir este șirul-părinte (interval-subșir)


R610 șir părinte este nume-variabilă-scalar

sau element-matrice
sau componentă-structură-scalară
sau scalar-constant

Intervalul subșirurilor R611 este [scalar-int-expr] : [scalar-int-expr]


Constrângere: șirul părinte trebuie să fie de tip caracter.

Valoarea primului scalar-int-expr din substring-range se numește punctul de pornire, iar


valoarea celui de-al doilea se numește punctul final. Lungimea a
Machine Translated by Google

8 Fortran modern explicat

subșir este numărul de caractere din subșir și este MAX( f +1,0), unde f și sunt punctele de
început și, respectiv, de sfârșit.

Aici sunt definite cele trei reguli de producție și constrângerea asociată pentru un subșir de caractere și este
explicată semnificația lungimii unui astfel de subșir.
Standardele sunt scrise în așa fel încât un procesor, la compilare, poate verifica dacă programul îndeplinește
toate constrângerile. În special, procesorul trebuie să ofere capacitatea de a detecta și raporta utilizarea oricăror

• caracteristică învechită;

• sintaxă suplimentară;

• parametru tip tip (Secțiunea 2.5) pe care nu îl suportă;

• formă sau caracter sursă nestandard;

• nume care nu este în concordanță cu regulile de acoperire; sau

• procedură intrinsecă non-standard.

În plus, trebuie să poată raporta motivul respingerii unui program. Aceste capabilități sunt de mare valoare pentru
a produce cod corect și portabil.
Machine Translated by Google

2. Elemente de limbaj

2.1 Introducere

Proza scrisă într-o limbă naturală, cum ar fi un text englezesc, este compusă în primul rând din elemente de
bază – literele alfabetului. Acestea sunt combinate în entități mai mari, cuvinte, care transmit conceptele de
bază ale obiectelor, acțiunilor și calificărilor. Cuvintele limbii pot fi combinate în continuare în unități, fraze și
propoziții mai mari, conform anumitor reguli. Un set de reguli definește gramatica. Aceasta ne spune dacă o
anumită combinație de cuvinte este corectă în sensul că se conformează cu sintaxa limbii, adică acele forme
recunoscute care sunt considerate redări corecte ale semnificațiilor pe care dorim să le exprimăm. La rândul
lor, propozițiile pot fi unite în paragrafe, care conțin în mod convențional sensul compus al propozițiilor lor
constitutive, fiecare paragraf exprimând o unitate de informație mai mare. Într-un roman, secvențele de
paragrafe devin capitole, iar capitolele împreună formează o carte, care de obicei este o lucrare autonomă, în
mare măsură independentă de toate celelalte cărți.

2.2 Setul de caractere Fortran

Analogii cu aceste concepte se găsesc într-un limbaj de programare. În Fortran 95, elementele de bază, sau
setul de caractere, sunt cele 26 de litere ale alfabetului englez, cele 10 cifre arabe, de la 0 la 9, liniuța de
subliniere, _ și așa-numitele caractere speciale
litere mici, enumerate
dar aproape în computerele
toate Tabelul 2.1. Fortran 95 nu
le acceptă necesită suport
în prezent.1 pentru
În sintaxa
Fortran, literele mici sunt echivalente cu literele mari corespunzătoare; ele se disting doar atunci când fac
parte din secvențe de caractere.

În această carte, caracterele semnificative din punct de vedere sintactic vor fi întotdeauna scrise cu litere mici.
Literele, cifrele și caracterele de subliniere sunt cunoscute ca caractere alfanumerice.
Cu excepția simbolului monedei, a cărui grafică poate varia (de exemplu, să fie £ în Regatul Unit), graficele
sunt fixe, deși stilurile lor nu sunt fixe. Caracterele speciale $ și ? nu au un sens specific în limbajul Fortran.2

În cursul acestui capitol și al următoarelor capitole, vom vedea cum pot fi trase alte analogii cu limbajul
natural. Unitatea de informare Fortran este simbolul lexical, care corespunde unui cuvânt sau semn de
punctuație. Jetoanele adiacente sunt de obicei separate prin spații sau sfârșitul unei linii, dar sunt permise
excepții sensibile la fel ca pentru un semn de punctuație în

1Fortran 2003 necesită suport pentru litere mici.


2Dintre caracterele speciale suplimentare din Fortran 2003, doar parantezele pătrate au o semnificație specifică.
Machine Translated by Google

10 Fortran modern explicat

Tabelul 2.1. Caracterele speciale ale limbajului Fortran.


Fortran 95 Fortran 95 Fortran 2003

= semnul egal : Colon \ Backslash

+ semnul plus Gol [ Paranteza pătrată din stânga

- Semnul minus ! Semnul exclamarii ] Paranteza pătrată dreaptă


* Asterisc " Ghilimele { Paranteza stângă
/ Bară oblică % Procent } Paranteza dreaptă
( Paranteza din stânga & Ampersand ~ Tilde

) Paranteza dreapta ; Punct i virgulă — Accent grav

, Virgulă < Mai puțin decât ^ Accent circumflex


. Punct zecimal > Mai mare decât | Linie verticala

$ Simbol valutar ? Semnul întrebării # Semnul numărului

'Apostrof @ Comercial la

proză. Secvențele de jetoane formează declarații, corespunzătoare propozițiilor. Enunțurile, precum


propozițiile, pot fi unite pentru a forma unități mai mari, cum ar fi paragrafele. În Fortran acestea sunt
cunoscute ca unități de program și din acestea se poate construi un program. Un program formează un
set complet de instrucțiuni către un computer pentru a efectua o secvență definită de operații. Cel mai
simplu program poate consta doar din câteva instrucțiuni, dar programele cu mai mult de 100 000 de
instrucțiuni sunt acum destul de comune.

2.3 Jetoane

În contextul Fortran, caracterele alfanumerice (literele, sublinierea și cifrele) pot fi combinate în secvențe
care au una sau mai multe semnificații. De exemplu, unul dintre semnificațiile secvenței 999 este o
constantă în sens matematic. În mod similar, data secvenței ar putea reprezenta, ca o posibilă
interpretare, o cantitate variabilă căreia îi atribuim data calendaristică.

Caracterele speciale sunt folosite pentru a separa astfel de secvențe și au, de asemenea, diferite semnificații.
Vom vedea cum asteriscul este folosit pentru a specifica operația de înmulțire, ca în x*y, și are, de
asemenea, o serie de alte interpretări.
Secvențele semnificative de bază de caractere alfanumerice sau de caractere speciale sunt denumite
jetoane; acestea sunt etichete, cuvinte cheie, nume, constante (altele decât constantele literale complexe),
operatori (listați în Tabelul 3.4, Secțiunea 3.8) și separatori, care sunt3

/ ( ) (/ /) , = => : :: ; %

De exemplu, expresia x*y conține cele trei simboluri x, * și y.


În afară de un șir de caractere sau într-un simbol, spațiile goale pot fi folosite liber pentru a îmbunătăți
aspectul. Astfel, în timp ce data variabilă nu poate fi scrisă ca dată,

3În Fortran 2003, caracterele [ și ] sunt, de asemenea, separatoare.


Machine Translated by Google

Elemente de limbaj 11

secvența x*y este echivalentă sintactic cu x*y. În acest context, mai multe spații libere sunt echivalente sintactic cu un
singur spațiu liber.
Un nume, o constantă sau o etichetă trebuie separate de un cuvânt cheie, un nume, o constantă sau o etichetă
adiacentă prin unul sau mai multe spații libere sau la sfârșitul unui rând. De exemplu, în

x real
derulează înapoi 10

30 face k=1,3

spațiile sunt necesare după real, rewind, 30 și do. De asemenea, cuvintele cheie adiacente trebuie în mod normal să fie
separate, dar unele perechi de cuvinte cheie, cum ar fi else if, nu trebuie să fie separate. În mod similar, unele cuvinte cheie
pot fi împărțite; de exemplu inout poate fi scris in out. Nu folosim aceste alternative, dar regulile exacte sunt date în Tabelul
2.2.

Tabelul 2.2. Cuvinte cheie adiacente unde separarea spațiilor libere este opțională.

totul se opreste** blocarea datelor dublă precizie altfel dacă


altundeva* asociat final* bloc final** datele blocului final

sfâr itul critic** sfâr itul face end enumerare* fișier final

sfâr itul pentru tot funcția finală sfâr itul dacă interfață finală

modul final încheie procedura** încheie program final select

end submodule** end subroutine tip final sfâr itul unde

mergi la în afară selectați cazul alege tipul*

* Fortran 2003 încoace; ** Numai Fortran 2008.

2.4 Forma sursă

Declarațiile din care este compus un program sursă sunt scrise pe rânduri. Fiecare rând poate conține până la 132 de
caractere4 și de obicei conține o singură instrucțiune. Deoarece spațiile de început nu sunt semnificative, este posibil să
începeți toate astfel de declarații în prima poziție a caracterului sau în orice altă poziție compatibilă cu aspectul ales de
utilizator. O declarație poate fi astfel scrisă
la fel de

x = (-y + rădăcina_discriminantului)/(2.0*a)

Pentru a putea amesteca comentariile potrivite cu codul la care se referă, Fortran permite oricărei linii să aibă un câmp
de comentariu final, după un semn de exclamare (!). Un exemplu este

x = y/a - b ! Rezolvați ecuația liniară

Orice comentariu se extinde întotdeauna până la sfârșitul liniei sursă și poate include caractere dependente de procesor
(nu este limitat la setul de caractere Fortran, Secțiunea 2.2). Orice linie al cărei prim caracter neblank este un semn de
exclamare sau conține doar spații libere sau care

4Randurile care conțin caractere de tip non-implicit (Secțiunea 2.6.4) sunt supuse unei limite dependente de procesor.
Machine Translated by Google

12 Fortran modern explicat

este gol, este pur comentariu și este ignorat de compilator. Astfel de linii de comentarii pot apărea oriunde
într-o unitate de program, inclusiv înaintea primei instrucțiuni (dar nu după unitatea finală de program5).
Un context de caracter (acele contexte definite în Secțiunile 2.6.4 și 9.12.4) poate conține !, deci ! nu inițiază
un comentariu în acest caz; în toate celelalte cazuri o face.

Deoarece este posibil ca o mențiune lungă să nu fie acomodată în cele 132 de poziții permise într-o
singură linie, sunt permise până la 39 de linii de continuare suplimentare.6 Așa-numitul semn de continuare
este caracterul ampersand (&) și acesta este atașat. la fiecare linie care este urmată de o linie de continuare.
Astfel, prima afirmație a acestei secțiuni (considerabil distanțată) ar putea fi scrisă ca

x= &

(-y + root_of_discriminant) /(2.0*a) &

În această carte, ampersand vor fi în mod normal aliniate pentru a îmbunătăți lizibilitatea. Pe o linie fără
comentarii, dacă & este ultimul caracter neblank sau ultimul caracter neblank înaintea simbolului de
comentariu !, instrucțiunea continuă de la caracterul care precede imediat &. În mod normal, continuarea
este la primul caracter al următoarei linii fără comentarii, dar dacă primul caracter neblank al următoarei
linii fără comentarii este &, continuarea este la caracterul care urmează după &. De exemplu, declarația de
mai sus poate fi scrisă

x= &

&(-y + root_of_discriminant)/(2.0*a)

În special, dacă un jeton nu poate fi conținut la sfârșitul unei linii, primul caracter neblank de pe următoarea
linie fără comentarii trebuie să fie un & urmat imediat de restul jetonului.

Comentariile pot conține orice caractere, inclusiv &, așa că nu pot fi continuate, deoarece un & final este
luat ca parte a comentariului. Cu toate acestea, liniile de comentarii pot fi intercalate liber între rândurile de
continuare și nu sunt luate în considerare pentru limita de 39 de rânduri.
Într-un context de caracter, continuarea trebuie să fie de la o linie fără un comentariu final și la o linie cu
un ampersand. Asta pentru că ambele! și & sunt permise atât în contexte de caractere, cât și în comentarii.

Nicio linie nu este permisă să aibă & ca singurul său caracter neblank, sau ca singurul său caracter
neblank înaintea !. Un astfel de rând este într-adevăr un comentariu și devine un comentariu dacă & este eliminat.
Când scrieți declarații scurte una după alta, poate fi convenabil să scrieți mai multe dintre ele pe o
singură linie. Caracterul punct și virgulă (;) este folosit ca separator de instrucțiuni în aceste circumstanțe,
de exemplu:

a = 0; b = 0; c = 0

Deoarece comentariul se extinde întotdeauna până la sfârșitul rândului, nu este posibil să se introducă
comentarii între declarații pe o singură linie. În principiu, este posibil să scrieți chiar și declarații lungi una
după alta într-un bloc solid de linii, fiecare cu 132 de caractere și

5Fortran 2003 permite linii de comentarii goale după unitatea finală de program.
6 Mai multe linii de continuare sunt permise în Fortran 2003, vezi Secțiunea 16.8.
Machine Translated by Google

Elemente de limbaj 13

cu punct și virgulă corespunzătoare care separă afirmațiile individuale. În practică, un astfel de cod este
imposibil de citit, iar utilizarea liniilor cu mai multe instrucțiuni ar trebui rezervată pentru cazuri banale,
cum ar fi cel prezentat în acest exemplu.
Orice declarație Fortran (care nu face parte dintr-o declarație compusă) poate fi etichetată, pentru a o
putea identifica. Pentru unele afirmații o etichetă este obligatorie. O etichetă de declarație precede
declarația și este privită ca un simbol. Eticheta este formată din una până la cinci cifre, dintre care una
trebuie să fie diferită de zero. Un exemplu de declarație etichetată este

100 continua

Zerourile de început nu sunt semnificative pentru a face distincția între etichete. De exemplu, 10 și 010
sunt echivalente.

2.5 Conceptul de tip

În Fortran, este posibil să definiți și să manipulați diferite tipuri de date. De exemplu, este posibil să avem
disponibilă valoarea 10 într-un program și să atribuim acea valoare unei variabile scalare întregi notate
cu i. Atât 10 cât și i sunt de tip întreg; 10 este o valoare fixă sau constantă, în timp ce i este o variabilă
căreia i se pot atribui alte valori. Sunt disponibile și expresii întregi, cum ar fi i+10.

Un tip de date constă dintr-un set de valori de date, un mijloc de denotare a acestor valori și un set de
operațiuni care sunt permise asupra acestora. Pentru tipul de date întreg, valorile sunt ...,
3, 2, 1,0,1,2,3,... între anumite limite în funcție de tipul de întreg și de sistemul informatic utilizat.
Asemenea simboluri ca acestea sunt constante literale și fiecare tip de date are propria sa formă de
exprimare. Pot fi stabilite variabile scalare numite, cum ar fi i.
În timpul execuției unui program, valoarea lui i se poate schimba în orice valoare validă sau poate deveni
nedefinită, adică nu are o valoare previzibilă. Operațiile care pot fi efectuate asupra numerelor întregi
sunt cele de aritmetică obișnuită; putem scrie 1+10 sau i-3 și obținem rezultatele așteptate. Constantele
numite pot fi stabilite și; acestea au valori care nu se modifică în timpul execuției programului.

Proprietăți precum cele menționate tocmai sunt asociate cu toate tipurile de date ale Fortran și vor fi
descrise în detaliu în acest capitol și în următoarele. Limbajul în sine conține cinci tipuri de date a căror
existență poate fi întotdeauna presupusă. Acestea sunt cunoscute ca tipuri de date intrinseci, ale căror
constante literale formează subiectul secțiunii următoare. Dintre fiecare tip intrinsec există un tip implicit
și un număr dependent de procesor de alte tipuri. Fiecare tip este asociat cu o valoare întreagă nenegativă
cunoscută sub numele de parametru tip tip. Acesta este folosit ca mijloc de identificare și deosebire a
diferitelor tipuri disponibile.
În plus, este posibil să se definească alte tipuri de date pe baza colecțiilor de date ale tipurilor intrinseci,
iar acestea sunt cunoscute ca tipuri de date derivate. Abilitatea de a defini tipuri de date de interes pentru
programator – matrici, forme geometrice, liste, numere de interval – este o caracteristică puternică a
limbajului, una care permite un nivel ridicat de abstractizare a datelor, adică abilitatea de a defini și
manipula obiecte de date. fără a fi preocupa i de reprezentarea lor reală într-un computer.
Machine Translated by Google

14 Fortran modern explicat

2.6 Constante literale de tip intrinsec

Tipurile de date intrinseci sunt împărțite în două clase. Prima clasă conține trei tipuri numerice care sunt
utilizate în principal pentru calcule numerice – întreg, real și complex. A doua clasă conține cele două tipuri
non-numerice care sunt utilizate pentru aplicații precum procesarea și controlul textului - caracter și logic.
Tipurile numerice sunt folosite împreună cu operatorii obișnuiți ai aritmeticii, cum ar fi + și care vor fi
descriși în Capitolul 3. -,
Fiecare include un zero, iar valoarea unui zero cu semn este aceeași cu cea a unui zero fără semn.7 Tipurile
nenumerice sunt utilizate cu seturi de operatori specifici fiecărui tip; de exemplu, datele de caractere pot fi
concatenate. Și acestea vor fi descrise în capitolul 3.

2.6.1 Constante literale întregi

Primul tip de constantă literală este constanta literală întreagă. Tipul implicit este pur și simplu o valoare
întreagă semnată sau nesemnată, de exemplu

10
-999 32767
+10

Intervalul numerelor întregi implicite nu este specificat în limbaj, dar pe un computer cu o dimensiune a
cuvântului de n biți, este adesea de la 2n 1 la +2n 1 1. Astfel, pe un computer pe 32 de biți8 gama
este adesea de la 2147483648 la +2147483647.
Pentru a fi sigur că intervalul va fi adecvat pe orice computer necesită specificarea tipului de număr
întreg, dând o valoare pentru parametrul tip tip. Acest lucru se face cel mai bine printr-o constantă întreagă
numită. De exemplu, dacă se dorește intervalul de la -999999 la 999999, k6 poate fi stabilit ca o constantă
cu o valoare adecvată prin declarația, explicată pe deplin mai târziu,

întreg, parametru :: k6=selected_int_kind(6)

și utilizat în constante astfel:

-123456_k6
+1_k6 2_k6

Aici, selected_int_kind(6) este un apel intrinsec al funcției de interogare și returnează o valoare a parametrului
tip care furnizează intervalul -999999 până la 999999 cu cea mai mică marjă (vezi Secțiunea 8.7.4).

Pe un procesor dat, s-ar putea să știe că valoarea tip necesară este 3. În acest caz,
prima dintre constantele noastre pot fi scrise

7Deși reprezentarea datelor este dependentă de procesor, pentru tipurile de date numerice standardul definește
reprezentările modelului și mijloacele de a investiga proprietățile acelor modele. Detaliile sunt amânate la Secțiune

8.7.8Fortran 2008 necesită, de asemenea, suport pentru, efectiv, un tip întreg pe 64 de biți, vezi Secțiunea 20.2.1.
Machine Translated by Google

Elemente de limbaj 15

-123456_3

dar această formă este mai puțin portabilă. Dacă mutam codul pe alt procesor, această valoare particulară poate fi
neacceptată sau poate corespunde unui interval diferit.
Multe implementări folosesc valori care indică numărul de octeți de stocare ocupați de o valoare, dar
standardul permite o mai mare flexibilitate. De exemplu, un procesor poate avea hardware numai pentru
numere întregi de 4 octeți și totuși să accepte valorile de tip 1, 2 și 4 cu acest hardware (pentru a ușura
portabilitatea de la procesoarele care au hardware pentru numere întregi de 1, 2 și 4 octeți). ). Cu toate
acestea, standardul nu face nicio declarație despre valorile tipului sau ordinea acestora, cu excepția
faptului că valoarea naturii nu este niciodată negativă.
Valoarea parametrului tip tip pentru un anumit tip de date pe un procesor dat poate fi
obținut din funcția intrinsecă tip (secțiunea 8.2):

fel(1) pentru valoarea implicită

fel(2_k6) de exemplu

iar intervalul exponentului zecimal (numărul de cifre zecimale acceptat) al unei anumite entități poate fi obținut
dintr-o altă funcție (secțiunea 8.7.2), ca în

interval(2_k6)

care în acest caz ar returna o valoare de cel puțin 6.

Pe lângă numerele întregi obișnuite ale sistemului numeric zecimal, pentru unele aplicații este foarte convenabil
să poți reprezenta numere întregi pozitive în formă binară, octală sau hexazecimală. Constantele fără semn ale
acestor forme există în Fortran și sunt reprezentate așa cum este ilustrat în aceste exemple:

binar (baza 2): octal b'01100110'


(baza 8): o'076543' hexazecimal (baza 16): z'10fa'

În forma hexazecimală, literele de la a la f reprezintă valorile dincolo de 9; pot fi folosite și cu litere mari. Delimitatorii
pot fi ghilimele sau apostrofe. Utilizarea acestor forme de constante este limitată la apariția lor ca numere întregi
implicite în declarația de date (Secțiunea 7.5.2). O constantă binară, octală sau hexazecimală poate apărea, de
asemenea, într-un fișier intern sau extern ca șir de cifre, fără litera de început și delimitatorii (vezi Secțiunea
9.12.2).9 Biții stocați ca o reprezentare întregă pot fi manipulați de intrinseci procedurile descrise în Secțiunea 8.8.10

2.6.2 Constante literale reale

Al doilea tip de constantă literală este constanta literală reală. Tipul implicit este o formă în virgulă mobilă
construită din unele sau din toate: o parte întreagă cu semnă sau fără semn, un punct zecimal, o parte fracțională
și o parte exponent cu semn sau fără semn. Una sau ambele părți întregi și

9Alte posibilități, în Fortran 2003, sunt descrise în Secțiunea 16.9.


10Fortran 2008 are proceduri intrinseci suplimentare de biți, vezi Secțiunea 20.10.
Machine Translated by Google

16 Fortran modern explicat

partea fracționată trebuie să fie prezentă. Partea de exponent este fie absentă, fie este formată din
litera e urmată de un întreg semnat sau fără semn. Una sau ambele puncte zecimale și partea de
exponent trebuie să fie prezente. Un exemplu este

-10,6e-11

adică 10,6×10 11, iar alte forme juridice sunt

1.
-0,1
1e-1
3.141592653

Constantele literale reale implicite sunt reprezentări ale unui subset al numerelor reale ale matematicii, iar
standardul nu specifică nici domeniul permis al exponentului, nici numărul de cifre semnificative reprezentate de
procesor. Multe procesoare sunt conforme cu standardul IEEE pentru aritmetica în virgulă mobilă și au valori de la
10 37 la 10+37 pentru interval și o precizie de șase zecimale.

Pentru a fi sigur că obțineți intervalul și semnificația dorite necesită specificarea unui fel
valoarea parametrului. De exemplu,

întreg, parametru :: lung = select_real_kind(9, 99)

asigură că constantele

1.7_lung
12.3456789e30_lung

au o precizie de cel puțin nouă zecimale semnificative și un interval de exponent de cel puțin 10 99 la 10+99.
Numărul de cifre specificat în semnificația nu are efect asupra tipului. În special, este permis să scrieți mai multe
cifre decât poate utiliza de fapt procesorul.
În ceea ce privește numerele întregi, multe implementări folosesc valori tip care indică numărul de
octeți de stocare ocupați de o valoare, dar standardul permite o mai mare flexibilitate. Se specifică doar
că valoarea tipului nu este niciodată negativă. Dacă se cunoaște valoarea dorită, aceasta poate fi utilizată
direct, ca în cazul

1.7_4

dar codul rezultat este atunci mai puțin portabil.


Procesorul trebuie să ofere cel puțin o reprezentare cu mai multă precizie decât cea implicită, iar această a doua
reprezentare poate fi specificată și ca precizie dublă. Amânăm descrierea acestei sintaxe alternative, dar depășite,
la apendicele B.
Funcția kind este valabilă și pentru valori reale:

fel(1,0) pentru valoarea implicită

fel(1,0_lung) de exemplu

În plus, există două funcții de interogare disponibile care returnează precizia și, respectiv, intervalul real al unei
anumite entități reale (vezi Secțiunea 8.7.2). Astfel, valoarea de
Machine Translated by Google

Elemente de limbaj 17

precizie (1,7_lung)

ar fi cel puțin 9, iar valoarea de

interval (1,7_lung)

ar fi cel puțin 99.

2.6.3 Constante literale complexe

Fortran, ca limbaj destinat calculelor științifice și inginerești, are avantajul de a avea ca al treilea tip de constantă
literală constanta literală complexă. Aceasta este desemnată printr-o pereche de constante literale, care sunt fie
întregi, fie reale, separate prin virgulă și cuprinse între paranteze. Exemplele sunt

(1, 3,2) (1,


.99e-2) (1,0,
3,7_8)

unde prima constantă a fiecărei perechi este partea reală a numărului complex, iar a doua constantă este partea
imaginară. Dacă una dintre părți este întreagă, tipul constantei complexe este cel al celeilalte părți. Dacă ambele
părți sunt întregi, tipul constantei este cel al tipului real implicit. Dacă ambele părți sunt reale și de același fel,
acesta este tipul de constantă. Dacă ambele părți sunt reale și de feluri diferite, tipul constantei este cel al uneia
dintre părți: partea cu precizia zecimală mai mare sau partea aleasă de procesor dacă preciziile zecimale sunt
identice.

O constantă complexă implicită este una a cărei valoare de tip este cea a implicită reală.
Funcțiile fel, precizie și interval sunt la fel de valabile pentru entitățile complexe.
Rețineți că dacă o implementare folosește numărul de octeți necesari pentru a stoca un real ca valoare
de tip, numărul de octeți necesar pentru a stoca o valoare complexă de tipul corespunzător este de două
ori mai mare decât valoarea de tip. De exemplu, dacă tipul real implicit are tipul 4 și are nevoie de patru
octeți de stocare, tipul complex implicit are tipul 4, dar are nevoie de opt octeți de stocare.

2.6.4 Constante literale de caractere

Al patrulea tip de constantă literală este constanta literală de caractere. Tipul implicit constă dintr-un șir de
caractere cuprinse fie într-o pereche de apostrofe, fie de ghilimele, de exemplu

'Orice merge'

"Piulițe"

Caracterele nu sunt limitate la setul Fortran (Secțiunea 2.2). Orice caracter grafic acceptat de procesor este permis,
dar nu caracterele de control precum „linia nouă”. Apostrofele și ghilimelele servesc ca delimitatori și nu fac parte
din valoarea constantei. Valoarea constantei
Machine Translated by Google

18 Fortran modern explicat

' IR'

este STRING. Observăm că în constantele de caractere caracterul gol este semnificativ. De exemplu

'o sfoară'

nu este la fel ca

'o sfoară'

Apare o problemă cu reprezentarea unui apostrof sau a unui ghilime într-o constantă de caractere.
Caracterele delimitare de un fel pot fi încorporate într-un șir delimitat de celălalt, ca în exemple

„El a spus „Bună ziua””


„Acesta conține un ' "

Alternativ, un delimitator dublat fără spații libere încorporate este privit ca un singur caracter al constantei.
De exemplu

„Nu-i așa că este o zi frumoasă”

are valoare, nu-i așa că este o zi frumoasă?


Numărul de caractere dintr-un șir se numește lungimea acestuia și poate fi zero. De exemplu, „” și „”
sunt constante de caractere de lungime zero.
Menționăm aici regula specială pentru forma sursă referitoare la constantele de caractere care sunt
scrise pe mai mult de o linie (necesară deoarece constantele pot include caracterele ! și &): nu numai că
fiecare linie care este continuată trebuie să nu aibă un comentariu final, ci fiecare linia de continuare
trebuie să înceapă cu un semn de continuare. Orice spații care urmează după un ampersand sau precedând
un ampersand înainte nu fac parte din constantă și nici nu fac parte din constantă. Orice altceva, inclusiv
spațiile libere, face parte din constantă. Un exemplu este

șir_lung = &

— Dacă aș fi cu ea, noaptea ar posta prea devreme; &

& Dar acum sunt minute adăugate la ore; & Ca să mă ciudă &

acum, fiecare minut pare o lună; Și totuși nu pentru mine, &

străluciți soarele pentru a ajuta florile! &

& Pack noapte, peep day; ziua bună, de noapte acum împrumută: & & Scurtă, noapte,
azi-noapte, și lungi-te mâine.'

Pe orice computer, personajele au o proprietate cunoscută sub numele de secvența lor de colare. Se
poate pune întrebarea dacă un personaj apare înainte sau după altul în secvență.
Această întrebare este pusă într-o formă naturală, cum ar fi „C precede M?” și vom vedea mai târziu cum
poate fi exprimată aceasta în termeni Fortran. Fortran necesită ca secvența de asamblare a computerului
să îndeplinească următoarele condiții:

• A este mai mic decât B este mai mic decât C ... este mai mic decât Y este mai mic decât Z;
Machine Translated by Google

Elemente de limbaj 19

• 0 este mai mic decât 1 este mai mic decât 2 ... este mai mic decât 8 este mai mic decât 9;

• blank este mai mic decât A și Z este mai mic decât 0, sau blank este mai mic decât 0 și 9 este mai mic decât A;

și, dacă sunt disponibile literele mici,

• a este mai mic decât b este mai mic decât c ... este mai mic decât y este mai mic decât z;

• spațiul liber este mai mic decât a și z este mai mic decât 0 sau spațiul liber este mai mic decât 0 și 9 este mai mic decât a.

Astfel, vedem că nu există nicio regulă dacă numerele preced sau urmează literelor, nici despre poziția vreunuia dintre
caracterele speciale sau liniuța de subliniere, în afară de regula conform căreia golul precedă ambele secvențe parțiale.
Orice sistem computerizat are o secvență completă de colare, iar majoritatea calculatoarelor din zilele noastre folosesc
secvența de colare a standardului ASCII (cunoscut și ca ISO/IEC 646: 1991). Cu toate acestea, Fortran este conceput pentru
a găzdui alte secvențe, în special EBCDIC, astfel încât, pentru portabilitate, niciun program nu ar trebui să depindă
vreodată de orice ordine în afara celei menționate mai sus. Alternativ, Fortran oferă acces la secvența de colating ASCII pe
orice computer prin funcții intrinseci (Secțiunea 8.5.1), dar acest acces nu este atât de convenabil și este mai puțin eficient
pe unele computere.

Este necesar un procesor pentru a oferi acces la tipul implicit de constantă de caractere descrisă. În plus, poate suporta
alte tipuri de constante de caractere, în special cele ale limbilor non-europene, care pot avea mai multe caractere decât
pot fi furnizate într-un singur octet. De exemplu, un procesor poate suporta Kanji cu valoarea parametrului kind 2; în acest
caz, poate fi scrisă o constantă de caractere Kanji

'
2_'
sau

"
kanji_"

unde kanji este un număr întreg numit constantă cu valoarea 2. Observăm că, în acest caz, parametrul tip kind precede în
mod excepțional constanta.11
Nu există nicio cerință ca un procesor să furnizeze mai mult de un fel de caractere, iar standardul nu necesită nicio
relație specială între valorile parametrilor de tip și seturile de caractere și numărul de octeți necesari pentru a le reprezenta.
De fapt, tot ceea ce este necesar este ca fiecare tip de set de caractere să includă un caracter gol. În ceea ce privește
celelalte tipuri de date, funcția kind oferă valoarea reală a parametrului tip kind, ca în

fel('ASCII')

Caracterele care nu sunt implicite sunt permise în comentarii.

2.6.5 Constante literale logice

Al cincilea tip de constantă literală este constanta literală logică. Tipul implicit are una dintre cele două valori, .true.
iar .fals. . Aceste constante logice sunt utilizate în mod normal doar pentru a inițializa
variabilele logice la valorile lor necesare, așa cum vom vedea în Secțiunea 3.6.

11Acest lucru este pentru a facilita pentru un compilator să accepte mai multe seturi de caractere diferite care apar într-un singur
fișier sursă.
Machine Translated by Google

20 Fortran modern explicat

Tipul implicit are o valoare a parametrului tip care depinde de procesor. Valoarea reală este disponibilă ca tip
(.true.). Ca și în cazul celorlalte tipuri intrinseci, parametrul fel poate fi specificat printr-o constantă întreagă după
un caracter de subliniere, ca în

.fals._1 .adevărat._lung

Tipurile logice care nu sunt implicite sunt utile pentru stocarea compactă a matricelor logice; amânăm mai departe
discuție până la Secțiunea 6.17.

2.7 Nume

Un program Fortran face referire la multe entități diferite după nume. Astfel de nume trebuie să conțină între 1 și
31 de caractere alfanumerice12 – litere, caractere de subliniere și cifre – dintre care prima trebuie să fie o literă.
Nu există alte restricții cu privire la nume; în special nu există cuvinte rezervate în Fortran. Vedem astfel că numele
valide sunt, de exemplu,

un_lucru x1

masa
q123
real
ora zborului

iar numele nevalide sunt

1a Primul caracter nu este alfabetic


un lucru Conține un gol

$semn Conține un caracter non-alfanumeric

În constrângerile sintaxei, este important pentru claritatea programului să aleagă nume care au o semnificație
clară - acestea sunt cunoscute ca nume mnemonice. Exemple sunt ziua, luna și anul, pentru variabile pentru a
stoca data calendaristică.
Utilizarea numelor pentru a se referi la constante, deja îndeplinite în Secțiunea 2.6.1, va fi descrisă pe deplin
în secțiunea 7.4.

2.8 Variabile scalare de tip intrinsec


Am văzut în secțiunea despre constantele literale că există cinci tipuri diferite de date intrinseci. Fiecare dintre

aceste tipuri poate avea și variabile. Cel mai simplu mod prin care o variabilă poate fi declarată ca fiind de un
anumit tip este prin specificarea numelui acesteia într-o declarație de tip, cum ar fi

întreg :: i

12 În Fortran 2003 sunt permise până la 63 de caractere, vezi Secțiunea 16.8.


Machine Translated by Google

Elemente de limbaj 21

real :: A

complex :: curent logic :: caracter


pravda :: litera

Aici, toate variabilele au tipul implicit, iar litera are lungimea implicită, care este 1. Cerințele explicite pot fi specificate și prin
parametrii de tip, ca în exemple.

intreg(kind=4) :: i

real(kind=long) :: A

character(len=20, kind=1) character(len=20, :: cuvânt englezesc


kind=kanji) :: kanji_word

Caracterul este singurul tip care are doi parametri, iar aici cele două variabile de caractere au
fiecare lungime de 20. Acolo unde este cazul, poate fi specificat doar unul dintre parametri,
lăsându-l pe celălalt să-și ia valoarea implicită, ca în cazurile

caracter(kind=kanji) :: literă_kanji :: cuvânt_în engleză


caracter(len=20)

Formele mai scurte

întreg(4) :: i

caracter real :: A

(lung) (20, 1) :: cuvânt_în engleză caracter(20, kanji) ::


cuvânt_canji caracter(20) :: cuvânt_engleză

sunt disponibile, dar rețineți că

caracter(kanji) :: literă_canji ! Ai grijă

nu este o abreviere pentru

caracter(kind=kanji) :: kanji_letter

deoarece un singur parametru nenumit este luat ca parametru de lungime.

2.9 Tipuri de date derivate

Când programați, este adesea util să puteți manipula obiecte care sunt mai sofisticate decât cele ale tipurilor intrinseci.
Imaginează-ți, de exemplu, că am dorit să specificăm obiecte care reprezintă persoane. Fiecare persoană din aplicația
noastră se distinge printr-un nume, o vârstă și un număr de identificare. Fortran ne permite să definim un tip de date
corespunzător în felul următor:

tip person
character(len=10) :: nume
real :: varsta ::
tipul de id

sfârșit întreg persoană


Machine Translated by Google

22 Fortran modern explicat

Aceasta este definiția tipului. Un obiect scalar de un astfel de tip se numește structură. Pentru a crea
o structură de acest tip, scriem o declarație de tip adecvată, cum ar fi

tip(persoana) :: tu

Variabila scalară you este atunci un obiect compus de tip person, care conține trei componente separate,
una corespunzătoare numelui, alta vârstei și o a treia numărului de identificare. După cum va fi descris
în Secțiunile 3.8 și 3.9, o variabilă precum dvs. poate apărea în expresii și atribuiri care implică alte
variabile sau constante de același tip sau diferite. În plus, fiecare dintre componentele variabilei poate fi
referită în mod individual utilizând procentul (%) caracterului selectorului de componente. Numărul dvs.
de identificare ar fi, de exemplu, accesat ca

tu%id

iar această cantitate este o variabilă întreagă care ar putea apărea într-o expresie precum

tu%id + 9

În mod similar, dacă ar exista un al doilea obiect de același tip:

tip(persoana) :: eu

diferen ele de vârste puteau fi stabilite prin scris

tu% vârstă - eu% vârstă

Se va arăta în Secțiunea 3.8 cum poate fi dat un sens unei expresii precum

tu eu

Așa cum tipurile de date intrinseci au constante asociate literale, la fel pot fi specificate și constantele
literale de tip derivat. Forma lor este denumirea tipului urmată de valorile constante ale componentelor,
în ordine și cuprinse între paranteze. Astfel, constanta

persoană („Smith”, 23.5, 2541)

poate fi scris presupunând tipul derivat definit la începutul acestei secțiuni și ar putea fi atribuit unei
variabile de același tip:

tu = persoană( „Smith”, 23.5, 2541)

Orice astfel de constructor de structură poate apărea numai după definirea tipului.
Un tip derivat poate avea o componentă care este de tip derivat definit anterior. Aceasta este
ilustrat în Figura 2.1. O variabilă de tip triunghi poate fi declarată astfel

tip (triunghi) :: t

și t are componente t%a, t%b și t%c toate de tip punct și t%a are componente t%a%x și t%a%y de tip real.
Machine Translated by Google

Elemente de limbaj 23

Figura 2.1
tip punct
real :: x, y tip
punct tip tip triunghi
tip (punct) :: a, b, c
triunghi tip capăt

2.10 Matrice de tip intrinsec

Un alt obiect compus suportat de Fortran este matricea. O matrice constă dintr-un set dreptunghiular
de elemente, toate de același tip și parametri de tip. Există mai multe moduri în care matricele pot fi
declarate; deocamdată vom lua în considerare doar declararea rețelelor de dimensiuni fixe. Pentru a
declara o matrice numită a din 10 elemente reale, adăugăm atributul dimension la instrucțiunea de
declarație de tip astfel:

real, dimensiune(10) :: a

Elementele succesive ale tabloului sunt a(1), a(2), a(3), ..., a(10). Numărul de elemente ale unui tablou se
numește dimensiunea acestuia. Fiecare element de matrice este un scalar.
Multe probleme necesită o declarație mai elaborată decât una în care se află primul element
desemnat 1 și este posibil în Fortran să se declare o limită inferioară și superioară:

real, dimensiune(-10:5) :: vector

Acesta este un vector de 16 elemente, vector(-10), vector(-9), ..., vector(5). Vedem astfel că, deși trebuie
întotdeauna să specificăm limita superioară, limita inferioară este opțională și, implicit, are valoarea 1.

O matrice se poate extinde în mai mult de o dimensiune, iar Fortran permite până la șapte
dimensiuni13 de specificat. De exemplu

real, dimensiune(5,4) :: b

declară o matrice cu două dimensiuni și

real, dimensiune(-10:5, -20:-1, 0:15, -15:0, 16, 16, 16) :: grilă

declară șapte dimensiuni, primele patru cu limite inferioare explicite. Se poate observa că dimensiunea
acestui al doilea tablou este

16×20×16×16×16×16×16 = 335 544 320,

și că rețele de mai multe dimensiuni pot impune astfel solicitări mari asupra memoriei unui computer.
Numărul de dimensiuni ale unui tablou este cunoscut sub numele de rangul său. Astfel, grila are un
rang de șapte. Scalarii sunt considerați ca având rangul zero. Numărul de elemente de-a lungul a

13Fortran 2008 permite cincisprezece dimensiuni.


Machine Translated by Google

24 Fortran modern explicat

dimensiunea unui tablou este cunoscută sub denumirea de extindere în acea dimensiune. Astfel, grila are întinderi
16, 20, . . . .
Secvența extinderilor este cunoscută sub numele de formă. De exemplu, grila are forma
(16,20,16,16,16,16,16).
Un tip derivat poate conține o componentă de matrice. De exemplu, următorul tip

tip triplet real


:: u
real, dimensiune(3) :: du real,
dimensiune(3,3) :: triplet tip final d2u

poate fi folosit pentru a menține valoarea unei variabile în trei dimensiuni și valorile derivatelor sale
prima și a doua. Dacă t este de tip triplet, t%du și t%d2u sunt tablouri de tip real.
Unele instrucțiuni tratează elementele unui tablou unul câte unul într-o ordine specială pe care o
numim ordinea elementelor de tablou. Se obține prin numărarea cel mai rapid în dimensiunile timpurii.
Astfel, elementele grilei în ordinea elementelor matrice sunt

grilă(-10, -20, 0, -15, 1, 1, 1) grilă( -9, -20, 0,


-15, 1, 1, 1)
:
grilă (5, -1, 15, 0, 16, 16, 16)

Acest lucru este ilustrat pentru o matrice de două dimensiuni în Figura 2.2. Majoritatea
implementărilor stochează de fapt matrice în stocare contiguă în ordinea elementelor de matrice,
dar subliniem că standardul nu necesită acest lucru.

Figura 2.2 Ordonarea elementelor din tabloul b(5,4).

b (1,1) b (1,2) b (1,3) b (1,4)

b (2,1) b (2,2) b (2,3) b (2,4)

b (3,1) b (3,2) b (3,3) b (3,4)

b (4,1) b (4,2) b (4,3) b (4,4)

b (5,1) b (5,2) b (5,3) b (5,4)

Facem referire la un element individual al unui tablou prin specificarea, ca în exemplele de mai sus, a
valorilor sale indice. În exemple am folosit constante întregi, dar în general fiecare indice poate fi format
dintr-o expresie întregă scalară, adică orice expresie aritmetică a cărei valoare este scalară și de tip
întreg. Fiecare indice trebuie să se încadreze în intervalele corespunzătoare definite în declarația matricei,
iar numărul de indice trebuie să fie egal cu rangul. Exemplele sunt
Machine Translated by Google

Elemente de limbaj 25

a(1)
a(i*j) ! i și j sunt de tip întreg ! x este de tip real !
a(nint(x+3.)) t este de tip derivat triplet
t%d2u(i+1,j+2)

unde nint este o funcție intrinsecă pentru a converti o valoare reală la cel mai apropiat număr întreg (vezi
Secțiunea 8.3.1). În plus, se poate face referire la subbariere, numite secțiuni, prin specificarea unui interval
pentru unul sau mai multe indice. Următoarele sunt exemple de secțiuni de matrice:

a(i:j) b(k, ! Matrice de rangul unu de dimensiunea


1:n) c(1:i, 1:j, j-i+1 ! Matrice de rangul unu de
k) dimensiunea n ! Matrice de rang doi cu întinderi i și j

Descriem secțiunile de matrice mai detaliat în Secțiunea 6.13. O secțiune de matrice este ea însăși o matrice,
dar elementele sale individuale nu trebuie accesate prin desemnarea secțiunii. Astfel, b(k, 1:n)(l) nu poate fi
scris; trebuie exprimat ca b(k, l).
O altă formă de indice este afișată în

a(ipoint) ! ipoint este un tablou întreg

unde ipoint este o matrice de indici, indicând elementele matricei. Astfel, se poate observa că a(ipoint), care
identifică atâtea elemente ale unui ipoint are elemente, este un exemplu de alt obiect cu valori de matrice, iar
ipoint este denumit un indice vectorial. Acest lucru va fi îndeplinit mai detaliat în Secțiunea 6.13.

Este adesea convenabil să poți defini o constantă de matrice. În Fortran, o matrice de rangul A simplă
14
poate fi construită ca o listă de elemente incluse între jetoane (/ și (/). exemplu este

(/ 1, 2, 3, 5, 10 /)

care este o serie de rangul unu și dimensiunea cinci. Pentru a obține o serie de valori, valorile individuale pot
fi definite printr-o expresie care depinde de o variabilă întreagă având valori într-un interval, cu un pas
opțional. Astfel, constructorul (/1, 2, 3, 4, 5/) poate fi scris ca (/ (i, i = 1,5) /)

și
(/2, 4, 6, 8/)
la fel de

(/ (i, i = 2,8,2) /)
și
(/ 1.1, 1.2, 1.3, 1.4, 1.5 /)
la fel de

(/ (i*0,1, i=11,15) /)
O constantă de matrice de rang mai mare decât unu poate fi construită utilizând funcția remodelare (vezi
Secțiunea 8.13.3) pentru a remodela o constantă de matrice de rang unu.
O descriere completă a constructorilor de matrice este rezervată pentru Secțiunea 6.16.

14În Fortran 2003, caracterele [ și ] pot fi folosite pentru a delimita un constructor de matrice.
Machine Translated by Google

26 Fortran modern explicat

2.11 Subșiruri de caractere

Este posibil să construiți matrice de caractere, la fel cum este posibil să construiți matrice de orice alt tip:

caracter, dimensiune(80) :: linie

declară un tablou, numit linie, de 80 de elemente, fiecare caracter în lungime. Fiecare caracter poate fi
adresat de referința obișnuită, linia (i), de exemplu. În acest caz, totuși, o declarație mai adecvată ar putea
fi

caracter(len=80) :: linie

care declară un obiect de date scalare de 80 de caractere. Acestea pot fi referite individual sau în grupuri
folosind o notație subșir

linie (i:j) ! i și j sunt de tip întreg

care face referire la toate caracterele de la i la j în linie. Coloana este folosită pentru a separa cele două
subindexe, care pot fi orice expresii întregi scalare. Coloana este obligatorie în referințele subșirurilor,
astfel încât referirea unui singur caracter necesită linie (i:i). Există valori implicite pentru subscriptele
subșirurilor. Dacă cea de jos este omisă, se presupune valoarea 1; dacă cel de sus este omis, se presupune
o valoare corespunzătoare lungimii caracterului. Prin urmare,

line(:i) este echivalent cu line(1:i) line(i:) este


echivalent cu line(i:80) line(:) este echivalent cu line
sau line(1:80)

Dacă i este mai mare decât j în linia (i:j), valoarea este un șir de dimensiune zero.
Acum putem combina declarația de lungime cu declarația de matrice pentru a construi matrice
obiecte caracter de lungime specificată, ca în

caracter(len=80), dimensiune(60) :: pagina

care ar putea fi folosit pentru a defini stocarea pentru caracterele unei pagini întregi, cu 60 de elemente
dintr-o matrice, fiecare cu lungimea de 80. Pentru a face referire la linia j pe o pagină, putem scrie pagina (j)
și pentru a face referire la caracterul i pe acea pagină linie am putea combina subscriptul matricei și notațiile
subșirurilor de caractere în care

pagina(j)(i:i)

Un subșir al unei constante de caractere sau al unei componente de structură poate fi, de asemenea, format:

„ABCDEFGHIJKLMNOPQRSTUVWXYZ”(j:j)
tu%name(1:2)

În acest moment trebuie să remarcăm o limitare asociată cu variabilele caracter, și anume aceea că
variabilele caracter trebuie să aibă o lungime maximă declarată, ceea ce face imposibilă manipularea
variabilelor caracter cu lungime variabilă, cu excepția cazului în care sunt definite corespunzător ca tip de
date derivate.15 Cu toate acestea, acest tip de date este adecvat pentru majoritatea aplicațiilor de
manipulare a caracterelor.

15Această limitare nu se aplică în Fortran 2003, vezi Secțiunea 15.2.


Machine Translated by Google

Elemente de limbaj 27

2.12 Obiecte și subobiecte

Am văzut că tipurile derivate pot avea componente care sunt matrice, ca în

tip triplet real,


dimensiune(3) :: vârf tip triplet

iar tablourile pot fi de tip derivat ca în exemplu

tip(triplet), dimensiune(10) :: t

O singură structură (de exemplu, t(2)) este întotdeauna privită ca un scalar, dar poate avea o componentă
(de exemplu, t(2)%vertex) care este o matrice. Tipurile derivate pot avea componente ale altor tipuri
derivate.
Un obiect referit printr-un nume necalificat (toate caracterele alfanumerice) se numește obiect numit
și nu face parte dintr-un obiect mai mare. Subobiectele sale au indicatori care constau din numele
obiectului urmat de unul sau mai mulți calificativi (de exemplu, t(1:7) și t(1)%vertex). Fiecare calificativ
succesiv specifică o parte a obiectului specificat de numele sau desemnatorul care îl precede.

Remarcăm că termenul „matrice” este folosit pentru orice obiect care nu este scalar, inclusiv o secțiune
de matrice sau o componentă cu valori de matrice a unei structuri. Termenul „variabilă” este folosit pentru
orice obiect numit care nu este specificat a fi o constantă și pentru orice parte a unui astfel de obiect,
inclusiv elemente de matrice, secțiuni de matrice, componente de structură și subșiruri.

2.13 Indicatori

În limbajul de zi cu zi, substantivele sunt adesea folosite într-un mod care face sensul lor precis doar din
cauza contextului. „Președintele a spus că...” va fi înțeles tocmai de cititorul care știe că contextul este
Comitetul Fortran care dezvoltă Fortran 90 și că președintele acestuia era atunci Jeanne Adams.

În mod similar, într-un program de calculator poate fi foarte util să poți folosi un nume care poate fi
făcut să se refere la diferite obiecte în timpul execuției programului. Un exemplu este înmulțirea unui
vector cu o succesiune de matrici pătrate. Am putea scrie cod care calculează

n
yi = ai j x j, i = 1,2,...,n
j=1

din vectorul x j, j = 1,2,... ,n. Pentru a utiliza acest lucru pentru a calcula

BCz

am putea face mai întâi ca x să se refere la z și A să se refere la C, folosind astfel codul nostru pentru a calcula y = Cz, apoi să
facem ca x să se refere la y și A să se refere la B, astfel încât codul nostru să calculeze vectorul rezultat pe care îl dorim în cele
din urmă.

Un obiect care poate fi făcut să se refere la alte obiecte în acest fel se numește pointer și
trebuie declarat cu atributul pointer, de exemplu
Machine Translated by Google

28 Fortran modern explicat

real, pointer real, :: fiule

pointer, dimensiune(:):: x, y real, pointer,


dimensiune(:,:) :: a

În cazul unei matrice, este declarat doar rangul (numărul de dimensiuni), iar limitele (și, prin urmare,
forma) sunt luate din cea a obiectului către care indică. Având în vedere o astfel de declarație, compilatorul
aranjează stocarea pentru un descriptor care va deține mai târziu adresa obiectului real (cunoscut ca
țintă) și păstrează, dacă este o matrice, limitele și pașii acestuia.
Pe lângă indicarea către variabile existente, un pointer poate fi făcut explicit să nu arate nimic:

anulează (fiul, x, y, a)

(nullify este descris în Secțiunea 3.13) sau poate primi stocare proaspătă printr-o declarație de alocare,
cum ar fi

alocă (fiul, x(10), y(-10:10), a(n, n))

În cazul tablourilor, limitele inferioare și superioare sunt specificate la fel ca și pentru atributul dimensiune
(Secțiunea 2.10), cu excepția faptului că este permisă orice expresie întreg scalar. Această utilizare a
pointerilor oferă un mijloc de a accesa stocarea dinamică, dar în Secțiunea 6.5 vom descrie o modalitate
mai bună de a face acest lucru în cazurile în care proprietatea „indicare” nu este esențială.
În mod implicit, pointerii sunt inițial nedefiniti (vezi și paragraful final al Secțiunii 3.3). Aceasta este o
stare foarte nedorită, deoarece nu există nicio modalitate de a testa. Cu toate acestea, poate fi evitat prin
utilizarea declarației:

real, pointer :: son => null()

(funcția null este descrisă în Secțiunea 8.15) și recomandăm ca aceasta să fie întotdeauna folosită.
Alternativ, pointerii pot fi definiți de îndată ce intră în domeniul de aplicare prin executarea unei
instrucțiuni de anulare sau a unei atribuiri de pointer.
Componentele tipurilor derivate au permisiunea de a avea atributul pointer. Acest lucru permite o
aplicare majoră a indicatorilor: construirea de liste legate. Ca exemplu simplu, am putea decide să
menținem un vector rar ca un lanț de variabile de tipul prezentat în Figura 2.3, care ne permite să
accesăm intrările una câte una; dat

tip (intrare), indicator :: lanț

unde chain este un scalar de acest tip și deține un lanț care are lungimea doi, intrările sale sunt
chain%index și chain%next%index, iar chain%next%next va fi anulat.
Pot fi create intrări suplimentare atunci când este necesar printr-o declarație de alocare adecvată.
Amânăm detaliile la Secțiunea 3.12.
Atunci când un pointer este de tip derivat și este selectată o componentă precum chain%index, așa este
de fapt o componentă a țintei pointerului care este selectată.
Un subobiect nu este un pointer decât dacă are un selector final de componentă pentru numele unei
componente pointer, de exemplu, lanț%next.
Indicatorii vor fi discutați în detaliu în capitolele ulterioare (în special secțiunile 3.12, 5.7.1, 6.14,
6.15, 7.5.3, 7.5.4 și 8.2).
Machine Translated by Google

Elemente de limbaj 29

Figura 2.3 Un tip pentru un care deține un vector rar ca un lanț de variabile.
introducere de tip real
:: valoare
tip întreg :: index
(intrare), pointer :: următoarea intrare
de tip final

2.14 Rezumat

În acest capitol, am introdus elementele limbajului Fortran. Setul de caractere a fost listat și a fost explicat
modul în care secvențele de caractere formează constante literale și numele. În acest context, am întâlnit
cele cinci tipuri de date intrinseci definite în Fortran și am văzut cum fiecare tip de date are constante
literale corespunzătoare și obiecte numite. Am văzut cum se pot construi tipuri derivate din tipurile
intrinseci. Am introdus o metodă prin care matricele pot fi declarate și am văzut cum elementele lor pot
fi referite prin expresii în indice. Conceptele de secțiune de matrice, subșir de caractere și pointer au fost
prezentate și au fost definiți câțiva termeni importanți. În capitolul următor vom vedea cum aceste
elemente pot fi combinate în expresii și enunțuri, echivalentele lui Fortran de „expresii” și „propoziții”.

Exerciții

1. Pentru fiecare dintre următoarele afirmații, precizați dacă este adevărată, falsă sau nu determinată, conform
secvențele de comparare Fortran:

b este mai mic decât m


8 este mai mic decat 2

* este mai mare decât T $ este


mai mic decât / blank este mai
mare decât Un blank este mai mic de
6

2. Care dintre liniile Fortran din cod

x=y 3 a
= b+c ! adăuga

cuvânt = „șir” a = 1,0; b


= 2,0 a = 15. ! inițializați a;
b = 22. ! și b cântec = „Viața este doar&

și un castron cu cireșe"

chide = 'Nu risipi, nu vrei!'

0 c(3:4) = „în sus”

sunt corect scrise conform cerințelor formularului sursă Fortran? Care dintre ele conțin comentarii? Care linii sunt linii inițiale și
care sunt linii de continuare?
Machine Translated by Google

30 Fortran modern explicat

3. Clasificați următoarele constante literale conform celor cinci tipuri de date intrinseci ale Fortran. Care nu sunt constante
literale legale?
-43 'cuvânt'
4.39 1,9-4
0,0001e+20 „lucruri și prostii”
49 (0.,1.)
(1.e3,2) 'Nu pot'

„(4.3e9, 6.2)” .adevărat._1


e5 „nu ar trebui”
1_2 "BINE"
z10 z'10'

4. Care dintre următoarele nume sunt denumiri legale Fortran?


Nume nume32
coeficientul 123
a182c3 nu arzi_
Stop!
impas nume__lung

5. Care sunt primul, al zecelea, al unsprezecelea și ultimul element ale următoarelor matrice?
real, dimensiune (11) real, :: A

dimensiune (0:11) :: b
real, dimensiune(-11:0) real, :: c
dimensiune(10,10) real, :: d
dimensiune(5,9) real, :: e
dimensiune(5,0:1,4) :: f Scrieți un constructor

de matrice de unsprezece elemente întregi.

6. Având în vedere declarația matricei

caracter(len=10), dimensiune(0:5,3) :: c

care dintre următorii desemnatori de subobiect sunt legale?


c(2,3) c(4:3)(2,1)
c(6,2) c(5,3)(9:9)
c(0,3) c(2,1)(4:8) c(3,2)
c(4,3)(:) c(5) (0:9) c(5:6) c(,)
(2:3) c(5,3)(9)

7. Scrieți definiții de tip derivat adecvate pentru:

i) înmatricularea unui vehicul;

ii) un cerc; iii)

o carte (titlu, autor și număr de pagini).

Dați un exemplu de constantă de tip derivat pentru fiecare.

8. Având în vedere declarația pentru t din Secțiunea 2.12, care dintre următoarele obiecte și subobiecte sunt
matrice?
t t(4)%vertex(1) t(5:6)
t(10) t(5:5)
t(1)%vertex
Machine Translated by Google

Elemente de limbaj 31

9. Scrieți specificațiile pentru aceste entități:

a) o variabilă întreagă în intervalul 1020 până la 1020;

b) o variabilă reală cu un minim de 12 cifre zecimale de precizie și un interval de la 10 100 la


10100;

c) o variabilă caracter Kanji pe un procesor care acceptă Kanji cu kind=2.


Machine Translated by Google

Această pagină a fost lăsată goală în mod intenționat


Machine Translated by Google

3. Expresii și sarcini

3.1 Introducere

Am văzut în capitolul anterior cum putem construi „cuvintele” din Fortran – constantele, cuvintele cheie
și numele – din elementele de bază ale setului de caractere. În acest capitol vom descoperi cum aceste
entități pot fi combinate în continuare în „expresii” sau expresii și cum acestea, la rândul lor, pot fi
combinate în „propoziții” sau enunțuri.
Într-o expresie, descriem un calcul care urmează să fie efectuat de computer. Rezultatul calculului
poate fi apoi atribuit unei variabile. O succesiune de atribuiri este modul în care precizăm, pas cu pas,
seria de calcule individuale care trebuie efectuate, pentru a ajunge la rezultatul dorit. Există seturi
separate de reguli pentru expresii și atribuiri, în funcție de dacă operanzii în cauză sunt numerici, logici,
de tip caracter sau derivat și dacă sunt scalari sau matrice. Există, de asemenea, reguli separate pentru
atribuirea pointerului. Vom discuta pe rând fiecare set de reguli, inclusiv o descriere a expresiilor
relaționale care produc un rezultat de tip logic și sunt necesare în instrucțiunile de control (vezi capitolul
următor). Pentru a simplifica discuția inițială, începem prin a lua în considerare expresiile și atribuirile
care sunt definite intrinsec și nu implică nici matrice, nici entități de tipuri de date derivate.

O expresie în Fortran este formată din operanzi și operatori, combinați într-un mod care urmează
regulile sintaxei Fortran. O expresie simplă care implică un operator diadic (sau binar) are forma

operand operator operand

un exemplu fiind

x+y

iar un operator unar sau monadic are forma

operand operator

un exemplu fiind

-y

Tipul și tipul rezultatului sunt determinate de tipul și tipul operanzilor și nu depind de valorile acestora.
Operanzii pot fi constante, variabile sau funcții (vezi Capitolul 5), iar o expresie poate fi folosită ca
operand. În acest fel, putem construi expresii mai complicate, cum ar fi
Machine Translated by Google

34 Fortran modern explicat

operand operator operand operator operand

unde operanzii consecutivi sunt separați printr-un singur operator. Fiecare operand trebuie să aibă o
valoare definită.
Regulile lui Fortran afirmă că părțile expresiilor fără paranteze sunt evaluate succesiv de la stânga la
dreapta pentru operatorii de prioritate egală, cu excepția lui ** (exponentiație, vezi Secțiunea 3.2). Dacă
este necesar să se evalueze o parte dintr-o expresie sau subexpresie înainte de alta, parantezele pot fi
folosite pentru a indica ce subexpresie trebuie evaluată mai întâi. În

operator operand (operand operator operand)

subexpresia din paranteze va fi evaluată, iar rezultatul folosit ca operand pentru primul operator.

Dacă o expresie sau o subexpresie nu are paranteze, procesorului i se permite să evalueze o expresie
echivalentă, adică o expresie care are întotdeauna aceeași valoare în afară, eventual, de efectele rotunjirii
numerice. De exemplu, dacă a, b și c sunt variabile reale, expresia

a/b/c

ar putea fi evaluat ca

a/(b*c)

pe un procesor care se poate multiplica mult mai repede decât poate împărți. De obicei, astfel de
modificări sunt binevenite pentru programator, deoarece programul rulează mai repede, dar atunci când
acestea nu sunt (de exemplu, pentru că ar duce la mai multă rotunjire) ar trebui să fie introduse
paranteze, deoarece procesorul este obligat să le respecte.
Dacă doi operatori urmează imediat unul pe altul, ca în

operand operator operator operand

singura interpretare posibilă este că al doilea operator este unar. Astfel, există o regulă generală că un
operator binar nu trebuie să urmeze imediat după un alt operator.

3.2 Expresii numerice scalare

O expresie numerică este o expresie ai cărei operanzi sunt unul dintre cele trei tipuri numerice – întreg,
real și complex – și ai cărei operatori sunt
**
exponentiație
* / înmulțire, împărțire + - adunare,
scădere

Acești operatori sunt cunoscuți ca operatori numerici intrinseci și sunt enumerați aici în ordinea lor de
prioritate. În lipsa parantezelor, exponențiațiile se vor efectua înainte de înmulțiri și împărțiri, iar acestea
înainte de adunări și scăderi.
Remarcăm că semnul minus (-) și semnul plus (+) pot fi folosiți ca operatori unari, ca în
Machine Translated by Google

Expresii și teme 35

-impozit

Deoarece nu este permis în notația matematică obișnuită, un minus sau un plus unar nu trebuie să
urmeze imediat după alt operator. Când este nevoie de acest lucru, ca și pentru x y, parantezele
trebuie plasate în jurul operatorului și operandului acestuia:

X y)

Parametrul tip și tip tip al rezultatului unei operații unare sunt cele ale operandului.
Excepția de la regula de la stânga la dreapta menționată în secțiunea 3.1 se referă la exponențiații.
În timp ce expresia

-a+b+c

va fi evaluat de la stânga la dreapta ca

((-a)+b)+c

expresia

a**b**c

va fi evaluat ca

a**(b**c)

Pentru datele întregi, rezultatul oricărei diviziuni va fi trunchiat spre zero, adică la valoarea întregului
a cărei mărime este egală cu sau doar mai mică decât mărimea rezultatului exact.
Astfel, rezultatul

6/3 este 2
8/3 este 2 -8/3
este 2

Acest fapt trebuie întotdeauna avut în vedere ori de câte ori sunt scrise diviziuni întregi. În mod similar,
rezultatul

2**3 este 8

întrucât rezultatul de

2**(-3) este 1/(2**3)

care este zero.


Regulile Fortran permit unei expresii numerice să conțină operanzi numerici de diferite tipuri sau
parametri de tip fel. Aceasta este cunoscută ca expresie în mod mixt. Cu excepția ridicării unei valori reale
sau complexe la o putere întreagă, obiectul celui mai slab (sau mai simplu) dintre cele două tipuri de date
va fi convertit, sau constrâns, în tipul celui mai puternic. Rezultatul va fi și cel al tipului mai puternic. Dacă,
de exemplu, scriem

a*i
Machine Translated by Google

36 Fortran modern explicat

când a este de tip real și i este de tip întreg, atunci i va fi convertit într-un tip de date real înainte de
efectuarea înmulțirii, iar rezultatul calculului va fi, de asemenea, de tip real. Regulile sunt rezumate
pentru fiecare combinație posibilă pentru operațiile +, -, * și / în Tabelul 3.1 și pentru operația ** în
Tabelul 3.2. Funcțiile real și cmplx la care fac referire sunt definite în Secțiunea 8.3.1. În ambele tabele, I
reprezintă întreg, R pentru real și C pentru complex.

Tabelul 3.1. Tip de rezultat al unui .op. b, unde .op. este +, -, * sau /.
Tip Tip Valoare de Valoarea Tip de
de a de ba folosit b folosit rezultat

eu eu A b eu

IR real(a,tip(b)) b R
eu
C cmplx(a,0,tip(b)) b C
R eu A real (b, fel (a)) R
R Ra b R
R C cmplx(a,0,tip(b)) b C
C eu A cmplx(b,0,tip(a)) C
C Ra cmplx(b,0,tip(a)) b C
C Ca C

Tabelul 3.2. Tipul rezultatului a**b.


Tip Tip Valoarea lui a din ba Valoarea Tip de
folosită b folosit rezultat
eu eu A b eu

IR real(a,tip(b)) b R
eu
C cmplx(a,0,tip(b)) b C
R eu A b R
R Ra b R
R C cmplx(a,0,tip(b)) b C
C eu A b C
C Ra cmplx(b,0,tip(a)) b C
C Ca C

Dacă ambii operanzi sunt de tip întreg, parametrul de tip tip al rezultatului este cel al
operandului cu intervalul de exponent zecimal mai mare sau este dependent de procesor dacă
tipurile diferă, dar intervalele de exponent zecimal sunt aceleași. Dacă ambii operanzi sunt de tip
real sau complex, parametrul tip tip al rezultatului este cel al operandului cu precizie zecimală
mai mare sau este dependent de procesor dacă tipurile diferă, dar preciziile zecimale sunt aceleași. Daca un
Machine Translated by Google

Expresii și teme 37

operandul este de tip întreg iar celălalt este de real sau complex, parametrul de tip al rezultatului este cel al
operandului real sau complex.
Rețineți că o constantă literală într-o expresie în mod mixt este menținută la propria sa precizie, care
poate fi mai mică decât cea a expresiei. De exemplu, având în vedere o variabilă a de tip lung (Secțiunea
2.6.2), rezultatul lui a/1.7 va fi mai puțin precis decât cel al lui a/1.7_long.
În cazul ridicării unei valori complexe la o putere complexă, se ia valoarea principală1.
Ridicarea unei valori reale negative la o putere reală nu este permisă, deoarece rezultatul exact are probabil
o parte imaginară diferită de zero.

3.3 Variabile definite și nedefinite

Pe parcursul explicațiilor din acest capitol și din următoarele, ne vom referi adesea la o variabilă care devine
definită sau nedefinită. În capitolul anterior, am arătat cum o variabilă scalară poate fi chemată în existență
printr-o declarație ca

real :: viteza

În acest caz simplu, viteza variabilă nu are, la începutul execuției programului, o valoare definită. Este
nedefinit. Nu trebuie făcută nicio încercare de a face referire la valoarea sa, deoarece nu are niciuna. Un
mod obișnuit în care ar putea deveni definit este să i se atribuie o valoare:

viteza = 2.997

După executarea unei astfel de instrucțiuni de atribuire, aceasta are o valoare și acea valoare poate fi
referită, de exemplu, într-o expresie:

viteza*0,5

Pentru un obiect compus, toate subobiectele sale care nu sunt pointeri trebuie definite individual înainte
ca obiectul ca întreg să fie considerat definit. Astfel, se spune că o matrice este definită numai atunci când
fiecare dintre elementele sale este definită, un obiect de tip de date derivat este definit numai atunci când
fiecare dintre componentele sale non-pointer este definită și o variabilă caracter este definită numai atunci
când fiecare dintre ele. caracterele sunt definite.
O variabilă care este definită nu își păstrează neapărat starea de definiție pe toată durata execuției unui
program. După cum vom vedea în Capitolul 5, o variabilă care este locală unui singur subprogram devine
de obicei nedefinită atunci când controlul este returnat de la acel subprogram. În anumite circumstanțe,
este chiar posibil ca un singur element de matrice să devină nedefinit și acest lucru face ca șirul considerat
ca un întreg să devină nedefinit; o regulă similară este valabilă pentru entitățile de tip de date derivat și
pentru variabilele caracter.
Un mijloc de a specifica valoarea inițială a unei variabile este explicat în Secțiunea 7.5.
În cazul unui pointer, starea de asociere a pointerului poate fi nedefinită, asociată cu o țintă sau disociată,
ceea ce înseamnă că nu este asociat cu o țintă, dar are o stare definită care poate fi testată de funcția
asociată (Secțiunea 8.2) . Chiar dacă un pointer este asociat cu o țintă, ținta în sine poate fi definită sau
nedefinită. Sunt furnizate mijloace pentru a specifica starea inițială de disociat (a se vedea secțiunea 7.5.3).

1Valoarea principală a lui ab este exp(b(log|a|+iarga)), cu π < arga π.


Machine Translated by Google

38 Fortran modern explicat

3.4 Atribuire numerică scalară

Forma generală a unei atribuiri numerice scalare este

variabilă = expr

unde variabilă este o variabilă numerică scalară și expr este o expresie numerică scalară. Dacă expr nu este de
același tip sau fel ca variabila, ea va fi convertită în acel tip și tip înainte de efectuarea atribuirii, conform setului de
reguli prezentat în Tabelul 3.3 (funcția int este definită în Secțiunea 8.3.1). ).

Tabelul 3.3. Conversie numerică pentru variabila declarație de atribuire = expr.

Tip de variabilă Valoare alocată întreg

int(expr, kind(variable)) real


kind(variable))
real(expr, cmplx(expr,
kind=kind(variable))

complex

Remarcăm că, dacă tipul de variabilă este întreg, dar expr nu este, atunci atribuirea va duce la o pierdere de
precizie, cu excepția cazului în care expr se întâmplă să aibă o valoare integrală. În mod similar, atribuirea unei
expresii reale unei variabile reale de un fel cu mai puțină precizie va provoca, de asemenea, o pierdere de precizie,
iar atribuirea unei cantități complexe unei variabile necomplexe implică pierderea părții imaginare. Astfel, valorile
din i și a în urma atribuirilor

i = 7,3 ! i de tipul implicit întreg


a = (4,01935, 2,12372) ! a de tip implicit real

sunt 7 și, respectiv, 4,01935. De asemenea, dacă o constantă literală este atribuită unei variabile cu o precizie mai
mare, rezultatul va avea acuratețea constantei. De exemplu, având în vedere o variabilă a de tip lung (Secțiunea
2.6.2), rezultatul

a = 1,7

va fi mai puțin precis decât cel al

a = 1,7_lung

3.5 Operatori relaționali scalari

Este posibil în Fortran să testăm dacă valoarea unei expresii numerice are o anumită relație cu cea a altei expresii
și, în mod similar, pentru expresiile de caractere. Operatorii relaționali
sunt

< sau .lt. mai puțin decât

<= sau .le. mai mic sau egal == sau .eq.

egal /= sau .ne. nu este egal cu > sau .gt.

mai mare decât >= sau .ge. mai mare

sau egal
Machine Translated by Google

Expresii și teme 39

Dacă una sau ambele expresii sunt complexe, sunt disponibili doar operatorii == și /= (sau .eq.
și .ne.).
Rezultatul unei astfel de comparații este una dintre valorile logice implicite .true.
sau .false. și vom vedea în capitolul următor cât de importante sunt astfel de teste în
controlul fluxului unui program. Exemple de expresii relaționale (pentru i și j de tipul
întreg, a și b de tip real și char1 de tipul caracter implicit) sunt
i<0 expresie relațională întreagă
a<b expresie relațională reală
a+b > ij expresie relațională în mod mixt
char1 == expresie relațională cu caracterul „Z”.

În a treia expresie de mai sus, observăm că cele două componente sunt de tipuri numerice diferite. În
acest caz, și ori de câte ori una dintre cele două componente sau ambele constau din expresii numerice,
regulile prevăd că componentele trebuie evaluate separat și convertite la tipul și tipul sumei lor înainte
de a se face comparația. Astfel, o expresie relațională precum

a+b <= ij

va fi evaluat prin conversia rezultatului (ij) la tipul real.


Pentru compararea caracterelor, tipurile trebuie să fie aceleași, iar literele sunt comparate din
stânga până când se găsește o diferență sau se constată că șirurile sunt identice. Dacă lungimile diferă,
cea mai scurtă este considerată căptușită cu spații libere2 în partea dreaptă. Două șiruri de dimensiune zero
sunt considerate a fi identice.
Nicio altă formă de operator relațional mixt nu este intrinsec disponibilă, deși un astfel de
operator poate fi definit (Secțiunea 3.8). Operatorii numerici au prioritate față de operatorii
relaționali.

3.6 Expresii logice scalare și atribuiri

Constantele logice, variabilele și funcțiile pot apărea ca operanzi în expresiile logice.


Operatorii logici, în ordinea descrescătoare a priorității, sunt:

operator
unar: .nu. negație logică

operatori binari:
.și. intersecție logică
.sau. uniune logică .eqv.
i .neqv. echivalența și nonechivalența logică

Dacă presupunem o declarație logică a formei

logic :: i,j,k,l

2Aici și în altă parte, caracterul de completare gol folosit pentru un tip care nu este implicit este dependent de procesor.
Machine Translated by Google

40 Fortran modern explicat

atunci următoarele sunt expresii logice valide:

.nu.jj .și.
ki .or.
teren. .nu.j ( .nu.k .și.
j .neqv. .nu.l) .sau. i

În prima expresie notăm folosirea lui .nu. ca operator unar. În cea de-a treia expresie, regulile de
precedență implică faptul că subexpresia l. și..nu.j va fi evaluată mai întâi, iar rezultatul combinat cu i. În
ultima expresie, cele două subexpresii .not.k.and.j și .not.l vor fi evaluate și comparate pentru
neechivalență. Rezultatul comparației, .adevărat. sau .fals., se va combina cu i.

Parametrul tip tip al rezultatului este cel al operandului pentru .not., iar pentru celelalte este
cel al operanzilor dacă au același fel sau dependent de procesor altfel.
Remarcăm că .or. operatorul este un operator inclusiv; .neqv. operatorul oferă o
logic exclusiv sau (a.și..nu.b .sau. .nu.a.și.b).
Rezultatul oricărei expresii logice este valoarea adevărată sau falsă, iar această valoare poate fi atunci
atribuit unei variabile logice, cum ar fi elementul 3 al steagului matricei logice din exemplu

steag(3) = ( .nu. k .eqv. l) .sau. j

Valorile parametrului tip tip ale variabilei și expresiei nu trebuie să fie identice.
O variabilă logică poate fi setată la o valoare predeterminată printr-o instrucțiune de atribuire:

steag(1) = .adevărat.
steag(2) = .fals.

În exemplele de mai sus, toți operanzii și rezultatele au fost de tip logic - fără alte date
tipului i se permite să participe la o operație sau o misiune logică intrinsecă.
Rezultatele mai multor expresii relaționale pot fi combinate într-o expresie logică,
și atribuit, ca în

real :: a, b, x, y logic :: cond

:
cond = a>b .or. x<0,0 .și. y>1,0

unde notăm preceden a operatorilor rela ionali fa ă de operatorii logici. Dacă valoarea unei astfel de
expresii logice poate fi determinată fără evaluarea unei subexpresii, procesorului i se permite să nu
evalueze subexpresia. Un exemplu este

i<=10 . i. ary(i)==0 ! pentru o matrice reală (10)

când i are valoarea 11. Cu toate acestea, programatorul nu trebuie să se bazeze pe un astfel de
comportament – un indice în afara limitelor poate fi referit dacă procesorul alege să evalueze
subexpresia din dreapta înaintea celei din stânga. Revenim la acest subiect în Secțiunea 5.10.1.
Machine Translated by Google

Expresii și teme 41

3.7 Expresii de caractere scalare și atribuiri

Singurul operator intrinsec pentru expresiile de caractere este operatorul de concatenare //, care are ca
efect combinarea a doi operanzi de caractere într-un singur rezultat de caractere. De exemplu, rezultatul
concatenării celor două constante de caractere AB și CD, scrise ca

„AB”//„CD”

este șirul de caractere ABCD. Operanzii trebuie să aibă aceleași valori ale parametrilor, dar pot fi variabile
caracter, constante sau funcții. De exemplu, dacă cuvântul1 și cuvântul2 sunt ambele de tip implicit și
lungimea 4 și conțin șirurile de caractere LOOP și, respectiv, HOLE, rezultatul

cuvântul1(4:4)//cuvântul2(2:4)

este șirul POLE.


Lungimea rezultatului unei concatenări este suma lungimilor operanzilor. Astfel, lungimea rezultatului
de

cuvânt1//cuvânt2//'S'

este 9, care este lungimea șirului LOOPHOLES.


Rezultatul unei expresii caracter poate fi atribuit unei variabile caracter de același fel. Asumând
declara iile

caracter(len=4) :: caracter 1, caracter 2


caracter(len=8) :: rezultat

putem scrie

char1 = 'orice '


char2 = 'carte'
rezultat = char1//car2

În acest caz, rezultatul va conține acum șirul orice carte. Observăm în aceste exemple că lungimile părților
din stânga și din dreapta celor trei atribuiri sunt în fiecare caz egale.
Dacă, totuși, lungimea rezultatului din partea dreaptă este mai mică decât lungimea părții stângi, atunci
rezultatul este plasat în partea cea mai din stânga a părții stângi, iar restul este umplut cu caractere goale.
Astfel, în

caracter(len=5) :: umplere
umplere(1:4) = 'AB'

fill(1:4) va avea valoarea ABbb (unde b reprezintă un caracter gol). Valoarea umplerii (5:5) rămâne
nedefinită, adică nu conține o valoare specifică și nu trebuie utilizată într-o expresie. În consecință,
umplerea este, de asemenea, nedefinită. Pe de altă parte, când partea din stânga este mai scurtă decât
rezultatul din partea dreaptă, capătul din dreapta al rezultatului este trunchiat. Rezultatul

caracter(len=5) :: trunc8
trunc8 = 'TUNCATE'
Machine Translated by Google

42 Fortran modern explicat

este de a plasa în trunc8 șirul de caractere TRUNC. Dacă o parte din stânga este de lungime zero, nu are loc nicio
atribuire.
Părțile din stânga și din dreapta ale unei sarcini se pot suprapune. Într-un astfel de caz, este întotdeauna
vechile valori care sunt folosite în expresia din partea dreaptă. De exemplu, sarcina

rezultat(3:5) = rezultat(1:3)

este valid și dacă rezultatul a început cu valoarea ABCDEFGH, ar rămâne cu valoarea ABCDEFGH.

Alte mijloace de manipulare a caracterelor și șirurilor de caractere, prin funcții intrinseci,


sunt descrise în secțiunile 8.5 și 8.6.

3.8 Constructori de structură și operatori definiți scalari

Nu sunt disponibili automat operatori pentru tipurile derivate, dar o structură poate fi construită din expresii
pentru componentele sale, la fel cum o structură constantă poate fi construită din constante (Secțiunea 2.9).
Forma generală a unui constructor de structură este

tip-nume (expr-list)

unde expr-list specifică valorile componentelor. De exemplu, dat fiind tipul

tip char10
integer :: lungime
character(len=10) :: valoare final tip
char10

și variabilele

character(len=4) :: char1, char2

următoarea este o valoare de tip char10:

char10(8, char1//char2)

Fiecare expresie din expr-list corespunde unei componente a structurii; dacă nu este o componentă pointer,
valoarea este atribuită componentei conform regulilor de atribuire intrinsecă; dacă este o componentă pointer,
expresia trebuie să fie o țintă validă pentru aceasta,3 ca într-o instrucțiune de atribuire a pointerului (Secțiunea
3.12).
Când un programator definește un tip derivat și dorește ca operatorii să fie disponibili, el sau ea trebuie să
definească și operatorii. Pentru un operator binar, acest lucru se realizează prin scrierea unei funcții, cu două
intenții în argumente, care specifică modul în care rezultatul depinde de operanzi și un bloc de interfață care
asociază funcția cu jetonul operatorului (funcțiile, intenția și blocurile de interfață vor fi explicat pe deplin în
capitolul 5). De exemplu, dat fiind tipul

tip interval real ::


sfârșitul inferior, superior tip
interval

3 În special, nu trebuie să fie o constantă.


Machine Translated by Google

Expresii și teme 43

care reprezintă intervale de numere între o limită inferioară și una superioară, putem defini adăugarea printr-un
modul (Secțiunea 5.5) care conține procedura

function add_interval(a,b) tip(interval)


tip(interval), intent(in) :: a, b :: add_interval
add_interval%lower = a%lower + b%lower ! Codul de
producție ar add_interval%upper = a%upper + b%upper ! permite rotunjire. sfârșitul funcției
add_interval

și blocul de interfață (Secțiunea 5.18)

procedura de modul operator


de interfață(+) add_interval end interface

Această funcție ar fi invocată într-o expresie precum

y+z

pentru a efectua această operație de adăugare definită de programator pentru variabilele scalare y și
z de tip interval. Un operator unar este definit de un bloc de interfață și de o funcție cu o singură
intenție în argument.
Jetonul operator poate fi oricare dintre jetoanele utilizate pentru operatorii intrinseci sau poate fi o secvență
de până la 31 de litere4 închisă cu zecimale, altele decât .true. sau .fals. . Un

exemplu este

.sumă.

În acest caz, linia antetului blocului de interfață va fi scrisă ca

operator de interfață (.sum.)

iar expresia ca

y.sum.z

Dacă se folosește un jeton intrinsec, numărul de argumente trebuie să fie același ca și pentru operația
intrinsecă, precedența operației este ca pentru operația intrinsecă, iar un minus sau plus unar nu trebuie să
urmeze imediat după alt operator. În caz contrar, are cea mai mare prioritate pentru operatorii unari definiți și
cea mai mică prioritate pentru operatorii binari definiți.
Setul complet de precedențe este prezentat în Tabelul 3.4. Acolo unde este necesară o altă prioritate
în cadrul unei expresii, trebuie folosite paranteze.
Păstrarea precedențelor intrinseci este utilă atât pentru lizibilitatea expresiilor, cât și pentru eficiența cu care
un compilator le poate interpreta. De exemplu, dacă + este folosit pentru unirea mulțimii și * pentru intersecția
mulțimii, putem interpreta expresia

i*j + k

4 Un token de operator poate avea până la 63 de caractere în Fortran 2003.


Machine Translated by Google

44 Fortran modern explicat

Tabelul 3.4. Precedența relativă a operatorilor (în ordine descrescătoare).


Tip de operație când este intrinsecă Operator


operator definit monadic (unar).
Numeric **

Numeric * sau /

Numeric monadic + sau -

Numeric diadic + sau - //


Caracter

Relațional == /= < <= > >=

.echiv. .ne. .lt. .le. .gt. .GE.

Logic .nu.

Logic .și.

Logic .sau.

Logic .echiv. sau .neqv.



operator diadic (binar) definit

pentru mulțimile i, j și k fără dificultate.


Dacă oricare dintre jetoanele intrinseci == și .eq. este folosit, definiția se aplică ambelor jetoane, astfel încât
acestea să fie întotdeauna echivalente. Același lucru este valabil și pentru celelalte perechi echivalente de
operatori relaționali.
Rețineți că un operator unar definit care nu utilizează un simbol intrinsec poate urma imediat
după un alt operator ca în

y .sum. .invers. X

Operatorii pot fi definiți pentru orice tip de operanzi, cu excepția cazului în care există o operație intrinsecă
pentru operator și tipuri. De exemplu, am putea dori să putem adăuga un număr de interval la un real obișnuit,
ceea ce se poate face prin adăugarea procedurii

funcția add_interval_real(a,b) :: add_interval_real


tip(interval)
tip(interval), intent(in) :: a real, intent(in) :: b
add_interval_real%lower = a%lower+b! Codul de
producție ar add_interval_real%upper = a%upper+b! permite rotunjire.

funcția final add_interval_real

și schimbarea blocului de interfață în

procedura de modul operator


de interfață(+) add_interval, add_interval_real end interface
Machine Translated by Google

Expresii și teme 45

Rezultatul unei operații definite poate avea orice tip. Tipul rezultatului, precum și al acestuia
valoare, trebuie specificată de funcție.
Rețineți că o operație care este definită intrinsec nu poate fi redefinită; astfel in

real :: a, b, c
:
c=a+b

sensul operației este întotdeauna fără ambiguitate.

3.9 Atribuții scalare definite

Atribuirea unei expresii de tip derivat unei variabile de același tip este disponibilă automat și are loc component
cu component. De exemplu, dacă a este de tipul intervalului definit la începutul Secțiunii 3.8, putem scrie

a = interval(0,0, 1,0)

(Constructorii de structură au fost întâlniți și în Secțiunea 3.8).


În alte circumstanțe, totuși, am putea dori să definim o acțiune diferită pentru o atribuire care implică un
obiect de tip derivat și, într-adevăr, acest lucru este posibil. O atribuire poate fi redefinită sau o altă atribuire
poate fi definită printr-o subrutină cu două argumente, primul având intent out sau intent inout și corespunzând
variabilei și al doilea având intenție în și corespunzătoare expresiei (subrutinele vor fi, de asemenea, tratate
complet). în capitolul 5). În cazul unei atribuiri care implică un obiect de tip derivat și un obiect de alt tip, trebuie
furnizată o astfel de definiție. De exemplu, atribuirea de reali la intervale și invers ar putea fi definită de un modul
care conține subrutinele

subrutina real_from_interval(a,b)
real, intent(out) :: A

tip(interval), intent(in) :: ba = (b%inferior +


b%sus)/2
sfârșitul subrutinei real_from_interval

și

subrutine interval_from_real(a,b)
tip(interval), intenție(out) :: a real, intent(in) :: b

a% mai mic = b
a%upper = b end
subrutine interval_from_real

și blocul de interfață

alocarea interfeței(=) procedura


modulului real_from_interval, interval_from_real end interface
Machine Translated by Google

46 Fortran modern explicat

Având în vedere acest lucru, putem scrie

tip(interval) :: aa = 0,0

O atribuire definită nu trebuie să redefinească semnificația unei atribuiri intrinseci pentru tipurile
intrinseci, adică o atribuire între două obiecte de tip numeric, de tip logic sau de tip caracter cu același
parametru de tip, dar poate redefini semnificația unei atribuiri intrinseci pentru două obiecte de același
tip derivat. De exemplu, pentru o atribuire între două variabile de tipul char10 (Secțiunea 3.8) care copiază
doar partea relevantă a componentei caracterului, am putea scrie

subrutine assign_string (stânga, dreapta) tip(char10),


intent(out) :: stânga tip(char10), intent(in) ::
dreapta stânga%lungime = dreapta%lungime
stânga%valoare(1:left%lungime) = dreapta
%value(1:dreapta%lungime)
sfâr itul subrutinei assign_string

Atribuirea intrinsecă pentru un obiect de tip derivat implică întotdeauna o atribuire intrinsecă
pentru toate componentele sale non-pointer, chiar dacă o componentă este de tip derivat pentru care
a fost redefinită atribuirea.

3.10 Expresii matrice

Până acum, în acest capitol, am presupus că toate entitățile dintr-o expresie sunt scalare.
Cu toate acestea, oricare dintre operațiile intrinseci unare poate fi, de asemenea, aplicată unei matrice
pentru a produce o altă matrice de aceeași formă (rang și extinderi identice, vezi secțiunea 2.10) și având
valoarea fiecărui element egală cu cea a operației aplicate elementului corespunzător al operandul. În
mod similar, operațiunile intrinseci binare pot fi aplicate unei perechi de rețele de aceeași formă pentru
a produce o matrice de acea formă, cu valoarea fiecărui element egală cu cea a operației aplicate
elementelor corespunzătoare ale operanzilor. Unul dintre operanzii unei operații binare poate fi un
scalar, caz în care rezultatul este ca și cum scalarul ar fi fost difuzat într-o matrice de aceeași formă ca
operandul matricei. Având în vedere declarațiile de matrice

real, dimensiune(10,20) :: a,b real,


dimensiune(5) :: v

următoarele sunt exemple de expresii matrice:

a/b ! Matrice de formă (10,20), cu elementele a(i,j)/b(i,j)


v+1. ! Matrice de forma (5), cu elemente v(i)+1.0 5/v+a(1:5,5) ! Matrice
de forma (5), cu elementele 5/v(i)+a(i,5) a == b
! Matrice logică de formă (10,20), cu elemente ! .Adevărat. dacă a(i,j)
== b(i,j) și .fals. in caz contrar

Se spune că două rețele de aceeași formă sunt conformabile și un scalar este conformabil cu orice
matrice.
Machine Translated by Google

Expresii și teme 47

Rețineți că corespondența este după poziție în extindere și nu după valoarea indicelui. De


exemplu,

a(2:9,5:10) + b(1:8,15:20)

are valori ale elementelor

a(i+1,j+4) + b(i,j+14), i=1,2,...,8, j=1,2,...,6

Acest lucru poate fi reprezentat pictural ca în Figura 3.1.

Figura 3.1 Suma a două secțiuni de matrice.

a(1,1) a(1,20)

a(2,5) a(2,10)

a(9,5) a(9,10)

a(10,1) a(10,20) rezultat


+ =
8x6
b(1,1) b(1,15)
b(1,20)

b(8,15) b(8,20)

b(10,1) b(10,20)

Ordinea în care sunt executate operațiile scalare din orice expresie matrice nu este specificată în
standard, permițând astfel unui compilator să organizeze execuția eficientă pe un computer
vectorial sau paralel.
Orice operator scalar intrinsec poate fi aplicat în acest fel matricelor și perechilor matrice-scalare.
Pentru operatorii derivați, programatorul poate defini o procedură elementară cu aceste proprietăți
(vezi Secțiunea 6.11). El sau ea poate defini, de asemenea, operatori direct pentru anumite ranguri
sau perechi de ranguri. De exemplu, tipul

tip matrice
real :: element end
tip matrice

ar putea fi definită pentru a avea operații scalare care sunt identice cu operațiile pentru reale, dar
pentru tablouri de rangurile unu și doi, operatorul *The
este definit pentru a însemna înmulțirea matricei.
Machine Translated by Google

48 Fortran modern explicat

matricea de tip ar fi, prin urmare, potrivită pentru aritmetica matriceală, în timp ce realele nu sunt potrivite
deoarece înmulțirea pentru tablourile reale se face element cu element. Acest lucru este discutat în continuare
în Secțiunea 6.7.

3.11 Alocarea matricei


Prin atribuire intrinsecă, o expresie matrice poate fi atribuită unei variabile matrice de aceeași formă, care
este interpretată ca și cum fiecare element al expresiei ar fi fost atribuit elementului corespunzător al
variabilei. De exemplu, cu declarațiile de la începutul ultimei secțiuni, atribuirea

a = a + 1,0

înlocuiește a(i,j) cu a(i,j)+1,0 pentru i = 1, 2, ... , 10 și j = 1, 2, ... , 20. Rețineți că, ca și


în cazul expresiilor, corespondența elementului este mai degrabă în funcție de poziție în cadrul extinderii decât
după valoarea indicelui. Acest lucru este ilustrat de exemplu

a(1,11:15) = v ! a(1,j+10) este atribuit din ! v(j), j=1,2,...,5

O expresie scalară poate fi atribuită unui tablou, caz în care valoarea scalară este difuzată către toate
elementele matricei.
Dacă expresia include o referință la variabila matrice sau la o parte a acesteia, expresia este interpretată
ca fiind evaluată complet înainte de începerea atribuirii. De exemplu, afirmația

v(2:5) = v(1:4)

rezultă ca fiecare element v(i) pentru i = 2, 3, 4, 5 având valoarea pe care v(i-1) o avea înainte de începerea
atribuirii. Această regulă este exact în paralel cu regula pentru subșiruri care a fost explicată în Secțiunea
3.7. Ordinea în care sunt alocate elementele matricei nu este specificată de standard, pentru a permite
optimizări.
Seturile de funcții intrinseci numerice și matematice, ale căror rezultate pot fi utilizate ca operanzi în
expresii scalare sau matrice și în atribuiri, sunt descrise în Secțiunile 8.3 și 8.4.

Dacă o atribuire definită (Secțiunea 3.9) este definită de o subrutină elementară (Secțiunea 6.11), aceasta
poate fi utilizată pentru a atribui o valoare scalară unei matrice sau o valoare a matricei unui tablou de
aceeași formă. O subrutină separată poate fi furnizată pentru orice combinație specială de ranguri și va
suprascrie atribuirea elementară. Dacă nu există o atribuire definită elementară, atribuirea intrinsecă este
încă disponibilă pentru acele combinații de ranguri pentru care nu există o atribuire definită corespunzătoare.

O formă de alocare a matricei sub o mască este descrisă în Secțiunea 6.8 și alocarea exprimată cu
ajutorul indicilor în Secțiunea 6.9.

3.12 Indicatori în expresii și sarcini


Un pointer poate apărea ca o variabilă în expresiile și atribuțiile pe care le-am luat în considerare până acum
în acest capitol, cu condiția să aibă o asociere validă cu o țintă. Ținta este
Machine Translated by Google

Expresii și teme 49

accesat fără a fi nevoie de un simbol explicit de dereferențiere. În special, dacă ambele părți ale unei
declarații de atribuire sunt pointeri, datele sunt copiate de la o țintă la cealaltă țintă.
Uneori apare nevoia unui alt tip de misiune. Este posibil să dorim ca indicatorul din stânga să indice o
altă țintă, mai degrabă decât ca ținta actuală să obțină date noi. Adică dorim ca descriptorul să fie
modificat. Aceasta se numește alocarea pointerului și are loc într-o instrucțiune de atribuire a pointerului:

pointer => țintă

unde pointer este numele unui pointer sau designatorul unei componente de structură care este un
pointer, iar ținta este de obicei o variabilă, dar poate fi și o referință la o funcție cu valoare de pointer
(vezi Secțiunea 5.10). De exemplu, afirmațiile

x => z
a => c

au variabile ca ținte și sunt necesare pentru prima înmulțire a matricei din Secțiunea 2.13, pentru a face
ca x să se refere la z și a să se refere la c. Declaratia

x => null()

(funcția null este descrisă în Secțiunea 8.15) anulează x. Atribuirea pointerului are loc și pentru o
componentă pointer a unei structuri atunci când structura apare în partea stângă a unei atribuiri
obișnuite. De exemplu, să presupunem că am folosit intrarea de tip din Figura 2.3 din Secțiunea 2.13
pentru a construi un lanț de intrări și dorim să adăugați o intrare nouă în față. Dacă primul indică la
prima intrare și curent este un pointer scalar de tip intrare, instrucțiunile

allocate (current) current


= entry(new_value, new_index, first) first => current

alocați o nouă intrare și conectați-o în partea de sus a lanțului. Declarația de atribuire are efect

current%next => primul

și stabilește legătura. Declarația de atribuire a indicatorului oferă mai întâi noua intrare ca țintă, fără a
modifica prima intrare veche. Misiunea obișnuită

primul = curent

ar fi incorectă deoarece ținta ar fi copiată, distrugând prima intrare veche, corespunzătoare asignărilor
componente

prima%valoare = valoarea%actuală ! Componentele! bătrânii


primul%index = curent%index se pierd mai întâi.
first%next => curent%next

În cazul în care lanțul începea cu lungimea doi și consta din

primul: (1.0, 10, asociat) first%next :


(2.0, 15, null)
Machine Translated by Google

50 Fortran modern explicat

în urma executării primului set de instruc iuni ar avea lungimea trei i consta din

first : (4.0, 16, asociat) first%next : (1.0, 10, asociat)


first%next%next : (2.0, 15, null)

Dacă ținta dintr-o instrucțiune de atribuire a indicatorului este o variabilă care nu este ea însăși un pointer sau un
subobiect al unui pointer țintă, acesta trebuie să aibă atributul target. De exemplu, afirmația

real, dimensiune(10), tinta :: y

declară y că are atributul target. Orice subobiect non-pointer al unui obiect cu atributul țintă are, de
asemenea, atributul țintă. Atributul target este necesar în scopul optimizării codului de către
compilator. Este foarte util pentru compilator să știe că o variabilă care nu este un pointer sau o țintă
nu poate fi accesată de o țintă pointer.
Ținta dintr-o instrucțiune de atribuire a pointerului poate fi un subobiect al unei ținte pointer. Pentru
exemplu, având în vedere declarația

caracter(len=80), dimensiune(:), indicator :: pagina

și o asociere adecvată, următoarele sunt toate țintele permise:

pagina, pagina(10), pagina(2:4), pagina(2)(3:15)

Rețineți că este suficient ca indicatorul să fie la orice nivel de selecție a componentelor. De exemplu,
având în vedere declarația

tip(intrare) :: nod

care are în continuare o componentă pointer, vezi Secțiunea 2.13 și o asociere adecvată,
node%next%value este o țintă permisă.
Dacă ținta dintr-o instrucțiune de atribuire a indicatorului este ea însăși o țintă a indicatorului,
atunci are loc o copie directă a descriptorului. Dacă starea asocierii pointerului este nedefinită sau
dezasociată, această stare este copiată.
Dacă ținta este un pointer sau un subobiect al unei ținte de pointer, noua asociere este cu ținta
acelui pointer și nu este afectată de nicio modificare ulterioară a stării sale de asociere a pointerului.
Acest lucru este ilustrat de următorul exemplu. Secvența

b => c ! c are atributul target


a => b
anula (b)

va lăsa un încă indicat către c.


Tipul, parametrii de tip și rangul pointerului și țintei într-o instrucțiune de atribuire a pointerului
trebuie să fie fiecare același. Dacă pointerul este o matrice, acesta își ia forma și se limitează de la țintă.
Limitele sunt așa cum ar fi returnate de funcțiile lbound și ubound (Secțiunea 8.12.2) pentru țintă, ceea
ce înseamnă că o secțiune de matrice sau o expresie de matrice este întotdeauna considerată ca având
valoarea 1 pentru o limită inferioară și extinderea pentru limita corespunzătoare. limita superioară.5

5 În Fortran 2003, poate fi specificată o limită inferioară, vezi Secțiunea 15.6


Machine Translated by Google

Expresii și teme 51

Fortran este neobișnuit prin faptul că nu necesită un caracter special pentru o referință la o țintă de
pointer, dar necesită unul pentru a distinge atribuirea pointerului de atribuirea obișnuită. Motivul pentru
această alegere a fost așteptarea că majoritatea programelor de inginerie și științifice se vor referi la
datele țintă mult mai des decât își schimbă țintele.

3.13 Declarația de anulare


Un pointer poate fi disociat în mod explicit de ținta sa prin executarea unei instrucțiuni nullify.
Forma sa generală este

nullify(listă-obiect-pointer)

Nu trebuie să existe dependențe între obiecte, pentru a permite procesorului să anuleze obiectele unul
câte unul în orice ordine. Declarația este, de asemenea, utilă pentru a da starea dezasociată unui pointer
nedefinit. Un avantaj de a anula pointerii, mai degrabă decât de a le lăsa nedefinite, este că aceștia pot fi
apoi testați de funcția intrinsecă asociată (Secțiunea 8.2).
De exemplu, sfârșitul lanțului din Secțiunea 3.12 va fi marcat ca un pointer disociat dacă instrucțiunea

anulează (în primul rând)

este executat inițial pentru a crea un lanț de lungime zero. Deoarece adesea există și alte modalități de a
accesa o țintă (de exemplu, printr-un alt pointer), instrucțiunea nullify nu dealoca ținte. Dacă dealocarea
este, de asemenea, necesară, ar trebui să fie executată o instrucțiune de dealocare (Secțiunea 6.5.3).

3.14 Rezumat
În acest capitol, am văzut cum pot fi formate expresii scalare și matrice de tipuri numerice, logice,
caractere și derivate și cum pot fi făcute atribuirile corespunzătoare ale rezultatelor. Au fost prezentate
și expresiile relaționale și utilizarea pointerilor. Acum avem informațiile necesare pentru a scrie secțiuni
scurte de cod care formează o secvență de instrucțiuni care să fie efectuate una după alta. În capitolul
următor vom vedea cum pot fi construite secvențe mai complicate, care implică ramificare și iterație.

Exerciții

1. Dacă toate variabilele sunt scalari numerici, care dintre următoarele sunt expresii numerice valide?
a+b -c
a+-c d+(-f)
(a+c)**(p+q) - (a+c)(p+q) 4.
(x+y)**i ((ad)-(a+4.*x)+1)

2. În următoarele expresii, adăugați parantezele care corespund regulilor de precedență ale lui Fortran (presupunând
că a, cf sunt scalari reali, in sunt scalari logici și b este un tablou logic); de exemplu, a+d**2/c devine a+((d**2)/
c).
Machine Translated by Google

52 Fortran modern explicat

c+4.*f
4.*g-a+d/2.
a**e**c**d
a*ec**d/a+ei .si.
j .sau. k .nu. l .sau. .nu.
eu si. m .neqv. nb(3).și.b(1).sau.b(6).sau..nu.b(2)

3. Care sunt rezultatele următoarelor expresii?

3+4/2 6/4/2
3.*4**2 3.***3/2
-1***2 (-1.)**3

4. O variabilă cu caracter scalar r are lungimea de opt. Care este conținutul lui r după fiecare dintre următoarele
sarcini?

r = 'ABCDEFGH' r =
'ABCD'//'01234'
r(:7) = „ABCDEFGH”
r(:6) = „ABCD”

5. Care dintre următoarele expresii logice sunt valide dacă b este o matrice logică?
.nu.b(1).și.b(2) .sau.b(1)
b(1).sau..nu.b(4) b(2)(.și.b(3).sau.b(4))

6. Dacă toate variabilele sunt scalari reali, care dintre următoarele expresii relaționale sunt valide?
d .le. c p .lt.t>0 x+y <
x-1 /= y 3 .or. > 4. q.eq.r . i. s>t
d.lt.c.and.3.0

7. Scrieți expresii pentru a calcula:

a) perimetrul unui pătrat de latura l;


b) aria unui triunghi de baza b si inaltimea h;
c) volumul unei sfere de raza r.

8. Un articol costă n cenți. Scrieți o declarație pentru variabilele adecvate și declarațiile de atribuire care
calculează modificarea care urmează să fie dată de la o bancnotă de 1 USD pentru orice valoare de n de la
1 la 99, folosind monede cu valori nominale 1, 5, 10 și 25 de cenți.

9. Având în vedere declarația de tip pentru interval din secțiunea 3.8, definițiile lui + date în secțiunea 3.8,
definițiile atribuirii date în secțiunea 3.9 și declarațiile

tip(interval) :: a,b,c,d real


:: r

care dintre următoarele afirmații sunt valide?


a=b+c
c = b + 1,0
d=b+1
r=b+c
a=r+2
Machine Translated by Google

Expresii și sarcini 53

10. Având în vedere declara iile de tip

real, dimensiune(5,6) :: a, b
real, dimensiune(5) :: c

care dintre următoarele afirmații sunt valide?


a=b c = a(:,2) + b(5,:5) c = a(2,:) +
a = c+1,0 b(:,5)
a(:,3) = c b(2:,3)=c+ b(:5,3)
Machine Translated by Google

Această pagină a fost lăsată goală în mod intenționat


Machine Translated by Google

4. Constructele de control

4.1 Introducere

Am învățat în capitolul anterior cum pot fi scrise instrucțiunile de atribuire și cum acestea pot fi ordonate
una după alta pentru a forma o secvență de cod care este executată pas cu pas. În majoritatea calculelor,
totuși, această succesiune simplă de afirmații este prin ea însăși inadecvată pentru formularea problemei.
De exemplu, este posibil să dorim să urmăm una dintre cele două căi posibile printr-o secțiune de cod, în
funcție de faptul dacă o valoare calculată este pozitivă sau negativă. Este posibil să dorim să însumăm
1000 de elemente dintr-o matrice și să facem acest lucru prin scrierea a 1000 de adăugiri și atribuiri este
în mod clar obositor; este necesară în schimb capacitatea de a repeta peste o singură adăugare. Este
posibil să dorim să trecem controlul de la o parte a unui program la alta sau chiar să oprim complet
procesarea.
În toate aceste scopuri, avem disponibile în Fortran diverse facilități pentru a permite controlul fluxului
logic prin instrucțiunile programului. Cea mai importantă formă este cea a unui construct bloc, adică un
construct care începe cu o instrucțiune inițială a cuvântului cheie, poate avea instrucțiuni de cuvinte cheie
intermediare și se termină cu o instrucțiune terminală care se potrivește și care poate fi introdus doar la
instrucțiunea inițială. Fiecare secvență de instrucțiuni dintre instrucțiunile de cuvinte cheie este numită
bloc. Un bloc poate fi gol, deși astfel de cazuri sunt rare.
Construcțiile bloc pot fi imbricate, adică un bloc poate conține un alt construct bloc. Într-un astfel de
caz, blocul trebuie să conțină întregul construct interior. Execuția unui bloc începe întotdeauna cu prima
declarație.

4.2 Construcția și declarația if

Construcția if conține una sau mai multe secvențe de instrucțiuni (blocuri), dintre care cel mult una este
aleasă pentru execuție. Forma generală este prezentată în Figura 4.1. Aici și pe tot parcursul cărții folosim
paranteze pătrate pentru a indica elementele opționale, urmate de puncte dacă poate exista un număr
(inclusiv zero) de astfel de elemente. Poate exista orice număr (inclusiv zero) de instrucțiuni else if și zero
sau one else. Numirea este opțională, dar o declarație else sau else if poate fi numită numai dacă
instrucțiunile if și end if corespunzătoare sunt denumite și trebuie să primească același nume. Numele
poate fi orice nume Fortran valid și distinct (vezi Secțiunea 5.15 pentru o discuție despre domeniul de
aplicare al numelor).
Un exemplu de construcție if în forma sa cea mai simplă este
Machine Translated by Google

56 Fortran modern explicat

Figura 4.1 Construcția if. [nume:] if


(scalar-logical-expr) atunci blocați

[else if (scalar-logical-expr) then [name] block]... [else


[nume] block] end if [nume]

swap: dacă (x < y) atunci temp =


x
x=yy =
sfârșitul
temp. dacă se schimbă

Blocul de trei instrucțiuni este executat dacă condiția este adevărată; în caz contrar, execuția continuă de la
instrucțiunea după instrucțiunea end if. Rețineți că blocul din interiorul construcției if este indentat. Acest lucru
nu este obligatoriu, dar face logica mai ușor de înțeles, mai ales în constructele imbricate if, așa cum vom vedea
la sfârșitul acestei secțiuni.
Următoarea formă simplă are un bloc else, dar nu blocuri else if. Acum există o
bloc alternativ pentru cazul în care condiția este falsă. Un exemplu este

dacă (x < y) atunci


x = -x
altfel
y = -y
sfâr itul dacă

în care semnul lui x este schimbat dacă x este mai mic decât y, iar semnul lui y este schimbat dacă x este mai mare
sau egal cu y.
Cel mai general tip de construct if folosește instrucțiunea else if pentru a face o succesiune de teste, fiecare
dintre ele având blocul său asociat de instrucțiuni. Testele se fac unul după altul până când unul este îndeplinit,
iar instrucțiunile asociate ale blocului relevant if sau else if sunt executate. Controlul trece apoi la sfârșitul
construcției if. Dacă nu este îndeplinit niciun test, nu se execută niciun bloc, cu excepția cazului în care există o
clauză finală „catch-all” else.
Există o formă de prescurtare utilă pentru cel mai simplu caz dintre toate. Un construct if al formei

if (scalar-logic-expr) atunci
ac iune-stmt
sfâr itul dacă

poate fi scris

if (scalar-logic-expr) action-stmt

Exemplele sunt
Machine Translated by Google

Construcții de control 57

dacă (xy > 0,0) x = 0,0 dacă


(cond .or. p<q .and. r<=1,0) s(i,j) = t(j,i)

Este permisă imbricarea unor construcții unul în celălalt la o adâncime arbitrară, așa cum se arată la două
niveluri în Figura 4.2, în care vedem necesitatea indentării codului pentru a putea înțelege cu ușurință logica.
Pentru cuibări și mai adânci, se recomandă denumirea. Construcțiile trebuie să fie imbricate corespunzător,
adică fiecare construcție trebuie să fie în întregime conținută într-un bloc al construcției exterioare următoare.

Figura 4.2 Un construct if imbricat. dacă


(i < 0) atunci
dacă (j < 0) atunci
x = 0,0
y = 0,0
altfel
z = 0,0
termina
dacă altfel dacă (k < 0) atunci
z = 1.0 altfel x = 1.0 y =
1.0 termina dacă

4.3 Construcția caz

Fortran oferă un alt mijloc de a selecta una dintre mai multe opțiuni, destul de asemănătoare cu cea a
constructului if. Principalele diferențe dintre cele două constructe sunt că, pentru constructul caz, doar o
expresie este evaluată pentru testare, iar expresia evaluată poate aparține nu mai mult de unul dintr-o serie de
seturi predefinite de valori. Forma constructului caz este prezentată de:

[name:] select case (expr) [case selector


[nume] bloc]... final select
[nume]

În ceea ce privește construcția if, instrucțiunile de început și de sfârșit trebuie fie să fie nenumite, fie ambele să
poarte același nume; o declarație de caz în cadrul acesteia poate fi denumită numai dacă declarația principală
este numită și poartă același nume. Expresia expr trebuie să fie scalară și de tip caracter, logic sau întreg, iar
valorile specificate în fiecare selector trebuie să fie de acest tip. În cazul caracterului, lungimile sunt permise să
difere, dar nu și tipurile. În logic
Machine Translated by Google

58 Fortran modern explicat

și cazuri întregi, tipurile pot diferi. Cea mai simplă formă de selector este o expresie constantă
scalară.1 în paranteze, cum ar fi în instrucțiunea

cazul 1)

Pentru caractere sau numere întregi, un interval poate fi specificat printr-o expresie constantă scalară inferioară
și superioară, separate prin două puncte:

caz (scăzut:înalt)

Fie scăzut, fie ridicat, dar nu ambele, pot fi absente; aceasta este echivalentă cu specificarea că cazul
este selectat ori de câte ori expr evaluează la o valoare care este mai mică sau egală cu mare, sau mai
mare sau egală cu scăzută, respectiv. Un exemplu este prezentat în Figura 4.3.

Figura 4.3 Un construct de caz.


selectați caz (număr) caz ! numărul este de tipul întreg ! toate
(:-1) valorile sub 0
n_sign = -1 caz
(0) n_sign = 0 caz ! doar 0
(1:) n_sign = 1
final select ! toate valorile peste 0

Forma generală a selectorului este o listă de valori și intervale care nu se suprapun, toate la fel
tastați ca expr, cuprins între paranteze, cum ar fi

caz (1, 2, 7, 10:17, 23)

Forma

caz implicit

este echivalent cu o listă a tuturor valorilor posibile ale expr care nu sunt incluse în ceilalți selectori ai
constructului. Deși recomandăm ca valorile să fie în ordine, ca în acest exemplu, acest lucru nu este
necesar. Valorile suprapuse nu sunt permise în cadrul unui selector și nici între altele diferite din
același construct.
Poate exista doar un singur selector implicit de caz într-un construct de caz dat, așa cum se arată
în Figura 4.4. Clauza implicită case nu trebuie să fie neapărat ultima clauză a constructului case.

Deoarece valorile selectoarelor nu sunt permise să se suprapună, cel mult un selector poate fi
satisfăcut; dacă niciuna nu este satisfăcută, controlul trece la următoarea instrucțiune executabilă
după instrucțiunea end select.
La fel ca și constructul if, constructele case pot fi imbricate una în alta.

1O expresie constantă este o formă restrânsă de expresie care poate fi verificată ca fiind constantă (restricțiile fiind
alese pentru ușurința implementării) Detaliile sunt plictisitoare și sunt amânate la Secțiunea 7.4. În această secțiune,
toate exemplele folosesc cea mai simplă formă de expresie constantă: constanta literală.
Machine Translated by Google

Construcții de control 59

Figura 4.4 O construcție de caz cu un selector implicit de caz. ! ch de tip


select case (ch) case caracter
('c', 'd', 'r':) ch_type = .true. caz
('i':'n') int_type = .true. caz
implicit

real_type = .true. final


select

4.4 Construcția do

Multe probleme de matematică necesită abilitatea de a repeta. Dacă dorim să însumăm elementele unui
tablou a de lungime 10, am putea scrie

suma = a(1)
suma = suma+a(2)
:
suma = suma+a(10)

care este clar laborios. Fortran oferă o facilitate cunoscută sub numele de construcția do, care ne permite să
reducem aceste zece linii de cod la

suma = 0,0
fac i = 1,10 ! i este de tipul întreg sum = sum+a(i) end
do

În acest fragment de cod setăm mai întâi suma la zero și apoi solicităm ca instrucțiunea dintre instrucțiunea
do și instrucțiunea end do să fie executată de zece ori. Pentru fiecare iterație există o valoare asociată a unui
indice, păstrată în i, care presupune valoarea 1 pentru prima iterație prin buclă, 2 pentru a doua și așa mai
departe până la 10. Variabila i este o variabilă întreagă normală, dar este supusă regulii că nu trebuie să fie
modificată în mod explicit în cadrul constructului do.

Declarația do are forme mai generale. Dacă am dori să însumăm elementele al patrulea până la al nouălea
noi am scrie

fac i = 4, 9

specificând astfel primele și ultimele valori necesare pentru i. Dacă, alternativ, am dori să însumăm toate
elementele impare, am scrie

face i = 1, 9, 2

unde al treilea dintre cei trei parametri de buclă, și anume 2, specifică faptul că i trebuie să fie incrementat în
pași de 2, mai degrabă decât cu valoarea implicită de 1, care este presupusă dacă nu este dat un al treilea
parametru. De fapt, putem merge mai departe, deoarece parametrii nu trebuie să fie deloc constante, ci
expresii întregi, ca în
Machine Translated by Google

60 Fortran modern explicat

face i = j+4, m, -k(j)**2

în care prima valoare a lui i este j+4, iar valorile ulterioare sunt decrementate cu k(j)**2 până când se
atinge valoarea lui m. Astfel, indicii do pot rula „înapoi”, precum și „înainte”. Dacă oricare dintre cei trei
parametri este o variabilă sau este o expresie care implică o variabilă, valoarea variabilei poate fi
modificată în cadrul buclei fără a afecta numărul de iterații, deoarece valorile inițiale ale parametrilor
sunt utilizate pentru controlul buclă.
Forma generală a acestui tip de clauză de control de construcție mărginită este

[nume:] do [,] variabilă = expr1, expr2 [,expr3]


bloc
sfârșit do [nume]

unde variabilă este o variabilă întreagă scalară numită, expr1, expr2 și expr3 (expr3 este opțională, dar
trebuie să fie diferită de zero atunci când este prezentă) sunt orice expresii întregi scalare valide, iar
nume este numele constructului opțional. Instrucțiunile do și end do trebuie fie să poarte același nume,
fie ambele să fie nenumite.
Numărul de iterații ale unui construct do este dat de formula

max((expr2-expr1+expr3)/expr3, 0)

unde max este o funcție pe care o vom întâlni în Secțiunea 8.3.2 și care returnează fie valoarea expresiei,
fie zero, oricare dintre acestea este mai mare. Din această definiție rezultă o consecință și anume că dacă
o buclă începe cu instrucțiunea

face i = 1, n

atunci corpul său nu va fi executat deloc dacă valoarea lui n la intrarea în buclă este zero sau mai mică.
Acesta este un exemplu de buclă zero-trip și rezultă din aplicarea funcției max.
O formă foarte simplă a declarației do este nelimitată

[nume:] face

care specifică o buclă fără sfârșit. În practică, este necesar un mijloc de a ieși dintr-o buclă fără sfârșit, iar
acesta este furnizat sub forma declarației de ieșire:

ieșire [nume]

unde name este opțional și este folosit pentru a specifica din ce construcție do ar trebui luată ieșirea în
cazul constructelor imbricate.2 Execuția unei instrucțiuni de ieșire face ca controlul să fie transferat la
următoarea instrucțiune executabilă după instrucțiunea end do la care se referă. .
Dacă nu este specificat niciun nume, se încheie execuția celui mai interior construcție în care este inclus.
Ca exemplu al acestei forme a do, să presupunem că am folosit intrarea de tip din Secțiunea 2.13 pentru
a construi un lanț de intrări într-un vector rar și dorim să găsim intrarea cu indicele 10, cunoscut a fi
prezent. Dacă mai întâi indică prima intrare, codul din Figura 4.5 este potrivit.

Instrucțiunea de ieșire este, de asemenea, utilă într-o buclă mărginită atunci când toate iterațiile nu sunt întotdeauna
necesare.

O declarație înrudită este instrucțiunea ciclului

2Fortran 2008 permite ca o ieșire numită să fie folosită pentru a ieși din aproape orice construcție, nu doar o buclă.
Machine Translated by Google

Construcții de control 61

Figura 4.5 Căutarea unei liste legate. tip


(intrare), indicator :: primul, curent
:
curent => mai întâi face

if (current%index == 10) exit current =>


current%next
sfâr itul face

ciclu [nume]

care transferă controlul la sfâr itul instruc iunii do a constructului corespunzător. Astfel, dacă mai urmează să
fie efectuate iterații ulterioare, următoarea este inițiată.
Valoarea unui index do construct (dacă este prezent) este incrementată la sfârșitul fiecărei iterații de buclă
pentru a fi utilizată în iterația ulterioară. Deoarece valoarea acestui index este disponibilă în afara buclei după
executarea sa, avem trei situații posibile, fiecare ilustrată de următoarea buclă:

face i = 1, n
:
dacă (i==j) ieșire
:
sfâr itul face

l=i

Situațiile sunt următoarele.

i) Dacă, la momentul execuției, n are valoarea zero sau mai mică, i este setat la 1, dar bucla nu este executată,
iar controlul trece la instrucțiunea după instrucțiunea end do.

ii) Dacă n are o valoare care este mai mare sau egală cu j, se va lua o ieșire la if
afirmație, iar l va dobândi ultima valoare a lui i, care este desigur j.

iii) Dacă valoarea lui n este mai mare decât zero dar mai mică decât j, bucla va fi executată de n ori, cu valorile
succesive ale lui i fiind 1,2,... etc. până la n. Când se ajunge la sfârșitul buclei pentru a n-a oară, i va fi
incrementat o ultimă dată, dobândind valoarea n+1, care va fi apoi atribuită lui l.

Vedem cât de important este să folosim cu grijă indicii buclei în afara blocului do, mai ales când există
posibilitatea ca numărul de iterații să preia valoarea limită a maximului pentru buclă.

Blocul do, tocmai menționat, este succesiunea de instrucțiuni dintre instrucțiunea do și instrucțiunea end do.
De oriunde în afara unui bloc do, este interzis să sari în bloc sau până la capătul lui do declarație.

În mod similar, este ilegal ca blocul unui construct do (sau orice alt construct, cum ar fi un construct if sau
case), să fie conținut doar parțial într-un bloc al altui construct. Construcția trebuie să fie complet cuprinsă în
bloc. Următoarele două secvențe sunt legale:
Machine Translated by Google

62 Fortran modern explicat

dacă (scalar-logic-expr) atunci face i =


1, n
:
sfâr itul face

altfel
:
sfâr itul dacă

și

do i = 1, n if
(scalar-logic-expr) atunci
:
sfâr itul dacă

sfâr itul face

Orice număr de constructe do pot fi imbricate. Astfel, putem scrie o înmulțire a matricei ca
prezentat în Figura 4.6.

Figura 4.6 Înmulțirea matricei ca un construct do cu imbricat triplu. do i = 1,


n do j = 1, ma(i,j) = 0.0 do l = 1, ka(i,j) = a(i,j)+b(i,l)*c(l,j ) sfâr itul
face

sfâr itul face

sfâr itul face

O altă formă de do-construct, în Fortran 2008, este descrisă în Secțiunea 20.4.1, și forme suplimentare, dar
redundante, de sintaxă do în Anexa B.5.
În cele din urmă, trebuie remarcat faptul că multe bucle scurte pot fi exprimate alternativ sub formă de
expresii matrice și atribuiri. Cu toate acestea, acest lucru nu este întotdeauna posibil și un pericol deosebit de
urmărit este cazul în care o iterație a buclei depinde de una anterioară.
Astfel, bucla

face i = 2, na(i) =
a(i-1) + b(i)
sfâr itul face

nu poate fi înlocuită cu declarația

a(2:n) = a(1:n-1) + b(2:n) ! Ai grijă

4.5 Declarația Go to

Doar ocazional, mai ales când avem de-a face cu condiții de eroare, constructele de control pe care le-am
descris pot fi inadecvate pentru nevoile programatorului. Remediul este cel mai mult
Machine Translated by Google

Constructele de control 63

declarație contestată în limbaje de programare – declarația go to. Este în general acceptat că este dificil
de înțeles un program care este întrerupt de mai multe ramuri, mai ales dacă există un număr mare de
ramuri înapoiate – cele care revin controlul unei instrucțiuni care precede ramurile în sine.

Forma declarației necondiționate Go to este

mergi la etichetă

unde label este o etichetă de declarație. Această etichetă de instrucțiune trebuie să fie prezentă pe o
instrucțiune executabilă (o instrucțiune care poate fi executată, spre deosebire de una de natură
informativă, cum ar fi o declarație). Un exemplu este

x = y + 3,0
mergeți la 4 3
x = x + 2,0
4z=x+y

în care observăm că după executarea primei instrucțiuni, o ramură este dusă la ultima instrucțiune,
etichetată 4. Aceasta este o instrucțiune țintă de ramură. Instrucțiunea etichetată 3 este sărită peste și
poate fi executată numai dacă există o ramură la eticheta 3 în altă parte. Dacă declarația care urmează
unui acces necondiționat nu este etichetată – nu poate fi niciodată accesată și executată, creând cod
mort, de obicei un semn de codare incorectă.
Declarațiile dintr-un bloc al unui construct pot fi etichetate, dar etichetele nu trebuie să fie niciodată
referite în așa fel încât să treacă controlul în intervalul unui bloc din afara acestuia, către o instrucțiune
else if sau else. Este permisă trecerea controlului de la o instrucțiune dintr-un construct la instrucțiunea
terminală a constructului sau la o instrucțiune din afara constructului său.
Instrucțiunea if este folosită în mod normal fie pentru a efectua o singură atribuire în funcție de o
condiție, fie pentru a ramifica în funcție de o condiție. Action-stmt nu poate fi etichetat separat. Exemplele
sunt

dacă (steagul) mergeți


la 6 dacă (xy > 0,0) x = 0,0

4.6 Rezumat
În acest capitol am introdus cele patru caracteristici principale prin care poate fi programat controlul în
codul Fortran – instrucțiunea go to, instrucțiunea și constructul if, constructul case și constructul do.
Utilizarea eficientă a acestor caracteristici este cheia codului de sunet.
Am atins conceptul de unitate de program ca fiind ca un capitol al unei cărți.
Așa cum o carte poate avea doar un capitol, tot așa un program complet poate consta dintr-o singură
unitate de program, care este cunoscută ca program principal. În forma sa cea mai simplă, constă dintr-o
serie de instrucțiuni de tipul cu care ne-am ocupat până acum și se termină cu o instrucțiune de final, care
acționează ca un semnal către computer pentru a opri procesarea programului curent.
Pentru a testa dacă o unitate de program de acest tip funcționează corect, trebuie să fim capabili să
transmitem, către un terminal sau către o imprimantă, valorile cantităților calculate. Acest subiect va fi
explicat pe deplin în Capitolul 9, iar pentru moment trebuie să știm doar că acest lucru se poate realiza
printr-o declarație de forma
Machine Translated by Google

64 Fortran modern explicat

print*,' var1 = ', var1 , ' var2 = ', var2

care va scoate o linie precum

var1 = 1,0 var2 = 2,0

În mod similar, datele de intrare pot fi citite prin instrucțiuni precum

citeste *, val1, val2

Acest lucru este suficient pentru a ne permite să scriem programe simple ca cel din Figura 4.7,
care scoate valorile convertite ale unei scale de temperatură între limitele specificate, și Figura 4.8,
care construiește o listă legată. Intrările valide sunt afișate la sfârșitul fiecărui exemplu.

Exerciții

1. Scrie un program care

a) definește un tablou să aibă 100 de

elemente; b) atribuie elementelor valorile 1,2,3,...,100; c)

citește două valori întregi în intervalul de la 1 la 100; d)

inversează ordinea elementelor tabloului în intervalul specificat de cele două valori.

2. Primii doi termeni ai seriei Fibonacci sunt ambii 1, iar toți termenii următori sunt definiți ca suma celor doi
termeni precedenți. Scrieți un program care citește o limită a unei valori întregi și care calculează și imprimă
coeficienții primilor termeni limită ai seriei.

3. Coeficienții ordinelor succesive ale expansiunii binomului sunt reprezentați în Pascal normal
forma de triunghi ca

1
11
121
1331
14641
etc.

Scrieți un program care citește o limită de valoare întreagă și imprimă coeficienții primelor linii limită ale
acestui triunghi Pascal.

4. Definiți o variabilă caracter cu lungimea de 80. Scrieți un program care citește o valoare pentru această variabilă.
Presupunând că fiecare caracter din variabilă este alfabetic, scrieți codul care le sortează în ordine alfabetică și
tipăriți frecvența de apariție a fiecărei litere.

5. Scrieți un program pentru a citi o limită a unei valori întregi și tipăriți primele numere prime limită, după oricare
metodă.

6. Scrieți un program care citește o valoare x și calculează și imprimă valoarea corespunzătoare x/(1.+x).
Cazul x = 1. ar trebui să producă un mesaj de eroare și să fie urmat de o încercare de a citi o nouă valoare a
lui x.

7. Având în vedere un lanț de intrări de tipul intrării din Secțiunea 2.13, modificați codul din Figura 4.5 (Secțiunea
4.4) astfel încât să elimine intrarea cu indexul 10 și să facă punctul de intrare anterioară la următoarea intrare.
Machine Translated by Google

Construcții de control 65

Figura 4.7 Tipărirea unui tabel de conversie.


! Tipăriți un tabel de conversie pentru Fahrenheit și Celsius

! scale de temperatură între limitele specificate. !

real :: Celsius, Fahrenheit întreg ::


low_temp, high_temp, temperatură caracter :: scară

read_loop: face !

! Citiți scara și limitele

citire *, scară, temperatură_scăzută, temperatură_înaltă


!
! Verificați datele valide dacă (scale /=

'C' .and. scale /= 'F') exit read_loop


!

! Buclă peste limitele do temperature =


low_temp, high_temp
!
! Alegeți formula de conversie

selectați majuscule (scale) case


(„C”)

Celsius = temperatura Fahrenheit =


9.0/5.0*celsius + 32.0 ! Imprimați intrarea în tabel

print *, celsius, 'grade C corespund cu', fahrenheit, 'grade F' case ('F') &

fahrenheit = temperatura celsius = 5,0/9,0*(fahrenheit-32,0)

! Imprimați intrarea în tabel


print *, Fahrenheit, 'grade F corespund cu',& celsius, 'grade C' sfâr itul selectării

sfâr itul face

end do read_loop
!
! Încetarea

print *, 'Sfârșitul datelor valide'


Sfâr it

C 90 100
F 20 32
*0 0
Machine Translated by Google

66 Fortran modern explicat

Figura 4.8 Construirea și tipărirea unei liste legate.


tastați intrarea! Tip pentru matrice rară :: valoare
real
tip întreg :: index
(intrare), pointer :: următoarea intrare
de tip final

tip (intrare), pointer :: primul, întreg curent


:: cheie
real :: valoare
!
! Creați o listă nulă anulați
(în primul rând)
!
! Completați lista
do
citește *, cheie, valoare
dacă (cheie <= 0) ieșire
alocă (curent) curent =
intrare (valoare, cheie, primul) primul =>
curent
sfâr itul face

!
! Tipăriți lista
curent => mai întâi
do
if (.not.associated(current)) exit print *,
current%index, current%value current =>
current%next
sfâr itul face

Sfâr it

14
29
00
Machine Translated by Google

5. Unități de program și proceduri

5.1 Introducere

După cum am văzut în capitolul anterior, este posibil să scrieți un program Fortran complet ca o singură
unitate, dar este de preferat să descompuneți programul în unități gestionabile. Fiecare astfel de unitate
de program corespunde unei sarcini de program care poate fi ușor înțeleasă și, în mod ideal, poate fi
scrisă, compilată și testată izolat. Vom discuta cele trei tipuri de unități de program, programul principal,
subprogramul extern și modulul.
Un program complet trebuie să includă cel puțin un program principal. Acesta poate conține declarații
de tipul pe care le-am întâlnit până acum în exemple, dar, în mod normal, cele mai importante declarații
sunt invocări sau apeluri la programe subsidiare cunoscute sub numele de subprograme. Un subprogram
definește o funcție sau o subrutină. Ele diferă prin faptul că o funcție returnează un singur obiect și, de
obicei, nu modifică valorile argumentelor sale (astfel încât să reprezinte o funcție în sens matematic), în
timp ce o subrutină îndeplinește de obicei o sarcină mai complicată, returnând mai multe rezultate prin
argumentele sale și prin alte mijloace. Funcțiile și subrutinele sunt cunoscute în mod colectiv ca proceduri.

Există diferite tipuri de subprograme. Un subprogram poate fi o unitate de program în sine, caz în
care se numește subprogram extern și definește o procedură externă.
Procedurile externe pot fi definite și prin alte mijloace decât Fortran. Un subprogram poate fi membru al
unei colecții dintr-o unitate de program numită modul, caz în care îl numim subprogram de modul și
definește o procedură de modul. Un subprogram poate fi plasat în interiorul unui subprogram de modul,
un subprogram extern sau un program principal, caz în care îl numim subprogram intern și definește o
procedură internă. Este posibil ca subprogramele interne să nu fie imbricate, adică să nu conțină alte
subprograme și ne așteptăm să fie în mod normal secvențe scurte de cod, să zicem până la aproximativ
douăzeci de linii. Ilustram imbricarea subprogramelor in unitati de program in Figura 5.1. Dacă o unitate
de program sau un subprogram conține un subprogram, acesta se numește gazda acelui subprogram.

Pe lângă faptul că conține o colecție de subprograme, un modul poate conține definiții de date,
definiții de tip derivat, blocuri de interfață (Secțiunea 5.11) și grupuri de liste de nume (Secțiunea 7.15).
Această colecție poate oferi facilități asociate cu o anumită sarcină, cum ar fi furnizarea de aritmetică
matrice, o facilitate de bibliotecă sau o bază de date. Poate fi uneori mare.
În acest capitol, vom descrie unitățile de program și instrucțiunile care sunt asociate cu acestea. În
cadrul unui program complet, acestea pot apărea în orice ordine, dar multe compilatoare necesită un
modul care să precedă alte unități de program care îl folosesc.
Machine Translated by Google

68 Fortran modern explicat

Figura 5.1 Imbricarea subprogramelor în unități de program.

Modul Principal

program

Subprograme interne

Subprograme interne Subprograme de module

Subprogram extern

Subprograme interne

5.2 Programul principal

Fiecare program complet trebuie să aibă un singur program principal. Opțional, poate conține apeluri către
subprograme. Un program principal are următoarea formă:

[nume program-program]
[specificare-stmts]
[executable-stmts]
[conține subprograme interne]
sfârșit [program [nume-
program]]

Declarația programului este opțională, dar recomandăm utilizarea acesteia. Numele programului poate fi
orice nume Fortran valid, cum ar fi modelul. Singura instrucțiune non-opțională este instrucțiunea finală care
are două scopuri. Acționează ca un semnal către compilator că a ajuns la sfârșitul unității de program și,
atunci când este executat, determină oprirea întregului program. Dacă include nume-program, acesta
trebuie să fie numele de pe instrucțiunea programului. Vă recomandăm să utilizați formularul complet, astfel
încât să fie clar atât pentru cititor, cât și pentru compilator exact ceea ce este terminat de instrucțiunea final.

Un program principal fără apeluri la subprograme este de obicei folosit doar pentru teste scurte, ca în

program test
print *, „Bună lume!” sfârșitul
testului programului

Declarațiile de specificație definesc mediul pentru instrucțiunile executabile. Până acum, am întâlnit
declarația de tip (întreg, real, complex, logic, caracter și tip (nume-tip)) care specifică tipul și alte proprietăți
ale entităților pe care le listează,
Machine Translated by Google

Unități de program și proceduri 69

și blocul de definire a tipului (delimitat de instrucțiunile tip nume-tip și tip final). Vom întâlni alte
specificații în acest capitol și în următoarele două capitole.
Instrucțiunile executabile specifică acțiunile care urmează să fie efectuate. Până acum, am întâlnit
instrucțiunea de atribuire, instrucțiunea de atribuire pointer, instrucțiunea if și construct, constructele
do și case, instrucțiunea go to și instrucțiunile read și print. Vom întâlni alte instrucțiuni executabile în
acest capitol și în capitolele ulterioare. Execuția unui program începe întotdeauna cu prima instrucțiune
executabilă a programului principal.
Declarația contains semnalează prezența unuia sau mai multor subprograme interne. Vom descrie
subprogramele interne în Secțiunea 5.6. Ele sunt excluse din secvența de instrucțiuni executabile a
programului principal, care se încheie cu ultima instrucțiune înainte de instrucțiunea contains urmată de
instrucțiunea final. Instrucțiunea final poate fi ținta unei ramuri dintr-una dintre instrucțiunile executabile.
Dacă instrucțiunea final este executată, programul se oprește.

5.3 Declarația stop

O altă modalitate de a opri execuția programului este să executați o instrucțiune stop. Această declarație
poate apărea în programul principal sau în orice subprogram. Un program bine conceput returnează în
mod normal controlul programului principal pentru terminarea programului, astfel încât instrucțiunea
stop ar trebui să apară acolo. Totuși, în aplicațiile în care mai multe instrucțiuni stop apar în diferite locuri
într-un program complet, este posibil să se distingă care dintre instrucțiunile stop a cauzat terminarea
adăugând la fiecare un cod stop constând dintr-o constantă de caracter implicită sau un șir de sus. la
cinci cifre ale căror zerouri nu sunt semnificative.1 Acest lucru poate fi folosit de un procesor dat pentru
a indica originea opririi într-un mesaj. Exemplele sunt

stop
stop 'Date incomplete. Programul s-a încheiat. oprire 12345

5.4 Subprograme externe

Subprogramele externe sunt apelate dintr-un program principal sau din altă parte, de obicei pentru a
efectua o sarcină bine definită în cadrul unui program complet. În afară de afirmația principală, au o
formă care seamănă foarte mult cu cea a unui program principal:

1Fortran 2008 permite orice expresie implicită de caractere întregi sau constante de caractere.
Machine Translated by Google

70 Fortran modern explicat

subroutine-stmt

[specification-stmts]
[executable-stmts] [conține
subprograme interne] end
[subrutine [subrutine-nume]]

sau

function-stmt
[specification-stmts]
[executable-stmts] [conține
subprograme interne] end
[funcție [nume-funcție]]

Instrucțiunea contains joacă exact același rol ca în cadrul unui program principal (vezi Secțiunea 5.2). Efectul executării
unei instrucțiuni de final într-un subprogram este de a returna controlul apelantului, mai degrabă decât de a opri execuția.
În ceea ce privește instrucțiunea programului final, vă recomandăm să folosiți formularul complet pentru instrucțiunea
final, astfel încât să fie clar atât pentru cititor, cât și pentru compilator exact ce se termină.

Cea mai simplă formă de subprogram extern definește o subrutină fără niciun argument și are un subrutine-stmt de
forma

subrutine nume-subrutine

Un astfel de subprogram este util atunci când un program constă dintr-o secvență de faze distincte, caz în care programul
principal constă dintr-o secvență de instrucțiuni de apel care invocă subrutinele ca în exemplu.

program jocul apel ! Program principal pentru a controla un joc de cărți! Mai întâi
shuffle amestecați cărțile.
apel afacere ! Acum ocupă-le.

apel play apel ! A juca acest joc.


afișa final program ! Afișați rezultatul.
joc ! Opriți execuția.

Dar cum gestionăm fluxul de informații dintre subrutine? Cum știe jocul ce cărți a împărțit? Există, de fapt, două
metode prin care informațiile pot fi transmise. Primul este prin intermediul datelor deținute într-un modul (Secțiunea 5.5)
și accesat de subprograme, iar al doilea este prin intermediul argumentelor (Secțiunea 5.7) în apelurile de procedură.

5.5 Module

Al treilea tip de unitate de program, modulul, oferă un mijloc de împachetare a datelor globale, a tipurilor derivate și a
operațiunilor asociate acestora, subprograme, blocuri de interfață (Secțiunea 5.11) și grupuri de liste de nume (Secțiunea
7.15). Tot ceea ce este asociat cu o anumită sarcină (cum ar fi intervalul
Machine Translated by Google

Unități de program și proceduri 71

aritmetică, vezi mai târziu în această secțiune) pot fi colectate într-un modul și accesate ori de câte ori este nevoie.
Acele părți care sunt asociate cu funcționarea internă și care nu prezintă interes pentru utilizator pot fi făcute
„invizibile” pentru utilizator, ceea ce permite modificarea designului intern fără a fi necesară modificarea programului
care îl folosește și previne alterarea accidentală a date interne. Bibliotecile Fortran constau adesea din seturi de
module.
Modulul are forma

modul nume-modul

[specificație-stmts]
[conține subprograme-module]
sfârșit [modul [nume-modul]]

În ceea ce privește instrucțiunile programului final, subrutinei de final și funcției de terminare, vă recomandăm să
utilizați formularul complet pentru instrucțiunea finală.
În forma sa cea mai simplă, corpul constă numai din specificații de date. De exemplu

starea modulului

întreg, dimensiune(52) :: carduri


starea modulului final

ar putea menține stadiul jocului din Secțiunea 5.4. Se accesează prin declarație

stare de utilizare

care apar la începutul programului principal jocul și subprogramele amestecă, se distribuie, se joacă și se afișează.
Matricea de cărți este setată prin amestecare pentru a conține valorile întregi de la 1 la 52 într-o ordine aleatorie,
unde fiecare valoare întreagă corespunde unei cărți de joc predefinite. De exemplu, 1 ar putea reprezenta asul de
trefte, 2 pentru cei doi de trefte etc. până la 52 pentru regele de pică. Cărțile de matrice sunt modificate prin
distribuirea și jocul subrutinelor și, în sfârșit, accesate prin afișarea subrutinei.

Un alt exemplu de date globale dintr-un modul ar fi definițiile valorilor parametrilor de tip tip care ar putea fi
solicitați în cadrul unui program (Secțiunea 2.6.2). Ele pot fi plasate într-un modul și utilizate oriunde sunt necesare.
Pe un procesor care acceptă toate tipurile enumerate, un exemplu ar putea fi:

modul numeric_kinds ! constante


numite pentru numere întregi de 4, 2 și 1 octet: întreg, parametru :: i4b =
selected_int_kind(9), i2b = selected_int_kind(4), i1b = selected_int_kind(2) &

&

&

! și pentru reale de precizie simplă, dublă și cvadruplă: întreg, parametru :: sp = kind(1.0),


dp = selected_real_kind(2*precision(1.0_sp)), & qp = selected_real_kind(2*precision(1.0_dp))
&

&

sfâr itul modulului numeric_kinds


Machine Translated by Google

72 Fortran modern explicat

Un rol foarte util pentru module este acela de a conține definiții ale tipurilor și operatorilor lor asociați.
De exemplu, un modul poate conține intervalul de tip din Secțiunea 3.8, așa cum se arată în Figura 5.2.
Având în vedere acest modul, orice unitate de program care are nevoie de acest tip și operatorii săi
trebuie să includă doar instrucțiunea

utilizați interval_arithmetic

în fruntea caietului de sarcini al acestuia.

Figura 5.2 Un modul pentru aritmetică de intervale.


modul interval_arithmetic tip interval real ::
tipul inferior, superior de tip interval
interfață operator(+) procedura
modulului add_intervals end interface

:
con ine
function add_intervals(a,b) tip(interval)
tip(interval), intent(in) :: a, b :: add_intervals
add_intervals%lower = a%lower + b%lower
add_intervals%upper = a%upper + b%upper end function
add_intervals

:
sfâr it modulul interval_aritmetică

Un subprogram de modul are exact aceeași formă ca un subprogram extern, cu excepția faptului că
funcția sau subprogramul trebuie să fie prezente în instrucțiunea final. Are întotdeauna acces la alte
entități ale modulului, inclusiv abilitatea de a apela alte subprograme ale modulului, mai degrabă ca și
cum ar conține o instrucțiune de utilizare pentru modulul său.
Un modul poate conține instrucțiuni de utilizare care accesează alte module. Nu trebuie să se acceseze
direct sau indirect printr-un lanț de instrucțiuni de utilizare, de exemplu a accesând b și b accesând a.
Standardul nu cere nicio ordonare a modulelor, dar practica obișnuită este ca fiecare modul să precedă
utilizarea acestuia. Vă recomandăm această practică, care va face imposibilă accesarea unui modul prin
alte module. Este cerut de mulți compilatori.

Este posibil în cadrul unui modul să specificați că unele dintre entități sunt private pentru acesta și nu
pot fi accesate din alte unități de program. De asemenea, există forme ale instrucțiunii de utilizare care
permit accesul doar la o parte a unui modul și formulare care permit redenumirea entităților accesate.
Aceste caracteristici vor fi explicate în Secțiunile 7.6 și 7.10. Pentru moment, presupunem că întregul
modul este accesat fără nicio redenumire a entităților din el.
Machine Translated by Google

Unități de program și proceduri 73

5.6 Subprograme interne

Am văzut că subprogramele interne pot fi definite în cadrul programelor principale și subprogramelor


externe și în cadrul subprogramelor modulelor. Au forma

subroutine-stmt
[specificație-stmts]
[executable-stmts]
sfârșitul subrutinei [subroutine-name]

sau

function-stmt
[specification-stmts]
[executable-stmts] final
function [nume-funcție]

adică aceeași formă ca un subprogram de modul, cu excepția faptului că acestea nu pot conține alte
subprograme interne. Rețineți că funcția sau subrutina trebuie să fie prezentă în instrucțiunea final. Un
subprogram intern are automat acces la toate entitățile gazdei, inclusiv capacitatea de a apela celelalte
subprograme interne ale acestuia. Subprogramele interne trebuie să fie precedate de o instrucțiune
contains în gazdă.
În restul acestui capitol, descriem câteva proprietăți ale subprogramelor care se aplică subprogramelor
externe, modulelor și interne. Prin urmare, nu trebuie să descriem subprogramele interne separat. Un
exemplu este dat în Figura 5.10 (Secțiunea 5.15).

5.7 Argumente ale procedurilor

Argumentele procedurii oferă un mijloc alternativ pentru ca două unități de program să acceseze aceleași
date. Revenind la exemplul nostru de joc de cărți, în loc să plasăm cărțile matrice într-un modul, am
putea să-l declarăm în programul principal și să-l transmitem ca argument real fiecărui subprogram, așa
cum se arată în Figura 5.3.

Figura 5.3 Apeluri subrutine cu argumente reale.


program joc întreg, dimensiune(52)
! Program
:: cărți
principal
apel amestecare(cărți)
pentru a controla apel
un joc de cărți
împărțire(cărți) apel joc(cărți) apel afișare(cărți) termina jocul program
! Mai întâi amestecați cărțile.
! Acum ocupă-le.

! A juca acest joc.


! Afișați rezultatul.
! Opriți execuția.

Fiecare subrutină primește carduri ca argument fals. De exemplu, shuffle are forma prezentată în
Figura 5.4.
Ne putem imagina, desigur, un joc de cărți în care împărțirea va împărți doar trei cărți fiecăruia dintre
cei patru jucători. În acest caz, ar fi o pierdere de timp ca shuffle să pregătească o
Machine Translated by Google

74 Fortran modern explicat

Figura 5.4 O subrutină cu un argument inactiv. amestecarea


subrutinei (carti)
! Subprogram care plasează valorile de la 1 la 52 în carduri ! în ordine aleatorie. întreg,
dimensiune(52) :: carduri ! Declarații care umple carduri

:
terminați amestecul subrutinei! Reveniți la apelant.

pachet de 52 de cărți când sunt necesare doar primele 12 cărți. Acest lucru poate fi realizat prin solicitarea
amestecării pentru a se limita la un număr de carduri care este transmis în secvența de apelare astfel:

amestecare apeluri (3*4, cărți (1:12))

În interiorul shuffle, am defini matricea să aibă lungimea dată, iar algoritmul de umplere a cardurilor ar fi conținut într-un
construct do cu acest număr de iterații, așa cum se arată în Figura 5.5.

Figura 5.5 O subrutină cu două argumente fictive.


subrutine amestecare(ncards, cards) întreg :: ncards,
icard integer, dimension(ncards) :: cards do icard
= 1, ncards

carduri(icard) = ...
sfâr itul face

terminați amestecul subrutinei

Am văzut cum este posibil să trecem o matrice și o expresie constantă între două unități de program. Un argument
real poate fi orice variabilă sau expresie (sau un nume de procedură, vezi Secțiunea 5.12). Fiecare argument inactiv al
procedurii apelate trebuie să fie în acord cu argumentul real corespunzător în tip, parametri de tip și formă.2 Cu toate
acestea, numele nu trebuie să fie aceleași. De exemplu, dacă ar fi fost necesare două pachete, am fi putut scrie codul astfel:

program joc întreg,


dimensiune(52) :: acards, bcards call shuffle(acards) call
shuffle(bcards) ! Mai întâi amestecați un pachet.
! Apoi amestecați pachetul b.
:

termina jocul programului

2 Cerințele privind lungimea caracterului și acordul de formă sunt relaxate în apendicele B.3.
Machine Translated by Google

Unități de program și proceduri 75

Punctul important este că subprogramele pot fi scrise independent unul de celălalt, asocierea argumentelor
fictive cu argumentele reale aparând de fiecare dată când apelul este executat. Ne putem imagina că shuffle
este folosit în alte programe care folosesc alte nume.
În acest fel, pot fi construite biblioteci de subprograme.
Posibilitatea de a avea nume diferite pentru argumentele reale și fictive oferă o flexibilitate utilă, dar ar
trebui să fie folosită numai atunci când este cu adevărat necesar. Când poate fi folosit același nume, codul
este mai ușor de citit.
Deoarece tipul unui argument real și argumentul simulat corespunzător trebuie să fie de acord, trebuie
avut grijă când se utilizează selecția componentelor într-un argument real. Astfel, presupunând că definițiile
de tip punct și triunghi din Figura 2.1 (Secțiunea 2.9) sunt disponibile într-un modul def, am putea scrie

folosi def
tip (triunghi) :: t
:
apel sub(t%a)
:
con ine
subrutină sub(p)
tip(punct) :: p

5.7.1 Argumente pointer

Un argument fals este permis să aibă pointerul de atribut. În acest caz, argumentul real trebuie să aibă și
indicatorul de atribut. Când este invocat subprogramul, rangul argumentului actual trebuie să se potrivească
cu cel al argumentului inactiv, iar starea de asociere a pointerului acestuia este transmisă argumentului
inactiv. La întoarcere, argumentul real ia în mod normal starea de asociere a pointerului de la cea a
argumentului inactiv, dar devine nedefinit dacă argumentul inactiv este asociat cu o țintă care devine
nedefinită atunci când este executată returnarea (de exemplu, dacă ținta este un local). variabilă care nu are
atributul de salvare, Secțiunea 7.9).

În cazul unui modul sau al unei proceduri interne, compilatorul știe când argumentul fals este un pointer.
În cazul unei proceduri externe sau dummy, compilatorul presupune că argumentul dummy nu este un
pointer decât dacă i se spune altfel într-un bloc de interfață (Secțiunea 5.11).

Un argument real pointer este, de asemenea, permis să corespundă unui argument inactiv non-pointer.
În acest caz, pointerul trebuie să aibă o țintă, iar ținta este asociată cu argumentul dummy, ca în (matricele
de formă presupusă sunt explicate în Secțiunea 6.3)

real, indicator :: a(:,:)


:
alocă ( a(80,80) ) apel găsi (a)

:
găsirea subrutinei (c)
real :: c(:,:) ! Matrice de formă asumată
Machine Translated by Google

76 Fortran modern explicat

5.7.2 Restricții privind argumentele reale

Există două restricții importante asupra argumentelor reale, care sunt concepute pentru a permite
compilatorului să optimizeze presupunând că argumentele inactiv sunt distincte între ele și de alte
entități care sunt accesibile în cadrul procedurii. De exemplu, un compilator poate aranja ca o matrice să
fie copiată într-o variabilă locală la intrare și copiată înapoi la întoarcere.
În timp ce un argument real este asociat cu un argument inactiv, următoarele afirmații sunt valabile.

i) Acțiunea care afectează starea de alocare sau starea de asociere a pointerului a argumentului sau
a oricărei părți a acestuia (orice alocare de pointer, alocare, dealocare sau anulare) trebuie luată
prin argumentul inactiv. Dacă se face acest lucru, atunci pe toată durata execuției procedurii,
argumentul poate fi referit numai prin argumentul inactiv.

ii) Acțiunea care afectează valoarea argumentului sau a oricărei părți a acestuia trebuie luată prin argumentul
inactiv, cu excepția cazului în care

a) argumentul dummy are atributul pointer; b) partea este

integral sau parțial dintr-un subobiect pointer; sau c)

argumentul dummy are atributul țintă, argumentul dummy nu are intenție în (Secțiunea 5.9),
argumentul dummy este scalar sau o matrice de formă presupusă (Secțiunea 6.3), iar
argumentul real este o țintă, alta decât un secțiune de matrice cu un indice vectorial.

Dacă valoarea argumentului sau a oricărei părți a acestuia este afectată printr-un argument inactiv
pentru care nici a), b), sau c) nu este valabil, atunci pe toată durata executării procedurii,
argumentul poate fi referit numai prin acel argument inactiv.

Un exemplu de i) este un pointer care este anulat (Secțiunea 3.13) în timp ce este încă asociat cu
argumentul inactiv. Ca exemplu de ii), luați în considerare

apel modificare(a(1:5), a(3:9))

Aici, a (3:5) nu poate fi schimbat prin niciun argument fals, deoarece aceasta ar încălca regula pentru
celălalt argument. Totuși, a (1:2) poate fi schimbat prin primul argument și a (6:9) poate fi schimbat prin
al doilea. Un alt exemplu este un argument real care este un obiect care este accesat dintr-un modul;
aici, același obiect nu trebuie accesat din modul prin procedură și redefinit. Ca un al treilea exemplu, să
presupunem că un apel de procedură internă asociază o variabilă gazdă h cu un argument inactiv d. Dacă
d este definit în timpul apelului, atunci h nu poate fi referit direct în niciun moment în timpul apelului.

5.7.3 Argumente cu atributul target

În majoritatea circumstanțelor, o implementare este permisă să facă o copie a unui argument real la
intrarea într-o procedură și să o copieze înapoi la returnare. Acest lucru poate fi de dorit din motive de
eficiență, în special atunci când argumentul real nu este păstrat în stocare contiguă. În orice caz, dacă un
argument fals nu are nici atributul țintă, nici indicatorul, vreun pointer asociat
Machine Translated by Google

Unități de program și proceduri 77

cu argumentul real nu devin asociate cu argumentul inactiv corespunzător, ci rămân asociate cu argumentul
real.
Cu toate acestea, copiere în copiere nu este permisă când

i) un argument dummy are atributul țintă și este fie scalar, fie este o matrice în formă presupusă; și

ii) argumentul real este o altă țintă decât o secțiune de matrice cu un indice vectorial.

În acest caz, argumentele fictive și reale trebuie să aibă aceeași formă, orice pointer asociat cu argumentul
actual devine asociat cu argumentul inactiv la invocare și orice pointer asociat cu argumentul inactiv la returnare
rămâne asociat cu argumentul real.

Când un argument dummy are atributul target, dar argumentul real nu este o țintă sau este o secțiune de
matrice cu un indice vectorial, orice pointer asociat cu argumentul dummy devine evident nedefinit la întoarcere.

În alte cazuri în care argumentul dummy are atributul țintă, dacă are loc copierea în copiere este dependentă
de procesor. Nu trebuie să se bazeze pe asocierile pointerului cu un astfel de argument după invocare.

5.8 Declarația de returnare

Am văzut în Secțiunea 5.2 că dacă ultima instrucțiune executabilă dintr-un program principal este executată și
nu provoacă o ramificare, instrucțiunea final este executată și programul se oprește. În mod similar, dacă ultima
instrucțiune executabilă dintr-un subprogram este executată și nu provoacă o ramură, instrucțiunea finală este
executată și controlul revine la punctul de invocare. Așa cum instrucțiunea stop este o instrucțiune executabilă
care oferă un mijloc alternativ de oprire a execuției, la fel și instrucțiunea return oferă un mijloc alternativ de
returnare a controlului dintr-un subprogram. Are forma

întoarcere

și nu trebuie să apară într-un program principal.

5.9 Intenția argumentului

În Figura 5.5, cardurile cu argument fictive au fost folosite pentru a transmite informații din shuffle, iar
argumentul fals ncards a fost folosit pentru a transmite informații. O a treia posibilitate este ca un argument
inactiv să fie utilizat atât pentru variabilele de intrare cât și pentru ieșire. Putem specifica o astfel de intenție în
declarația de tip declarație pentru argument, de exemplu:

subrutine amestecare(ncards, cards) intreg,


intent(in) integer, intent(out), :: ncards
dimension(ncards) :: cards

Pentru argumentele de intrare/ieșire, poate fi specificată intenția inout.


Machine Translated by Google

78 Fortran modern explicat

Dacă un argument fals este specificat cu intenție, acesta (sau orice parte a acestuia) nu trebuie să fie
redefinit de procedură, să zicem prin apariția în partea stângă a unei sarcini sau prin transmiterea ca
argument real unei proceduri. care o redefinește. Pentru intenția de specificație inout, argumentul actual
corespunzător trebuie să fie o variabilă, deoarece așteptarea este că va fi redefinit de procedură. Pentru
intenția specificației, argumentul actual corespunzător trebuie să fie din nou o variabilă; în acest caz,
intenția este ca acesta să fie folosit doar pentru a transmite informații, astfel încât să devină nedefinit la
intrarea în procedură, în afară de orice componente cu inițializare implicită (Secțiunea 7.5.4).

Dacă o funcție specifică un operator definit (Secțiunea 3.8), argumentele fictive trebuie să aibă intent
in. Dacă o subrutină specifică o atribuire definită (Secțiunea 3.9), primul argument trebuie să aibă intent
out sau inout, iar al doilea argument trebuie să aibă intent in.
Dacă un argument inactiv nu are nicio intenție, argumentul real poate fi o variabilă sau o expresie,
dar argumentul real trebuie să fie o variabilă dacă argumentul inactiv este redefinit. A fost tradițional ca
compilatorii Fortran să nu verifice această regulă, deoarece de obicei compilează fiecare unitate de
program separat. Încălcarea regulii poate duce la erori de program în timpul execuției, care sunt foarte
greu de găsit. Vă recomandăm ca tuturor argumentelor false să li se dea o intenție declarată. Nu numai
că această documentație este bună, dar le permite compilatorilor să facă mai multe verificări în timpul
compilării.
Dacă un argument fals are atributul pointer, intenția lui nu este permisă să fie specificată.
Acest lucru se datorează ambiguității dacă intenția se aplică obiectului de date țintă sau asocierii
pointerului.3 Dacă un argument fals este de tip derivat cu componente pointer, atributul său intent se
referă și la starea de asociere pointer a acelor componente. De exemplu, dacă intenția este în, nu este
permisă nicio alocare, alocare sau dealocare de pointer.

Standardul Fortran 95 nu specifică dacă atributul intent se aplică țintei unei componente pointer.4

5.10 Funcții

Funcțiile sunt similare subrutinelor în multe privințe, dar sunt invocate în cadrul unei expresii și returnează
o valoare care este utilizată în expresie. De exemplu, subprogramul din Figura 5.6 returnează distanța
dintre două puncte din spațiu și instrucțiunea

dacă (distanța(a, c) > distanța(b, c) ) atunci

invocă funcția de două ori în expresia logică pe care o conține.


Notați declarația de tip pentru rezultatul funcției. Rezultatul se comportă la fel ca un argument fals cu
intenție. Este inițial nedefinit, dar odată definit poate apărea într-o expresie și poate fi redefinit. Tipul
poate fi definit și pe instrucțiunea funcției astfel:

distanta functie reala (p, q)

Este permis să scrieți funcții care schimbă valorile argumentelor lor, modifică valorile în module, se
bazează pe datele locale salvate (Secțiunea 7.9) dintr-o invocare anterioară sau efectuează

3În Fortran 2003, intenția este permisă și se referă la starea asocierii pointerului (vezi Secțiunea 16.2).
4Fortran 2003 este clar că atributul intent nu se aplică țintei unei componente pointer.
Machine Translated by Google

Unități de program și proceduri 79

Figura 5.6 O funcție care returnează distanța dintre două puncte din spațiu. Funcția intrinsecă sqrt este
definită în Secțiunea 8.4.
funcția distanță(p, q) reală
:: distanta
real, intent(in), dimensiune(3) :: p, q distanta = sqrt( (p(1)-
q(1))**2 + (p(2)-q(2))**2 + (p(3)-q(3))**2 ) distanța funcției de sfârșit &

operațiuni de intrare/ieșire. Cu toate acestea, acestea sunt cunoscute ca efecte secundare și sunt în
conflict cu bunele practici de programare. Acolo unde sunt necesare, trebuie folosită o subrutină. Este
liniștitor să știi că atunci când o funcție este apelată, nimic altceva nu se întâmplă „în spatele scenei” și
poate fi foarte util pentru un compilator de optimizare, în special pentru subprogramele interne și de module.
Este oferit un mecanism formal pentru evitarea efectelor secundare, dar amânăm descrierea acestuia la
Secțiunea 6.10.
Rezultatul unei funcții poate fi un tablou, caz în care trebuie declarat ca atare.
Un rezultat al funcției poate fi și un pointer.5 Rezultatul este inițial nedefinit. În cadrul funcției, aceasta
trebuie să devină asociată sau definită ca disociată. Ne așteptăm ca referința funcției să fie, de obicei,
astfel încât să aibă loc o atribuire de pointer pentru rezultat, adică referința apare ca partea dreaptă a
unei atribuiri de pointer (Secțiunea 3.12), de exemplu,

real :: x(100)
real, indicator :: y(:)
:
y => compact(x)

sau ca o componentă pointer a unui constructor de structură. Referința poate apărea, de asemenea, ca
element principal al unei expresii sau ca partea dreaptă a unei sarcini obișnuite, caz în care rezultatul
trebuie să devină asociat cu o țintă care este definită și este utilizată valoarea țintei. Cu toate acestea, nu
recomandăm această practică, deoarece este probabil să conducă la pierderi de memorie, discutate la
sfârșitul Secțiunii 6.5.3.
Valoarea returnată de o funcție non-pointer trebuie întotdeauna definită.
Pe lângă faptul că este o valoare scalară sau matrice de tip intrinsec, rezultatul unei funcții poate fi și
o valoare scalară sau matrice de tip derivat, așa cum am văzut deja în Secțiunea 3.8. Când funcția este
invocată, valoarea funcției trebuie utilizată ca un întreg, adică nu este permisă să fie calificată prin selecție
subșir, subscript matrice, secțiune matrice sau structură-componentă.
Deși acest lucru nu este foarte util, unei funcții i se permite să aibă o listă de argumente goală.
În acest caz, parantezele sunt obligatorii atât în cadrul instrucțiunii funcției, cât și la fiecare invocare.

5 Cu toate acestea, nu este posibil ca un pointer să aibă o funcție ca țintă. Cu alte cuvinte, legarea dinamică sau asocierea
unui pointer cu o funcție în timpul rulării nu este disponibilă. Această deficiență este remediată în Fortran 2003 (a se vedea
secțiunea 13.6).
Machine Translated by Google

80 Fortran modern explicat

5.10.1 Efecte secundare interzise

Pentru a ajuta un compilator de optimizare, standardul interzice dependența de anumite efecte secundare.
Specifică că nu este necesar ca un procesor să evalueze toți operanzii unei expresii sau să evalueze în
întregime fiecare operand, dacă valoarea expresiei poate fi determinată altfel. De exemplu, în evaluare

x>y .sau. l(z) ! x, y și z sunt reale; l este o funcție logică

referința funcției nu trebuie făcută dacă x este mai mare decât y. Deoarece unele procesoare vor efectua
apelul, iar altele nu, orice variabilă (de exemplu z) care este redefinită de funcție este considerată nedefinită
în urma unei astfel de evaluări a expresiei. În mod similar, nu este necesar ca un procesor să evalueze orice
expresii subscript sau subșir pentru o matrice de dimensiune zero sau obiect caracter cu lungime de caracter
zero.
O altă interdicție este aceea că o referință de funcție nu trebuie să redefinească valoarea unei variabile
care apare în aceeași instrucțiune sau să afecteze valoarea unei alte referințe de funcție din aceeași
instrucțiune. De exemplu, în

d = max(distanta(p,q), distanta(q,r))

distanța este necesară pentru a nu-și redefini argumentele. Această regulă permite oricăror expresii care
sunt argumente ale unui singur apel de procedură să fie evaluate în orice ordine. În ceea ce privește această
regulă, o declarație if,

if (lexpr) stmt

este tratat ca echivalent if construct

dacă (lexpr) atunci


stmt
sfâr itul dacă

și același lucru este valabil pentru declarația where (Secțiunea 6.8) și declarația forall (Secțiunea 6.9).

5.11 Interfețe explicite și implicite

Un apel către un subprogram intern trebuie să provină dintr-o instrucțiune din aceeași unitate de program.
Se poate presupune că compilatorul va procesa unitatea de program ca întreg și, prin urmare, va ști totul
despre orice subprogram intern. În special, va ști despre interfața sa, adică dacă definește o funcție sau o
subrutină, numele și proprietățile argumentelor și proprietățile rezultatului dacă definește o funcție. Acest
lucru, de exemplu, permite compilatorului să verifice dacă argumentele reale și inactiv se potrivesc așa cum
ar trebui. Spunem că interfața este explicită.

Un apel la un subprogram de modul trebuie să fie fie dintr-o altă instrucțiune din modul, fie dintr-o
instrucțiune care urmează unei instrucțiuni de utilizare a modulului. În ambele cazuri, compilatorul va ști
totul despre subprogram și din nou spunem că interfața este explicită. În mod similar, procedurile intrinseci
(Capitolul 8) au întotdeauna interfețe explicite.
Machine Translated by Google

Unități de program și proceduri 81

Când compilați un apel către o procedură externă sau fictică (Secțiunea 5.12), compilatorul nu are în mod
normal un mecanism de accesare a codului său. Spunem că interfața este implicită.
Tot ce are compilatorul este informațiile despre interfață care sunt implicite în declarațiile din mediul invocării,
de exemplu, numărul de argumente și tipurile acestora.
Pentru a specifica faptul că un nume este cel al unei proceduri externe sau fictive, este disponibilă declarația
externă. Are forma

listă-nume-externă externă

și apare împreună cu alte instrucțiuni de specificație, după orice instrucțiuni de utilizare sau implicite
(Secțiunea 7.2) și înainte de orice instrucțiuni executabile. Parametrii de tip și tip ai unei funcții cu o interfață
implicită sunt de obicei specificați printr-o declarație de tip pentru numele funcției; o alternativă este prin
regulile de tastare implicită (Secțiunea 7.2) aplicate numelui, dar aceasta nu este disponibilă într-un modul
decât dacă funcția are atributul privat (vezi Secțiunea 7.6).

Declarația externă specifică doar că fiecare nume extern este numele unei proceduri externe sau fictive.
Nu specifică interfața, care rămâne implicită.
Cu toate acestea, este prevăzut un mecanism pentru ca interfața să fie specificată. Se poate face printr-un
bloc de interfață al formularului

interfata
interfață-corp
interfață capăt

În mod normal, corpul interfeței este o copie exactă a antetului subprogramului, a specificațiilor argumentelor
sale și a rezultatului funcției și a declarației sale de final. In orice caz,

• denumirile argumentelor pot fi schimbate;

• pot fi incluse alte specificații (de exemplu, pentru o variabilă locală), dar nu interne
proceduri, declarații de date sau declarații de format;

• informa iile pot fi date printr-o combina ie diferită de enun uri;6

• în cazul unui argument de matrice sau a unui rezultat al funcției, expresiile care specifică o limită pot
diferi atâta timp cât valorile lor nu pot diferi niciodată; și

• o procedură recursivă (Secțiunile 5.16 și 5.17) sau o procedură pură (Secțiunea 6.10) necesită
să nu fie specificată ca atare dacă nu este numită ca atare.

Un corp de interfață poate fi furnizat pentru un apel către o procedură externă definită prin alte mijloace
decât Fortran (de obicei C sau limbaj de asamblare).
Numirea unei proceduri într-o declarație externă sau acordarea unui corp de interfață (amândouă nu este
permisă) asigură că este o procedură externă sau fictică. Recomandăm cu tărie practica pentru proceduri
externe, deoarece, în caz contrar, procesorul are voie să interpreteze

6O practică care este permisă de standard, dar pe care nu o recomandăm, este ca un argument fals să fie declarat
implicit ca procedură prin invocarea lui într-o instrucțiune executabilă. Dacă subprogramul are o astfel de procedură
inactivă, interfața va avea nevoie de o declarație externă pentru acea procedură inactivă.
Machine Translated by Google

82 Fortran modern explicat

denumirea ca a unei proceduri intrinseci. Este necesar pentru portabilitate, deoarece procesatorilor li se
permite să ofere proceduri intrinseci suplimentare. Denumirea unei proceduri într-o declarație externă face
ca toate versiunile unei proceduri intrinseci având același nume să nu fie disponibile.
Același lucru este valabil și pentru a-i oferi un corp de interfață în modul descris în secțiunea următoare (dar
nu și atunci când interfața este generică, Secțiunea 5.18).
Blocul de interfață este plasat într-o secvență de declarații de specificație și acest lucru este suficient
pentru a face interfața explicită. Poate cel mai convenabil mod de a face acest lucru este să plasați blocul de
interfață printre instrucțiunile de specificație ale unui modul și apoi să utilizați modulul.
Bibliotecile pot fi scrise ca seturi de subprograme externe împreună cu module care dețin blocuri de interfață
pentru ele. Acest lucru păstrează modulele de dimensiuni modeste. Rețineți că, dacă o procedură este
accesibilă într-o unitate de definire a domeniului, interfața sa este fie explicită, fie implicită acolo. O procedură
externă poate avea o interfață explicită în unele unități de definire și o interfață implicită în altele.

Blocurile de interfață pot fi, de asemenea, utilizate pentru a permite procedurilor să fie apelate ca operatori
definiți (Secțiunea 3.8), ca atribuiri definite (Secțiunea 3.9) sau sub un singur nume generic. Prin urmare,
amânăm descrierea generalității complete a blocului de interfață până la Secțiunea 5.18, unde este discutată
supraîncărcarea.
O interfață explicită este necesară pentru a invoca o procedură cu un argument inactiv pointer sau țintă
sau un rezultat al funcției pointer și este necesară pentru câteva caracteristici utile pe care le vom întâlni mai
târziu în acest capitol și în următorul capitol. Este necesar pentru ca procesorul să poată face legătura
corespunzătoare. Chiar și atunci când nu este strict necesar, oferă compilatorului posibilitatea de a examina
dependențele de date și, prin urmare, de a îmbunătăți optimizarea. Interfețele explicite sunt, de asemenea,
de dorit din cauza securității suplimentare pe care o oferă. Este simplu să vă asigurați că toate interfețele
sunt explicite și vă recomandăm practica.

5.12 Procedurile ca argumente

Până acum, am considerat argumentele reale ale unei invocări de proceduri ca fiind variabile și expresii, dar
o altă posibilitate este ca acestea să fie proceduri. Să luăm în considerare cazul unui subprogram de bibliotecă
pentru minimizarea funcției. Trebuie să primească funcția utilizatorului, la fel cum amestecarea subrutinei
din Figura 5.5 trebuie să primească numărul necesar de carduri.
Codul de minimizare ar putea arăta ca codul din Figura 5.7. Observați modul în care argumentul procedurii
este declarat de un bloc de interfață care joacă un rol similar cu cel al declarației de tip pentru un obiect de
date.
Așa cum tipul și forma obiectelor de date reale și fictive trebuie să fie de acord, la fel trebuie să fie de
acord și proprietățile procedurilor reale și fictive. Acordul este exact ca pentru o procedură și un corp de
interfață pentru acea procedură (a se vedea secțiunea 5.11). Nu ar avea sens să specificați un atribut de
intenție (Secțiunea 5.9) pentru o procedură inactivă, iar acest lucru nu este permis.
Din partea utilizatorului, codul poate arăta ca în Figura 5.8. Observați că structura este mai degrabă ca un
sandwich: codul scris de utilizator invocă codul de minimizare care, la rândul său, invocă codul scris de
utilizator. O procedură externă aici ar necesita în schimb utilizarea unui bloc de interfață sau, cel puțin,
numele procedurii ar trebui să fie declarat într-o declarație externă.
Procedura care este trecută poate fi doar o procedură externă sau de modul și numele ei specific trebuie
trecut atunci când are și un nume generic (Secțiunea 5.18). Procedurile interne sunt
Machine Translated by Google

Unități de program și proceduri 83

Figura 5.7 Un subprogram de bibliotecă pentru minimizarea funcției.


funcția reală minim (a, b, func) ! Returnează minimul
! valoarea functiei func(x) in intervalul (a,b) real, intent(in) :: a, b interfata

funcția reală func(x)


real, intent(in) :: x end function func

interfață finală

real :: f,x
:

f = func(x) ! invocarea funcției utilizator.


:
funcția finală minimă

nu este permis7 deoarece se anticipează că acestea pot fi implementate destul de diferit (de exemplu, prin cod în linie) și
din cauza necesității de a identifica adâncimea recursiunii atunci când gazda este recursivă (Secțiunea 5.16) și procedura
implică variabile gazdă .

5.13 Cuvânt cheie și argumente opționale

În aplicațiile practice, listele de argumente pot deveni lungi, iar apelurile reale pot avea nevoie doar de
câteva argumente. De exemplu, o subrutină pentru minimizarea constrânsă poate avea forma

subrutina mincon(n, f, x, superior, inferior, &

egalități, inegalități, convexe, xstart)

La multe apeluri, s-ar putea să nu existe limite superioare, sau limite inferioare, sau egalități sau inegalități, sau s-ar putea
să nu se știe dacă funcția este convexă sau s-ar putea să nu știe un punct de plecare sensibil. Toate argumentele dummy
corespunzătoare pot fi declarate opționale (vezi și Secțiunea 7.8). De exemplu, limitele ar putea fi declarate de declarație

real, opțional, dimensiune(n) :: superior, inferior

Dacă primele patru argumente sunt singurele dorite, putem folosi afirmația

apelați mincon(n, f, x, superior)

dar de obicei argumentele dorite sunt risipite. În acest caz, putem urma o listă de argumente poziționale
obișnuită (posibil goală) pentru argumentele principale de o listă de argumente de cuvinte cheie, ca în
instrucțiunea

apelați mincon(n, f, x, egalități=q, xstart=x0)

7O regulă abolită în Fortran 2008, vezi secțiunea 20.5.5.


Machine Translated by Google

84 Fortran modern explicat

Figura 5.8 Invocarea codului bibliotecii din Figura 5.7.


codul modulului
conține
funcția reală fun(x)
real, intentia(in) :: x
:
sfârșitul funcției distracție
codul modulului final
codul de utilizare principal

al programului

real :: f
:
f = minim (1,0, 2,0, distracție)
:
termina programul principal

Cuvintele cheie sunt nume de argument inactiv și nu trebuie să existe alte argumente poziționale după primul
argument al cuvântului cheie.
Acest exemplu ilustrează, de asemenea, meritele argumentelor poziționale și ale cuvintelor cheie în ceea
ce privește lizibilitatea. Un număr mic de argumente poziționale principale (de exemplu, n, f și x) sunt ușor
legate în mintea cititorului de argumentele simulate corespunzătoare. Dincolo de aceasta, cuvintele cheie
sunt foarte utile cititorului în realizarea acestor link-uri. Recomandăm utilizarea lor pentru liste lungi de
argumente chiar și atunci când nu există lacune cauzate de argumente opționale care nu sunt prezente.

Un argument non-opțional trebuie să apară exact o dată, fie în lista pozițională, fie în lista de cuvinte cheie.
Un argument opțional poate apărea cel mult o dată, fie în lista pozițională, fie în lista de cuvinte cheie. Un
argument nu trebuie să apară în ambele liste.
Subprogramul apelat are nevoie de o modalitate de a detecta dacă un argument este prezent, astfel încât
să poată lua măsurile corespunzătoare atunci când nu este. Aceasta este asigurată de funcția intrinsecă
prezentă (vezi Secțiunea 8.2). De exemplu

prezent(xstart)

returnează valoarea .true. dacă apelul curent a oferit un punct de plecare și .fals. in caz contrar. Când este
absent, subprogramul poate, de exemplu, să folosească un generator de numere aleatorii pentru a oferi un
punct de plecare.
O ușoară complicație apare dacă un argument fals opțional este utilizat în cadrul subprogramului ca
argument real într-o invocare a unei proceduri. De exemplu, subrutina noastră de minimizare ar putea începe
prin a apela o subrutină care tratează problema egalității corespunzătoare prin apel.

apelați mineq (n, f, x, egalități, convex, xstart)

Într-un astfel de caz, un argument opțional absent este, de asemenea, privit ca absent în subprogramul de al
doilea nivel. De exemplu, atunci când convex este absent în apelul lui mincon, este considerat ca
Machine Translated by Google

Unități de program și proceduri 85

absent și în mineq. Astfel de argumente absente pot fi propagate prin orice număr de apeluri, cu condiția ca
argumentul fals să fie opțional în fiecare caz. Un argument absent furnizat în continuare ca argument real trebuie
specificat ca întreg, și nu ca subobiect. În plus, un pointer absent nu este permis să fie asociat cu un argument
inactiv non-pointer (ținta este de două ori absentă).

Deoarece compilatorul nu va putea face asocierile adecvate decât dacă cunoaște cuvintele cheie (nume de
argumente false), interfața trebuie să fie explicită (Secțiunea 5.11) dacă oricare dintre argumentele fictive sunt
opționale sau sunt utilizate argumente cheie. Rețineți că un bloc de interfață poate fi furnizat pentru o procedură
externă pentru a face interfața explicită. În toate cazurile în care este furnizat un bloc de interfață, numele
argumentelor fictive din bloc sunt folosite pentru a rezolva asocierile.

5.14 Domeniul de aplicare al etichetelor

Execuția programului principal sau a unui subprogram începe întotdeauna de la prima sa instrucțiune executabilă
și orice ramificare are loc întotdeauna de la una dintre instrucțiunile sale executabile la alta.
Într-adevăr, fiecare subprogram are propriul său set independent de etichete. Acesta include cazul unui subprogram
gazdă cu mai multe subprograme interne. Aceeași etichetă poate fi utilizată în gazdă și subprogramele interne fără
ambiguitate.
Aceasta este prima noastră întâlnire cu scopul. Scopul unei etichete este un program principal sau un
subprogram, excluzând orice subprograme interne pe care le conține. Eticheta poate fi folosită fără ambiguitate
oriunde dintre instrucțiunile executabile ale domeniului său. Observați că instrucțiunea host end poate fi etichetată
și poate fi o țintă de ramură dintr-o instrucțiune gazdă, adică subprogramele interne lasă o gaură în domeniul
gazdei (vezi Figura 5.9).

5.15 Domeniul de aplicare al denumirilor

În cazul unei entități numite, există un set similar de declarații în care numele poate fi întotdeauna folosit pentru a
se referi la entitate. Aici, definițiile de tip și blocurile de interfață, precum și subprogramele pot face găuri în
domeniul de aplicare. Acest lucru ne conduce să considerăm fiecare unitate de program ca fiind formată dintr-un
set de unități de acoperire care nu se suprapun. O unitate de acoperire este una dintre următoarele:

• o definiție de tip derivat;

• un corp de interfață de procedură, excluzând orice definiții de tip derivat și corpuri de interfață
cuprinse în ea; sau

• o unitate de program sau un subprogram, excluzând definițiile de tip derivat, corpurile de interfață și
subprogramele cuprinse în acesta.

Un exemplu care conține cinci unități de scoping este prezentat în Figura 5.9.
Odată ce o entitate a fost declarată într-o unitate de acoperire, numele acesteia poate fi folosit pentru a se referi
la ea în acea unitate de acoperire. O entitate declarată într-o altă unitate de acoperire este întotdeauna o entitate
diferită, chiar dacă are același nume și exact aceleași proprietăți.8 Fiecare este cunoscută ca o entitate locală. Acest

8În afară de efectul asocierii depozitării, care nu se discută până în Anexa B și a cărui utilizare o
descurajăm cu tărie.
Machine Translated by Google

86 Fortran modern explicat

Figura 5.9 Un exemplu de domenii imbricate. domeniul


de aplicare al modulului 1 ! domeniul 1! domeniul
1! domeniul 1!
: domeniul 2!
conține domeniul 3!
subrutina scope2 tip scope3 domeniul 3!
domeniul 3!
: domeniul 2!
interfață scope3 de tip domeniul de
final aplicare 4!
: domeniul 2!
interfață finală domeniul 2!
: scope 2
con ine function
scope5(...) ! domeniul 5! scope 5 end function
: scope5 ! sfera
5 sfâr itul subrutinei scope2 ! domeniul 2! sfera
de aplicare 1 modul final sfera de aplicare 1

este foarte util pentru programator, care nu trebuie să fie îngrijorat de posibilitatea unor ciocniri accidentale de nume.
Rețineți că acest lucru este valabil și pentru tipurile derivate. Chiar dacă două tipuri derivate au același nume și aceleași
componente, entitățile declarate cu ele sunt tratate ca fiind de tipuri diferite.9

O declarație de utilizare a formularului

utilizați modul-nume

este privită ca o re-declarare a tuturor entităților de modul din interiorul unității locale de scoping, cu exact aceleași nume
și proprietăți. Se spune că entitățile modulului sunt accesibile prin asociere de utilizare. Numele entităților din modul nu
pot fi folosite pentru a declara entități locale (dar vezi Secțiunea 7.10 pentru o descriere a facilităților suplimentare furnizate
de declarația de utilizare atunci când este necesară o mai mare flexibilitate).

În cazul unei definiții de tip derivat, a unui subprogram de modul sau a unui subprogram intern, numele unei entități
din gazdă (inclusiv o entitate accesată prin asociere de utilizare) este tratat în mod similar ca fiind re-declarat automat cu
aceleași proprietăți, cu condiția ca nicio entitate cu acest nume să nu fie declarată local, să fie un argument inactiv local
sau un rezultat al funcției sau să fie accesată prin asociere de utilizare. Se spune că entitatea gazdă este accesibilă de către
asociația gazdă. De exemplu, în subrutina din interiorul figurii 5.10, x este accesibil prin asocierea gazdei, dar y este o
variabilă locală separată și y a gazdei este inaccesibil. Observam ca inner apeleaza o alta procedura interna care este o
functie, f; nu trebuie să conțină o specificație de tip pentru acea funcție, deoarece interfața este deja explicită. O astfel de
specificație ar declara, de fapt, o funcție externă diferită a acestei denumiri. Aceeași observație se aplică unei proceduri de
modul care apelează o funcție din același modul.

9 În afară de efectele asocierii depozitării (Anexa B).


Machine Translated by Google

Unități de program și proceduri 87

Figura 5.10 Exemple de asociere gazdă.


subrutină exterioară

real :: x, y
:
con ine
subrutina reală interioară ::

yy = f(x) + 1. ! x și f
accesate de asociația gazdă
:
sfâr itul subrutinei interior
funcția f(z)
real :: f

real, intenție(în) :: z
:
funcția finală f
sfâr itul subrutinei exterioară

Rețineți că gazda nu are acces la entitățile locale ale unei subrutine pe care le conține.

Asocierea gazdei nu se extinde la blocurile de interfață.10 Acest lucru permite ca un corp de interfață să fie construit
mecanic din declarațiile de specificație ale unei proceduri externe.
Rețineți, totuși, că dacă un tip derivat necesar pentru interfață este accesat dintr-un modul, blocul de interfață construit din
procedură nu poate fi plasat în modulul care definește tipul, deoarece unui modul nu i se permite să se acceseze singur.
De exemplu, încercarea de acces din Figura 5.11 nu este permisă.

Figura 5.11 Încercarea de a scrie o interfață într-un modul pentru o procedură care utilizează modulul.
modulul m

tip t
întreg :: i, j, k final tip t interfață
g subrutină s(a)

folosește m ! Acces ilegal la modul.


tip(t) :: un
subprogram final s modul
final al interfeței de capăt m

În cadrul unei unități de acoperire, fiecare obiect de date denumit, procedură, tip derivat, construcție
numită și grup de listă de nume (Secțiunea 7.15) trebuie să aibă un nume distinct, cu o singură excepție
a numelor generice de proceduri (care vor fi descrise în Secțiunea 5.18). Rețineți că aceasta înseamnă că

10 În Fortran 2003, acest lucru este remediat prin declarația de import, Secțiunea 16.4.
Machine Translated by Google

88 Fortran modern explicat

orice apariție a numelui unei proceduri intrinseci într-un alt rol face ca procedura intrinsecă să fie inaccesibilă prin
numele său (facilitatea de redenumire descrisă în Secțiunea 7.10 permite accesarea unei proceduri intrinseci dintr-
un modul și redenumită). În cadrul unei definiții de tip, fiecare componentă a tipului, fiecare procedură intrinsecă
la care se face referire și fiecare tip derivat sau constantă numită accesată de asociere gazdă, trebuie să aibă un
nume distinct. În afară de aceste reguli, numele pot fi refolosite. De exemplu, un nume poate fi folosit pentru
componentele a două tipuri sau argumentele a două proceduri la care se face referire cu apeluri de cuvinte cheie.

Numele unităților de program și procedurilor externe sunt globale, adică sunt disponibile oriunde într-un
program complet. Fiecare trebuie să fie distinct de celelalte și de oricare dintre entitățile locale ale unității de
program.
La cealaltă extremă, variabila do a unui implicit-do într-o instrucțiune de date (Secțiunea 7.5.2) sau un constructor
de matrice (Secțiunea 6.16) are un domeniu care este doar implicit-do. Este diferită de orice altă entitate cu același
nume.

5.16 Recursie directă

În mod normal, un subprogram nu se poate invoca pe sine, fie direct, nici indirect, printr-o secvență de alte invocări.
Cu toate acestea, dacă instrucțiunea principală are prefix recursiv, acest lucru este permis. Acolo unde subprogramul
este o funcție care se autoinvocă direct în acest mod, numele funcției nu poate fi folosit pentru rezultatul funcției
și este nevoie de un alt nume. Acest lucru se face prin adăugarea unei clauze suplimentare la instrucțiunea funcției

ca în Figura 5.12, care ilustrează utilizarea unei funcții recursive pentru a însuma intrările dintr-un lanț (vezi
Secțiunea 2.13).

Figura 5.12 Însumarea intrărilor dintr-o listă legată. funcție


recursivă sum(top) rezultat(e) tip(intrare), pointer :: top real

:: s
dacă (asociat(sus)) atunci s =
top%valoare + sum(top%next) else

s = 0,0
sfâr it dacă
func ia finală suma

Tipul funcției (și rezultatul acesteia) poate fi specificat fie în instrucțiunea funcției
înainte sau după simbolul recursiv:

întreg funcția recursivă factorial(n) rezultat(res)


sau

funcție număr întreg recursiv factorial(n) rezultat(res)

sau într-o declarație de tip pentru numele rezultatului (ca în Figura 5.12). De fapt, numele rezultatului, mai degrabă
decât numele funcției, trebuie utilizat în orice instrucțiune de specificație. În instrucțiunile executabile, numele
funcției se referă la funcția în sine și la numele rezultatului
Machine Translated by Google

Unități de program și proceduri 89

trebuie utilizat pentru variabila rezultat. Dacă nu există o clauză de rezultat, numele funcției este folosit
pentru rezultat și nu este disponibil pentru un apel recursiv de funcție.
Clauza rezultat poate fi folosită și într-o funcție nerecursivă.
La fel ca în Figura 5.12, orice procedură recursivă care se autoinvocă direct trebuie să conțină un test
condiționat care termină secvența de apeluri la un moment dat, altfel se va numi pe sine pe termen
nelimitat.
De fiecare dată când este invocată o procedură recursivă, este creat un nou set de obiecte de
date locale, care încetează să mai existe la întoarcere. Ele constau din toate obiectele de date
declarate în instrucțiunile de specificație ale procedurii sau declarate implicit (vezi Secțiunea 7.2),
dar cu excepția celor cu atributul data sau save (vezi Secțiunile 7.5 și 7.9) și orice argumente fictive.
Interfața este explicită în cadrul procedurii.

5.17 Recursie indirectă

O procedură poate fi invocată și prin recursivitate indirectă, adică se poate apela prin apeluri către
alte proceduri. Pentru a ilustra faptul că acest lucru poate fi util, să presupunem că dorim să
realizăm o integrare bidimensională, dar avem doar procedura pentru integrarea unidimensională
prezentată în Figura 5.13. De exemplu, să presupunem că se dorește să se integreze o funcție f a lui x și y

Figura 5.13 Un cod de bibliotecă pentru integrarea unidimensională.


funcția recursivă integra (f, limite)
! Integrați f(x) de la bounds(1) la bounds(2) real :: integrați
interfața

funcția f(x)
real :: f
real, intent(in) :: x end function
f end interface real, dimension(2),
intent(in) :: limite

:
se integrează funcția finală

peste un dreptunghi. Am putea scrie o funcție Fortran într-un modul pentru a primi valoarea lui x ca
argument și valoarea lui y de la modul însuși prin asociere gazdă, așa cum se arată în Figura 5.14. Apoi
putem integra peste x pentru o anumită valoare a lui y, așa cum se arată în Figura 5.15, unde integrare
ar putea fi așa cum se arată în Figura 5.13. Acum putem integra astfel peste întregul dreptunghi

volum = integra (fy, ybounds)

Rețineți că integrați apelurile fy, care la rândul lor se integrează.


Machine Translated by Google

90 Fortran modern explicat

Figura 5.14 O funcție bidimensională care trebuie integrată.


modul func
real :: yval
real, dimensiune(2) :: xbounds, ybounds conține

funcția f(xval) real :: f


real, intent(in) :: xval f =

... ! Expresie care implică xval și yval


funcția finală f
modul final func

Figura 5.15 Integrare peste x.


function fy(y) use func

real :: fy
real, intent(in) :: y yval = y fy =
integrate(f, xbounds) end
function fy

5.18 Supraîncărcare și interfețe generice

Am văzut în Secțiunea 5.11 cum să folosim un bloc de interfață simplu pentru a oferi o interfață
explicită unei proceduri externe sau fictive. O altă utilizare este supraîncărcarea, adică posibilitatea de
a apela mai multe proceduri cu același nume generic. Aici, blocul de interfață conține mai multe
corpuri de interfață, iar instrucțiunea de interfață specifică numele generic. De exemplu, codul din
Figura 5.16 permite invocarea ambelor funcții sgamma și dgamma folosind numele generic gamma.

Un nume specific pentru o procedură poate fi același cu numele său generic. De exemplu, cel
procedura sgamma ar putea fi redenumită gamma fără a invalida blocul de interfață.
În plus, un nume generic poate fi același cu un alt nume generic accesibil. Într-un astfel de caz, toate
procedurile care au această denumire generică pot fi invocate prin intermediul acestuia. Această
capacitate este importantă, deoarece un modul poate avea nevoie să extindă funcțiile intrinseci, cum ar
fi sin, la un tip nou, cum ar fi interval (Secțiunea 3.8).
Dacă se dorește supraîncărcarea unei proceduri de modul, interfața este deja explicită, așa că este
inadecvat să specificați un corp de interfață. În schimb, declarația

procedura modulului lista-nume-procedura

este inclus în blocul de interfață pentru a denumi procedurile modulului pentru supraîncărcare; dacă
funcțiile sgamma și dgamma de mai sus au fost definite într-un modul, blocul de interfață devine
Machine Translated by Google

Unități de program și proceduri 91

Figura 5.16 Un bloc de interfață generic.


interfață gamma function sgamma(x)
real (selected_real_kind( 6)) real
(selected_real_kind( 6)), intent(in) :: x end :: sgamma
function sgamma function dgamma(x) real (selected_real_kind(12)) ::
dgamma real (selected_real_kind(12)) (12)), intent(in) :: x end function
dgamma end interface

interfață gamma
modul procedura sgamma, dgamma interfață
finală

Cel mai convenabil este probabil să plasați un astfel de bloc în modul însuși.
Orice specificație generică a unei instrucțiuni de interfață poate fi repetată pe corespondent
ing end interface statement, de exemplu,

sfâr itul interfe ei gamma

În ceea ce privește celelalte declarații finale, vă recomandăm utilizarea acestei forme mai complete.

O altă formă de supraîncărcare apare atunci când un bloc de interfață specifică o operație definită
(Secțiunea 3.8) sau o atribuire definită (Secțiunea 3.9) pentru a extinde o operație sau o atribuire intrinsecă.
Sfera de aplicare a operațiunii sau atribuirii definite este unitatea de acoperire care conține blocul de
interfață, dar poate fi accesat în altă parte prin utilizare sau asociere gazdă. Dacă un operator intrinsec este
extins, numărul de argumente trebuie să fie consecvent cu forma intrinsecă (de exemplu, nu este posibil să
se definească un operator * unar).
Forma generală a blocului de interfață este

interfață [spec-generic] [corp-


interfață]... [procedura
modulului listă-nume-procedură]...
! Corpuri de interfață și modul ! declarațiile
de procedură pot apărea în orice ordine. interfață finală [specificație
generică]

unde este generic-spec

operator cu
nume generic (operator definit)
sau

sarcina(=)
Machine Translated by Google

92 Fortran modern explicat

O declarație de procedură de modul este permisă numai atunci când este prezentă o specificație generică
și toate procedurile trebuie să fie proceduri de modul accesibile (după cum se arată în modulul complet din
Figura 5.18 de mai jos). Nici un nume de procedură nu poate primi o anumită specificație generică de mai
multe ori în blocurile de interfață accesibile într-o unitate de definire a domeniului. Trebuie prevăzut un
corp de interfață pentru o procedură externă sau inactivă.
Dacă operatorul este specificat în instrucțiunea de interfață, toate procedurile din bloc trebuie să fie
funcții cu unul sau două argumente non-opționale care au intenție. 11 Dacă este specificată

atribuirea, toate procedurile trebuie să fie subrutine cu două argumente non-opționale, primul având intent
out sau inout și al doilea intent in. Pentru ca invocările să fie întotdeauna lipsite de ambiguitate, dacă două
proceduri au același operator generic și același numărul de argumente sau ambele definesc atribuirea,
unul trebuie să aibă un argument inactiv care corespunde după poziție în lista de argumente unui argument
inactiv al celuilalt care are un tip diferit, un parametru de tip diferit sau un rang diferit.

Toate procedurile care au un nume generic dat trebuie să fie subrutine sau toate trebuie să fie funcții,
inclusiv cele intrinseci atunci când o procedură intrinsecă este extinsă. Orice două proceduri non-intrinseci
cu același nume generic trebuie să aibă argumente care pot fi distinse (au tip de date incompatibile, tip sau
rang) pentru ca orice invocare să fie lipsită de ambiguitate. Regula este că fie

i) unul dintre ele are mai multe argumente non-opționale de obiect de date de un anumit tip de date,
parametru de tip tip și rang decât celălalt are argumente de obiect de date (inclusiv argumente
opționale de obiect de date) de acel tip de date, parametru de tip de tip , și rang; sau

ii) cel puțin unul dintre ele le are pe amândouă

• un argument dummy non-opțional care corespunde după poziție în lista de argumente unui
argument dummy care se poate distinge de acesta, sau pentru care niciun argument dummy
nu corespunde după poziție; și

• un argument dummy non-opțional cu același nume ca un argument dummy care se distinge


de acesta sau pentru care nu există niciun argument dummy cu acel nume.

Aceste două argumente trebuie fie să fie aceleași, fie argumentul care corespunde poziției trebuie
să apară mai devreme în lista de argumente inactiv.

Pentru cazul ii), ambele reguli sunt necesare pentru a satisface atât listele de argumente de cuvinte cheie,
cât și de poziție. De exemplu, interfața din Figura 5.17 este invalidă deoarece cele două funcții se pot
distinge întotdeauna într-un apel pozițional, dar nu într-un apel de cuvinte cheie, cum ar fi f(i=int, x=posn).
Dacă o invocare generică este ambiguă între o procedură neintrinsecă și o procedură intrinsecă, se invocă
procedura neintrinsecă.
Rețineți că prezența sau absența atributului pointer este insuficientă pentru a asigura o invocare
neechivocă, deoarece un argument real pointer poate fi asociat cu un argument dummy non-pointer, vezi
Secțiunea 5.7.1.

11Deoarece intentia nu trebuie specificata in Fortran 95 pentru un argument dummy pointer (Sectiunea 5.7.1),
aceasta implica ca daca un operand de tip de date derivat are si atributul pointer, valoarea tinta este transmisa
functiei. definirea operatorului, și nu indicatorul în sine. Starea pointerului este inaccesibilă în cadrul funcției. În
Fortran 2003, intenția poate fi specificată pentru un argument dummy pointer.
Machine Translated by Google

Unități de program și proceduri 93

Figura 5.17 Un exemplu de regulă de supraîncărcare încălcată.


interfață f ! Bloc interfață nevalid
funcția fxi(x,i)
real :: fxi
real, intent(in) :: x întreg :: i end
function fxi function fix(i,x) real

:: repara
real, intent(in) :: x integer :: i end
function fix

interfață finală

Există multe aplicații științifice în care este util să se verifice tipurile de cantități implicate într-un calcul. De
exemplu, în analiza dimensională, în timp ce ar putea fi rezonabil să împărțim lungimea în timp pentru a obține
viteza, nu este sensibil să adăugați timp la viteză.
Nu există o modalitate intrinsecă de a face acest lucru, dar încheiem această secțiune cu un exemplu
general, vezi figurile 5.18 și 5.19, despre cum ar putea fi realizat folosind tipuri derivate.
Rețineți că sunt necesare și definiții pentru operațiunile între entități similare, așa cum este arătat de
time_plus_time. În mod similar, orice funcție intrinsecă care ar putea fi necesară, aici sqrt, trebuie să fie
supraîncărcată corespunzător. Desigur, acest lucru poate fi evitat dacă componentele variabilelor sunt
referite direct, ca în

t%secunde = t%secunde + 1,0

5.19 Lungimea caracterului presupus

Un argument dummy caracter poate fi declarat cu un asterisc pentru valoarea parametrului de tip lungime, caz
în care preia automat valoarea din argumentul actual. De exemplu, o subrutină pentru sortarea elementelor
unui tablou de caractere ar putea fi scrisă astfel

subrutine sortare(n,caractere)
întreg, intenție(în) caracter(len=*), :: n
dimensiune(n), intenție(în) :: caractere
:
sfârșitul sortării subrutinei

Dacă lungimea argumentului real asociat este necesară în cadrul procedurii, poate fi invocată funcția
intrinsecă len (Secțiunea 8.6.1), ca în Figura 5.20.
Nu trebuie utilizat un asterisc pentru o valoare a parametrului de tip tip. Acest lucru se datorează
faptului că o modificare a lungimii caracterului este analogă cu o modificare a dimensiunii unui tablou
și poate fi ușor de introdus în codul obiect, în timp ce o schimbare de tip necesită probabil o instrucțiune
diferită a mașinii pentru fiecare operație care implică argumentul fals. O versiune diferită a procedurii
Machine Translated by Google

94 Fortran modern explicat

Figura 5.18 Un modul pentru distingerea entităților reale.


sortarea modulelor
tastați time
real :: secunde
tip de sfârșit tip
de timp viteza
reală :: metri_per_secundă
tipul de viteză tipul
lungime
reali :: metri
tip final lungime tip
lungime_pătrat real ::
metri_pătrat
tipul final length_squared
interface operator(/) procedura
de modul length_by_time end interface

Interfață operator(+) modul


procedura time_plus_time final interfață

interfață modulul
sqrt procedura sqrt_metres_squared interfață
finală
con ine
funcția length_by_time(s, t)
tip(lungime), intent(in) :: s tip(timp),
intent(in) :: t tip(viteza)
length_by_time%metri_per_second ::
= lungime_cu_timp
s%metri / t%secunde
sfârșitul funcției length_by_time
function time_plus_time(t1, t2) tip(timp),
intent(in) :: t1, t2 type(time) :: time_plus_time
time_plus_time%secunde = t1%secunde
t2%secunde
+ sfârșitul
funcției time_plus_time funcția sqrt_meters_squared(l2)
tip(lungime_pătrat), intenție(in) :: l2 tip(lungime) :: sqrt_metres_squared
sqrt_metres_squared%meters = sqrt(l2%metri_squared) funcție finală
sqrt_metres_squared

sfârșitul sortărilor de module


Machine Translated by Google

Unități de program și proceduri 95

Figura 5.19 Utilizarea modulului din Figura 5.18.


sortarea utilizării testului programului

tip(lungime) :: s = lungime(10,0), l
tip(lungime_pătrat) :: s2 = lungime_pătrat(10,0) tip(viteză) tip(timp) v=s/t
:: v
:: t = timp(3,0)

! Notă: v = s + t sau v=s * ar fi ilegal


t=t+ time(1.0) l = sqrt(s2)
print *, v, t, l

sfârșitul testului programului

Figura 5.20 O funcție cu un argument de lungime presupusă a caracterelor. număr


de funcții întregi (litera, șir) caracter (1), intent (în) :: caracter de literă (*), intent
(în) :: șir

! Numărați numărul de apariții ale literei în șir


număr = 0
do i = 1, len(șir)
if (șir(i:i) == litera) count = count + 1 end do

sfârșitul numărului de funcții

ar trebui generat pentru fiecare valoare de tip posibilă a fiecărui argument. Caracteristica de
supraîncărcare (secțiunea anterioară) oferă programatorului o funcționalitate echivalentă cu
control explicit asupra versiunilor generate.

5.20 Subrutinele și instrucțiunile de funcție

Încheiem acest capitol dând sintaxa subrutinei și a instrucțiunilor de funcție, care au fost explicate până acum
prin exemple. Este

[prefix] subrutină subrutine-nume [[([dummy-argument-list])]

și

[prefix] funcția nume-funcție ([listă-argumente-fictivă]) și [rezultat(nume-


rezultat)]

unde este prefixul

prefix-spec [ prefix-spec ] ...


Machine Translated by Google

96 Fortran modern explicat

iar prefix-spec este tip, recursiv, pur sau elementar. Un prefix-spec nu trebuie repetat.
Pentru detalii despre tip, vezi Secțiunea 7.13; aceasta, desigur, nu trebuie să fie prezentă într-o instrucțiune de
subrutină.

În afară de pur și elementar, care vor fi explicate în Secțiunile 6.10 și 6.11, fiecare caracteristică a fost explicată
separat, iar semnificațiile sunt aceleași în combinațiile permise de sintaxă.

5.21 Rezumat
Un program constă dintr-o secvență de unități de program. Trebuie să conțină exact un program principal, dar poate
conține orice număr de module și subprograme externe. Am descris fiecare tip de unitate de program. Modulele
conțin definiții de date, definiții de tip, grupuri de liste de nume, blocuri de interfață și subprograme de module,
toate acestea putând fi accesate în alte unități de program cu instrucțiunea use. Unitățile de program pot fi în orice
ordine, dar multe compilatoare necesită module pentru a preceda utilizarea lor.

Subprogramele definesc proceduri, care pot fi funcții sau subrutine. Ele pot fi, de asemenea, definite intrinsec
(Capitolul 8), iar procedurile externe pot fi definite prin alte mijloace decât Fortran. Am explicat modul în care
informațiile sunt transmise între unitățile de program și către proceduri prin liste de argumente și prin utilizarea
modulelor. Procedurile pot fi numite recursiv cu condiția ca acestea să fie specificate în mod corespunzător.

Interfața cu o procedură poate fi explicită sau implicită. Dacă este explicit, pot fi făcute apeluri de cuvinte cheie,
iar procedura poate avea argumente opționale. Blocurile de interfață permit invocarea procedurilor ca operații sau
atribuiri sau printr-un nume generic. Lungimile caracterelor argumentelor fictive pot fi presupuse.

Am explicat, de asemenea, domeniul de aplicare al etichetelor și al numelor Fortran și am introdus conceptul de


unitate de definire a domeniului.

Exerciții

1. O subrutină primește ca argumente o matrice de valori, x și numărul de elemente din x, n. Dacă


media și varianța valorilor din x sunt estimate prin

1 n
înseamnă =
n x(i)
i=1

și

1 n
varianță = 2
n 1 i=1 (x(i) medie)

scrieți o subrutină care returnează aceste valori calculate ca argumente. Subrutina ar trebui să verifice
valorile nevalide ale lui n ( 1).

2. O subrutină matrix_mult înmulțește împreună două matrice A și B, ale căror dimensiuni sunt i× j și,
respectiv, j ×k, returnând rezultatul într-o matrice C dimensionată i×k. Scrieți matrix_mult, având în
vedere că fiecare element al lui C este definit de
Machine Translated by Google

Unități de program și proceduri 97

J
C(m,n) = (A(m, )×B(,n))
=1

Matricele ar trebui să apară ca argumente pentru matrix_mult.

3. Subrutina random_number (Secțiunea 8.16.3) returnează un număr aleatoriu în intervalul de la 0,0 la 1,0,
acesta este

apelați numărul_aleatoriu (r) !0 r<1

Folosind această funcție, scrieți amestecul subrutinei din Figura 5.4.

4. Un șir de caractere este format dintr-o succesiune de litere. Scrieți o funcție care să returneze acea literă a șirului
care apare cel mai devreme în alfabet; de exemplu, rezultatul aplicării funcției la DGUMVETLOIC este C.

5. Scrieți o procedură internă pentru a calcula volumul, πr2, al unui cilindru cu raza r și lungimea , folosind ca valoare
a lui π rezultatul acos(-1,0) și referiți-l într-o procedură gazdă.

6. Alegând un joc simplu de cărți la alegere și utilizând procedura numerelor aleatoare (Secțiunea 8.16.3), scrieți
subrutinele și jocul din Secțiunea 5.4, folosind datele dintr-un modul pentru a comunica între ele.

7. Obiectele de tip intrinsec au o lungime fixă. Scrieți un modul care să conțină o definiție a unui șir de caractere de
lungime variabilă, cu lungimea maximă 80, precum și procedurile necesare
la:

i) atribuiți o variabilă caracter unui șir;

ii) atribuirea unui șir unei variabile caracter;

iii) returnează lungimea unui șir; iv)

concatenează două șiruri.


Machine Translated by Google

Această pagină a fost lăsată goală în mod intenționat


Machine Translated by Google

6. Caracteristici ale matricei

6.1 Introducere

Într-o epocă în care multe computere au capacitatea hardware pentru procesarea eficientă a operanzilor matricei, este
de la sine înțeles că un limbaj bazat pe numere, cum ar fi Fortran, ar trebui să aibă facilități de notație potrivite. Astfel
de facilități oferă nu numai o comoditate notațională pentru programator, ci oferă și o oportunitate de a îmbunătăți
optimizarea.
Matricele au fost introduse în secțiunile 2.10 până la 2.13, utilizarea lor în expresii simple și în atribuiri a fost
explicată în secțiunile 3.10 și 3.11 și au fost folosite ca argumente de procedură în capitolul 5. Aceste descrieri au fost
restricționate în mod deliberat deoarece Fortran conține un set foarte complet de matrice. caracteristici a căror
descriere completă ar fi dezechilibrat acele capitole. Scopul acestui capitol este de a descrie caracteristicile matricei în
detaliu, dar fără a anticipa descrierile procedurilor intrinseci ale matricei din Capitolul 8; setul bogat de proceduri
intrinseci ar trebui privit ca parte integrantă a caracteristicilor matricei.

6.2 Matrice de dimensiune zero

S-ar putea crede că o matrice ar avea întotdeauna cel puțin un element. Cu toate acestea, o astfel de cerință ar forța
programele să conțină cod suplimentar pentru a face față anumitor situații naturale. De exemplu, codul din figura 6.1
rezolvă o mulțime de ecuații liniare triunghiulară inferioară. Când i are valoarea n, secțiunile au dimensiunea zero,
care este exact ceea ce este necesar.

Figura 6.1 O buclă do a cărei iterație finală are o matrice de dimensiune zero. face i
= 1,nx(i) = b(i) / a(i, i) b(i+1:n) = b(i+1:n) - a(i+1:n, i) * x(i)

sfâr itul face

Fortran permite matricelor să aibă dimensiune zero în toate contextele. Ori de câte ori o limită inferioară depășește
limita superioară corespunzătoare, matricea are dimensiunea zero.
Există puține reguli speciale pentru tablourile de dimensiune zero, deoarece urmează regulile obișnuite, deși poate
fi necesară o anumită atenție în interpretarea lor. De exemplu, două matrice de dimensiune zero de același rang pot
avea forme diferite. Unul poate avea forma (0,2) iar celălalt (0,3) sau (2,0).
Machine Translated by Google

100 Fortran modern explicat

Astfel de rețele de formă diferită nu sunt conformabile și, prin urmare, nu pot fi utilizate împreună ca operanzi
ai unei operații binare. Cu toate acestea, o matrice este întotdeauna conformabilă cu un scalar, deci declarația

zero-sized-array = scalar

este valid și scalarul este „difuzat către toate elementele matricei”, făcând aceasta o declarație „nu face nimic”.

O matrice de dimensiune zero este considerată ca fiind definită întotdeauna, deoarece nu are valori care să
poată fi nedefinite.

6.3 Matrice de formă asumată

În afara apendicelui B, solicităm ca formele argumentelor reale și fictive să fie de acord și până acum am reușit
acest lucru prin trecerea întinderilor argumentelor matricei ca argumente suplimentare. Cu toate acestea,
este posibil să se ceară ca forma matricei fictive să fie luată automat ca fiind cea a argumentului real al matricei
corespunzător. Se spune că o astfel de matrice este o matrice de formă presupusă. Când forma este declarată
prin clauza de dimensiune, fiecare dimensiune are forma

[limita inferioară]:

unde limita inferioară este o expresie întreagă care poate depinde de datele modulului sau de celelalte
argumente (vezi Secțiunea 7.14 pentru regulile exacte). Dacă limita inferioară este omisă, valoarea implicită
este 1. Rețineți că forma este trecută, și nu limitele superioare și inferioare. De exemplu, dacă matricea reală
este a, declarată astfel:

real, dimensiune(0:10, 0:20) :: a

iar tabloul dummy este da, declarat astfel:

real, dimensiune(:, :) :: da

atunci a(i,j) corespunde lui da(i+1,j+1); pentru a obține corespondența naturală, limita inferioară trebuie
declarată:

real, dimensiune(0:, 0:) :: da

Pentru ca compilatorul să știe că trebuie furnizate informații suplimentare, interfața trebuie să fie explicită
(Secțiunea 5.11) la punctul de apel. O matrice dummy cu atributul pointer nu este privită ca o matrice de formă
presupusă, deoarece forma sa nu este neapărat asumată.

6.4 Obiecte automate

O procedură cu argumente fictive care sunt matrice a căror dimensiune variază de la apel la apel poate avea
nevoie și de matrice locale a căror dimensiune variază. Un exemplu simplu este lucrul cu matrice în subrutină
pentru a schimba două matrice, care este prezentat în Figura 6.2.
O matrice ale cărei extinde variază în acest fel se numește matrice automată și este un exemplu de obiect
de date automat. Un astfel de obiect nu este un argument fals și declarația lui
Machine Translated by Google

Caracteristicile matricei 101

Figura 6.2 O procedură cu o matrice automată. dimensiunea este descrisă în Secțiunea 8.12.2.
schimb de subrutine (a, b)
real, dimensiune(:), intent(inout) :: a, b real,
dimensiune(size(a)) :: lucru ! matrice automată! sizedimensiunea
oferă unui tablou

munca =
aa=b
b = munca
terminați schimbul de subrutine

conține una sau mai multe valori care nu sunt cunoscute la momentul compilării; adică nu o expresie
constantă (Secțiunea 7.4). O implementare este probabil să le aducă în existență atunci când procedura
este apelată și să le distrugă la întoarcere, menținându-le pe o stivă.1 Valorile trebuie definite prin expresii
de specificație (Secțiunea 7.14).
Celălalt mod în care apar obiectele automate este prin variația lungimii caracterelor. Cuvântul variabil2
în

exemplu de subrutină(cuvânt1)
caracter(len = *), intenție(inout) :: cuvânt1 caracter(len =
len(cuvânt1)) :: cuvânt2

este un exemplu. Dacă rezultatul unei funcții are o lungime de caractere diferită, interfața trebuie să fie explicită
la punctul de apel, deoarece compilatorul trebuie să știe acest lucru, așa cum se arată în Figura 6.3.

Figura 6.3 Un modul care conține o procedură cu un scalar automat.


programul caracter
loren (len = *), parametru :: a = 'doar un simplu test' print *, dublu(a) conține

functia dubla(a)
caracter (len = *), intent(in) :: un caracter (len =
2*len(a)) dublu = a//a :: dublu

funcția finală dublă

termina programul loren

O matrice legată sau lungimea caracterelor unui obiect automat este fixată pe durata fiecărei execuții a
procedurii și nu variază dacă valoarea expresiei de specificație variază sau devine nedefinită.

Câteva restricții mici privind utilizarea obiectelor de date automate apar în Secțiunile 7.5, 7.9 și 7.15.

1A stivă este un mecanism de gestionare a memoriei prin care stocarea proaspătă este stabilită și stocarea veche este
eliminată pe baza „ultimul intrat, primul ieșit”, adesea în memoria contigue.
Machine Translated by Google

102 Fortran modern explicat

6.5 Alocarea datelor

Există o presupunere de bază în Fortran că procesorul furnizează un mecanism pentru gestionarea


stocării heap2. Declarațiile descrise în această secțiune reprezintă interfața cu utilizatorul pentru acel
mecanism.

6.5.1 Atributul alocabil

Uneori se cere ca o matrice să aibă o dimensiune care este cunoscută numai după ce unele date au fost
citite sau după ce au fost efectuate unele calcule. În acest scop, unui tablou i se poate atribui atributul
alocabil printr-o instrucțiune precum
real, dimensiune(:, :), alocabil :: a
O astfel de matrice se numește alocabil. Rangul său este specificat atunci când este declarat, dar limitele
sunt nedefinite până la o instrucțiune de alocare, cum ar fi
alocă (a(n, 0:n+1)) ! n de tip întreg
a fost executat pentru aceasta. Statutul său de alocare este fie alocat, fie nu este alocat în prezent.
Starea sa inițială nu este alocată în prezent și devine alocată după executarea cu succes a unei instrucțiuni
de alocare.
Un exemplu important este prezentat în Figura 6.4. Lucrarea cu matrice este plasată într-un modul și este
alocată la începutul programului principal la o dimensiune care depinde de datele de intrare. Matricea este
apoi disponibilă pe toată durata execuției programului în orice subprogram care are o instrucțiune de
utilizare pentru work_array.

Figura 6.4 O matrice alocabilă într-un modul.


modul work_array întreg
real, dimensiune(:,:,:), :: n
alocabil :: work
modul final work_array
program utilizare principală
work_array citire *, n
alocare (work(n, 2*n, 3*n))

Când o matrice alocabilă a nu mai este necesară, aceasta poate fi dealocată prin executarea instrucțiunii

dealocarea (a)
în urma căruia matricea este „nu este alocată în prezent”. Declarația deallocate este descrisă mai detaliat
în Secțiunea 6.5.3.
Dacă este necesar să se facă vreo modificare a limitelor unei matrice alocabile, matricea trebuie să fie
dealocată și apoi alocată din nou.3 Este o eroare să aloci o matrice alocabilă care este
2Un heap este un mecanism de gestionare a memoriei prin care poate fi stabilită o stocare nouă, iar stocarea veche poate fi
eliminată în orice ordine. De obicei sunt necesare mecanisme care să facă față fragmentării progresive a memoriei.
3Această restricție este eliminată în Fortran 2003, vezi Secțiunea 15.5.3.
Machine Translated by Google

Caracteristicile matricei 103

deja alocat, sau pentru a dealoca o matrice alocabilă care nu este alocată în prezent, dar una care poate fi ușor
evitată prin utilizarea funcției intrinseci alocate (Secțiunea 8.12.1).
Nu poate apărea o stare de alocare nedefinită. La întoarcerea dintr-un subprogram, o matrice alocabilă
alocată fără atributul de salvare (Secțiunea 7.9) este dealocată automat dacă este locală subprogramului.4
Această dealocare automată evită pierderea accidentală a memoriei.

6.5.2 Declarația de alocare

Am menționat în Secțiunea 2.13 că instrucțiunea alocare poate fi folosită și pentru a oferi stocare proaspătă
pentru o țintă pointer direct. Un pointer devine asociat (Secțiunea 3.3) după executarea cu succes a instrucțiunii.
Forma generală a declarației de alocare este

alocare ( lista de alocare [, stat=stat ] )

unde lista-alocare este o listă de alocări din formular

alocare-obiect [ ( lista-limite-matrice ) ]

fiecare legat de matrice are forma

[ low-bound : ] upper-bound

iar stat este o variabilă întregă scalară care nu trebuie să facă parte dintr-un obiect care este alocat.
Dacă specificatorul stat= este prezent, lui stat i se dă fie valoarea zero după o alocare reușită, fie o valoare
pozitivă după o alocare nereușită (de exemplu, dacă este disponibilă o stocare insuficientă). După o execuție
nereușită, fiecare matrice care nu a fost alocată cu succes își păstrează starea anterioară de alocare sau asociere
pointer. Dacă stat= este absent și alocarea nu are succes, execuția programului se oprește.

Fiecare obiect alocat este o matrice alocabilă sau un pointer. Este permis să aibă zero
lungimea caracterului.
Fiecare limita inferioara si fiecare limita superioara este o expresie intreaga scalara. Valoarea implicită pentru
limita inferioară este 1. Numărul de limite ale matricei dintr-o listă trebuie să fie egal cu rangul obiectului alocat.
Ele determină limitele matricei, care nu se modifică dacă valoarea unei variabile dintr-una dintre expresii se
modifică ulterior. O matrice poate fi alocată să aibă dimensiunea zero.
Limitele tuturor tablourilor care sunt alocate sunt considerate nedefinite în timpul execuției instrucțiunii de
alocare, astfel încât niciuna dintre expresiile care specifică limitele nu poate depinde de niciuna dintre limite
sau de valoarea variabilei stat=. De exemplu,

alocă (a(mărimea(b)), b(mărimea(a))) ! ilegal

sau chiar

alocă (a(n), b(mărimea(a))) ! ilegal

nu este permis, dar

alocă (a(n)) alocă


(b(mărimea(a)))

4 Strict vorbind, depinde de procesor dacă o matrice alocabilă rămâne alocată sau este dealocată dacă este
locală unui modul și este accesată numai de subprogram, dar o astfel de dealocare nu este permisă în Fortran
2008 și nu cunoaștem nicio implementare Fortran 95. asta o face.
Machine Translated by Google

104 Fortran modern explicat

este valabil. Această restricție permite procesorului să efectueze alocările într-o singură instrucțiune de alocare
în orice ordine.
Spre deosebire de cazul unei matrice alocabile, unui pointer i se poate aloca o nouă țintă chiar dacă este
asociată în prezent cu o țintă. În acest caz, asocierea anterioară este ruptă.
Dacă ținta anterioară a fost creată prin alocare, aceasta devine inaccesibilă dacă nu i se asociază un alt pointer.
Listele legate sunt create în mod normal folosind un singur pointer într-o instrucțiune de alocare pentru fiecare
nod al listei. Există un exemplu în Figura 4.8.

6.5.3 Declarația dealocate

Când nu mai este necesară o matrice alocabilă sau o țintă pointer, stocarea acesteia poate fi recuperată utilizând
instrucțiunea deallocate. Forma sa generală este

dealocare (alocare-listă-obiecte [,stat=stat] )

unde fiecare alocare-obiect este o matrice alocabilă care este alocată sau un pointer care este asociat cu
întreaga țintă care a fost alocată printr-un pointer într-o instrucțiune de alocare.5 Aici, stat este o variabilă
întreg scalară care nu trebuie dealocată de către declarația și nici nu depind de un obiect care este dealocat de
instrucțiune. Dacă stat= este prezent, stat primește fie valoarea zero după o execuție cu succes, fie o valoare
pozitivă după o execuție nereușită (de exemplu, dacă un pointer este disociat). După o execuție nereușită,
fiecare matrice care nu a fost dealocată cu succes își păstrează starea anterioară de alocare sau asociere
pointer. Dacă stat= este absent și dealocarea nu are succes, execuția programului se oprește.

Un pointer devine disociat (Secțiunea 3.3) după executarea cu succes a instrucțiunii. Dacă există mai multe
obiecte în listă, nu trebuie să existe dependențe între ele, pentru a permite procesorului să dealocați obiectele
unul câte unul în orice ordine.
Un pericol în utilizarea instrucțiunii deallocate este acela că stocarea poate fi dealocată în timp ce pointerii
sunt încă asociați cu țintele pe care le deținea. Astfel de indicatoare sunt lăsate „atârnând” într-o stare nedefinită
și nu trebuie reutilizate până când nu sunt asociate din nou cu o țintă reală.
Pentru a evita o acumulare de stocare neutilizată și inutilizabilă, toată spațiul de stocare alocat în mod
explicit ar trebui să fie dealocat în mod explicit atunci când nu mai este necesar (deși, după cum s-a menționat
la sfârșitul secțiunii 6.5.1, pentru matricele alocabile, există circumstanțe în care acest lucru este automată).
Această gestionare explicită este necesară pentru a evita o suprasarcină potențial semnificativă din partea
procesorului în gestionarea modelelor de alocare și de referință arbitrar complexe.

De asemenea, rețineți că standardul nu specifică dacă procesorul recuperează spațiul de stocare alocat
printr-un pointer, dar nu mai este accesibil prin acest sau orice alt pointer. Acest eșec de recuperare a spațiului
de stocare este cunoscut sub numele de scurgere de memorie. Ar putea fi important acolo unde, de exemplu,
o funcție pointer este referită într-o expresie – programatorul nu se poate baza pe compilator pentru a aranja
dealocarea. Pentru a vă asigura că nu există pierderi de memorie, este necesar să utilizați astfel de funcții numai
în partea dreaptă a asignărilor pointerului sau ca valori ale componentelor pointerului în constructorii de
structură și să dealocați pointerul atunci când nu mai este necesar.

5Rețineți că acest lucru exclude un pointer care este asociat cu o matrice alocabilă.
Machine Translated by Google

Caracteristicile matricei 105

6.5.4 Argumente fictive alocabile

O matrice inactivă este permisă să aibă atributul alocabil. În acest caz, argumentul actual corespunzător trebuie
să fie o matrice alocabilă de același tip, parametri de tip și rang; de asemenea, interfața trebuie să fie explicită.
Argumentul inactiv primește întotdeauna starea de alocare (descriptor) a argumentului real la intrare, iar
argumentul real primește pe cea a argumentului inactiv la returnare. În ambele cazuri, aceasta include limitele
și poate fi „nealocată în prezent”.

Ne așteptăm ca unii compilatori să efectueze copiere în copiere a descriptorului.


Regula i) din Secțiunea 5.7.2 este aplicabilă și este concepută pentru a permite compilatorilor să facă acest
lucru. În special, aceasta înseamnă că nu este permisă nicio referire la argumentul real (de exemplu, prin faptul
că acesta este o variabilă de modul) din procedura invocată dacă tabloul dummy este alocat sau dealocat acolo.

Pentru tabloul în sine, situația este la fel ca și în cazul în care argumentele reale și fictive sunt ambele
tablouri de formă explicită (vezi Secțiunea 5.7.3). Copiere-in-copy-out este permisă, cu excepția cazului în care
ambele matrice au atributul target.
Un argument fals alocabil este permis să aibă intenție și acest lucru se aplică atât stării de alocare
(descriptorului), cât și matricei în sine. Dacă intenția este în, matricea nu are voie să fie alocată sau dealocată și
valoarea nu este permisă să fie modificată. Dacă intenția este eliminată și matricea este alocată la intrare,
aceasta devine dealocată. Un exemplu de aplicare a unui argument inactiv alocabil la citirea matricelor de limite
variabile este prezentat în Figura 6.5.

Figura 6.5 Citirea tablourilor a căror dimensiune nu este cunoscută în prealabil.


încărcare subrutină (matrice, unitate)
real, alocabil, intent(out), dimension(:, :, :) :: matrice întreg, intent(in) :: unitate întreg :: n1,
n2, n3 citire (unitate) n1, n2, n3 alocare (array( n1, n2, n3)) matrice de citire (unitate).

sfârșitul încărcării subrutinei

6.5.5 Funcții alocabile

Un rezultat al funcției matrice este permis să aibă atributul alocabil, care este foarte util atunci când
dimensiunea rezultatului depinde de un calcul în funcție în sine, așa cum este ilustrat în Figura 6.6. Starea de
alocare pentru fiecare intrare în funcție este „nu este alocată în prezent”.
Rezultatul poate fi alocat și dealocat de orice număr de ori în timpul executării procedurii, dar trebuie să fie
alocat și să aibă o valoare definită la returnare.
Interfața trebuie să fie explicită în orice unitate de acoperire în care se face referire la funcția.
Matricea de rezultate este dealocată automat după executarea instrucțiunii în care apare referința, chiar dacă
are atributul target.
Machine Translated by Google

106 Fortran modern explicat

Figura 6.6 O funcție alocabilă pentru a elimina valorile duplicate.


program no_leak real,
dimension(100) :: x, y
:
y(:size(compact(x))) = compact(x)**2
:
con ine
funcția compact(x) ! Pentru a elimina duplicatele din tabloul x real, allocabil,
dimensiune(:):: real compact, dimensiune(:), intent(in) :: x întreg

:: n
: ! Aflați numărul de valori distincte, n
aloca (compact(n))
: ! Copiați valorile distincte în compact end function
compact
termina programul no_leak

6.5.6 Componente alocabile

Componentele matricei de tip derivat au permisiunea de a avea atributul alocabil. De exemplu, o


matrice triunghiulară inferioară poate fi deținută prin utilizarea unei matrice alocabile pentru fiecare rând.
Luați în considerare tipul

tip rând
real, dimensiune(:), alocabil :: r rând de tip final

și matricele

tip(rând), dimensiune(n) :: s, t ! n de tip întreg

Depozitarea pentru rânduri poate fi alocată astfel

face i = 1, n ! i de tipul întreg alocă (t(i)


%r(1:i)) ! Alocați rândul i cu lungimea i end do

Atribuirea matricei

s=t

ar fi atunci echivalent cu sarcinile

s(i)%r = t(i)%r

pentru toate componentele.


La fel ca pentru o matrice alocabilă obișnuită, starea inițială a unei componente alocabile este „nu
este alocată în prezent”. Acest lucru este valabil și pentru o componentă alocabilă finală (Secțiunea
9.3) a unui obiect creat de o instrucțiune de alocare. Prin urmare, nu este nevoie de inițializare implicită
Machine Translated by Google

Caracteristicile matricei 107

a componentelor alocabile. De fapt, inițializarea într-o definiție de tip derivat (Secțiunea 7.11) a unei componente
alocabile nu este permisă.
Într-un constructor de structură (Secțiunea 3.8), o expresie corespunzătoare unei componente alocabile
trebuie să fie o matrice sau o referință la funcția intrinsecă nulă fără argumente.
Dacă este o matrice alocabilă, componenta are aceeași stare de alocare și, dacă este alocată, aceleași limite și
aceeași valoare. Dacă este o matrice, dar nu o matrice alocabilă, componenta este alocată cu aceleași limite și i
se atribuie aceeași valoare. Dacă este o referință la funcția intrinsecă nulă fără argumente, componenta primește
starea de alocare „nealocată în prezent”.

Componentele alocabile sunt ilustrate în Figura 6.7, unde cod pentru a manipula polinomi
este afișat al cu un număr variabil de termeni.

Figura 6.7 Utilizarea componentelor alocabile pentru adăugarea de polinoame.


modul real_polinom_module tip real_polinom

real, alocabil, dimensiune(:) :: tip final coeff real_polinomial


interfață operator(+) procedura de modul rp_add_rp

operator de interfață finală (+)


con ine
funcția rp_add_rp(p1, p2)
tip(polinom_real) :: rp_add_rp tip(polinom_real), intent(in) :: p1, p2
întreg :: m, m1, m2 m1 = ubound(p1%coeff,1) m2 = ubound( p2%coeff,1)
alocă (rp_add_rp%coeff(max(m1,m2))) m = min(m1,m2)
rp_add_rp%coeff(:m)
= p1%coeff(:m) +p2%coeff(:m) ) dacă (m1 > m) rp_add_rp%coeff(m+1:) =
p1%coeff(m+1:) dacă (m2 > m) rp_add_rp%coeff(m+1:) = p2%coeff(m+1: )

sfârșitul funcției rp_add_rp


sfârșit modulul real_polynomial_module exemplu
de program folosiți tipul
real_polynomial_module(real_polinom) :: p, q,
rp = real_polynomial((/4.0, 2.0, 1.0/))! Setați p la
4+2x+x**2 q = real_polinom((/-1.0, 1.0/)) r=p+q print *, 'Coeficienții sunt: ', r%coeff

exemplu de program final


Machine Translated by Google

108 Fortran modern explicat

Așa cum o matrice alocabilă nu are permisiunea de a avea atributul de parametru (fie o constantă),
la fel un obiect de tip care are o componentă alocabilă finală nu are permisiunea de a avea atributul
de parametru; în plus, un constructor de structură de un astfel de tip nu poate fi o constantă și astfel
o expresie constantă nu poate avea un astfel de tip.6
Atunci când o variabilă de tip derivat este dealocată, orice componentă alocabilă finală care este
alocată în prezent este, de asemenea, dealocată, ca printr-o instrucțiune dealocare. Variabila poate fi
un pointer sau o matrice alocabilă, iar regula se aplică recursiv, astfel încât toate componentele
alocabile alocate la toate nivelurile (cu excepția oricăror componente dincolo de indicatori) sunt
dealocate. Astfel de dealocari de componente apar, de asemenea, atunci când o variabilă este asociată
cu un argument fals intenționat.
Atribuire intrinsecă

variabilă = expr

pentru un tip cu o componentă alocabilă finală (ca în r=p+q din Figura 6.7) constă din următorii pași
pentru fiecare astfel de componentă.

i) Dacă componenta variabilei este în prezent alocată, aceasta este dealocată.

ii) Dacă componenta expr este alocată în prezent, componenta variabilei este alocată cu aceleași
limite și valoarea este apoi transferată folosind atribuirea intrinsecă.

Dacă componenta alocabilă a expr este „nu este alocată în prezent”, nu se întâmplă nimic în pasul ii),
deci componenta variabilei este lăsată „nealocată în prezent”. Rețineți că, dacă componenta variabilei
este deja alocată cu aceeași formă, compilatorul poate alege să evite costurile de dealocare și realocări.
De asemenea, rețineți că, dacă compilatorul poate spune că nu va exista nicio referință ulterioară la
expr, deoarece este o referință de funcție sau o variabilă temporară care conține rezultatul evaluării
expresiei, nu este necesară nicio alocare sau atribuire - tot ceea ce trebuie să se întâmple este
dealocarea. a oricăror componente alocabile finale ale variabilei, urmată de copierea descriptorului.

Dacă o componentă este ea însăși de tip derivat cu o componentă alocabilă, atribuirea intrinsecă
din pasul ii) va implica și aceste reguli. De fapt, ele sunt aplicate recursiv la toate nivelurile, iar copierea
are loc în fiecare caz. Acest lucru este cunoscut sub numele de copiere profundă, spre deosebire de
copierea superficială, care are loc pentru componentele pointerului, unde descriptorul este copiat și
nu se face nimic pentru componentele componentelor pointerului.
Dacă un argument real și argumentul inactiv corespunzător au o componentă alocabilă finală, se
aplică regula i) din Secțiunea 5.7.2 și necesită ca toate alocările și dealocarea componentei să fie
efectuate prin argumentul inactiv, în cazul în care copierea în copiere. este în vigoare.

Dacă o instrucțiune conține o referință la o funcție al cărei rezultat este de tip cu o componentă
alocabilă finală, orice componente alocabile finale ale rezultatului funcției sunt dealocate după execuția
instrucțiunii. Aceasta este paralelă cu regula pentru rezultatele funcțiilor alocabile (Secțiunea 6.5.5).

6Toate acestea sunt permise în Fortran 2003 cu condiția ca componenta să fie specificată ca „nu este alocată în
prezent” explicit cu null() sau implicit prin faptul că nu i se dă o valoare. Componenta va fi întotdeauna „nu este alocată
în prezent”.
Machine Translated by Google

Caracteristicile matricei 109

6.5.7 Matrice alocabile vs. pointeri

De ce sunt necesare matrice alocabile? Nu sunt toate funcționalitățile lor disponibile (și mai multe) cu
tablourile de pointeri? Motivul este că există avantaje semnificative pentru gestionarea memoriei și viteza
de execuție în utilizarea matricelor alocabile atunci când funcționalitatea adăugată a pointerilor nu este
necesară.

• Codul pentru o matrice de pointeri este probabil să fie mai puțin eficient, deoarece trebuie luate
în considerare alte progrese decât unitatea. De exemplu, ținta sa ar putea fi vectorul secțiunii
(1:n:2) sau matricea secțiunii (i,1:n) cu pași non-unități, în timp ce majoritatea computerelor dețin
matrice alocabile în memoria contiguă.

• Dacă o operație definită implică o variabilă temporară de tip derivat cu o componentă pointer,
compilatorul probabil nu va putea să-și dealocați ținta atunci când stocarea pentru variabilă este
eliberată. Luați în considerare, de exemplu, afirmația

a = b + c*d ! a, b, c și d sunt de același tip derivat

Acest lucru va crea un temporar pentru c*d, care nu este necesar odată ce b + c*d a fost calculat.
Este puțin probabil ca compilatorul să fie sigur că niciun alt pointer nu are componenta sau o
parte a acestuia ca țintă, așa că este puțin probabil să-l dealocați.

• Atribuirea intrinsecă este adesea nepotrivită pentru un tip derivat cu o componentă pointer
deoarece misiunea

a=b

va lăsa a și b împărtășind aceeași țintă pentru componenta lor pointer. Prin urmare, o atribuire
definită care alocă o țintă nouă și copiază datele va fi utilizată în schimb.
Cu toate acestea, acest lucru este foarte risipitor dacă partea dreaptă este temporară, cum ar fi
cea a atribuirii paragrafului anterior.

• Considerații similare se aplică la invocarea unei funcții în cadrul unei expresii. Este puțin probabil
ca compilatorul să poată dezaloca pointerul după ce expresia a fost calculată.

• Când o variabilă de tip derivat este dealocată, orice componentă alocabilă finală care este alocată
în prezent este, de asemenea, dealocată. Pentru a evita pierderea memoriei cu componentele
pointer, programatorul ar trebui să dealocați fiecare în mod explicit și să aibă grijă să ordoneze
corect dealocarea.

Deși standardul Fortran nu menționează descriptori, este foarte util să ne gândim la o matrice alocabilă
ca fiind ținută ca un descriptor care înregistrează dacă este alocat și, dacă da, adresa și limitele sale în
fiecare dimensiune. Acesta este ca un descriptor pentru un pointer, dar nu este necesar să se țină pași,
deoarece acestea sunt întotdeauna unitate. În ceea ce privește indicatorii, se așteaptă ca matricea în sine
să fie ținută separat.
Machine Translated by Google

110 Fortran modern explicat

6.6 Operații și sarcini elementare

Am văzut în Secțiunea 3.10 că un operator intrinsec poate fi aplicat operanzilor conformabili, pentru a
produce un rezultat matrice ale cărui valori ale elementelor sunt valorile operației aplicate elementelor
corespunzătoare ale operanzilor. O astfel de operație se numește elementară.
Nu este esențial să folosiți notația operatorului pentru a obține acest efect. Multe dintre procedurile
intrinseci (Capitolul 8) sunt elementare și au argumente simulate scalare care pot fi apelate cu argumente
reale ale matricei, cu condiția ca toate argumentele matricei să aibă aceeași formă. Pentru o funcție, forma
rezultatului este forma argumentelor matricei. De exemplu, putem găsi rădăcinile pătrate ale tuturor
elementelor unui tablou real astfel:

a = sqrt(a)

Dacă orice argument real dintr-o invocare a subrutinei este valorizat de matrice, toate argumentele
reale care corespund argumentelor fictive cu intent out sau inout trebuie să fie matrice. Dacă o procedură
care invocă o funcție elementară are un argument dummy cu valoare de matrice opțional care este absent,
acel argument dummy nu trebuie utilizat în invocarea elementară decât dacă o altă matrice de același
rang este asociată cu un argument neopțional al procedurii elementare. (pentru a se asigura că rangul nu
variază de la apel la apel).
În mod similar, o atribuire intrinsecă poate fi utilizată pentru a atribui un scalar tuturor elementelor
unui tablou sau pentru a atribui fiecare element al unui tablou elementului corespunzător al unui tablou
de aceeași formă (Secțiunea 3.11). O astfel de misiune este numită și elementară.
Pentru un operator definit, un efect similar poate fi obținut cu o interfață generică cu funcții pentru
fiecare rang sau pereche de ranguri dorite. De exemplu, modulul din Figura 6.8 oferă o însumare pentru
scalari și matrice de intervale de rang unu (Secțiunea 3.8). Alternativ, o procedură elementară poate fi
definită în acest scop (Secțiunea 6.11).
În mod similar, versiunile elementare ale sarcinilor definite pot fi furnizate în mod explicit sau un
procedura elementară poate fi definită în acest scop (Secțiunea 6.11).

6.7 Funcții cu valori de matrice

Am menționat în Secțiunea 5.10 că o funcție poate avea un rezultat cu valori de matrice și am folosit
această caracteristică de limbaj în Figura 6.8, unde interpretarea este evidentă.
Pentru ca compilatorul să cunoască forma rezultatului, interfața trebuie să fie explicită (Secțiunea 5.11)
ori de câte ori se face referire la o astfel de funcție. Forma este specificată în definiția funcției prin atributul
dimensiune pentru numele funcției. Cu excepția cazului în care rezultatul funcției este alocabil sau un
pointer, limitele trebuie să fie expresii explicite și sunt evaluate la intrarea în funcție. Pentru un alt exemplu,
vezi declarația rezultatului funcției din Figura 6.9.

O funcție cu valori matrice nu este neapărat elementară. De exemplu, la sfârșitul secțiunii


3.10 am luat în considerare tipul

tip matrice
real :: element
matrice de tip final
Machine Translated by Google

Caracteristicile matricei 111

Figura 6.8 Adăugarea de intervale pentru scalari și tablouri de rang unu.


modul interval_addition tip
interval real :: interfață de tip
final inferior, superior
interval operator(+) procedura
modulului add00, add11
interfață finală

con ine
function add00 (a, b) tip
(interval) tip (interval), :: add00
intent(in) :: a, b add00%lower = a%lower +
b%lower ! Codul de producție ar
add00%sus = a%sus + b%sus ! permite rotunjire.
funcția final add00

funcția add11 (a, b)


tip (interval), dimensiune (:), intenție (în) tip (interval), :: A

dimensiune (dimensiune (a)) tip (interval), dimensiune :: add11


(dimensiune (a)), intenție (în) :: b adăugați11% mai mic = a% mai mic + b%
mai mic ! Codul de producție ar adăuga 11%sus = a%sus + b%sus ! permite
rotunjire.
end function add11 end
module interval_addition

Operațiile sale scalare și de rang unu ar putea fi ca pentru reale, dar pentru a înmulți o matrice de rangul doi
cu o matrice de rangul unu, am putea folosi funcția de modul prezentată în Figura 6.9 pentru a furniza matrice
prin multiplicare vectorială.

6.8 Declarația și constructul where

De multe ori se dorește să se efectueze o operație de matrice doar pentru anumite elemente, să zicem
cele ale căror valori sunt pozitive. Declarația where oferă această facilitate. Un exemplu simplu este

unde ( a > 1,0 ) a = 1,0/a ! a este o matrice reală

care inversează acele elemente ale lui a care sunt mai mari de 1,0 și lasă restul nealterate.
Forma generală este

unde (logical-array-expr) variabilă-matrice = expr

Expresia matrice logică logic-array-expr trebuie să aibă aceeași formă ca și variabilă-matrice.


Se evaluează mai întâi și apoi sunt evaluate doar acele elemente ale expr care corespund elementelor
matricei logice-expr care au valoarea true și sunt atribuite elementelor corespunzătoare ale matricei-
variabile. Toate celelalte elemente ale variabilei-matrice sunt lăsate nealterate. Misiunea poate fi o
sarcină definită, cu condiția să fie elementară (Secțiunea 6.11).
Machine Translated by Google

112 Fortran modern explicat

Figura 6.9 O funcție pentru matrice prin multiplicare vectorială. dimensiunea este definită în secțiunea 8.12.
funcția mult(a, b) !

tip(matrice), dimensiune(:, :) tip(matrice), :: A

dimensiune(dimensiune(a, 2)) :: b tip(matrice),


dimensiune(dimensiune(a, 1)) :: mult întreg :: j, n

!
mult = 0,0 ! O misiune definită dintr-un real! scalar la o matrice
de rang unu.
n = size(a, 1) do j = 1,
size(a, 2) mult = mult + a(1:n,
j) * b(j)
! Utilizează operații definite pentru adăugarea ! două matrice
de rangul unu și înmulțire! a unei matrice de rang unu printr-o
matrice scalară.
sfâr itul face

end function mult

O singură expresie de matrice logică poate fi utilizată pentru o secvență de atribuiri de matrice toate
aceeași formă. Forma generală a acestui construct este

unde (logical-array-expr) asignarea-


matrice se termină unde

Expresia de matrice logică logic-array-expr este mai întâi evaluată și apoi fiecare atribuire de matrice este
efectuată pe rând, sub controlul acestei măști. Dacă oricare dintre aceste atribuiri afectează entitățile din logic-
array-expr, valoarea obținută atunci când este executată instrucțiunea where este folosită ca mască.

Construcția unde poate lua forma

unde (logical-array-expr) asignari-


matrice în altă parte

asignarea-matrice se
termină unde

Aici, asignările din primul bloc de alocări sunt efectuate pe rând sub controlul logic-array-expr și apoi atribuirile
din al doilea bloc sunt efectuate pe rând sub controlul .not.logical-array-expr. Din nou, dacă oricare dintre aceste
atribuiri afectează entitățile din logic-array-expr, valoarea obținută atunci când este executată instrucțiunea
where este folosită ca mască.

Un exemplu simplu de construct unde este


Machine Translated by Google

Caracteristicile matricei 113

unde (presiune <= 1,0) presiune =


presiune + inc_pressure temp = temp + 5,0

în altă parte

ploua = .adevărat.
sfâr itul unde

unde presiunea, inc_pressure, temperatura și ploaia sunt rețele de aceeași formă.


Dacă o instrucțiune sau o construcție unde maschează o referință de funcție elementară, funcția este
apelată numai pentru elementele dorite. De exemplu,

unde(a>0)a= log(a)

(log este definit în Secțiunea 8.4) nu ar duce la apeluri eronate de log pentru argumente negative.

Această mascare se aplică tuturor referințelor de funcții elementare, cu excepția celor care se află într-un
argument al unei referințe de funcție non-elementală. Mascarea nu se extinde la argumentele matrice ale
unei astfel de funcții. În general, astfel de argumente au o formă diferită, astfel încât mascarea nu ar fi
posibilă. De exemplu, în cazul

unde (a > 0) a = a/sum(log(a))

(suma este definită în Secțiunea 8.11) logaritmii fiecăruia dintre elementele lui a sunt însumați și declarația
va eșua dacă nu sunt toți pozitivi.
Dacă o referință de funcție non-elementală sau un constructor de matrice este mascată, aceasta este evaluată complet
înainte de aplicarea mascării.
Este permisă mascarea nu numai a declarației where a constructului where, ci și a oricărei declarații de
alt loc pe care o conține. Expresiile de mascare implicate trebuie să fie de aceeași formă. Un construct unde
poate conține orice număr de instrucțiuni mascate elsewhere, dar cel mult o instrucțiune elsewhere fără
mască, iar aceasta trebuie să fie cea finală. În plus, unde constructele pot fi imbricate unele în altele;
expresiile de mascare ale constructelor imbricate trebuie să fie de aceeași formă, la fel ca și variabilele
matrice din partea stângă a atribuirilor.

O declarație where simplă, cum ar fi cea de la începutul acestei secțiuni, este permisă într-un construct
unde și este interpretată ca și cum ar fi constructul where corespondent care conține o atribuire de matrice.

În cele din urmă, un construct unde poate fi denumit în același mod ca și alte constructe.
Un exemplu care ilustrează mai complicat în cazul în care constructele care sunt denumite este prezentat
în Figura 6.10.
Toate instrucțiunile unui construct unde sunt executate una câte una în secvență, inclusiv instrucțiunile
where și elsewhere. Expresiile matrice logice din instrucțiunile where și elsewhere sunt evaluate o dată și
controlul atribuirilor ulterioare nu este afectat de modificările valorilor acestor expresii. De-a lungul unui
construct unde există o mască de control și o mască în așteptare care se schimbă după evaluarea fiecărei
declarații where, alsewhere și end where, așa cum este ilustrat în Figura 6.10.
Machine Translated by Google

114 Fortran modern explicat

Figura 6.10 Construcții imbricate unde, arătând mascarea.


assign_1: unde (cond_1)
: ! mascat de cond_1
altundeva (cond_2)
: ! mascat de !
: cond_2.și..nu.cond_1
atribui_2: unde (cond_4)
: ! mascat de !
: cond_2.și..nu.cond_1.și.cond_4
în altă parte
: ! mascat de !
: cond_2.și..nu.cond_1.și..nu.cond_4
sfâr itul unde atribuie_2
:
altundeva (cond_3) assign_1 ! mascat
: de !
: cond_3.și..nu.cond_1.și..nu.cond_2
altundeva assign_1 !
: mascat de !
: nu.cond_1.și..nu.cond_2.și..nu.cond_3
sfâr itul unde atribuie_1

6.9 Enunțul și constructul general

Când elementelor unui tablou li se atribuie valori printr-o construcție do, cum ar fi
do i = 1, na(i, i)
= 2,0 * x(i) end do ! a este rangul-2 și x rangul-1

procesorul este obligat să efectueze fiecare iterație succesivă în ordine și una după alta.
Acest lucru reprezintă un impediment potențial sever pentru optimizarea pe un procesor paralel, așa că,
în acest scop, Fortran are declarația forall. Bucla de mai sus poate fi scrisă ca
forall(i = 1:n) a(i, i) = 2.0 * x(i)

care specifică faptul că setul de expresii notat de partea dreaptă a atribuirii este mai întâi evaluat în orice
ordine, iar rezultatele sunt apoi atribuite elementelor de matrice corespunzătoare, din nou în orice ordine
de execuție. Declarația forall poate fi considerată a fi o alocare de matrice exprimată cu ajutorul indicilor.
În acest exemplu particular, observăm, de asemenea, că această operație nu ar putea fi reprezentată
altfel ca o simplă atribuire de matrice. Alte exemple de afirmație forall sunt

forall(i = 1:n, j = 1:m) forall(i = 1:n, a(i, j)=i+j


j = 1:n, y(i, j) /= 0.) x(j, i) = 1.0/y( i, j)

unde, în a doua afirmație, notăm condiția de mascare – atribuirea nu este efectuată pentru zero
elemente ale lui y.
Machine Translated by Google

Caracteristicile matricei 115

Construcția forall există și. Echivalentul total al atribuirilor de matrice

a(2:n-1, 2:n-1) = a(2:n-1, 1:n-2) + a(2:n-1, 3:n) + a(1:n-2 , 2:n-1) + a(3:n, 2:n-1) &

b(2:n-1, 2:n-1) = a(2:n-1, 2:n-1)

este

forall(i = 2:n-1, j = 2:n-1)


a(i, j) = a(i, j-1) + a(i, j+1) + a(i-1, j) + a(i+1, j) b(i, j) = a (i, j) final forall

Acest lucru stabilește fiecare element intern al lui a egal cu suma celor mai apropiați patru vecini ai săi și copiază
rezultatul în b. Versiunea forall este mai lizibilă. Rețineți că fiecare atribuire dintr-un forall este ca o atribuire
matrice; efectul este ca și cum toate expresiile au fost evaluate în orice ordine, păstrate în stocare temporară, apoi
toate atribuțiile efectuate în orice ordine. Fiecare declarație dintr-un construct forall trebuie să se completeze
complet înainte ca următoarea să poată începe.
O instrucțiune sau o construcție forall poate conține atribuiri de indicatori. Un exemplu este

tip element
caracter(32), pointer :: nume final tip element
tip(element) :: diagramă(200) caracter(32), țintă ::
nume(200)

: ! definiți nume

forall(i =1:200) chart(i)


%name => nume(i)
sfâr itul pentru tot

Rețineți că nu există o sintaxă de matrice pentru a efectua, ca în acest exemplu, o matrice de atribuiri de pointer.

Ca și în cazul tuturor constructelor, toate constructele pot fi imbricate. Secvența

total (i = 1:n-1)
total (j = i+1:n)
a(i, j) = a(j, i) final forall ! a este o matrice de rang 2

sfâr itul pentru tot

atribuie transpunerea triunghiului inferior al lui a triunghiului superior al lui a.


Un construct forall poate include o declarație sau un construct unde. Fiecare declarație de unde

constructul este executat în succesiune. Un exemplu cu o declarație where este

forall (i = 1:n) unde


( a(i, :) == 0) a(i, :) = ib(i, :)=i/ a(i, :)

sfâr itul pentru tot

Aici, fiecare element zero al lui a este înlocuit cu valoarea indicelui de rând și, în urma acestei operații complete,
elementelor rândurilor lui b li se atribuie reciprocele elementelor corespunzătoare ale lui a înmulțite cu indicele de
rând corespunzător.
Machine Translated by Google

116 Fortran modern explicat

Sintaxa completă a constructului forall este

[nume:] forall(index = inferior: superior [:stride] & [, index =


inferior: superior [:stride]]... [,scalar-logical-expr] )
[body]
end forall [nume]

unde index este o variabilă scalară întreagă numită. Scopul său este cel al constructului; adică alte
variabile pot avea numele, dar sunt separate și nu sunt accesibile în forall. Este posibil ca indicele să nu
fie redefinit în cadrul constructului. În cadrul unui construct imbricat, fiecare index trebuie să aibă un
nume distinct. Expresiile inferior, superior și pas (pasul este opțional, dar trebuie să fie diferit de zero
atunci când sunt prezente) sunt expresii întregi scalare și formează o secvență de valori ca pentru un
indice de secțiune (Secțiunea 6.13); pot să nu facă referire la niciun index al aceleiași declarații, dar pot
face referire la un index al unui forall exterior. Odată ce aceste expresii au fost evaluate, expresia-logică-
scalară, dacă este prezentă, este evaluată pentru fiecare combinație de valori ale indexului. Cele pentru
care are valoarea .adevărat. sunt active în fiecare afirmație a constructului. Numele este numele
construcției opționale; dacă este prezent, trebuie să apară atât pe enunțul forall, cât și pe cel final forall.

Corpul în sine constă din una sau mai multe: instrucțiuni de atribuire, instrucțiuni de atribuire pointer,
instrucțiuni sau constructe unde și alte instrucțiuni sau constructe pentru toate. Subobiectul din partea
stângă a fiecărei sarcini din corp ar trebui să facă referire la fiecare index al constructelor în care este
conținut ca parte a identificării subobiectului, fie că este o variabilă non-pointer sau un obiect pointer.7

În cazul unei instrucțiuni de atribuire definită, subrutina care este invocată nu trebuie să facă referire
la nicio variabilă care devine definită de instrucțiune și nici la niciun obiect pointer care devine asociat.

Un construct forall al cărui corp este o singură atribuire sau o instrucțiune de atribuire a indicatorului
poate fi scrisă ca o singură declarație forall.
Procedurile pot fi referite în sfera unui forall, atât în expresia scalară logică care formează masca
opțională, cât și, direct sau indirect (de exemplu, ca operație sau atribuire definită), în corpul constructului.
Toate aceste proceduri trebuie să fie pure (vezi Secțiunea 6.10).

Ca și în alocări la secțiuni de matrice (Secțiunea 6.13), nu este permis să se facă o atribuire mai multe
la unu. Construcția

forall (i = 1:10) a(index(i))


= b(i) ! a, b și index sunt tablouri
sfâr itul pentru tot

este valid dacă și numai dacă index(1:10) nu conține valori repetate. În mod similar, nu este permisă
asocierea mai multor ținte cu același pointer.

7 Aceasta nu este de fapt o cerință, dar orice indice lipsă ar trebui să fie limitat la o singură valoare pentru a satisface
cerințele ultimului paragraf al acestei secțiuni. De exemplu, afirmația

total (i = i1:i2, j = j1:j2) a(j) = a(j) + b(i, j)

este valabil numai dacă i1 și i2 au aceeași valoare.


Machine Translated by Google

Caracteristicile matricei 117

6.10 Proceduri pure

În descrierea funcțiilor din Secțiunea 5.10, am remarcat faptul că, deși este permis să scrieți funcții cu efecte secundare,
acest lucru este considerat nedorit. De fapt, folosită în toate instrucțiunile sau constructele (Secțiunea 6.9), posibilitatea ca
o referință de funcție sau subrutină să aibă efecte secundare este un impediment sever în optimizarea pe un procesor
paralel – ordinea de execuție a sarcinilor ar putea afecta rezultatele. Pentru a controla această situație, este posibil ca
programatorul să afirme că o procedură nu are efecte secundare prin adăugarea cuvântului cheie pur la instrucțiunea
subrutinei sau funcției. În termeni practici, aceasta este o afirmație că procedura

i) dacă o funcție, nu modifică niciun argument fals;

ii) nu modifică nicio parte a unei variabile accesate de gazdă sau asociere de utilizare;

iii) nu conține nicio variabilă locală cu atributul de salvare (Secțiunea 7.9);

iv) nu efectuează nicio operațiune pe un fișier extern (Capitolele 9 și 10); și

v) nu conține nicio instrucțiune stop.

Pentru a vă asigura că aceste cerințe sunt îndeplinite și că un compilator poate verifica cu ușurință dacă acest lucru este
deci, există următoarele reguli suplimentare:

i) orice argument fals care este o procedură și orice procedură la care se face referire trebuie să fie pur
și au o interfață explicită;

ii) intenția unui argument fals trebuie declarată, cu excepția cazului în care este o procedură sau un pointer,
iar această intenție trebuie să fie în cazul unei funcții;

iii) orice procedură internă unei proceduri pure trebuie să fie pură; și

iv) o variabilă care este accesată de gazdă sau asociere de utilizare sau este o intenție în argument inactiv sau orice
parte a unei astfel de variabile nu trebuie să fie ținta unei instrucțiuni de atribuire a pointerului; nu trebuie să fie
partea dreaptă a unei atribuiri intrinseci dacă partea stângă este de tip derivat cu o componentă pointer la orice
nivel de selecție a componentei; și nu trebuie să fie asociat ca argument real cu un argument fals care este un
pointer sau are intent out sau inout.

Această ultimă regulă asigură că un pointer local nu poate provoca un efect secundar.
Funcția din Figura 5.6 (Secțiunea 5.10) este pură și aceasta ar putea fi specificată în mod explicit:

funcția pură distanță (p, q)

O procedură externă sau inactivă care este utilizată ca procedură pură trebuie să aibă un bloc de interfață care o
specifică ca fiind pură. Cu toate acestea, procedura poate fi utilizată în alte contexte fără utilizarea unui bloc de interfață
sau cu un bloc de interfață care nu îl specifică ca pur. Acest lucru permite ca procedurile bibliotecii să fie specificate ca pure,
fără a limita utilizarea lor ca atare.
Motivul principal pentru a permite subrutinele pure este acela de a putea folosi o atribuire definită într-o instrucțiune
sau construcție forall și astfel, spre deosebire de funcțiile pure, acestea pot avea argumente fictive care au intent out sau
inout sau atributul pointer. Existența lor oferă și posibilitatea de a efectua apeluri subrutine din interiorul funcțiilor pure.
Machine Translated by Google

118 Fortran modern explicat

Toate funcțiile intrinseci (Capitolul 8) sunt pure și, astfel, pot fi referite liber în interior
procedee pure. De asemenea, subrutinele intrinseci elementare mvbits (Secțiunea 8.8.3) sunt pure.
Atributul pur este dat automat oricărei proceduri care are atributul elementar (secțiunea următoare).

6.11 Proceduri elementare

Am întâlnit deja noțiunea de proceduri intrinseci elementare (Secțiunea 6.6 și, mai târziu, Capitolul 8) –
cele cu argumente simulate scalare care pot fi numite cu argumente reale de matrice, cu condiția ca
argumentele de matrice să aibă aceeași formă (adică, cu condiția ca toate argumentele sunt conformabile).
Pentru o funcție, forma rezultatului este forma argumentelor matricei. Această caracteristică există și
pentru procedurile non-intrinsece. Acest lucru necesită prefixul elementar pe instrucțiunea funcției sau
subrutinei. De exemplu, am putea face funcția add_intervals din Secțiunea 3.8 elementară, așa cum se
arată în Figura 6.11. Acesta este un ajutor pentru optimizare pe procesoare paralele.

Figura 6.11 O funcție elementară. funcția


elementară add_intervals(a,b) tip(interval) tip(interval),
:: add_intervals
intent(in) :: a, b add_intervals%lower = a%lower
+ b%lower ! Cod de producție add_intervals%upper
= a%upper + b%upper ! ar permite! rotunji.

sfârșitul funcției add_intervals

O procedură elementară trebuie să satisfacă toate cerințele unei proceduri pure (secțiunea anterioară);
de fapt, are automat atributul pur.8 În plus, toate argumentele fictive și rezultatele funcției trebuie să fie
variabile scalare fără atributul pointer. Un argument fals sau subobiectul său poate fi utilizat într-o
expresie de specificație doar ca argument pentru funcțiile intrinseci bit_size, kind, len sau funcții de
interogare numerică din Secțiunea 8.7.2. Un exemplu este

funcția reală elementară f(a) real,


intenție(în) :: A

real(select_real_kind(precizie(a)*2)) :: lucru
:
funcția finală f

Această restricție previne ca funcțiile de caractere să producă un rezultat de matrice cu elemente de


lungimi de caractere diferite și permite implementărilor să creeze versiuni cu valori de matrice care
utilizează matrice obișnuite în interior. Un exemplu simplu care ar încălca regula este

8Aceste cerințe pot fi depășite în Fortran 2008 de atributul impur, vezi Secțiunea 20.5.4.
Machine Translated by Google

Caracteristicile matricei 119

functie elementara c(n) caracter


(len=n) intreg, intent(in) :: n :: c ! Invalid
real
:: munca(n) ! Invalid
:
funcția finală c

Dacă acest lucru ar fi permis, o versiune de rang unu ar trebui să dețină lucrul ca o matrice ragged-edge de
rangul doi.
Un bloc de interfață pentru o procedură externă este necesar dacă procedura în sine nu este
intrinsecă și elementară. Interfața trebuie să-l specifice ca elementar. Acest lucru se datorează faptului
că compilatorul poate folosi un mecanism de apelare diferit pentru a acomoda eficient cazul matricei.
Acesta contrastează cu cazul procedurilor pure, unde este permisă mai multă libertate (vezi secțiunea
anterioară).
Pentru o subrutină elementară, dacă orice argument real este evaluat în matrice, toate argumentele
reale care corespund argumentelor fictive cu intenție inout sau out trebuie să fie matrice. De exemplu,
putem face ca schimbarea subrutinei din Figura 6.2 (Secțiunea 6.4) să își îndeplinească sarcina pe matrice
de orice formă sau dimensiune, așa cum se arată în Figura 6.12. Apelarea swap cu o matrice și un
argument scalar este evident eronată și nu este permisă.

Figura 6.12 Versiunea elementară a subrutinei din Figura 6.2.


schimb de subrutine elementare(a, b) real,
intent(inout) :: a, b
real :: muncă
munca = a
a=b
b = schimb

de subrutine la sfârșitul lucrului

Dacă o referință de procedură generică (Secțiunea 5.18) este compatibilă atât cu o procedură
elementară, cât și cu una non-elementală, se invocă procedura non-elementală. De exemplu, am putea
scrie versiuni ale add_intervals (Figura 6.11) pentru tablouri de rang unu și ne bazăm pe funcția
elementară pentru alte ranguri. În general, trebuie să vă așteptați ca versiunea elementară să se execute
mai lent pentru un anumit rang decât versiunea non-elementală corespunzătoare.
Observăm că o procedură elementară non-intrinsecă nu poate fi folosită ca argument real.
O procedură nu este permisă să fie atât elementară, cât și recursivă.

6.12 Elemente de matrice

În Secțiunea 2.10, am limitat descrierea elementelor matricei la cazuri simple. În general, un element de
matrice este un scalar de formă

parte-ref [%part-ref]...

unde este parte-ref


Machine Translated by Google

120 Fortran modern explicat

nume-parte[(lista-indice)]

iar ultima parte-ref are o listă de indice. Numărul de indice din fiecare listă trebuie să fie egal cu rangul matricei
sau componentei matricei și fiecare indice trebuie să fie o expresie întreagă scalară a cărei valoare se află în
limitele dimensiunii sale a matricei sau componentei matricei.
Pentru a ilustra acest lucru, luați tipul

tip triplet real


:: u
real, dimensiune(3) :: du real,
dimensiune(3,3) :: triplet tip final d2u

care a fost luat în considerare în Secțiunea 2.10. O matrice poate fi declarată de acest tip:

tip(triplet), dimensiune(10,20,30) :: gudron

și

gudron(n,2,n*n) ! n de tip întreg

este un element de matrice. Este un scalar de tip triplet și

tar(n, 2, n*n)%du

este o matrice reală cu

tar(n, 2, n*n)%du(2)

ca unul dintre elementele sale.

Dacă un element de matrice este de tip caracter, acesta poate fi urmat de o referință subșir:

(sub șir-gamă)

de exemplu,

pagina (k*k) (i+1:j-5) ! i, j, k de tipul întreg

Prin convenție, un astfel de obiect este numit mai degrabă un subșir decât un element de matrice.
Observați că lista de indice se califică pentru numele părții matricei. Nu este permisă aplicarea unei
astfel de liste de indice unui desemnator de matrice decât dacă desemnatorul se termină cu un nume de
parte a matricei. O secțiune de matrice, o referință de funcție sau o expresie de matrice între paranteze
nu trebuie să fie calificate printr-o listă de indice.

6.13 Subobiecte matrice

Secțiunile de matrice au fost introduse în Secțiunea 2.10 și oferă o modalitate convenabilă de a accesa un
subbary obișnuit, cum ar fi un rând sau o coloană dintr-o matrice de rangul doi:

a(i, 1:n) ! Elementele de la 1 la n din rândul i


a(1:m, j) ! Elementele de la 1 la m ale coloanei j

Pentru simplitatea descrierii, nu am explicat că una sau ambele limite pot fi omise atunci când se dorește
limita corespunzătoare a matricei în sine și că poate fi specificat un pas diferit de unul:
Machine Translated by Google

Caracteristicile matricei 121

a(i, :) a(i, ! Întregul rând i

1:n:3) ! Elementele 1, 4, ... din rândul i

O altă formă de indice de secțiune este o expresie întreagă de rang unu. Toate elementele
expresiei trebuie definite cu valori care se află în limitele indicelui matricei părinte. De exemplu,

v( (/ 1, 7, 3, 2 /) )

este o secțiune cu elementele v(1), v(7), v(3) și v(2), în această ordine. Un astfel de indice se numește
indice vectorial. Dacă există repetiții în valorile elementelor unui indice vectorial, secțiunea se
numește o secțiune mai multe-unu deoarece mai mult de un element al secțiunii este mapat pe un
singur element de matrice. De exemplu,

v( (/ 1, 7, 3, 7 /) )

are elementele 2 și 4 mapate pe v(7). O secțiune mai multe-unu nu trebuie să apară în stânga
unei instrucțiuni de atribuire, deoarece ar exista mai multe valori posibile pentru un singur element.
De exemplu, declarația

v( (/ 1, 7, 3, 7 /) ) = (/ 1, 2, 3, 4 /) ! Ilegal

nu este permis deoarece valorile 2 și 4 nu pot fi stocate ambele în v(7). Extinderea este zero dacă
indicele vectorial are dimensiunea zero.
Când o secțiune de matrice cu un indice vectorial este un argument real, este privită ca o
expresie și argumentul inactiv corespunzător nu trebuie să fie definit sau redefinit și nu trebuie să
aibă intent out sau inout. Ne așteptăm ca compilatorii să facă o copie ca o matrice obișnuită
temporară la intrare, dar să nu efectueze o copie înapoi la întoarcere. De asemenea, o secțiune de
matrice cu un indice vectorial nu este permisă să fie o țintă de pointer, deoarece permiterea
acestora ar complica serios mecanismul pe care compilatorii ar trebui să-l stabilească altfel pentru
pointeri. Din motive similare, o astfel de secțiune de matrice nu este permisă să fie un fișier intern (Secțiunea
În plus față de modelele de subscriptie obișnuite și neregulate descrise tocmai acum, funcția intrinsecă
de deplasare circulară cshift (Secțiunea 8.13.5) oferă un mecanism care manipulează secțiunile de
matrice într-un mod „înfășurat”. Acest lucru este util în gestionarea limitelor anumitor tipuri de probleme
periodice ale grilei, deși este supus unor restricții similare cu cele ale subscriptelor vectoriale. Dacă o
matrice v(5) are valoarea [1,2,3,4,5], atunci cshift(v, 2) are valoarea [3,4,5,1,2].

Forma generală a unui subobiect este

parte-ref[%part-ref]... [(substring-interval)]

unde ref part are acum forma

nume-parte [(listă-secțiune-indice)]

unde numărul de indice de secțiune din fiecare listă trebuie să fie egal cu rangul matricei sau
componentei matricei. Fiecare indice-secțiune este fie un indice (secțiunea 6.12), o expresie întreagă
de rang unu (indice vectorial), fie un indice-triplet al formei

[jos] : [sus] [ : pas]


Machine Translated by Google

122 Fortran modern explicat

unde inferior, superior și stride sunt expresii întregi scalare. Dacă inferior este omis, valoarea implicită
este limita inferioară pentru acest indice al matricei. Dacă upper este omis, valoarea implicită este limita
superioară pentru acest indice al matricei. Dacă pasul este omis, valoarea implicită este unu.
Pasul poate fi negativ, astfel încât să fie posibil să luăm, de exemplu, elementele unui rând în ordine
inversă, specificând o secțiune precum

a(i, 10:1:-1)

Extinderea este zero dacă pas>0 și inferior>superior sau dacă pas<0 și inferior<sus. Valoarea pasului nu
trebuie să fie zero.
În mod normal, ne așteptăm ca atât valorile inferioare cât și cele superioare să fie în limitele indicelui
matricei corespunzător. Totuși, tot ceea ce este necesar este ca fiecare valoare folosită efectiv pentru a
selecta un element să fie în limite. Prin urmare,

a(1, 2:11:2)

este legal chiar dacă limita superioară a celei de-a doua dimensiuni a lui a este doar 10.
Tripletul-indice specifică o secvență de valori de indice,

mai jos, mai jos + pas, mai jos + 2*pas,...

mergând pe cât posibil fără a depăși partea superioară (deasupra ei când pas> 0 sau sub ea când pas <
0). Lungimea secvenței pentru al i-lea indice-triplet determină a i-a întindere a tabloului care este format.

Rangul unei părți-ref cu o listă-secțiune-indice este numărul de subscripte vectoriale și triplete de


subindice pe care le conține. Până acum, în această secțiune, toate exemplele au fost de rangul unu;
dimpotrivă, elementul de matrice obișnuit

a(1,7)

este un exemplu de o parte-ref de rang zero și secțiune

a(:,1:7)

este un exemplu de parte-ref de rangul doi. Rangul unei părți-ref fără o listă-secțiune-subscript este
rangul obiectului sau al componentei. O parte-ref poate fi o matrice; de exemplu,

tar%du(2)

pentru matricea tar din Secțiunea 6.12 este o secțiune de matrice cu elemente tar(1,1,1)%du(2),
tar(2,1,1)%du(2), tar(3,1,1)% du(2), .... A putea forma secțiuni în acest fel din tablouri de tip derivat,
precum și prin selectarea unor seturi de elemente, este o caracteristică foarte utilă a limbajului. Un
exemplu mai prozaic, având în vedere specificația

tip(persoană), dimensiune(1:50) :: grupul_meu

pentru tipul de persoană din Secțiunea 2.9, este subobiectul my_group%id care este o secțiune a matricei
întregi de dimensiunea 50.
Din păcate, nu este permis ca mai mult de o parte-ref să fie o matrice; de exemplu, nu este permis să
scrieți

tar%du ! Ilegal

pentru matricea tar din Secțiunea 6.12. Motivul pentru aceasta este că, dacă tar%du ar fi considerat o
matrice, elementul său (1,2,3,4) ar corespunde cu
Machine Translated by Google

Caracteristicile matricei 123

tar(2,3,4)%du(1)

ceea ce ar fi o nota ie prea confuză.


Partea-ref cu rang diferit de zero determină rangul și forma subobiectului. Dacă oricare dintre întinderile sale
este zero, subobiectul însuși are dimensiunea zero. Se numește secțiune matrice dacă reful-parte finală are o
listă-subscript-secțiune sau un alt ref-parte are un rang diferit de zero.
Un interval-subșir poate fi prezent numai dacă ultima parte-ref este de tip caracter și este fie un scalar, fie
are o listă-secțiune-subscript. Prin convenție, obiectul rezultat este numit mai degrabă o secțiune decât un
subșir. Se formează din secțiunea necalificată luând subșirul specificat al fiecărui element. Rețineți că, dacă c
este o matrice de caractere de rang unu,

c(i:j)

este secțiunea formată din elementele de la i la j; dacă se dorește subșiruri ale tuturor elementelor matricei,
putem scrie secțiunea
c(:)(k:l)

O secțiune de matrice care se termină cu un nume de componentă se mai numește și componentă de structură.
Rețineți că, dacă componenta este scalară, secțiunea nu poate fi calificată printr-o listă de indice de sfârșit sau o
listă de indice de secțiune. Astfel, folosind exemplul din Secțiunea 6.12,
tar%u

este o astfel de secțiune de matrice și

tar(1, 2, 3)%u

este o componentă a unui element valid de tar. Forma tar%u(1,


2, 3) ! Nu sunt acceptate
nu este permis.
În plus, un nume de parte din dreapta unui ref de parte cu rang diferit de zero nu trebuie să aibă atributul
alocabil sau pointer. Acest lucru se datorează faptului că un astfel de obiect ar reprezenta o matrice ale cărei
elemente au fost alocate independent și ar necesita un mecanism de implementare foarte diferit de cel necesar
pentru o matrice obișnuită. De exemplu, luați în considerare matricea

tip(intrare), dimensiune(n) :: rânduri ! n de tip întreg

pentru intrarea de tip definită aproape de sfârșitul secțiunii 6.5.2. Dacă ni s-ar permite să scriem rândurile
obiectului% în continuare, ar fi interpretat ca o altă matrice de dimensiune n și tip de intrare, dar elementele
sale sunt probabil să fie stocate fără nici un model obișnuit (fiecare având spațiu separat de o instrucțiune de
alocare) și într-adevăr, unele vor fi nule dacă vreunul dintre indicatori este disociat. Rețineți că nu există nicio
problemă cu accesarea pointerelor individuale, cum ar fi rândurile(i)%next.

6.14 Matrice de pointeri

Deși matricele de pointeri ca atare nu sunt permise în Fortran, efectul echivalent poate fi obținut prin crearea
unui tip care conține o componentă pointer. Acest lucru este util atunci când construiți o listă legată care este
mai complicată decât lanțul descris în Secțiunea 2.13. De exemplu, dacă este necesar un număr variabil de
legături la fiecare intrare, intrarea de tip recursiv din Figura 2.3 ar putea fi extinsă la perechea de tipuri:
Machine Translated by Google

124 Fortran modern explicat

tip ptr
tip(intrare), pointer :: point end type ptr
tip intrare real

:: valoare
tip întreg :: index
(ptr), pointer :: copii (:) intrare tip final

După alocări corespunzătoare și asocieri de pointeri, este posibil să ne referim la indexul fiului j al
nodului ca

node%copii(j)%punct%index

Acest nivel suplimentar de indirectare este necesar deoarece elementele individuale ale copiilor nu
au ele însele atributul pointer - aceasta este o proprietate numai a întregului tablou.
De exemplu, putem lua două noduri existente, să spunem a și b, fiecare dintre ele fiind o rădăcină de copac, și să
facem astfel un copac mare

tree%children(1)%point => un
arbore%children(2)%point => b

ceea ce nu ar fi posibil cu intrarea de tip original.

6.15 Pointerii ca alias

Dacă o secțiune de matrice fără indice vectoriale, cum ar fi

tabel (m:n, p:q)

este dorită frecvent în timp ce variabilele întregi m, n, p și q nu își schimbă valorile, este convenabil să
se poată face referire la secțiune ca o matrice numită, cum ar fi

fereastră

O astfel de facilitate este furnizată în Fortran de pointeri și instrucțiunea de atribuire a pointerului. Aici,
fereastra ar fi declarată astfel

real, dimensiune(:, :), pointer :: fereastră

și asociat cu table, care trebuie să aibă, desigur, atributul target sau pointer,9 by
executarea declara iei

fereastra => tabel (m:n, p:q)

Dacă, mai târziu, dimensiunea ferestrei trebuie schimbată, tot ce este nevoie este o altă instrucțiune de
atribuire a pointerului. Rețineți, totuși, că limitele indicelui pentru fereastră din acest exemplu sunt (1:n-
m+1, 1:q-p+1), deoarece acestea sunt furnizate de funcțiile lbound și ubound (Secțiunea 8.12.2).

Facilitatea furnizează un mecanism de subscriere sau sec ionare a matricelor, cum ar fi

9În Fortran 2003, declara ia asociat oferă un mijloc de a realiza acest lucru fără a fi nevoie de intă.
sau atributul pointer, vezi Secțiunea 14.4.
Machine Translated by Google

Caracteristicile matricei 125

tar%u

unde tar este un tablou și u este o componentă scalară, discutat în Secțiunea 6.13. Aici putem
efectua asocierea pointerului
taru => tar%u

dacă taru este un indicator de rangul trei de tipul corespunzător. Abonament ca în

taru(1, 2, 3)

atunci este admisibil. Aici limitele indicelui pentru taru vor fi cele ale tarului.

6.16 Constructori de matrice

Sintaxa pe care am introdus-o în Secțiunea 2.10 pentru constantele matrice poate fi folosită pentru a
construi matrice de rang unu mai general. Forma generală a unui constructor de matrice este

(/ array-constructor-value-list /)

unde fiecare valoare-constructor-matrice este una dintre expr sau constructor-implied-do.


Matricea astfel construită este de rang unu cu secvența sa de elemente formată din secvența de
expresii scalare și elemente ale expresiilor de matrice în ordinea elementelor de matrice. Un
constructor implicit-do are forma

(array-constructor-value-list, variabilă = expr1, expr2 [,expr3])

unde variabila este o variabilă scalară cu nume întreg, iar expr1, expr2 și expr3 sunt expresii cu
numere întregi scalare. Interpretarea sa este ca și cum ar fi fost scrisă lista de valori-constructor-matrice

max ( (expr2 - expr1 + expr3)/expr3,0)

ori, cu variabila înlocuită cu expr1, expr1+expr3,..., ca și pentru constructul do (Secțiunea 4.4).


Un exemplu simplu este

(/ (i,i=1,10) /)

care este egal cu

(/ 1 2 3 4 5 6 7 8 9 10 /)

Rețineți că sintaxa permite imbricarea unui constructor-implicit-do în altul, ca în exemplu

(/ ((i,i=1,3), j=1,3) /)

care este egal cu

(/ 1, 2, 3, 1, 2, 3, 1, 2, 3 /)

și imbricarea constructorilor de structuri în cadrul constructorilor de matrice (și invers), de


exemplu, pentru tipul din Secțiunea 6.7,

(/ (matrice (0,0), i = 1, limită) /)


Machine Translated by Google

126 Fortran modern explicat

Secvența poate fi goală, caz în care se construiește o matrice de dimensiune zero. Sfera variabilei este
constructor-implied-do. Alte instrucțiuni, sau chiar alte părți ale constructorului de matrice, se pot referi
la o altă variabilă având același nume. Valoarea celeilalte variabile nu este afectată de execuția
constructorului de matrice și este disponibilă cu excepția structurii constructor-implied-do.

Parametrii de tip și tip ai unui constructor de matrice sunt cei ai primului expr și fiecare expr trebuie
să aibă aceiași parametri de tip și tip. Dacă fiecare expr, expr1, expr2 și expr3 este o expresie constantă
(Secțiunea 7.4), constructorul de matrice este o expresie constantă.
O matrice de rang mai mare decât unu poate fi construită dintr-un constructor de matrice
utilizând remodelarea funcției intrinseci (Secțiunea 8.13.3). De exemplu,

remodelare( sursa = (/ 1,2,3,4,5,6 /), forma = (/ 2,3 /) )

are valoarea

135
246

Unele deficiențe ale constructorilor de matrice au fost eliminate în Fortran 2003 (detaliile sunt în
Secțiunea 15.9).

6.17 Matrice de masca

Matricele logice sunt necesare pentru mascarea instrucțiunilor și constructelor în where (Secțiunea
6.8) și joacă un rol similar în multe dintre funcțiile intrinseci ale matricei (Capitolul 8). Adesea, astfel
de matrice sunt mari și poate exista un câștig de stocare util din utilizarea unor tipuri logice care nu
sunt implicite, dacă sunt disponibile. De exemplu, unele procesoare pot folosi octeți pentru a stoca
elemente ale matricelor logice(kind=1) și biții pentru a stoca elemente ale matricelor logice(kind=0).
Din păcate, nu există o facilitate portabilă pentru a specifica astfel de matrice, deoarece nu există
nicio funcție intrinsecă comparabilă cu selected_int_kind și selected_real_kind.
Matricele logice sunt formate implicit în anumite expresii, de obicei ca variabile temporare generate
de compilator. În

unde (a > 0,0) a = 2,0 * A

sau

dacă (oricare (a > 0,0)) atunci

(orice este descris în Secțiunea 8.11.1) expresia a > 0.0 este o matrice logică. Într-un astfel de caz, se
poate aștepta ca un compilator de optimizare să aleagă un parametru de tip tip adecvat pentru tabloul
temporar.
Machine Translated by Google

Caracteristicile matricei 127

6.18 Rezumat
Am explicat că tablourile pot avea dimensiune zero și că nu sunt necesare reguli speciale pentru ele. O
matrice dummy își poate lua forma din argumentul real corespunzător.
Stocarea pentru o matrice poate fi alocată automat la intrarea într-o procedură și dealocată automat la
returnare, sau alocarea poate fi controlată în detaliu de către program. Funcțiile pot fi evaluate de
matrice fie prin mecanismul unei referințe elementare care efectuează același calcul pentru fiecare
element de matrice, fie prin funcția cu adevărat cu valori de matrice. Atribuțiile de matrice pot fi mascate
prin utilizarea instrucțiunii și a constructului where. Componentele de structură pot fi matrice dacă
părintele este o matrice sau componenta este o matrice, dar nu ambele. Un subbaraj poate fi fie formulat
direct ca o secțiune de matrice, fie indirect prin utilizarea atribuirii pointerului pentru a-l asocia cu un
pointer. O matrice poate fi construită dintr-o secvență de expresii. O matrice logică poate fi folosită ca
mască.
Funcțiile intrinseci sunt o parte importantă a caracteristicilor matricei și vor fi descrise în Capitolul 8.

Încheiem acest capitol cu un program complet, figurile 6.13 și 6.14, care ilustrează utilizarea expresiilor
de matrice, alocărilor de matrice, matricelor alocabile, matricelor automate și secțiunilor de matrice.
Modulul liniar conține o subrutină pentru rezolvarea unui set de ecuații liniare, iar aceasta este numită
dintr-un program principal care solicită utilizatorului problema și apoi o rezolvă.
Machine Translated by Google

128 Fortran modern explicat

Figura 6.13 Prima parte a unui modul pentru rezolvarea unui set de ecuații liniare. dimensiunea este
descrisă în secțiunea 8.12.2 și maxloc este descrisă în secțiunea 8.14. modul liniar

întreg, parametru, public :: kind=selected_real_kind(10) public :: solve

con ine
rezolvarea subrutinei (a, piv_tol, b, ok) !
argumente real(kind), intent(inout),
dimension(:,:) :: a
! Matricea a.
real(kind), intent(in) :: piv_tol ! Cel mai mic
pivot acceptabil.
real(tip), intentie(inout), dimensiune(:) :: b ! Vectorul din partea
dreaptă pe ! intrare. Suprascris de soluție. ::
Bine
logic, intenție (out)
! Adevărat după o intrare reușită! si fals in
rest.

! Variabile locale
întreg :: i întreg :: ! Index de rând.
j întreg :: n ! Indexul coloanei.
real(tip), ! Ordinea matricei.
dimensiune(mărime(b)) :: rând ! Matrice automată
necesară pentru spațiul de lucru;
real(tip) :: element ! Variabila spatiului de lucru.

n = dimensiune (b)

ok = dimensiune(a, 1) == n .și. dimensiune (a, 2) == n dacă


(.nu.ok) atunci
întoarcere

sfâr itul dacă

face j = 1, n

! Actualizați elementele din coloana j.


do i = 1, j - 1 a(i+1:n, j) = a(i+1:n, j) -
a(i,j) * a(i+1:n, i) end do

! Găsiți pivot și verificați dimensiunea acestuia


i = maxloc(abs(a(j:n, j)), dim=1)+j-1 if (abs(a(i, j)) < piv_tol)
atunci
ok = .false.
întoarcere

sfâr itul dacă


Machine Translated by Google

Caracteristicile matricei 129

Figura 6.14 A doua parte a modulului Figura 6.13 și un program care îl folosește. Descriptorii de editare utilizați în
instrucțiunile de scriere sunt descriși în Secțiunea 9.12 !

Dacă este necesar, aplicați schimbul de rânduri dacă (i/=j)


apoi rând = a(j, :); a(j, :) = a(i, :); a(i, :) = element rând
= b(j); b(j) = b(i); b(i) = element final if

! Calculați elementele j+1 : n din j-a coloană. a(j+1:n, j) = a(j+1:n, j)/


a(j, j) end do

! Înlocuire înainte

face i = 1, n-1 b(i+1:n)


= b(i+1:n) - b(i)*a(i+1:n, i)
sfâr itul face

! Înlocuirea spatelui

face j = n, 1, -1 b(j) = b(j)/


a(j, j) b(1:j-1) = b(1:j-1) -
b(j)*a( 1:j-1, j) sfâr itul do

sfâr itul rezolvării subrutinei


modul final liniar

utilizarea principală a
programului liniar

întreg :: i, n real(tip),
alocabil :: a(:, :), b(:) logic :: ok

print *, 'Ordinea matricei?' citit *,


n

alocă ( a(n, n), b(n) ) do i = 1, n scrie (*, '(a,


i2, a)') ' Elementele rândului ', i, ' ale a?'
citește *, a(i,:) scrie (*, '(a, i2, a)') ' citește *, b(i) sfârșit face

Componenta ', i, ' din b?'

apelați solve(a, maxval(abs(a))*1.0e-10, b, ok) dacă (ok) atunci scrieți (*,


'(/,a,/,(5f12.4))') ' Soluția este ', altfel

print *, 'Matricea este singulară' end if

termina programul principal


Machine Translated by Google

130 Fortran modern explicat

Exerciții

1. Având în vedere declarația matricei

real, dimensiune(50,20) :: a

scrieți secțiuni de matrice reprezentând

i) primul rând al lui a;

ii) ultima coloană a lui a;

iii) fiecare al doilea element din fiecare rând și coloană;

iv) ca și pentru (iii) în ordine inversă în ambele dimensiuni;

v) o matrice de dimensiune zero.

2. Scrieți o declarație where pentru a dubla valoarea tuturor elementelor pozitive ale unui tablou z.

3. Scrieți o declarație de matrice pentru un tablou j care urmează să fie complet definit de instrucțiune

j = (/ (3, 5, i=1,5), 5,5,5, (i, i = 5,3,-1 ) /)

4. Clasificați următoarele matrice:

exemplu de subrutină (n, a, b)


real, dimensiune(n, 10) :: w real :: a(:),
b(0:) :: d(:, :)
real, indicator

5. Scrieți o declarație și o instrucțiune de atribuire a pointerului potrivite pentru a face referire ca și matrice a tuturor
celor trei elemente ale componentei du din elementele matricei tar având toate cele trei valori de indice chiar
(Secțiunea 6.12).

6. Având în vedere declarațiile de matrice

întreg, dimensiune(100, 100), țintă :: l, m, n întreg, dimensiune(:, :),


indicator :: ll, mm, nn

rescrie enun urile

l(j:k+1, j-1:k) = l(j:k+1, j-1:k) + l(j:k+1, j-1:k) l(j:k+ 1, j-1:k) = m(j:k+1, j-1:k) +
n(j:k+1, j-1:k) + n(j:k+1, j:k +1)

așa cum ar putea apărea în urma executării declarațiilor

ll => l(j:k+1, j-1:k) mm =>


m(j:k+1, j-1:k) nn => n(j:k+1,
j-1:k )

7. Finalizați exercițiul 1 din capitolul 4 folosind sintaxa matrice în loc de constructele do.

8. Scrieți un modul pentru a menține o structură de date constând dintr-o listă legată de numere întregi, cu posibilitatea
de a adăuga și șterge membri ai listei, în mod eficient.

9. Scrieți un modul care conține exemplul din Figura 6.9 (Secțiunea 6.7) ca procedură de modul și care sprijină operațiunile
și atribuțiile definite pe care le conține.

10. Folosind stiva de tipuri din Secțiunea 6.5.6, scrieți cod pentru a defini o variabilă de acel tip cu o lungime a
componentei alocabile de patru și apoi pentru a extinde acea matrice alocabilă cu două valori suplimentare.
Machine Translated by Google

Caracteristicile matricei 131

11. Având în vedere tipul

tip emfield real,


alocabil :: putere(:,:) tip final

inițializați o variabilă de tip emfield astfel încât componenta sa să aibă limite (1:4,1:6) și valoarea 1 peste
tot. Extindeți această variabilă astfel încât componenta să aibă limite (0:5,0:8), păstrând valorile elementelor
vechi și setând valorile elementelor noi la zero.

12. Ca 11., dar cu noi limite (1:6,1:9) și folosind funcția intrinsecă de remodelare.
Machine Translated by Google

Această pagină a fost lăsată goală în mod intenționat


Machine Translated by Google

7. Caietul de sarcini

7.1 Introducere

În capitolele precedente am învățat elementele limbajului Fortran, cum pot fi combinate în expresii și
sarcini, cum putem controla fluxul logic al unui program, cum să împărțim un program în părți
gestionabile și am luat în considerare modul în care matricele pot fi prelucrate. Am văzut că aceste
cunoștințe sunt suficiente pentru a scrie programe, atunci când sunt combinate cu o instrucțiune
rudimentară print și cu instrucțiunea final.
Deja în capitolele 2 până la 6, am îndeplinit câteva instrucțiuni de specificație atunci când am declarat
tipul și alte proprietăți ale obiectelor de date, dar pentru a ușura sarcina cititorului nu am explicat
întotdeauna toate opțiunile disponibile. În acest capitol umplem acest gol. Pentru început, totuși, este
necesar să reamintim locul declarațiilor de specificație într-un limbaj de programare. Un program este
procesat de un computer în etape. În prima etapă, compilare, codul sursă (textul) al programului este
citit de un program cunoscut sub numele de compilator care îl analizează și generează fișiere care conțin
cod obiect. Fiecare unitate de program a programului complet este de obicei procesată separat. Codul
obiect este o traducere a codului sursă într-o formă care poate fi înțeleasă de hardware-ul computerului
și conține instrucțiuni precise cu privire la operațiunile pe care computerul trebuie să le efectueze.
Folosind aceste fișiere, se construiește un program executabil. Etapa finală constă în execuție, prin care
sunt executate instrucțiunile codificate și rezultatele calculelor sunt puse la dispoziție.

În prima etapă, compilatorul solicită informații despre entitățile implicate.


Aceste informații sunt furnizate la începutul fiecărei unități de program sau subprogram prin declarații
de specificație. Descrierea celor mai multe dintre acestea este subiectul acestui capitol. Declarațiile de
specificație asociate cu interfețele de procedură, inclusiv blocurile de interfață și instrucțiunea de interfață
și, de asemenea, instrucțiunea externă, au fost explicate în Capitolul 5. Declarația intrinsecă este explicată
în Capitolul 8.

7.2 Tastare implicită


Multe limbaje de programare necesită ca toate entitățile tipizate să aibă tipurile specificate în mod
explicit. Orice entitate de date care este întâlnită într-o instrucțiune executabilă fără ca tipul acesteia să
fi fost declarat va determina compilatorul să indice o eroare. Aceasta, precum și o interdicție privind
amestecarea tipurilor, sunt cunoscute sub numele de tastare puternică. În cazul Fortran, o entitate care
nu este accesată prin folosire sau asociere gazdă și nu este tastată explicit prin apariția într-o declarație
de tip este tastată implicit, fiindu-i atribuit un tip conform litera inițială a numelui său.
Machine Translated by Google

134 Fortran modern explicat

Implicit într-o unitate de program sau într-un bloc de interfață este că entitățile ale căror nume încep cu
una dintre literele i, j, ..., n sunt de tipul
implicit implicit
real.1 întreg,
Această iar variabilele
absență a tastăriicare încep cu
puternice literele
poate ducez la
sunt dede
erori tipul
greșit, numele
neprevăzute.
greșit va Din
daprogram
naștere
acest motiv,
unei
a, b, recomandăm
...,
variabile
h sau o,
separate
p, ...;
evitarea
decare,
exemplu,
tastării
dacă este
dacă
implicite.
folosită,
un numePentru
poate
de nicio
variabilă
ducetastare
la este
consecințe
scris
implicită, declarația

implicit nici unul

este disponibil și recomandăm utilizarea acestuia în întregul program.


O instrucțiune implicită none poate fi precedată în cadrul unei unități de scoping numai de instrucțiuni
de utilizare (și formatare). O declarație implicită none dintr-un modul i se aplică acestuia, subprogramelor
modulelor și subprogramelor interne ale acestora. O declarație implicită none într-un program principal
sau într-un subprogram, se aplică acestuia și subprogramelor sale interne.

7.3 Declararea entităților de diferite forme

Până acum, am folosit instrucțiuni separate de declarație de tip, cum ar fi

întreg :: a, b întreg, dimensiune(10) :: c, d


întreg, dimensiune(8,7) :: e

să declare mai multe entități de același tip, dar cu forme diferite. De fapt, Fortran permite utilizarea unei
singure declarații. Indiferent dacă există sau nu un atribut de dimensiune prezent, matricele pot fi declarate
prin plasarea informațiilor despre formă după numele matricei:

întreg :: a, b, c(10), d(10), e(8, 7)

Dacă este prezent atributul dimensiune, acesta oferă o formă implicită pentru entitățile care nu sunt
urmată de propriile informații de formă și este ignorată pentru cele care sunt:

întreg, dimensiune(10) :: c, d, e(8, 7)

7.4 Constante denumite și expresii constante

În interiorul unui program, deseori trebuie să definim o constantă sau un set de constante. De exemplu,
într-un program care necesită utilizarea repetată a vitezei luminii, am putea folosi o variabilă reală c căreia
îi este dată valoarea prin declarația

c = 2,99792458

Un pericol în această practică este acela că valoarea lui c poate fi suprascrisă din neatenție, de exemplu
pentru că un alt programator reutiliza c ca o variabilă pentru a conține o cantitate diferită, neobservând că
numele este deja în uz.
S-ar putea, de asemenea, ca programul să conțină specificații precum

1Consultați Secțiunea B.9 pentru modalitățile de specificare a altor mapări între litere și tipuri.
Machine Translated by Google

Caietul de sarcini 135

real :: x(10), y(10), z(10) întreg ::


mesh(10, 10), ipoint(100)

unde toate dimensiunile sunt 10 sau 102. Astfel de specificații pot fi utilizate pe scară largă și 10 poate
apărea chiar ca o constantă explicită, să spunem ca un parametru într-un do-construct care procesează
aceste matrice:

fac i = 1, 10

Mai târziu, se poate realiza că valoarea 20 mai degrabă decât 10 este necesară, iar noua valoare trebuie
înlocuită oriunde apare cea veche, o întreprindere predispusă la erori.
Încă un alt caz a fost întâlnit în Secțiunea 2.6, în care erau necesare constante denumite pentru valorile
parametrului tip tip.
Pentru a face față tuturor acestor situații, Fortran conține ceea ce sunt cunoscute ca constante numite.
Acestea nu pot apărea niciodată în partea stângă a unei instrucțiuni de atribuire, dar pot fi utilizate în
expresii în orice mod în care poate fi utilizată o constantă literală, cu excepția unei constante complexe
(Secțiunea 2.6.3).2 O instrucțiune de declarație de tip poate fi folosit pentru a specifica astfel
o constanta:

real, parametru :: c = 2,99792458

Valoarea este protejată, deoarece c este acum numele unei constante și nu poate fi folosită ca nume de
variabilă în aceeași unitate de acoperire. La fel, putem scrie

întreg, parametrul :: lungime = 10 întreg real


:: x(lungime), y(lungime), z(lungime) ::
plasă(lungime, lungime), ipoint(lungime**2)
:
do i = 1, lungime

ceea ce are avantajul clar că pentru a schimba valoarea de la 10 la 20 trebuie modificată doar o singură
linie, iar noua valoare este apoi propagată corect.
În acest exemplu, lungimea expresiei**2 a apărut într-una dintre specificațiile legate de matrice. Acesta
este un exemplu particular de expresie constantă. Se așteaptă ca o astfel de expresie să fie evaluată în
timpul compilării, deci este restricționată în forma sa.3 O expresie constantă este o expresie în care fiecare
operație este intrinsecă, fiecare operator de exponențiere are o putere întreagă și fiecare primar este

i) o constantă sau un subobiect al unei constante;

ii) un constructor de matrice ale cărui expresii (inclusiv limite și pași) au primare
care sunt expresii constante;

iii) un constructor de structură ale cărui componente sunt expresii constante;

iv) o referință de funcție intrinsecă a unui întreg sau caracter elementar ale cărei argumente sunt
expresii constante de tipul întreg sau caracter;

2Această neregulă este corectată în Fortran 2003.


3Aceste restricții sunt relaxate în Fortran 2003, vezi Secțiunea 15.10.
Machine Translated by Google

136 Fortran modern explicat

v) o referire la una dintre funcțiile intrinseci de transformare repetare, remodelare, selecționat_int_kind,


selectat_real_kind, transfer și tăiere cu argumente reale care sunt expresii constante;

vi) o referire la funcția intrinsecă transformațională nulă cu un argument care este fie de alt tip decât
caracter, fie are lungimea caracterului care este definită printr-o expresie constantă și nu este
presupusă;

vii) o referire la

o funcție de interogare a matricei (Secțiunea 8.12), alta decât cea alocată, funcția de interogare
biți bit_size, funcția de interogare a caracterelor len, felul funcției de interogare tip sau o
funcție de interogare numerică (Secțiunea 8.7.2)

unde fiecare argument este fie o expresie constantă, fie o variabilă ai cărei parametri de tip sau
limite interogați nu sunt nici presupuși, definiti de o altă expresie decât o expresie constantă,
definită printr-o instrucțiune de alocare sau definită de o atribuire de pointer;

viii) o variabilă implicit-do cu expresii constante ca limite și pași; sau

ix) o expresie constantă cuprinsă între paranteze;

și unde fiecare indice, indice de secțiune sau legat subșir este o expresie constantă.
Dacă o expresie constantă invocă o funcție de interogare pentru un parametru de tip sau o matrice
legată de un obiect, parametrul de tip sau legat de matrice trebuie să fie specificat într-o instrucțiune
anterioară de specificație sau la stânga în aceeași instrucțiune de specificație.
În definirea unei constante numite putem folosi orice expresie constantă, iar constanta devine definită
cu valoarea expresiei conform regulilor de atribuire intrinsecă. Acest lucru este ilustrat de exemplu

întreg, parametru :: lungime=10, lung=select_real_kind(12) real, parametru


:: lsq = lungime**2

Rețineți din acest exemplu că este posibil într-o singură instrucțiune să definiți mai multe constante
numite, în acest caz două, separate prin virgulă.
O constantă numită poate fi o matrice, ca în cazul

real, dimensiune(3), parametru :: câmp = (/ 0,0, 10,0, 20,0 /)

Pentru o matrice de rang mai mare de unu, trebuie aplicată funcția de remodelare descrisă în Secțiunea
8.13.3.
O constantă numită poate fi de tip derivat, ca în cazul

tip(posn), parametru :: a = posn(1.0,2.0,0)

pentru tipul

tip posn real


:: x, y
întreg :: z end tip posn
Machine Translated by Google

Caietul de sarcini 137

Rețineți că un subobiect al unei constante nu trebuie să aibă neapărat o valoare constantă. De exemplu,
dacă i este o variabilă întreagă, câmpul (i) poate avea valoarea 0,0, 10,0 sau 20,0. Rețineți, de asemenea,
că o constantă poate să nu fie un pointer, o matrice alocabilă, un argument inactiv sau un rezultat al
funcției, deoarece acestea sunt întotdeauna variabile. Cu toate acestea, poate fi de tip derivat cu o
componentă pointer care este disociată (Secțiunea 7.5.4):

tip(intrare), parametru :: e = intrare(0.0, null())

În mod clar, deoarece o astfel de componentă pointer face parte dintr-o constantă, nu este permisă alocarea
sau atribuirea pointerului.
Orice constantă numită utilizată într-o expresie constantă trebuie fie să fie accesată de la gazdă, fie
accesată dintr-un modul, fie declarată într-o declarație anterioară, fie să fie declarată la stânga utilizării
sale în aceeași instrucțiune. Un exemplu care utilizează o expresie constantă care include o constantă
numită care este definită în aceeași declarație este

întreg, parametru :: măr = 3, pară = măr**2

În cele din urmă, există un punct important referitor la definirea unei constante scalare numite de tip caracter.
Lungimea sa poate fi specificată ca un asterisc și luată direct din valoarea sa, ceea ce evită necesitatea numărării
lungimii unui șir de caractere, făcând modificările la definiția acestuia mult mai ușoare. Un exemplu în acest sens
este

caracter(len=*), parametru :: șir = „Nu trebuie să numărați”

Din păcate, este necesar să se numără atunci când o matrice de caractere este definită folosind un constructor
de matrice, deoarece toate elementele trebuie să aibă aceeași lungime:4

caracter(len=7), parametru, dimensiune(3) :: &

c=(/'Cohen ', 'Metcalf', 'Reid '/)

nu ar fi corectă fără cele două spații libere din „Cohen” și cele trei din „Reid”.

Atributul de parametru este un mijloc important prin care constantele pot fi protejate de suprascriere, iar
programele pot fi modificate într-un mod sigur. Ar trebui să fie folosit în aceste scopuri cu fiecare ocazie posibilă.

7.5 Valori inițiale pentru variabile

7.5.1 Inițializarea în declarațiile de tip declarație

O variabilă poate primi o valoare inițială într-o declarație de tip, pur și simplu urmând numele variabilei printr-un
semn egal și o expresie constantă (Secțiunea 7.4), ca în exemple.

real :: a = 0,0
real, dimensiune(3) :: b = (/ 0,0, 1,2, 4,5 /)

Valoarea inițială este definită de valoarea expresiei corespunzătoare conform regulilor de atribuire intrinsecă.
Variabila dobândește automat atributul de salvare (Secțiunea 7.9). Nu trebuie să fie un argument fals, un pointer,
o matrice alocabilă, un obiect automat sau un rezultat al funcției.

4Această restricție poate fi ocolită în Fortran 2003, vezi Secțiunea 15.9.


Machine Translated by Google

138 Fortran modern explicat

7.5.2 Declarația de date

O modalitate alternativă de a specifica o valoare inițială pentru o variabilă este prin declarația de date. Are
forma generală

listă-obiect de date /listă-valoare/ [[,] listă-obiect /listă-valoare/]...

unde object-list este o listă de variabile și bucle implicite-do; iar value-list este o listă de constante scalare și
constructori de structură. Un exemplu simplu este

real :: a, b, c
întreg :: i, j, ka,b,c/1.,2.,3./,
i,j,k/1,2,3/ date

în care o variabilă a capătă valoarea inițială 1., b valoarea 2. etc.


Dacă orice parte a unei variabile este inițializată în acest mod, variabila dobândește automat atributul de
salvare. Variabila nu trebuie să fie un argument inactiv, o matrice alocabilă, un obiect automat sau un rezultat
al funcției. Poate fi un pointer și valoarea corespunzătoare trebuie să fie o referință la funcția intrinsecă nulă
fără argumente.
După ce orice matrice sau secțiune de matrice din listă de obiecte a fost extinsă într-o secvență de elemente
scalare în ordinea elementelor de matrice, trebuie să existe tot atâtea constante în fiecare listă de valori cât și
elemente scalare din lista de obiecte corespunzătoare. Fiecărui element scalar i se atribuie constanta scalară
corespunzătoare.
Constantele care se repetă pot fi scrise o dată și combinate cu un număr întreg scalar de repetare care poate
fi o constantă numită sau literală:

date i,j,k/3*0/

Valoarea numărării repetate trebuie să fie pozitivă sau zero. Ca exemplu, luați în considerare afirmația

date r(1:lungime)/lungime*0./

unde r este o matrice reală și lungimea este o constantă numită care ar putea lua valoarea zero.
Matricele pot fi inițializate în trei moduri diferite: ca întreg, prin element sau printr-o buclă do implicită.
Aceste trei moduri sunt prezentate mai jos pentru o matrice declarată de

real :: a(5, 5)

În primul rând, pentru întreaga matrice, declarația

date a/25*1.0/

setează fiecare element al lui a la 1.0.

În al doilea rând, elementele și secțiunile individuale ale unui pot fi inițializate, ca în

date a(1,1), a(3,1), a(1,2), a(3,3) /2*1,0, 2*2,0/ date a(2:5,4) /4*1,0 /

în fiecare dintre care sunt inițializate doar cele patru elemente specificate și secțiunea. Fiecare indice de matrice
trebuie să fie o expresie constantă, la fel ca orice indice de subșir de caractere.
Atunci când elementele care urmează să fie selectate se încadrează într-un model care poate fi reprezentat prin
indici do-loop, este posibil să scrieți declarații de date într-o a treia cale, cum ar fi
Machine Translated by Google

Caietul de sarcini 139

date ((a(i,j), i=1,5,2), j=1,5) /15*0./

Forma generală a unei bucle implicite este

(dlist, do-var = expr, expr[, expr])

unde dlist este o listă de elemente de matrice, componente de structură scalară și bucle implicite-do, do-
var este o variabilă scalară întreagă numită și fiecare expr este o expresie întregă scalară. Este interpretat
ca pentru un construct do (Secțiunea 4.4), cu excepția faptului că variabila do are domeniul de aplicare al
implicit-do ca într-un constructor de matrice (Secțiunea 6.16). O variabilă dintr-o expr trebuie să fie un do-
var al unui exterior implicit-do:

întreg :: j, k întreg, parametru ::


l=5, l2=((l+1)/2)**2 real :: a(l,l)

date ((a(j,k), k=1,j), j=1,l,2) / l2 * 1,0 /

Acest exemplu setează la 1.0 primul element al primului rând al lui a, primele trei elemente ale celui de-
al treilea rând și toate elementele ultimului rând, așa cum se arată în Figura 7.1.

Figura 7.1 Rezultatul unei bucle implicite-do într-o instrucțiune de date.


1.0. . . .
.....
1.0 1.0 1.0 . .
.....
1.0 1.0 1.0 1.0 1.0

Singurele variabile permise în expresiile indice din instrucțiunile de date sunt indicii do
aceeași buclă sau o buclă de nivel exterior și toate operațiunile trebuie să fie intrinseci.
Un obiect de tip derivat poate apărea într-o declarație de date. În acest caz, valoarea corespunzătoare
trebuie să fie un constructor de structură având o expresie constantă pentru fiecare componentă.
Folosind definiția tipului posn din Secțiunea 7.4, putem scrie

tip(posn):: poziție1, poziție2 date poziție1 /


posn(2., 3., 0)/, poziția2%z /4/

În exemplele date până acum, tipurile și parametrii de tip ai constantelor dintr-o listă de valori au fost
întotdeauna aceleași cu tipul variabilelor din lista de obiecte. Acesta nu trebuie să fie cazul, dar ele trebuie
să fie compatibile pentru atribuirea intrinsecă, deoarece entitatea este inițializată urmând regulile pentru
atribuirea intrinsecă. Este astfel posibil să se scrie enun uri precum

date q/1/, i/3.1/, b/(0.,1.)/

(unde b și q sunt reale și i este întreg). Valorile întregi pot fi constante binare, octale sau
hexazecimale (Secțiunea 2.6.1).
Fiecare variabilă trebuie fie să fi fost introdusă într-o declarație anterioară de declarație de
tip în unitatea de acoperire, fie tipul ei este cel asociat cu prima literă a numelui său conform
Machine Translated by Google

140 Fortran modern explicat

regulile implicite de tastare ale unității de acoperire. În cazul tastării implicite, apariția numelui variabilei într-o
declarație de tip ulterioară în unitatea de scoping trebuie să confirme parametrii de tip și tip. În mod similar, orice
variabilă matrice trebuie să fi fost declarată anterior ca atare.

Nicio variabilă sau parte a unei variabile nu poate fi inițializată de mai multe ori într-o unitate de definire.
Vă recomandăm să utilizați declarația de tip declarație mai degrabă decât instrucțiunea de date, dar
instrucțiunea de date trebuie utilizată atunci când doar o parte a unei variabile trebuie inițializată.

7.5.3 Inițializarea pointerului și funcția null

Sunt disponibile mijloace pentru a evita ca starea inițială a unui pointer să fie nedefinită. Aceasta ar fi starea cea
mai nedorită, deoarece un astfel de indicator nu poate fi testat nici măcar de funcția intrinsecă asociată (Secțiunea
8.2). Pointerilor li se poate da statutul inițial de disociat într-o declarație de tip, cum ar fi

real, pointer, dimensiune(:):: vector => null()

sau o declarație de date

real, pointer, dimensiune(:):: vector de date vectoriale/


null() /

Aceasta, desigur, implică atributul de salvare, care se aplică stării de asociere a pointerului.
Pointerul nu trebuie să fie un argument fals sau un rezultat al funcției. Aici, sau dacă atributul de salvare este
nedorit (pentru o variabilă locală într-o procedură recursivă, de exemplu), variabila poate fi anulată în mod explicit
la începutul subprogramului.
Recomandarea noastră este ca toți pointerii să fie inițializați astfel încât să reducă riscul de efecte bizare din
utilizarea accidentală a indicatoarelor nedefinite. Acesta este un ajutor și în scrierea codului care evită scurgerile
de memorie.
Funcția null este o funcție intrinsecă (Secțiunea 8.15), a cărei formă simplă null(), așa cum este folosită în
exemplul de mai sus, este aproape întotdeauna potrivită, deoarece atributele sunt imediat evidente din context.
De exemplu, având în vedere intrarea de tip din Secțiunea 6.5.2, constructorul structurii

intrare (0.0, 0, null())

este disponibil. De asemenea, pentru un vector pointer, instrucțiunea

vector => null()

este echivalent cu

anula (vector)

Forma cu argumentul este necesară atunci când null este un argument real care corespunde unui argument
inactiv cu lungimea caracterului presupus (Secțiunea 5.19) sau este într-o referință la o procedură generică și este
necesar tipul, parametrul de tip sau rangul pentru a rezolva problema. referință (secțiunea 5.18).

Nu există niciun mecanism pentru a inițializa un pointer ca asociat.5

5O restricție a fost ridicată în Fortran 2008.


Machine Translated by Google

Caietul de sarcini 141

7.5.4 Inițializarea implicită a componentelor

Sunt disponibile mijloace pentru a specifica faptul că oricărui obiect de tip derivat i se dă o valoare inițială
implicită pentru o componentă. Valoarea trebuie specificată atunci când componenta este declarată ca parte a
definiției tipului (Secțiunea 2.9). Dacă componenta nu este un pointer, acest lucru se face în mod obișnuit
(Secțiunea 7.5.1) cu semnul egal urmat de o expresie constantă și se aplică regulile de atribuire intrinsecă
(inclusiv specificarea unei valori scalare pentru toate elementele unui tablou). componentă). Dacă componenta
este un pointer, singura inițializare permisă este simbolul de atribuire a pointerului urmat de o referință la
funcția intrinsecă nulă fără argumente.
Inițializarea nu trebuie să se aplice tuturor componentelor unui anumit tip derivat. Un exemplu
pentru tipul definit în secțiunea 6.5.2 este

tip de intrare
real :: valoare = 2,0 ::
integer index
tip(intrare), pointer :: next => null() intrare de tip final

Având în vedere o declarație de matrice, cum ar fi

tip(intrare), dimensiune(100) :: matrice

subobiectele precum matrix(3)%value vor avea valoarea inițială 2.0, iar referința asociată(matrix(3)%next) va
returna valoarea false.
Pentru un obiect de tip derivat imbricat, inițializările asociate cu componente
nivelurile sunt recunoscute. De exemplu, având în vedere specificațiile

tip nod tip


întreg :: tejghea

(intrare) :: element final tip tip


nod (nod) :: n

componenta n%element%value va avea valoarea inițială 2.0.


Spre deosebire de inițializarea explicită într-o declarație de tip sau o declarație de date, inițializarea implicită
nu implică faptul că obiectele au atributul de salvare.6 Obiectele
pot fi încă inițializate explicit într-o declarație de tip, ca în

tip(intrare), dimensiune(100) :: matrice=intrare(uriaș(0,0) și


imens(0),null())

caz în care inițializarea implicită este ignorată. În mod similar, inițializarea implicită poate fi suprascrisă într-o
definiție de tip imbricat, cum ar fi

6Totuși, un obiect de un astfel de tip care este declarat într-un modul trebuie să aibă atributul de salvare, cu excepția
cazului în care este un pointer sau o matrice alocabilă. Acest lucru se datorează dificultății pe care le-ar avea unele
implementări în a determina când un obiect nesalvat ar trebui să fie reinițializat. Nu se aplică în Fortran 2008, unde toate
obiectele de date dintr-un modul au atributul de salvare.
Machine Translated by Google

142 Fortran modern explicat

tip nod
întreg :: tejghea

tip(intrare) :: element=entry(0.0, 0 , nul())


nod de tip final

Cu toate acestea, nicio parte a unui obiect non-pointer cu inițializare implicită nu este permisă într-o
declarație de date (Secțiunea 7.5.2).
Pe lângă aplicarea valorilor inițiale ale datelor statice, inițializarea implicită se aplică și oricăror date
care sunt create dinamic în timpul execuției programului. Aceasta include alocarea cu declarația de
alocare. De exemplu, afirmația

alocă (matrice(1)%next)

creează un obiect parțial inițializat de tip intrare. Se aplică, de asemenea, variabilelor locale
nesalvate (inclusiv obiectelor automate), rezultatelor funcției și argumentelor fictive cu intenție.
Se aplică chiar dacă definiția tipului este privată sau componentele sunt private.

7.6 Atributele publice și private

Modulele (Secțiunea 5.5) permit ca specificațiile să fie „ambalate” într-o formă care le permite să fie
accesate în altă parte a programului. Până acum, am presupus că toate entitățile din modul trebuie să
fie accesibile, adică să aibă atributul public, dar uneori este de dorit să se limiteze accesul. De exemplu,
mai multe proceduri dintr-un modul pot avea nevoie de acces la o matrice de lucru care conține
rezultatele calculelor pe care le-au efectuat. Dacă accesul este limitat doar la procedurile modulului, nu
există posibilitatea unei corupări accidentale a acestor date de către o altă procedură și se pot face
modificări de design în cadrul modulului fără a afecta restul programului. În cazurile în care entitățile nu
trebuie să fie accesibile în afara propriului modul, li se poate atribui atributul privat.

Aceste două atribute pot fi specificate cu atributele publice și private pe tip


declarații din modul, ca în

real, public :: x, y, z întreg, privat :: u,


v, w

sau în declarații publice și private, ca în

public :: x, y, z, operator(.add.) privat :: u, v, w,


atribuire(=), operator(*)

care au formele generale

public [ [ :: ] access-id-list] privat [ [ :: ]


access-id-list]

unde access-id este un nume sau o specificație generică (Secțiunea 5.18).


Rețineți că dacă o procedură are un identificator generic, accesibilitatea numelui său specific este
independentă de accesibilitatea identificatorului său generic. Unul poate fi public, în timp ce celălalt
este privat, ceea ce înseamnă că este accesibil numai prin numele său specific sau numai prin
identificatorul său generic.
Machine Translated by Google

Caietul de sarcini 143

Dacă o declarație publică sau privată nu are o listă de entități, aceasta confirmă sau resetează valoarea implicită.
Astfel, afirmația

public

confirmă public ca valoare implicită și declarația

privat

setează valoarea implicită pentru modul la accesibilitate privată. De exemplu,

privat
public :: mijloace

conferă entității mijloacelor atributul public, în timp ce toate celelalte sunt private. Poate exista cel mult o
declarație de accesibilitate fără o listă într-o unitate de definire a domeniului.
Entitățile care pot fi specificate după nume în listele publice sau private sunt variabile cu nume,
proceduri (inclusiv proceduri generice), tipuri derivate, constante denumite și grupuri de liste de nume.
Astfel, pentru a face un nume de procedură generic accesibil, dar numele specifice corespunzătoare
inaccesibile, am putea scrie

exemplu de modul
privat specific_int, specific_real interface
generic_name
procedura de modul specific_int, specific_real interfața finală
conține

subrutine specific_int(i)
:
subrutine specific_real(a)
:
exemplu de modul final

Un tip care este accesat dintr-un modul poate primi atributul privat în modulul de acces (vezi Secțiunea
7.10). Dacă o entitate de acest tip are atributul public, o instrucțiune de utilizare ulterioară pentru aceasta
poate fi însoțită de o instrucțiune de utilizare pentru tipul din modulul original.

Un obiect nu trebuie să aibă atributul public dacă tipul său a fost definit inițial cu atributul privat. În
mod similar, dacă o procedură de modul are un argument inactiv sau un rezultat al funcției de un astfel
de tip, procedurii trebuie să primească atributul privat și nu trebuie să aibă un identificator generic care
7
este public.
Utilizarea declarației private pentru componente de tipuri derivate în contextul
definirea accesului unei entități în cadrul unui modul va fi descrisă în Secțiunea 7.11.
Atributele publice și private pot apărea numai în specificațiile unui modul.

7Restricțiile din acest paragraf au fost ridicate în Fortran 2003, vezi Secțiunea 16.14.
Machine Translated by Google

144 Fortran modern explicat

7.7 Instrucțiunile pointer, țintă și alocabile

De dragul regularității în limbaj, există instrucțiuni pentru specificarea pointerului, țintei și atributelor alocabile
ale entităților. Ele iau formele:

pointer [::] nume-obiect[(spec.-matrice)] [,nume-


obiect [(spec-matrice)]]... țintă [::] nume-
obiect[(spec-matrice)] [,obiect- nume [(spec.-matrice)]]...

și

alocabil [::] nume-matrice[(spec-matrice)]


[,nume-matrice [(spec.-matrice)]]...

ca în

real :: a, son, y
alocable :: a(:,:) pointer target
:: fiule

:: a, y(10)

Credem că este mult mai clar să specificați aceste atribute în declarațiile de tip declarație și,
prin urmare, nu folosiți aceste formulare.

7.8 Intenția și declarațiile opționale

Atributul intent (Secțiunea 5.9) pentru un argument inactiv care nu este o procedură sau un pointer inactiv
poate fi specificat într-o declarație de tip sau într-o declarație de intenție de forma

intent( inout ) [::] lista-nume-argument-dummy

unde inout este in, out sau inout. Exemplele sunt


rezolvarea subrutinei (a, b, c, x, y, z) :: a, b, c, x, y, z
real intent(in) :: a, b, c intent(out) ::
x, y, z

Atributul opțional (Secțiunea 5.13) pentru un argument inactiv poate fi specificat într-o
instrucțiune de declarație de tip sau într-o instrucțiune opțională de forma

opțional [::] dummy-argument-name-list

Un exemplu este

opțional :: a, b, c

Atributul opțional este singurul atribut care poate fi specificat pentru un argument fals care este o procedură.

Rețineți că intenția și atributele opționale pot fi specificate numai pentru argumente fictive. În ceea ce
privește declarațiile din Secțiunea 7.7, credem că este mult mai clar să specificați aceste atribute pe declarațiile
de tip declarație și, prin urmare, nu folosiți aceste formulare.
Machine Translated by Google

Caietul de sarcini 145

7.9 Atributul de salvare

Să presupunem că dorim să reținem valoarea unei variabile locale într-un subprogram, de exemplu pentru
a număra de câte ori este introdus subprogramul. Am putea scrie o secțiune de cod ca în Figura 7.2. În acest
exemplu, variabilele locale, a și contorul, sunt inițializate la zero și se presupune că valorile lor curente sunt
disponibile de fiecare dată când este apelată subrutina. Acesta nu este neapărat cazul. Fortran permite ca
sistemul informatic să „uite” o nouă valoare, variabila devenind nedefinită la fiecare returnare, cu excepția
cazului în care are atributul de salvare. În Figura 7.2, este suficient să schimbați declarația lui a to

real, salvează :: a

pentru a fi sigur că valoarea sa este întotdeauna păstrată între apeluri. Acest lucru poate fi făcut și pentru
contor, dar nu este necesar deoarece toate variabilele cu valori inițiale dobândesc atributul de salvare
automat (Secțiunea 7.5).

Figura 7.2 Numărarea numărului de invocări ale unei proceduri.


subrutine orice(x) real :: a, x
întreg :: counter=0!
Inițializați
contorul
:
contor = contor + 1
dacă (contor==1) atunci
a = 0,0
altfel
a=a+x
sfâr itul dacă

O situație similară apare cu utilizarea variabilelor în module (Secțiunea 5.5). În teorie, la întoarcerea de
la un subprogram care accesează o variabilă al cărei scop este un modul, variabila devine nedefinită decât
dacă programul principal accesează modulul, un alt subprogram în execuție accesează modulul sau variabila
are atributul de salvare. În practică, compilatorii tratează variabilele modulului ca având atributul de
salvare.8 Dacă o variabilă care devine nedefinită are asociat un pointer, indicatorul indicatorului

statutul de asociere devine nedefinit.


Atributul de salvare nu trebuie specificat pentru un argument fals, un rezultat al funcției sau un obiect
automat (Secțiunea 6.4). Poate fi specificat pentru un pointer, caz în care starea asocierii pointerului este
salvată. Poate fi specificat pentru o matrice alocabilă, caz în care starea și valoarea alocării sunt salvate. O
variabilă salvată într-un subprogram recursiv este partajată de toate instanțele subprogramului.

O alternativă la specificarea atributului de salvare pe o instrucțiune de declarație de tip este instrucțiunea


de salvare:

salvați [ [::] lista-nume-variabilă]

8În Fortran 2008, toate obiectele de date dintr-un modul au atributul de salvare.
Machine Translated by Google

146 Fortran modern explicat

O instrucțiune de salvare fără listă este echivalentă cu o listă care conține toate numele posibile și, în acest caz,
unitatea de acoperire nu trebuie să conțină alte instrucțiuni de salvare și nici atribute de salvare în instrucțiunile
de declarație de tip. Recomandarea noastră este împotriva acestei forme de salvare. Dacă un programator
încearcă să dea atributul de salvare în mod explicit unui obiect automat, va rezulta un diagnostic. Pe de altă
parte, el sau ea ar putea crede că salvarea fără o listă ar face și asta și nu ar obține comportamentul dorit. De
asemenea, există o pierdere de eficiență asociată cu salvarea pe unele procesoare, așa că cel mai bine este să o
restricționați la acele obiecte pentru care este cu adevărat nevoie.
Declarația de salvare sau atributul de salvare poate apărea în instrucțiunile de declarație dintr-un program
principal, dar nu are efect.

7.10 Declarația de utilizare

În Secțiunea 5.5, am introdus instrucțiunea use în cea mai simplă formă

utilizați modul-nume

care oferă acces la toate obiectele de date denumite publice, tipurile derivate, blocurile de interfață, procedurile,
identificatorii generici și grupurile de liste de nume din modulul numit. Orice instrucțiuni de utilizare trebuie să
precedă alte instrucțiuni de specificație într-o unitate de definire a domeniului. Singurul atribut al unei entități
accesate care poate fi specificat din nou este public sau privat (și asta doar într-un modul), dar entitatea poate fi
inclusă în unul sau mai multe grupuri de liste de nume (Secțiunea 7.15).
Dacă este necesar accesul la două sau mai multe module care au fost scrise independent, același nume poate
fi utilizat în mai multe module. Acesta este motivul principal pentru a permite entităților accesate să fie
redenumite prin declarația de utilizare. Redenumirea este disponibilă și pentru a rezolva o ciocnire de nume
între o entitate locală și o entitate accesată dintr-un modul, deși preferința noastră este să folosim un editor de
text sau alt instrument pentru a schimba numele local. Odată cu redenumirea, instrucțiunea use are forma

utilizați modul-nume, redenumiți-lista

unde fiecare redenumire are forma

nume-local => nume-utilizare

și se referă la o entitate publică din modul care urmează să fie accesată cu un alt nume local.
Ca exemplu,

folosește stats_lib, sprod => prod folosește


maths_lib

face accesibile toate entitățile publice din stats_lib și maths_lib. Dacă maths_lib conține o entitate numită prod,
aceasta este accesibilă prin propriul nume, în timp ce entitatea prod a stats_lib este accesibilă ca sprod.

Redenumirea nu este necesară dacă există o ciocnire de nume între două entități care nu sunt necesare.
O ciocnire de nume este permisă dacă nu există nicio referire la nume în unitatea de acoperire.
O ciocnire de nume este, de asemenea, permisă pentru un nume generic care este necesar. Aici, toate
interfețele generice accesate după nume sunt tratate ca un singur bloc de interfață concatenat. Acest lucru este
valabil și pentru operatorii și sarcinile definiți, unde nu este disponibilă nicio facilitate de redenumire. In toate
Machine Translated by Google

Caietul de sarcini 147

în aceste cazuri, oricare două proceduri având același identificator generic trebuie să difere așa cum este
explicat în secțiunea 5.18. Ne imaginăm că acesta va fi de obicei exact ceea ce este necesar. De exemplu, am
putea accesa module pentru aritmetică de interval și aritmetică matrice, ambele având nevoie de funcțiile
sqrt, sin, etc., operatorii +, -, etc. și de atribuire, dar pentru diferite tipuri.
Pentru cazurile în care este nevoie doar de un subset de nume ale unui modul, singura opțiune este
disponibilă, având forma

folosește modul-nume, numai : [numai-listă]

unde fiecare are doar forma

access-id

sau

[local-name =>] use-name

unde fiecare access-id este o entitate publică din modul și este fie un nume de utilizare, fie o specificație
generică (Secțiunea 5.18). Aceasta oferă acces la o entitate dintr-un modul numai dacă entitatea este publică
și este specificată ca nume de utilizare sau cod de acces. Acolo unde un nume-utilizare este precedat de un
nume-local, entitatea este cunoscută local prin numele-local. Un exemplu de astfel de afirmație este

utilizați stats_lib, numai : sprod => prod, mult

care oferă acces la prod cu numele local sprod și la mult prin propriul nume.
Vă recomandăm ca o singură instrucțiune de utilizare pentru un anumit modul să fie plasată într-o unitate
de definire a domeniului, dar sunt permise mai multe. Dacă există o instrucțiune de utilizare fără un singur
calificator, toate entitățile publice din modul sunt accesibile, iar listele de redenumire și listele de numai nume
sunt interpretate ca și cum ar fi concatenate într-o singură listă de redenumire (cu forma nume de utilizare
într-o singură listă). fiind tratat ca redenumirea nume-utilizare => nume-utilizare). Dacă toate declarațiile au
singura calificare, numai acele entități numite într-una sau mai multe dintre listele unice sunt accesibile, adică
toate listele unice sunt interpretate ca și cum ar fi concatenate într-o singură listă unică.
O singură listă va fi destul de neîndemânatică dacă se dorește aproape tot modulul. Efectul unei clauze „cu
excepția” poate fi obținut prin redenumirea entităților nedorite. De exemplu, dacă un program mare (cum ar
fi unul scris în Fortran 77) conține multe proceduri externe, o bună practică este să colectați blocuri de
interfață pentru toate într-un modul care este referit în fiecare unitate de program pentru o verificare
reciprocă completă. Într-o procedură externă, am putea scrie:

utilizați all_interfaces, except_this_one => nume

pentru a evita să aibă două interfețe explicite pentru sine (unde all_interfaces este numele modulului și name
este numele procedurii).
Când un modul conține instrucțiuni de utilizare, entitățile accesate sunt tratate ca entități din modul. Li se
poate atribui atributul privat sau public în mod explicit sau prin regula implicită în vigoare în modul. Astfel,
având în vedere cele două module din Figura 7.3 și o a treia unitate de program care conține o instrucțiune
de utilizare pentru doi, variabila i este accesibilă acolo doar dacă conține și o instrucțiune de utilizare pentru
unul sau dacă i este făcută publică în mod explicit în două.
O entitate poate fi accesată prin mai multe nume locale. Acest lucru este ilustrat în Figura 7.4, unde
modulul b accesează s din modulul a cu numele local bs; dacă un subprogram precum c
Machine Translated by Google

148 Fortran modern explicat

Figura 7.3 Transformarea privată a unei entități accesate dintr-un modul.


modulul unu

întreg :: i
sfârșitul modulului unu
modulul doi
foloseste unul

privat
:
termina modulul doi

accesează atât a cât și b, va accesa s atât prin numele său original, cât și prin numele bs. Figura 7.4 ilustrează, de asemenea,
că o entitate poate fi accesată cu același nume prin mai multe rute (vezi variabila t).

Figura 7.4 Accesarea unei variabile prin mai multe nume locale.
modulul a

real :: s, t
:
finalul modulului a
modulul b

utilizați a, bs => s
:
modul final b
subrutină c
folosește o

utilizați b

:
sfârșitul subrutinei c

O modalitate mai directă pentru ca o entitate să fie accesată de mai multe nume locale este să o facă
apar de mai multe ori ca nume de utilizare. Aceasta nu este o practică pe care o recomandăm.
Desigur, toate denumirile locale ale entităților accesate din module trebuie să difere unele de altele și de numele
entităților locale. Dacă unei entități locale i se dă accidental același nume ca o entitate accesibilă dintr-un modul, acest lucru
va fi observat în timpul compilării dacă entitatea locală este declarată în mod explicit (deoarece nicio entitate accesată nu
poate primi niciun atribut la nivel local, altul decât privat sau public, si asta doar intr-un modul). Totuși, dacă entitatea locală
se dorește a fi tastată implicit (Secțiunea 7.2) și nu apare în declarații de specificație, atunci fiecare apariție a numelui va fi
luată, incorect, ca referință la variabila accesată. Pentru a evita acest lucru, recomandăm, ca întotdeauna, utilizarea
conștiincioasă a tastării explicite într-o unitate de definire a domeniului care conține una sau mai multe instrucțiuni de
utilizare. Pentru o mai mare siguranță, singura opțiune poate fi folosită pe o declarație de utilizare pentru a se asigura că
toate accesele sunt intenționate.
Machine Translated by Google

Caietul de sarcini 149

7.11 Definiții de tip derivat

Când tipurile derivate au fost introduse în Secțiunea 2.9, au fost date câteva exemple de definiții simple,
dar generalitatea completă nu a fost inclusă. Un exemplu care ilustrează mai multe caracteristici este

tip, public :: blocare întreg


privat, pointer :: cheie(:)
logic :: stare tip final blocare

Forma generală (în afară de caracteristicile redundante, vezi apendicele B.2 și C.1.3) este

tip [[,access]:: ] nume-tip [ privat ]


component-def-stmt [component-
def-stmt]...

tip final [nume-tip]

Fiecare component-def-stmt are forma

tip [[ ,component-attr-list] :: ]component-decl-list

unde tipul specifică tipul și parametrii de tip (Secțiunea 7.13), fiecare component-attr este alocabil, pointer
sau dimensiune (bounds-list) și fiecare component-decl este

nume-componentă [ (listă-limite)][ *char-len ]

sau

nume-componentă [ (listă-limite)][ *char-len ] [ comp-int ]

Semnificația lui *char-len este explicată în Secțiunea 7.13 și comp-int reprezintă inițializarea componentelor,
așa cum este explicat în Secțiunea 7.5.4. Dacă tipul este un tip derivat și nu este specificat nici atributul
alocabil, nici atributul pointer, tipul trebuie să fie definit anterior în unitatea de definire a gazdei sau
accesibil acolo prin utilizare sau asociere gazdă. Dacă este specificat atributul alocabil sau pointer, tipul
poate fi, de asemenea, cel definit (de exemplu, intrarea tip din Secțiunea 2.13) sau unul definit în altă
parte în unitatea de definire.
Un nume-tip nu trebuie să fie același cu numele oricărui tip intrinsec sau al unui tip derivat accesat
dintr-un modul.
Limitele unei componente ale unui tablou sunt declarate printr-o listă de limite, unde fiecare limită este doar

pentru o componentă alocabilă sau pointer (vezi exemplul în Secțiunea 6.14) sau

[lower-bound:] upper-bound

pentru o componentă care nu este nici alocabilă, nici pointer și limita inferioară și limita superioară sunt
expresii de specificație (Secțiunea 7.14) ale căror valori nu depind de cele ale variabilelor.
Machine Translated by Google

150 Fortran modern explicat

În mod similar, lungimea caracterului unei componente de tip caracter trebuie să fie o expresie de
specificație a cărei valoare nu depinde de cea a unei variabile. Dacă există o listă de limite atașată numelui-
componentă, aceasta definește limitele. Dacă un atribut de dimensiune este prezent în instrucțiune, lista
sa de limite se aplică oricărei componente din instrucțiune fără propria listă de limite.

Numai dacă unitatea de definire a gazdei este un modul poate apărea calificativul de acces sau
declarația privată. Calificatorul de acces pe o instrucțiune de tip poate fi public sau privat și specifică
accesibilitatea tipului. Dacă este privat, atunci numele tipului, constructorul structurii pentru tip, orice
entitate de tip și orice procedură cu un argument fals sau rezultat al funcției de tip sunt toate inaccesibile
în afara modulului gazdă. Accesibilitatea poate fi specificată și într-o declarație privată sau publică în
gazdă. În absența ambelor, tipul ia accesibilitatea implicită a modulului gazdă. Dacă apare o declarație
privată pentru un tip cu accesibilitate publică, componentele tipului sunt inaccesibile în orice unitate de
scoping care accesează modulul gazdă, astfel încât nici selecția componentelor, nici construcția structurii
nu sunt disponibile acolo. De asemenea, dacă orice componentă este de tip derivat care este privat, tipul
definit trebuie să fie privat sau să aibă componente private.

Astfel, putem distinge trei niveluri de acces:

i) toate publice, acolo unde tipul și toate componentele sale sunt accesibile, iar componentele oricărui
obiect de acest tip sunt accesibile oriunde obiectul este accesibil;

ii) un tip public cu componente private, unde tipul este accesibil, dar componentele sale sunt ascunse;

iii) toate private, unde atât tipul, cât și componentele sale sunt utilizate numai în gazdă
modul și sunt ascunse unei proceduri de accesare.

Cazul ii) are, acolo unde este cazul, avantajul de a permite efectuarea de modificări ale tipului fără a
afecta în niciun fel codul în procedura de accesare. Cazul iii) oferă acest avantaj și are meritul suplimentar
de a nu aglomera spațiul de nume al procedurii de accesare. Utilizarea accesibilității private pentru
componente sau pentru întregul tip este astfel recomandată ori de câte ori este posibil.

Observăm că, chiar dacă două definiții de tip derivat sunt identice din toate punctele de vedere, cu
excepția numelor lor, atunci entitățile din aceste două tipuri nu sunt echivalente și sunt considerate ca
fiind de tipuri diferite. Chiar dacă și numele sunt identice, tipurile sunt diferite (cu excepția cazului în care
au atributul secvență, o caracteristică pe care nu o recomandăm și a cărei descriere este lăsată la Anexa
B.2.1). Dacă un tip este necesar în mai multe unități de program, definiția ar trebui să fie plasată într-un
modul și accesată printr-o instrucțiune use oriunde este nevoie. A avea o singură definiție este mult mai
puțin predispusă la erori.

7.12 Declarația de tip

Am întâlnit deja multe exemple simple de declarații ale entităților numite prin instrucțiuni întregi, reale,
complexe, logice, caractere și tip (nume-tip). Forma generală este

tastați [ [ , atribut]... :: ] listă-entitate


Machine Translated by Google

Caietul de sarcini 151

unde tipul specifică tipul și parametrii de tip (Secțiunea 7.13), atributul este unul dintre următoarele:

parametru dimensiune(listă-limite)
public privat intent(inout) opțional
pointer țintă
alocabil salva

extern
intrinsec

iar fiecare entitate este

nume-obiect [(lista de limite)] [*char-len] [=constant-expr]

sau

nume-funcție [*char-len]

sau

nume-pointer [(lista de limite)] [*char-len] [=> null-init]

unde null-init este o referință la funcția intrinsecă null fără argumente. Semnificația lui *char-len este
explicată la sfârșitul Secțiunii 7.13; o listă de limite specifică rangul și eventual limitele entităților cu
valori de matrice.
Niciun atribut nu poate apărea de mai multe ori într-o declarație de tip dată. Colonele duble :: nu
trebuie să apară în cazul simplu fără atribute și fără =constant-expr; de exemplu

real a, b, c(10)

Dacă instrucțiunea specifică un atribut de parametru, trebuie să apară =constant-expr.


Dacă este specificat un atribut pointer, nu trebuie specificate atributele țintă, intentie, externe și
intrinseci. Este posibil ca atributele țintă și ale parametrilor să nu fie specificate pentru aceeași entitate,
iar pointerul și atributele alocabile pot să nu fie specificate pentru aceeași matrice. Dacă este specificat
atributul țintă, nu pot fi specificate nici atributul extern, nici atributul intrinsec.

Dacă un obiect este specificat cu atributul intent sau parametru, acesta este partajat de toate
subobiectele sale. Atributul pointer nu este partajat în acest mod, dar rețineți că o componentă de tip
de date derivate poate fi ea însăși un pointer. Cu toate acestea, atributul țintă este partajat de toate
subobiectele sale, cu excepția celor care sunt componente pointer.
Atributul alocabil, parametrul sau salvarea nu trebuie să fie specificat pentru un dummy
rezultatul argumentului sau al funcției.
Intenția și atributele opționale pot fi specificate numai pentru argumente fictive.
Pentru un rezultat al funcției, specificarea atributului extern este o alternativă la instrucțiunea
externă (Secțiunea 5.11) pentru declararea funcției ca fiind externă, iar specificarea atributului intrinsec
este o alternativă la instrucțiunea intrinsecă (Secțiunea 8.1.3) pentru declararea funcției a fi intrinsec.
Aceste două atribute se exclud reciproc.
Machine Translated by Google

152 Fortran modern explicat

Fiecare dintre atribute poate fi specificat și în instrucțiuni (cum ar fi salvare) care listează entitățile care
au atributul. Acest lucru duce la posibilitatea ca un atribut să fie specificat explicit de mai multe ori pentru
o anumită entitate, dar acest lucru nu este permis. Recomandarea noastră este să evitați astfel de declarații
pentru că este mult mai clar să aveți toate atributele pentru o entitate colectate într-un singur loc.

7.13 Specificarea parametrului tip și tip

Am folosit tipul pentru a reprezenta una dintre următoarele

întreg [( [kind=] natura-valoare)] real


[( [kind=] tip-valoare)] complex [( [kind=]
natura-valoare)] caracter [(lista-parametri-
actuali)] logic [([ fel=] valoare-tip) ] tip (nume-
tip)

în instrucțiunea funcției (Secțiunea 5.20), instrucțiunea de definire a componentei (Secțiunea 7.11) și


declarația de tip (Secțiunea 7.12). O valoare-tip trebuie să fie o expresie constantă (Secțiunea 7.4) și trebuie
să aibă o valoare care este valabilă pentru procesorul utilizat.
Pentru caracter, fiecare parametru real are forma

[len=] valoare-len

sau

[kind=] natură-valoare

și oferă o valoare pentru unul dintre parametri. Este permis să se omite kind= dintr-un parametru real de
tip doar atunci când len= este omis și len-value este prezent și este primul, la fel ca pentru o listă de
argumente reale (Secțiunea 5.13). Niciun parametru nu poate fi specificat de mai multe ori.

Pentru o constantă denumită scalară sau pentru un argument inactiv al unui subprogram, o valoare len
poate fi specificată ca un asterisc, caz în care valoarea este presupusă din aceea a constantei în sine sau a
argumentului actual asociat. În ambele cazuri, funcția intrinsecă len (Secțiunea 8.6.1) este disponibilă dacă
lungimea reală este necesară direct, de exemplu ca număr de iterații do-construct. Un exemplu combinat
este

character(len=len(char_arg)) function line(char_arg) :: char_arg


caracter(len=*)
caracter(len=*), parametru :: char_const = 'pagină' dacă ( len(char_arg) <
len(char_const) ) atunci
:

O valoare len care nu este un asterisc trebuie să fie o expresie de specificație (Secțiunea 7.14).
Valorile negative declară entitățile de caractere a fi de lungime zero.
În plus, este posibil să atașați o formă alternativă de len-value la entitățile individuale într-o declarație
de tip folosind entitatea de sintaxă*char-len, unde char-len este fie (valoare len), fie len și len este un
număr întreg scalar constantă literală care specifică o lungime pentru entitate.
Machine Translated by Google

Caietul de sarcini 153

Lentila constantă nu trebuie să aibă un parametru de tip tip specificat pentru el. O ilustrare a acestei
forme este

caracter(len=8) :: cuvânt(4), punct*1, text(20)*4

unde, cuvântul, punctul și textul au lungimea caracterelor 8, 1 și, respectiv, 4. În mod similar, forma
alternativă poate fi utilizată pentru componente individuale într-o declarație de definire a componentei.

7.14 Expresii de specificație

Expresiile întregi scalare non-constante pot fi folosite pentru a specifica limitele matricei (exemple în
Secțiunea 6.4) și lungimile caracterelor obiectelor de date dintr-un subprogram și ale rezultatelor funcției.
O astfel de expresie poate depinde numai de valorile datelor care sunt definite la intrarea în subprogram.
Nu trebuie să depindă de un argument opțional, chiar dacă este prezent. Orice variabilă la care se face
referire nu trebuie să aibă parametrii de tip și tip specificați mai târziu în aceeași secvență de instrucțiuni
de specificație, cu excepția cazului în care sunt cei implicați de regulile implicite de tastare.
Constructorii de matrice și constructorii de tip derivat sunt permisi. Expresia poate face referire la o
funcție de interogare pentru o matrice legată sau pentru un parametru de tip al unei entități care fie este
accesată prin utilizare sau asociere gazdă, fie este specificată mai devreme în aceeași secvență de
specificații, dar nu mai târziu în secvență.9 Un element de o matrice specificată în aceeași secvență de
specificație poate fi referită numai dacă limitele matricei sunt specificate mai devreme în secvență.10 O
astfel de expresie se numește expresie de specificație.
O matrice ale cărei limite sunt declarate folosind expresii de specificație se numește matrice de formă
explicită.
O varietate de posibilități sunt prezentate în Figura 7.5.
Limitele și lungimile caracterelor nu sunt afectate de nicio redefinire sau nedefinire a variabilelor din
expresii în timpul execuției procedurii.

7.14.1 Funcții de specificație

Oricare dintre funcțiile intrinseci definite de standard poate fi utilizată într-o expresie de specificație. În
plus, poate fi utilizată o funcție pură non-intrinsecă cu condiția ca o astfel de funcție să nu fie nici o
funcție internă, nici recursivă, să nu aibă un argument de procedură inactiv și ca interfața să fie explicită.
Funcțiile care îndeplinesc aceste condiții sunt denumite funcții de specificație. Argumentele unei funcții
de specificație atunci când sunt utilizate într-o expresie de specificație sunt supuse acelorași restricții ca
și cele privind expresiile de specificație în sine, cu excepția faptului că nu trebuie neapărat să fie scalare.

9Acest lucru evită un astfel de caz ca

personaj (len=len(a)) :: personaj distractiv


(len=len(a)) :: a

10Acest lucru evită un astfel de caz ca

întreg, parametru, dimensiune (j(1):j(1)+1) :: i = (/0,1/) j = (/1,2/) întreg,


dimensiune (i(1):i (1)+1) :: parametru,
Machine Translated by Google

154 Fortran modern explicat

Figura 7.5 O varietate de declarații într-un subprogram.


eșantion de subrutină (arr, n, șir)
folosiți definiții! Conține a real și întregul set de date mărimea întregului, intent(în) real,
dimensiune(n), intent(out) :: n
:: arr ! Caracter de matrice de formă
explicită (len=*), intenție (în) :: șir ! Lungime presupusă reală, dimensiune (datasetsize+5)
:: X ! Caracter automat de
matrice (len=n+len(șir)) :: cc ! Număr întreg automat
al obiectului, parametru :: pa2 = selected_real_kind(2*precision(a)) real(kind=pa2)
:: z ! Precizia lui z este de cel puțin două ori

! precizia a

Întrucât interfețele funcțiilor de specificație trebuie să fie explicite, totuși nu pot fi interne
funcții,11 astfel de funcții sunt probabil cel mai convenabil scrise ca proceduri de modul.
Această caracteristică este o mare comoditate pentru expresiile de specificație care nu pot fi scrise ca
expresii simple. Iată un exemplu,

funcția rezolvă (a, ... folosește


matrix_ops tip(matrice),
intent(în) :: a real :: work(wsize(a))

unde matricea este un tip definit în modulul matrix_ops și destinat să dețină o matrice rară și factorizarea
sa LU:

tip matrice
întreg :: n întreg :: ! Ordinea matricei.
nz logic :: nou ! Numărul de intrări diferite de zero.
= .adevărat. ! Fie că acesta este un nou, nefactorizat! matrice.

:
matrice de tip final

și wsize este o procedură de modul care calculează dimensiunea necesară a matricei:

funcție întreg pur wsize(a)


tip(matrice), intent(in):: a wsize = 2*a%n
+ 2 if(a%new) wsize = a%nz + wsize end
function wsize

11 Acest lucru îi împiedică să se întrebe, prin asociere gazdă, despre obiectele care sunt specificate în setul de declarații din
la care se face referire la funcția de specificație în sine.
Machine Translated by Google

Caietul de sarcini 155

7.15 Instrucțiunea listei de nume

Uneori este convenabil să aduni un set de variabile într-un singur grup, pentru a facilita operațiunile de
intrare/ieșire (I/O) asupra grupului ca întreg. Utilizarea efectivă a unor astfel de grupuri este explicată în
Secțiunea 9.10. Metoda prin care un grup este declarat este prin instrucțiunea namelist care în forma sa
simplă are sintaxa

namelist namelist-spec

unde este namelist-spec

/namelist-group-name/ lista-nume-variabilă

Listă-nume-grup-nume este numele dat grupului pentru utilizare ulterioară în instrucțiunile I/O. O variabilă
numită în listă nu trebuie să fie o matrice inactivă cu o limită neconstantă, o variabilă cu lungime de
caractere neconstantă, un obiect automat, o matrice alocabilă, un pointer sau să aibă o componentă la
orice adâncime de selecție a componentelor care este un pointer, este alocabil sau este inaccesibil.12 Un
exemplu este

real :: covor, tv, perii(10) lista de nume /


articole_casnice/ covor, tv, perii

Este posibil să declarați mai multe grupuri de liste de nume într-o singură instrucțiune, cu sintaxa

namelist namelist-spec [[,]namelist-spec]...

ca in exemplu

lista de nume /list1/ a, b, c /list2/ x, y, z

Este posibil să continuați o listă în cadrul aceleiași unități de acoperire prin repetarea numelui listei de
nume pe mai multe instrucțiuni. Prin urmare,

lista de nume /lista/ a, b, c lista de


nume / lista/ d, e, f

are același efect ca o singură instrucțiune care conține toate numele variabilelor în aceeași ordine.
Un obiect de grup de liste de nume poate apărea de mai multe ori într-un grup de liste de nume și poate aparține
mai mult de un grup de liste de nume.
Dacă tipul, parametrii de tip sau forma unei variabile liste de nume sunt specificate într-o instrucțiune
de specificație în aceeași unitate de acoperire, instrucțiunea de specificație trebuie fie să apară înaintea
instrucțiunii listei de nume, fie să fie o declarație de tip care confirmă regula implicită de tastare în vigoare.
în unitatea de scoping pentru litera inițială a variabilei. De asemenea, dacă grupul listei de nume are
atributul public, nicio variabilă din listă nu poate avea atributul privat sau să aibă componente private.

12Toate aceste restricții au fost ridicate în Fortran 2003, cu excepția faptului că matricele de dimensiuni presupuse rămân interzise.
Machine Translated by Google

156 Fortran modern explicat

7.16 Rezumat
În acest capitol au fost descrise majoritatea specificațiilor Fortran. Au fost introduse următoarele
concepte: tastarea implicită și pericolele aferente acesteia, constante denumite, expresii constante,
inițializarea datelor, controlul accesibilității entităților în module, salvarea datelor între apeluri de
procedură, accesul selectiv al entităților dintr-un modul, redenumirea entităților accesate din un modul,
expresii de specificație care pot fi utilizate la specificarea obiectelor de date și a rezultatelor funcției și
formarea variabilelor în grupuri de liste de nume. Am explicat, de asemenea, modalități alternative de
specificare a atributelor.
Încheiem acest capitol cu un program complet, Figura 7.6, care utilizează un modul pentru a sorta
adresele în stil american (nume, stradă, oraș și stat cu un cod poștal numeric) în ordinea codului poștal.
Acesta ilustrează interacțiunea dintre multe dintre caracteristicile descrise până acum, dar rețineți că nu
este un cod de producție, deoarece rutina de sortare nu este foarte eficientă și întreaga gamă de adrese
din SUA nu este gestionată. Datele de testare adecvate sunt:

Prof. James Bush, 206


Church St. SE, Minneapolis,
MN 55455

JE Dougal,
Universitatea Rice,
Houston,
TX 77251

Jack Finch,
Sala Ayres 104,
Knoxville,
TN 37996
Machine Translated by Google

Figura 7.6 Un modul pentru sortarea adreselor poștale și un program care îl folosește. maxloc este
descris în Secțiunea 8.14. Declarațiile de citire și scriere de aici sunt explicate în Secțiunile 9.7 și 9.12.
sortarea modulelor ! Pentru a sorta adresele poștale după codul poștal.
implicit nici unul
privat public ::
selectie_sort întreg, parametru ::
string_length = 30 tip, public :: adresa caracter(len =
string_length) :: nume, stradă, oraș și stat*2

adresa :: cod postal


de tipul întregului final
con ine
subrutină recursivă selectie_sort (array_arg)
tip (adresă), dimensiune (:), intenție (inout) &

:: array_arg ::
integer current_size :: mare
integer
current_size = dimensiune (array_arg) if
(current_size > 0) then big = maxloc
(array_arg(:)%zip_code, dim=1) call swap (big, current_size)
call selection_sort (array_arg(1: current_size - 1)) end dacă

con ine
subrutine swap (i, j) întreg,
intent (în) :: i, j tip (adresă) :: temp
temp = array_arg(i) array_arg(i) =
array_arg(j) array_arg(j) = temp

final subrutine swap final


subrutine selection_sort
sortare modul final
program zippy
utilizați sortarea

implicit niciun
întreg, parametru :: array_size = 100 tip (adresă), dimensiune (array_size) ::
data_array integer :: i, n do i = 1, array_size read (*, '(/a/a/a/a2,i8) )', end=10)
data_array(i) write (*, '(/a/a/a/a2,i8)') data_array(i)

sfâr itul face

10 n = i - 1
apelați selectie_sort (data_array(1: n)) scrieți (*, '(//
a)') 'după sortare:' do i = 1, n scrieți (*, '(/a/a/a/a2,i8)
') data_array(i) end do

termina programul zippy


Machine Translated by Google

158 Fortran modern explicat

Exerciții

1. Scrieți declarații de tip adecvate pentru următoarele mărimi:

i) o matrice care să dețină numărul de numărări în fiecare dintre cele 100 de casete ale unei histograme numerotate de la
1 până la 100;

ii) o matrice care să mențină temperatura la două zecimale semnificative în puncte, pe o foaie de fier, distanțate
egal la intervale de 1 cm pe o grilă dreptunghiulară de 20 cm pătrați, cu puncte în fiecare colț (punctul de topire
al fierului este 1530 C);

iii) o matrice pentru a descrie starea a 20 de comutatoare pornit/oprit;

iv) o matrice care să conțină informațiile destinate unei pagini tipărite de 44 de rânduri, fiecare din 70 de litere
sau cifre.

2. Explicați diferența dintre următoarele perechi de declarații:

real :: i = 3,1

și

real, parametru :: i = 3,1

Care este valoarea lui i în fiecare caz?

3. Scrieți declarații de tip declarație care inițializează:

i) toate elementele unui tablou întreg de lungime 100 până la valoarea zero;

ii) toate elementele impare ale aceluiași tablou la 0 și elementele pare la 1;

iii) elementele unui tablou pătrat real de 10×10 la 1,0;

iv) un șir de caractere la cifrele de la 0 la 9.

4. În modulul următor, identificați toate unitățile de definire a domeniului și enumerați mapările pentru tastarea implicită pentru
toate literele din toate:

modul mod
caracter implicit (10, 2) (ab)
:
con ine
subrutină exterioară
implicit nici unul
:
con ine
subrutină interioară (distracție)
implicit complex (z) interfață

funcția fun(x)
real implicit (f, x)
:
sfârșitul funcției distracție
interfață finală
sfâr itul subrutinei interior
sfâr itul subrutinei exterioară
modul final mod
Machine Translated by Google

Caietul de sarcini 159

5. i) Scrieți o declarație de tip care declară și inițializează o variabilă de tip derivat person (Secțiunea
2.9).
ii) Oricare

A. scrieți o declarație de tip care declară și inițializează o variabilă de intrare de tip


(Secțiunea 2.13); sau

b. scrieți o declarație de tip pentru o astfel de variabilă și o instrucțiune de date pentru a inițializa
componentele sale non-pointer.

6. Care dintre următoarele sunt expresii constante:

i) fel(x), pentru x de tip real


ii) felul_real_selectat(6, 20)
iii) 1,7**2
iv) 1,7**2,0
v) (1,7, 2,3)**(-2)
vi) (/ (7*i, i=1, 10) /)
vii) persoană ("Reid", 25*2.0, 22**2)
viii) intrare (1.7, 1, null_pointer)
Machine Translated by Google

Această pagină a fost lăsată goală în mod intenționat


Machine Translated by Google

8. Proceduri intrinseci

8.1 Introducere

Într-un limbaj care are o orientare clară către aplicații științifice, există o cerință evidentă ca funcțiile
matematice solicitate cel mai frecvent să fie furnizate ca parte a limbajului în sine, mai degrabă decât să se
aștepte ca fiecare utilizator să le codeze din nou. Când sunt furnizate împreună cu compilatorul, acestea
sunt în mod normal codificate pentru a fi foarte eficiente și vor fi fost bine testate pe întreaga gamă de
valori pe care le acceptă. Este dificil să concurezi cu standardul ridicat de cod oferit de furnizori.

Eficiența procedurilor intrinseci la manipularea matricelor este deosebit de marcată deoarece un singur
apel poate determina efectuarea unui număr mare de operațiuni individuale, în timpul execuției cărora se
poate profita de natura specifică a hardware-ului.
O altă caracteristică a unui număr substanțial de proceduri intrinseci este că extind puterea limbajului,
oferind acces la facilități care nu sunt altfel disponibile.
Exemple sunt funcțiile de interogare pentru prezența unui argument opțional, părțile unui număr în
virgulă mobilă și lungimea unui șir de caractere.
Există peste o sută de proceduri intrinseci în total, un set deosebit de bogat. Ele se împart în grupuri
distincte, pe care le descriem pe rând. O listă în ordine alfabetică, cu descrieri pe o singură linie, este dată
în Anexa A.1 Unele procesoare pot oferi proceduri intrinseci suplimentare. Rețineți că un program care
conține referințe la astfel de proceduri este portabil numai pentru alte procesoare care furnizează aceleași
proceduri. De fapt, un astfel de program nu este conform standardului.

Toate procedurile intrinseci sunt generice.

8.1.1 Apeluri cu cuvinte cheie

Procedurile pot fi apelate cu argumente reale ale cuvintelor cheie, folosind numele de argumente fictive
ca cuvinte cheie. Această facilitate nu este foarte utilă pentru cei cu un singur argument non-opțional, dar
este utilă pentru cei cu mai multe argumente opționale. De exemplu

apel data_and_time (data=d)

returnează data în variabila caracter scalară d. Regulile pentru listele de argumente poziționale și de
cuvinte cheie au fost explicate în Secțiunea 5.13. În acest capitol, argumentele false care sunt

1Anexa A listează, de asemenea, puținele care au fost adăugate la Fortran 2003 și numărul substanțial care au fost adăugate
la Fortran 2008.
Machine Translated by Google

162 Fortran modern explicat

opționale sunt indicate cu paranteze drepte. Am luat o „licență poetică” cu această notație, ceea ce ar
putea sugera cititorului că forma pozițională este permisă în urma unui argument absent (nu este cazul).

8.1.2 Categorii de proceduri intrinseci

Există patru categorii de proceduri intrinseci.

i) Proceduri elementare (Secțiunea 6.6).

ii) Funcțiile de anchetă returnează proprietăți ale argumentelor lor principale care nu depind de
valorile lor; într-adevăr, pentru variabile, valorile lor pot fi nedefinite.

iii) Funcțiile transformaționale sunt funcții care nu sunt nici elementare, nici investigative; de obicei
au argumente matrice și un rezultat matrice ale cărui elemente depind de multe dintre
elementele argumentelor.

iv) Subrutine non-elementale.

Toate funcțiile sunt pure (Secțiunea 6.10).

8.1.3 Enun ul intrinsec

Un nume poate fi specificat ca fiind cel al unei proceduri intrinseci într-o declarație intrinsecă, care are
forma generală

intrinsec [::] listă-nume-intrinsecă

unde lista-nume-intrinsecă este o listă de nume de proceduri intrinseci. Un nume nu trebuie să apară
de mai multe ori în instrucțiunile intrinseci ale unei unități de definire a domeniului și nu trebuie să
apară într-o declarație externă acolo (dar poate apărea ca nume generic pe un bloc de interfață dacă o
procedură intrinsecă este extinsă, vezi Secțiunea 5.18). Este posibil să se includă o astfel de declarație
în fiecare unitate de scoping care conține referințe la proceduri intrinseci, pentru a face utilizarea clară
pentru cititor. Recomandăm în special această practică atunci când faceți referire la proceduri intrinseci
care nu sunt definite de standard, pentru că atunci ar trebui să fie produs un mesaj de diagnostic clar
dacă programul este portat la un procesor care nu acceptă procedurile extra intrinseci.

8.1.4 Intenții de argumentare

Deoarece toate funcțiile sunt pure, argumentele lor au toate intenția. Pentru subrutine, intențiile
variază de la caz la caz (vezi descrierile date mai târziu în capitol).

8.2 Funcții de interogare pentru orice tip

Următoarele sunt funcții de interogare ale căror argumente pot fi de orice tip.
Machine Translated by Google

Proceduri intrinseci 163

asociat (pointer [,țintă] ), când ținta este absentă, returnează valoarea true dacă pointer-ul este asociat
cu o țintă și fals în caz contrar. Starea de asociere a pointerului nu trebuie să fie nedefinită. Dacă
ținta este prezentă, trebuie să aibă același tip, parametri de tip și rang ca indicator. Valoarea este
adevărată dacă pointerul este asociat cu ținta și falsă în caz contrar. În cazul matricei, true este
returnat numai dacă formele sunt identice și elementele de matrice corespunzătoare, în ordinea
elementelor de matrice, sunt asociate între ele. Dacă lungimea caracterului sau dimensiunea
matricei este zero, se returnează false. O limită diferită, ca în cazul asociat(p,a) în urma atribuirii
pointerului p => a(:) când lbound(a) = 0, este insuficientă pentru a provoca returnarea false.
Argumentul țintă poate fi el însuși un pointer, caz în care ținta sa este comparată cu ținta
pointerului; starea de asociere a pointerului țintei nu trebuie să fie nedefinită și dacă fie pointerul,
fie ținta este disociat, rezultatul este fals.

prezent (a) poate fi apelat într-un subprogram care are un argument dummy opțional a sau accesează
un astfel de argument dummy din gazda sa. Returnează valoarea true dacă argumentul actual
corespunzător este prezent în apelul curent către acesta și false în caz contrar.
Dacă un argument fals absent este folosit ca argument real într-un apel al unui alt subprogram,
acesta este considerat și absent în subprogramul apelat.

Există o funcție de interogare al cărei argument poate fi de orice tip intrinsec:

felul (x) are un întreg implicit de tip și o valoare egală cu valoarea parametrului tip tip a lui x.

8.3 Funcții numerice elementare

Există 17 funcții elementare pentru efectuarea sarcinilor numerice simple, dintre care multe efectuează
conversii de tip pentru unele sau toate tipurile de argumente permise.

8.3.1 Funcții elementare care pot converti

Dacă kind este prezent în următoarele funcții elementare, aceasta trebuie să fie o expresie constantă cu
număr întreg scalar și să furnizeze un parametru de tip kind care este suportat de procesor.

abs (a) returnează valoarea absolută a unui argument de tip întreg, real sau complex. Rezultatul este de
tip integer dacă a este de tip integer și în caz contrar este real. Are același tip de parametru ca și a.

aimag (z) returnează partea imaginară a valorii complexe z. Tipul este real și parametrul tip tip este cel al
lui z.

aint (a [,kind] ) trunchiază o valoare reală a spre zero pentru a produce un real care este un număr întreg.
Valoarea parametrului tip tip este valoarea parametrului kind dacă este prezent sau a unui alt tip.

anint (a [,kind] ) returnează un real a cărui valoare este cel mai apropiat număr întreg de valoarea reală
a. Valoarea parametrului tip tip este valoarea argumentului kind, dacă este prezent, sau cea a
unui alt mod.
Machine Translated by Google

164 Fortran modern explicat

plafonul (a [,kind] ) returnează cel mai mic număr întreg mai mare sau egal cu argumentul său real. Dacă
kind este prezent, valoarea parametrului tip kind al rezultatului este valoarea kind, altfel este cea a
tipului întreg implicit.

cmplx (x [,y] [,kind] ) convertește x sau (x, y) în tip complex, valoarea parametrului tip tip fiind valoarea
argumentului tip dacă este prezent sau cea a complexului implicit în caz contrar. Dacă y este
absent, x poate fi de tip întreg, real sau complex. Dacă y este prezent, trebuie să fie de tip întreg
sau real și x trebuie să fie de tip întreg sau real.

floor (a [,kind] ) returnează cel mai mare număr întreg mai mic sau egal cu argumentul său real.
Dacă kind este prezent, valoarea parametrului tip kind al rezultatului este valoarea kind, altfel este
cea a tipului întreg implicit.

int (a [,kind] ) se convertește în tipul întreg, valoarea parametrului tip kind fiind valoarea argumentului
kind, dacă este prezent, sau cea a întregului implicit în caz contrar. Argumentul a poate fi

• întreg, caz în care int(a)=a;

• real, caz în care valoarea este trunchiată spre zero; sau

• complex, caz în care partea reală este trunchiată spre zero.

nint (a [,kind] ) returnează valoarea întreagă care este cea mai apropiată de a reală. Dacă kind este prezent,
valoarea parametrului tip kind al rezultatului este valoarea kind, altfel este cea a tipului întreg
implicit.

real (a [,kind] ) se convertește în tip real, valoarea parametrului tip tip fiind aceea de tip dacă este prezent.
Dacă kind este absent, parametrul tip tip este cel implicit real atunci când a este de tip întreg sau
real și este cel al a când a este tip complex.
Argumentul a poate fi de tipul întreg, real sau complex. Dacă este complexă, partea imaginară este
ignorată.

8.3.2 Funcții elementare care nu se convertesc

Următoarele sunt funcții elementare al căror rezultat este de tip și tip de parametru care sunt cele ale
primului sau singurului argument. Pentru cei care au mai mult de un argument, toate argumentele trebuie
să aibă același parametru de tip și tip de tip.

conjg (z) returnează conjugatul valorii complexe z.

dim (x, y) returnează max(xy, 0.) pentru argumentele care sunt ambele întregi sau ambele reale.

max (a1, a2 [,a3,...] ) returnează maximum două sau mai multe numere întregi sau real2
valorile.

min (a1, a2 [,a3,...] ) returnează minimum două sau mai multe numere întregi sau reale2
valorile.

2În Fortran 2003, tipul de caracter este, de asemenea, acceptat.


Machine Translated by Google

Proceduri intrinseci 165

mod (a, p) returnează restul unui modulo p, adică a-int(a/p)*p. Valoarea p


nu trebuie să fie zero; a și p trebuie să fie ambele întregi sau ambele reale.

modulo (a, p) returnează un modulo p când a și p sunt ambele întregi sau ambele reale, adică a-floor(a/
p)*p în cazul real și a-floor(a÷p)*p în cazul întregului, unde ÷ reprezintă diviziunea matematică
obișnuită. Valoarea lui p nu trebuie să fie zero.

semnul (a, b) returnează valoarea absolută a lui a ori semnul lui b. Argumentele a și b trebuie să fie
ambele întregi sau ambele reale. Dacă b este zero, semnul său este considerat pozitiv. Totuși,
dacă b este real cu valoarea zero și procesorul poate distinge între un zero real negativ și unul
pozitiv, rezultatul are semnul lui b (vezi și Secțiunea 8.7.1).

8.4 Funcții matematice elementare

Următoarele sunt funcții elementare care evaluează funcții matematice elementare. Parametrul tip și tip
tip al rezultatului sunt cei ai primului argument, care este de obicei singurul argument.

acos (x) returnează valoarea funcției arc cosinus (cosinus invers) pentru valorile reale x astfel încât
|x| 1, exprimat în radiani în intervalul 0 acos(x) π.

asin (x) returnează valoarea funcției arc sinus (sinus invers) pentru valorile reale x astfel încât |x| 1,
π π
exprimat în radiani în interval 2 asin(x) 2.

atan (x) returnează valoarea funcției arc tangentă (tangentă inversă) pentru x real, exprimată în
π π
radiani în intervalul 2 atan(x) 2.

atan2 (y, x) returnează valoarea funcției arc tangentă (tangentă inversă) pentru perechile de reali, x și y,
de același tip și parametru de tip. Rezultatul este valoarea principală a argumentului numărului
complex (x, y), exprimată în radiani în interval
3
π < atan2(y,x) π. Valorile lui x și y nu trebuie să fie ambele zero.

cos (x) returnează valoarea funcției cosinus pentru un argument de tip real sau complex, adică
tratată ca valoare în radiani.

cosh (x) returnează valoarea funcției cosinus hiperbolic pentru un argument real x.

exp (x) returnează valoarea funcției exponențiale pentru un argument real sau complex x.

log (x) returnează funcția de logaritm natural pentru un argument real sau complex x. În cazul real, x
trebuie să fie pozitiv. În cazul complex, x nu trebuie să fie zero, iar partea imaginară w a rezultatului
4
se află în intervalul π<w π.

log10 (x) returnează logaritmul comun (de bază 10) al unui argument real a cărui valoare trebuie
fii pozitiv.

3În Fortran 2003, dacă aritmetica este IEEE, se returnează o aproximare a π dacă x < 0 și y este un zero negativ.
4În Fortran 2003, dacă aritmetica este IEEE, se returnează o aproximare la π dacă partea reală a lui x este mai mică decât
zero și partea imaginară este un zero negativ.
Machine Translated by Google

166 Fortran modern explicat

sin (x) returnează valoarea funcției sinus pentru un argument real sau complex care este tratat ca o
valoare în radiani.

sinh (x) returnează valoarea funcției sinus hiperbolice pentru un argument real.

sqrt (x) returnează valoarea funcției rădăcină pătrată pentru un argument real sau complex x. Dacă
x este real, valoarea sa nu trebuie să fie negativă. În cazul complex, partea reală a rezultatului
nu este negativă, iar când este zero, partea imaginară a rezultatului nu este negativă.5

tan (x) returnează valoarea funcției tangente pentru un argument real care este tratat ca valoare în
radiani.

tanh (x) returnează valoarea funcției tangente hiperbolice pentru un argument real.

8.5 Caracter elementar și funcții logice

8.5.1 Conversii caracter-întreg

Următoarele sunt funcții elementare pentru conversiile de la un singur caracter la un număr întreg
și invers.

achar (i) este de tipul caracter implicit cu lungimea unu și returnează caracterul în poziția din
secvența de colare ASCII care este specificată de întregul i. Valoarea lui i trebuie să fie în
intervalul 0 i 127, altfel rezultatul depinde de procesor.

char (i [,kind] ) este de tipul caracter și lungimea unu, cu o valoare a parametrului de tip kind aceea
a valorii tipului dacă este prezentă, sau implicită în caz contrar. Acesta returnează caracterul
în pozi ia i în secven a de cola ionare a procesorului asociată cu parametrul de tip
relevant. Valoarea lui i trebuie să fie în intervalul 0 i n 1, unde n este numărul de
caractere din secvența de cola ionare a procesorului. Dacă kind este prezent, trebuie să fie
o expresie constantă cu un întreg scalar și să furnizeze un parametru de tip kind care este
acceptat de procesor.

iachar (c)6 este de tipul default integer și returnează poziția în secvența de colating ASCII a
caracterului implicit c. Dacă c nu este în secvență, rezultatul este dependent de procesor.

ichar (c)6 este de tipul implicit întreg și returnează poziția caracterului c în secvența de colație a
procesorului asociată cu parametrul kind al lui c.

5 În Fortran 2003, dacă aritmetica este IEEE, se returnează un rezultat imaginar negativ dacă partea reală a rezultatului este
zero și partea imaginară a lui x este mai mică decât zero.
6În Fortran 2003, există un argument final opțional care trebuie să fie o expresie constantă scalară întreagă și
controlează tipul rezultatului.
Machine Translated by Google

Proceduri intrinseci 167

8.5.2 Funcții de comparație lexicală

Următoarele funcții elementare acceptă șiruri de caractere implicite ca argumente, fac o comparație lexicală
bazată pe secvența de colare ASCII și returnează un rezultat logic implicit.
Dacă șirurile au lungimi diferite, cel mai scurt este căptușit în dreapta cu spații.

lge (șir_a, șir_b) returnează valoarea adevărată dacă șirul_a urmează șirul_b în
secvența de comparare ASCII sau este egală cu aceasta, iar valoarea falsă în caz contrar.

lgt (șir_a, șir_b) returnează valoarea adevărată dacă șirul_a urmează șirul_b în
secvența de comparare ASCII, iar valoarea falsă în caz contrar.

lle (șir_a, șir_b) returnează valoarea adevărată dacă șirul_b urmează șirul_a în
secvența de comparare ASCII sau este egală cu aceasta, iar valoarea falsă în caz contrar.

llt (șir_a, șir_b) returnează valoarea adevărată dacă șirul_b urmează șirul_a în
secvența de comparare ASCII și false în caz contrar.

8.5.3 Funcții elementare de manipulare a șirurilor

Următoarele sunt funcții elementare care manipulează șiruri. Argumentele șir, subșir și set sunt întotdeauna
de tip caracter, iar acolo unde sunt prezente două au același parametru de tip tip. Valoarea parametrului
de tip tip al rezultatului este cea a șirului.

adjustl (șir) ajustează stânga pentru a returna un șir de aceeași lungime prin eliminarea tuturor spațiilor
libere de început și inserând același număr de spații libere finale.

adjustr (șir) ajustează la dreapta pentru a returna un șir de aceeași lungime prin eliminarea tuturor spațiilor
libere din urmă și inserând același număr de spații libere de început.

index (șir, subșir [,back] )7 are tipul implicit întreg și returnează poziția de pornire a subșirului ca subșir de
șir sau zero dacă nu apare ca subșir. Dacă back este absent sau prezent cu valoarea false, este
returnată poziția de pornire a primului astfel de subșir; valoarea 1 este returnată dacă subșirul are
lungime zero.
Dacă back este prezent cu valoarea true, este returnată poziția de pornire a ultimului astfel de
subșir; valoarea len(șir)+1 este returnată dacă subșirul are lungime zero.

len_trim (șir)7 returnează un întreg implicit a cărui valoare este lungimea șirului fără caractere goale.

scan (șir, set [,back] )7 returnează un întreg implicit a cărui valoare este poziția unui caracter de șir care
este în set, sau zero dacă nu există un astfel de caracter. Dacă spatele logic este absent sau prezent
cu valoarea false, este returnată poziția celui mai din stânga astfel de caracter. Dacă back este
prezent cu valoarea true, este returnată poziția celui mai din dreapta astfel de caracter.

7În Fortran 2003, există un argument final opțional care trebuie să fie o expresie constantă scalară întreagă și
controlează tipul rezultatului.
Machine Translated by Google

168 Fortran modern explicat

verify (șir, set [,back] )7 returnează valoarea integrală implicită 0 dacă fiecare caracter din șir apare în
set sau poziția unui caracter de șir care nu este în set. Dacă spatele logic este absent sau
prezent cu valoarea false, este returnată poziția celui mai din stânga astfel de caracter. Dacă
back este prezent cu valoarea true, este returnată poziția celui mai din dreapta astfel de caracter.

8.5.4 Conversie logică

Următoarea funcție elementară se convertește dintr-o valoare logică cu un parametru de tip tip la altul.

logic (l [,kind] ) returnează o valoare logică egală cu valoarea l logic. Valoarea parametrului tip tip al
rezultatului este valoarea tipului dacă este prezentă sau valoarea logică implicită în caz contrar.
Dacă kind este prezent, trebuie să fie o expresie constantă cu un întreg scalar și să furnizeze un
parametru de tip kind care este acceptat de procesor.

8.6 Funcții non-elementale de gestionare a șirurilor

8.6.1 Funcția de interogare pentru gestionarea șirurilor

len (șir)7 este o funcție de interogare care returnează un întreg scalar implicit care conține numărul
de caractere din șir dacă este scalar, sau într-un element de șir dacă este valorizat în matrice.
Nu este necesar să se definească valoarea șirului.

8.6.2 Funcții transformaționale de manipulare a șirurilor

Există două funcții care nu pot fi elementare deoarece parametrul de tip lungime al rezultatului
depinde de valoarea unui argument.

repeat (șir, ncopies) formează șirul format din concatenarea ncopies copii ale șirului, unde ncopies
este de tip integer și valoarea sa nu trebuie să fie negativă. Ambele argumente trebuie să fie
scalare.

trim (șir) returnează șirul cu toate spațiile goale eliminate. Șirul de argument
trebuie să fie scalar.

8.7 Funcții numerice de interogare și manipulare

8.7.1 Modele pentru date întregi și reale

Funcțiile de interogare numerică și de manipulare sunt definite în termeni de un set model de numere
întregi și un set de model de valori reale pentru fiecare tip de întreg și tip de date reale implementate.
Pentru fiecare tip de număr întreg, este mulțimea

q
i = s× k 1
saptamana ×r

k=1
Machine Translated by Google

Proceduri intrinseci 169

unde s este ±1, q este un întreg pozitiv, r este un întreg care depășește 1 (de obicei 2) și fiecare wk este un
întreg în intervalul 0 wk < r. Pentru fiecare tip de real, acesta este setul

x=0

și
p
x = s×fi × fk ×b k
k=1

unde s este ±1, p și b sunt numere întregi care depășesc 1, e este un număr întreg într-un interval emin e emax și fiecare
fk este un număr întreg în intervalul 0 fk < b, cu excepția faptului că f1 este, de asemenea, diferit de zero.
Valorile parametrilor din aceste modele sunt alese pentru procesor astfel încât să se potrivească cel mai
bine cu hardware-ul cu condiția ca toate numerele de model să fie reprezentabile. Rețineți că este destul de
probabil să existe unele numere de mașină care se află în afara modelului. De exemplu, multe computere
reprezintă întregul -rq, iar standardul IEEE pentru aritmetica binară în virgulă mobilă (IEEE 754-1985 sau IEC
60559: 1989) conține reali cu f1 = 0 (numite numere denormalizate) și numere de registru cu precizie și gamă
crescute. .
În primul paragraf al secțiunii 2.6, am observat că valoarea unui zero cu semn este considerată aceeași cu
cea a unui zero fără semn. Cu toate acestea, multe procesoare disting la nivel hardware între o valoare zero
reală negativă și o valoare zero reală pozitivă, iar standardul IEEE folosește acest lucru acolo unde este posibil.
De exemplu, atunci când rezultatul exact al unei operații este diferit de zero, dar rotunjirea produce un zero,
semnul este păstrat.
În Fortran, cele două zerouri sunt tratate identic în toate operațiile relaționale, ca argumente de intrare
pentru toate funcțiile intrinseci (cu excepția semnului), sau ca expresie scalară în instrucțiunea aritmetică if
(Anexa C.1.7). Totuși, semnul funcției (Secțiunea 8.3.2) este de așa natură încât semnul celui de-al doilea
argument poate fi luat în considerare chiar dacă valoarea acestuia este zero. Pe un procesor care are aritmetică
IEEE, valoarea semnului (2,0, -0,0) este -2,0. De asemenea, un procesor Fortran este necesar pentru a
reprezenta toate numerele negative la ieșire, inclusiv zero, cu semnul minus.

8.7.2 Funcții de interogare numerică

Există nouă funcții de interogare care returnează valori din modelele asociate cu argumentele lor. Fiecare are
un singur argument care poate fi evaluat scalar sau matrice și fiecare returnează un rezultat scalar. Valoarea
argumentului nu trebuie definită.

cifre (x), pentru x real sau întreg, returnează întregul implicit a cărui valoare este numărul de
cifre semnificative în modelul care include x, adică p sau q.

epsilon (x), pentru x real, returnează un rezultat real cu același parametru de tip ca x care este aproape
neglijabil în comparație cu valoarea unu din modelul care include x, adică b1 p.

huge (x), pentru x real sau întreg, returnează cea mai mare valoare din modelul care include x. Are
parametrul tip și tip al lui x. Valoarea este

(1 b p)bemax

sau

rq 1
Machine Translated by Google

170 Fortran modern explicat

maxexponent (x), pentru x real, returnează întregul implicit emax, exponentul maxim în
modelul care include x.

minexponent (x), pentru x real, returnează întregul implicit emin, exponentul minim în
modelul care include x.

precizia (x), pentru x real sau complex, returnează un întreg implicit care conține precizia zecimală
echivalentă în model reprezentând numere reale cu aceeași valoare a parametrului de tip ca x.
Valoarea este
int((p 1) log10(b)) +k

unde k este 1 dacă b este o putere integrală de 10 și 0 în caz contrar.

radix (x), pentru x real sau întreg, returnează întregul implicit care este baza în modelul care include x,
adică b sau r.

intervalul (x), pentru x întreg, real sau complex, returnează un număr întreg implicit care conține intervalul
de exponent zecimal echivalent în modelele care reprezintă numere întregi sau reale cu aceeași
valoare a parametrului de tip ca x. Valoarea este int(log10(huge)) pentru numerele întregi și

int(min(log10(uriaș), -log10(mic)))

în realitate, unde uriașe și mici sunt cele mai mari și mai mici numere pozitive din modele.

tiny (x), pentru x real, returnează cel mai mic număr pozitiv

bemin 1

în modelul care include x. Are parametrul de tip și tip x.

8.7.3 Funcții elementare pentru manipularea realelor

Există șapte funcții elementare al căror prim sau singur argument este de tip real și care returnează valori
legate de componentele valorilor modelului asociate cu valoarea reală a argumentului. Pentru funcțiile
exponent, fracție și set_exponent, dacă valoarea lui x se află în afara intervalului de numere de model,
valoarea sa e este determinată ca și cum modelul nu ar avea limite de exponent.

exponentul (x) returnează întregul implicit a cărui valoare este partea de exponent e a lui x atunci când
este reprezentat ca număr de model. Dacă x=0, rezultatul are valoarea zero.

fracția (x) returnează un real cu același parametru de tip ca x a cărui valoare este partea fracționară a lui x
atunci când este reprezentat ca număr de model, adică x b e.

cel mai apropiat (x, s) returnează un real cu același parametru de tip ca x a cărui valoare este cel mai
apropiat număr diferit de mașină în direcția dată de semnul s real. Valoarea lui s nu trebuie să fie
zero.
Machine Translated by Google

Proceduri intrinseci 171

rrspacing (x) returnează un real cu același parametru de tip ca x a cărui valoare este reciproca distanței
relative a numerelor de model lângă x. Dacă valoarea lui x este un număr de model, acesta este |
xb e|bp.

scara (x, i) returnează un real cu același parametru de tip ca x, a cărui valoare este x bi, unde b este baza
în model pentru x și i este de tip întreg.

set_exponent (x, i) returnează un real cu același parametru de tip ca x, a cărui parte fracțională este
partea fracțională a reprezentării modelului lui x și a cărui parte exponent este i, adică x bi e.

spacing (x) returnează un real cu același parametru de tip ca x a cărui valoare este spațierea absolută a
numerelor de model lângă x.

8.7.4 Funcții de transformare pentru valorile tip

Există două funcții care returnează valoarea parametrului de tip cel mai mic tip care va îndeplini o
anumită cerință numerică. Au argumente scalare și rezultate, deci sunt clasificate ca transformaționale.

selected_int_kind (r) returnează scalarul întreg implicit, care este valoarea parametrului de tip tip pentru
un tip de date întreg capabil să reprezinte toate valorile întregi n în intervalul 10r < n < 10r unde
, exponent
r este un întreg scalar. Dacă sunt disponibile mai
zecimal (și multe,
cea se alege
mai mică un fel
valoare decu
tipcel maimai
dacă micmulte
interval
au de
cel
mai mic interval de exponent zecimal). Dacă nu este disponibil niciun fel corespunzător, rezultatul
este 1.

selected_real_kind ([p] [, r]) returnează scalarul întreg implicit, care este valoarea parametrului tip tip
pentru un tip de date real cu precizie zecimală (așa cum este returnată de precizia funcției) cel
puțin p și intervalul exponentului zecimal (așa cum este returnat de intervalul de funcții) cel puțin
r. Dacă sunt disponibile mai multe, se alege un fel cu cea mai mică precizie zecimală (și cea mai
mică valoare de tip dacă mai multe au cea mai mică precizie zecimală). Atât p cât și r sunt numere
întregi scalare; cel puțin unul dintre ei trebuie să fie prezent. Dacă nu este disponibilă nicio valoare
de tip corespunzătoare, rezultatul este 1 dacă nu este disponibilă o precizie suficientă, 2 dacă
nu este disponibil un interval suficient de exponent și 3 dacă ambele sunt indisponibile.

8.8 Proceduri de manipulare a biților

Există unsprezece proceduri pentru manipularea biților ținuți în numere întregi. Ele se bazează pe cele
din Standardul Militar SUA MIL-STD 1753. Diferă doar prin faptul că aici sunt elementare, acolo unde este
cazul, în timp ce procedurile originale acceptau doar argumente scalare.

Aceste intrinseci se bazează pe un model în care un număr întreg deține s biți wk, k = 0,1,...,s 1, într-
o secvență de la dreapta la stânga, bazată pe valoarea nenegativă

s 1

saptamana
×2k
k=0
Machine Translated by Google

172 Fortran modern explicat

Acest model este valabil doar în contextul acestor intrinseci. Este identic cu modelul pentru
numere întregi din Secțiunea 8.7.1 când r = 2 și ws 1 = 0, dar când r = 2 sau ws 1 = 1
modelele nu corespund, iar valoarea exprimată ca număr întreg poate varia de la procesor la procesor.

8.8.1 Funcția de interogare

bit_size (i) returnează numărul de biți din model pentru biți dintr-un număr întreg de același tip
de parametru ca i. Rezultatul este un număr întreg scalar având același parametru de tip
ca i.

8.8.2 Funcții elementare

btest (i, pos) returnează valoarea logică implicită adevărată dacă bitul pos al întregului i are
valoarea 1 și fals în caz contrar. pos trebuie să fie un număr întreg cu o valoare în intervalul
0 pos < bit_size(i).

i și (i, j) returnează logica și a tuturor biților din i și biții corespunzători din j,


conform tabelului de adevăr

eu 1100
j 1010 i și (i, j) 1000

Argumentele i și j trebuie să aibă aceeași valoare a parametrului de tip, care este valoarea
parametrului de tip a rezultatului.

ibclr (i, pos) returnează un număr întreg, cu același parametru de tip ca i și o valoare egală cu cea
a lui i, cu excepția faptului că bitul pos este șters la 0. Argumentul pos trebuie să fie un
număr întreg cu o valoare în intervalul 0 pos < bit_size(i).

ibits (i, pos, len) returnează un număr întreg, cu același parametru de tip ca i și o valoare
egală cu biții len ai lui i începând cu bitul poz dreapta ajustat și toți ceilalți biți zero.
Argumentele pos și len trebuie să fie numere întregi cu valori nenegative astfel
încât pos+len bit_size(i).

ibset (i, pos) returnează un număr întreg, cu același parametru de tip ca i și o valoare egală cu cea a
lui i, cu excepția faptului că bitul pos este setat la 1. Argumentul pos trebuie să fie un număr
întreg cu o valoare în intervalul 0 pos < bit_size(i).

ieor (i, j) returnează exclusivul logic sau al tuturor biților din i și biții corespunzători din
j, conform tabelului de adevăr

i 1100
1010
j ieor(i, j) 0110
Machine Translated by Google

Proceduri intrinseci 173

Argumentele i și j trebuie să aibă aceeași valoare a parametrului de tip, care este valoarea
parametrului de tip a rezultatului.

ior (i, j) returnează inclusiv logica sau a tuturor biților din i și biții corespunzători din
j, conform tabelului de adevăr

i 1100
1010
j ior(i, j) 1110

Argumentele i și j trebuie să aibă aceeași valoare a parametrului de tip, care este valoarea
parametrului de tip a rezultatului.

ishft (i, shift) returnează un număr întreg, cu același parametru de tip ca i și o valoare egală cu
cea a lui i, cu excepția faptului că biții sunt deplasați shift places la stânga (-shift places la
dreapta dacă shift este negativ). Zerourile sunt mutate de la celălalt capăt. Argumentul
deplasare trebuie să fie un întreg cu valoare care satisface inegalitatea |shift| bit_size(i).

ishftc (i, shift [, size] ) returnează un număr întreg, cu același parametru de tip ca i și o valoare egală cu
cea a lui i, cu excepția faptului că dimensiunea biților din dreapta (sau toți biții dacă dimensiunea
este absentă) sunt deplasați circular. la stânga (-deplasarea se plasează la dreapta dacă
schimbarea este negativă). Argumentul shift trebuie să fie un număr întreg cu valoare absolută
care nu depășește valoarea size (sau bit_size(i) dacă dimensiunea este absentă).

not (i) returnează complementul logic al tuturor biților din i, conform tabelului de adevăr

i 0 1
nu(i) 1 0

8.8.3 Subrutină elementară

call mvbits (from, frompos, len, to, topos) copiază secvența de biți din care începe la
poziția frompos și are lungimea len to to, începând cu poziția topos. Celelalte
biți din to nu sunt modificate. Argumentele de la, frompos, len și topos sunt
toate numere întregi cu intent în și trebuie să aibă valori care să satisfacă
inegalitățile: frompos+len bit_size(from), len 0, frompos 0, topos+len
bit_size( to), iar topos 0. Argumentul to este un întreg cu intent inout;
trebuie să aibă același tip de parametru ca de la. Aceeași variabilă poate fi
specificată pentru de la și până.

8.9 Funcția de transfer

Funcția de transfer permite ca datele de un tip să fie transferate la altul fără ca reprezentarea
fizică să fie modificată. Acest lucru ar fi util, de exemplu, în scrierea unui sistem generic de
stocare și recuperare a datelor. Sistemul în sine ar putea fi scris pentru un tip, să spunem un
întreg implicit și alte tipuri gestionate de transferuri către și de la acel tip, de exemplu:
Machine Translated by Google

174 Fortran modern explicat

caracter :: magazin

întreg(len=4) :: cuvânt ! Pentru a fi stocat și recuperat


:
magazin = transfer (cuvânt, depozit) ! Înainte de depozitare
:
cuvânt = transfer (magazin, cuvânt) ! După recuperare
:

transfer (sursă, matriță [,dimensiune] ) returnează un rezultat al parametrilor tip și tip cei ai matriței. Când dimensiunea
este absentă, rezultatul este scalar dacă mucegaiul este scalar și este de rangul unu și dimensiunea suficientă
pentru a păstra toată sursa dacă mucegaiul este evaluat în matrice. Când dimensiunea este prezentă,
rezultatul este de rangul unu și dimensiunea mărimii. Dacă reprezentarea fizică a rezultatului este la fel de
lungă sau mai lungă decât cea a sursei, rezultatul conține sursa ca parte principală, iar valoarea restului este
dependentă de procesor; în caz contrar, rezultatul este partea principală a sursei. Deoarece rangul rezultatului
poate depinde de dacă dimensiunea este specificată sau nu, argumentul real corespunzător nu trebuie să fie
el însuși un argument fals opțional.

8.10 Funcții de multiplicare vectorială și matriceală

Există două funcții transformaționale care efectuează înmulțiri vectoriale și matrice.


Fiecare are două argumente care sunt ambele de tip numeric (întreg, real sau complex) sau ambele de tip logic.
Rezultatul este de același tip și tip de parametru ca pentru operația de multiplicare sau și între doi astfel de scalari.
Funcțiile sum și orice, utilizate în definiții, sunt definite în Secțiunea 8.11.1.

dot_product (vector_a, vector_b) necesită două argumente fiecare de rang unu și de aceeași dimensiune.
Dacă vector_a este de tip întreg sau de tip real, se întoarce sum(vector_a *
vector_b); dacă vector_a este de tip complex, returnează sum(conjg(vector_a) * vector_b); iar dacă vector_a
este de tip logic, returnează orice(vector_a .și. vector_b).

matmul (matrice_a, matrice_b) efectuează înmulțirea matricei. Pentru argumentele numerice, sunt posibile trei cazuri:

i) matricea_a are forma (n,m) iar matricea_b are forma (m,k). Rezultatul are forma (n, k) iar elementul (i, j)
are valoarea sum(matrice_a(i, :) * matrice_b(:, j)).

ii) matricea_a are forma (m) iar matricea_b are forma (m, k). Rezultatul are forma (k)
iar elementul (j) are valoarea
sum(matrice_a * matrice_b(:, j)). iii) matricea_a

are forma (n, m) iar matricea_b are forma (m). Rezultatul are forma (n) iar elementul (i) are valoarea
sum(matrice_a(i, :) * matrice_b).

Pentru argumentele logice, formele sunt ca pentru argumentele numerice, iar valorile sunt determinate prin
înlocuirea „suma” și „*” în expresiile de mai sus cu „oricare” și „.și.”.
Machine Translated by Google

Proceduri intrinseci 175

8.11 Funcții transformaționale care reduc tablourile

Există șapte funcții de transformare care efectuează operații pe matrice, cum ar fi însumarea elementelor lor.

8.11.1 Caz cu un singur argument

În forma lor cea mai simplă, aceste funcții au un singur argument de matrice și returnează un rezultat scalar.
Toate, cu excepția numărului, au un rezultat de același tip și parametru de tip ca și argumentul. Masca matrice de
matrice, folosită ca argument în orice, all, count și opțional în altele, este descrisă și în Secțiunea 6.17.

all (mask) returnează valoarea true dacă toate elementele maschei matricei logice sunt adevărate sau masca
are dimensiunea zero și, în caz contrar, returnează valoarea false.

orice (mască) returnează valoarea adevărată dacă oricare dintre elementele măștii matricei logice este adevărată,
și returnează valoarea false dacă niciun element nu este adevărat sau dacă masca are dimensiunea zero.

count (mask)8 returnează valoarea întreagă implicită care este numărul de elemente ale
masca matrice logica care au valoarea true.

maxval (matrice) returnează valoarea maximă a unui element dintr-un tablou întreg sau real9.
Dacă matricea are dimensiunea zero, returnează valoarea negativă de cea mai mare magnitudine suportată
de procesor.

minval (matrice) returnează valoarea minimă a unui element dintr-un tablou întreg sau real9.
Dacă matricea are dimensiunea zero, returnează cea mai mare valoare pozitivă acceptată de procesor.

produs (matrice) returnează produsul elementelor unui tablou întreg, real sau complex. Returnează valoarea unu dacă
tabloul are dimensiunea zero.

sum (matrice) returnează suma elementelor unui tablou întreg, real sau complex. Aceasta
returnează valoarea zero dacă tabloul are dimensiunea zero.

8.11.2 Argument opțional dim

Toate aceste funcții au un al doilea argument opțional dim care este un întreg scalar. Dacă aceasta este prezentă,
operația este aplicată tuturor secțiunilor de rangul unu care se întind prin dimensiunea dim pentru a produce o matrice
de rang redus cu unul și extinderi egale cu întinderile din celelalte dimensiuni sau un scalar dacă rangul inițial este
unul. . De exemplu, dacă a este o matrice reală de formă (4,5,6), sum(a,dim=2) este o matrice reală de formă (4,6), iar
elementul (i, j) are valoarea sum(a (i,:,j)).

Deoarece rangul rezultatului depinde de dacă este specificat dim (cu excepția cazului în care originalul este rang
unu), argumentul real corespunzător nu trebuie să fie el însuși un argument fals opțional.

8În Fortran 2003, există un argument final opțional care trebuie să fie o expresie constantă scalară întreagă și
controlează tipul rezultatului.

9În Fortran 2003, tipul de caracter este, de asemenea, acceptat.


Machine Translated by Google

176 Fortran modern explicat

8.11.3 Mască de argument opțională

Funcțiile maxval, minval, product și sum au un al treilea argument opțional, o mască de matrice logică. Dacă
aceasta este prezentă, trebuie să aibă aceeași formă ca primul argument și operația se aplică elementelor
corespunzătoare elementelor adevărate de mască; de exemplu, sum(a, mask = a>0) însumează elementele
pozitive ale tabloului a. Masca de argument afectează doar valoarea funcției și nu afectează evaluarea
argumentelor care sunt expresii matrice. Masca de argument este permisă ca al doilea argument pozițional
atunci când dim este absent.

8.12 Funcții de interogare a matricei

Există cinci funcții pentru întrebări despre limitele, forma, dimensiunea și starea de alocare a unei matrice de
orice tip. Deoarece rezultatul depinde numai de proprietățile matricei, valoarea matricei nu trebuie definită.

8.12.1 Starea alocării

alocat (matrice) returnează, atunci când matricea de matrice alocabilă este în prezent alocată,
valoarea adevărată; în caz contrar, returnează valoarea false.

8.12.2 Limite, formă și dimensiune

Următoarele funcții se interesează despre limitele unui tablou. În cazul unui tablou alocabil, acesta trebuie să fie
alocat; iar în cazul unui pointer, acesta trebuie să fie asociat cu o țintă. O secțiune de matrice sau o expresie de
matrice este considerată ca având limite inferioare 1 și limite superioare egale cu extensiile (ca o matrice de
formă presupusă fără limite inferioare specificate). Dacă o dimensiune are dimensiunea zero, limita inferioară
este considerată 1, iar limita superioară este considerată 0.

lbound (matrice [,dim] )10 când dim este absent, returnează o matrice întregă implicită de rang unu care deține
limitele inferioare. Când dim este prezent, acesta trebuie să fie un întreg scalar și rezultatul este un întreg
scalar implicit care deține limita inferioară a dimensiunii dim. Deoarece rangul rezultatului depinde de
faptul dacă dim este specificat, argumentul real corespunzător nu trebuie să fie el însuși un argument fals
opțional.

shape (sursă)10 returnează o matrice întregă implicită de rang unu care deține forma matricei sau a sursei
scalare. În cazul unui scalar, rezultatul are dimensiunea zero.

size (matrice [,dim] )10 returnează un întreg scalar implicit care este dimensiunea matricei sau extinderea de-a
lungul dimensiunii dim dacă este prezent întregul scalar dim.

10
ubound (matrice [,dim] ) este similar cu lbound, cu excepția faptului că returnează limite superioare.

10În Fortran 2003, există un argument final opțional, care trebuie să fie o expresie constantă cu un întreg scalar
și controlează tipul rezultatului.
Machine Translated by Google

Proceduri intrinseci 177

8.13 Funcții de construcție și manipulare a tabloului

Există opt funcții care construiesc sau manipulează tablouri de orice tip.

8.13.1 Funcția elementară de îmbinare

merge (tsource, fsource, mask) este o funcție elementară. Argumentul tsource poate avea orice tip, iar
fsource trebuie să aibă aceiași parametri de tip și tip. Masca de argument trebuie să fie de tip
logic. Rezultatul este tsource dacă mask este adevărat și fsource în caz contrar.

Aplicația principală a merge este atunci când cele trei argumente sunt tablouri având aceeași formă,
caz în care tsource și fsource sunt îmbinate sub controlul mascai.
Rețineți, totuși, că tsource sau fsource pot fi scalare, caz în care regulile elementare le transmit efectiv la
o matrice de forma corectă.

8.13.2 Ambalarea și dezambalarea matricelor

Pachetul de funcții transformaționale împachetează într-o matrice de rang unu acele elemente ale unei
matrice care sunt selectate printr-o matrice logică de formă conformă, iar funcția de transformare
despachetă efectuează operația inversă. Elementele sunt luate în ordinea elementelor de matrice.

pack (array, mask [,vector] ), când vectorul este absent, returnează un tablou de rang unu care conține
elementele matricei corespunzătoare elementelor adevărate ale maschei în ordinea elementelor
matricei; masca poate fi scalară cu valoarea true, caz în care toate elementele sunt selectate. Dacă
vectorul este prezent, acesta trebuie să fie o matrice de rang unu de același tip și parametri de
tip ca și matrice și dimensiune cel puțin egală cu numărul t de elemente selectate; rezultatul are
dimensiunea egală cu dimensiunea n a vectorului; dacă t < n, elementele i ale rezultatului pentru
i > t sunt elementele corespunzătoare ale vectorului.

unpack (vector, mască, câmp) returnează o matrice a parametrilor de tip și tip de vector și forma măștii.
Masca de argument trebuie să fie o matrice logică, iar vectorul trebuie să fie o matrice de rangul
unu cu dimensiunea de cel puțin numărul de elemente adevărate ale măștii. câmpul trebuie să
fie de același tip și parametri de tip ca vector și trebuie fie să fie scalar, fie să aibă aceeași formă
ca masca. Elementul rezultat care corespunde celui de-al i-lea element adevărat al măștii, în
ordinea elementelor matrice, este al i-lea element al vectorului; toate celelalte sunt egale cu
elementele corespunzătoare de câmp dacă este o matrice sau cu câmp dacă este un scalar.

8.13.3 Reformarea unei matrice

Reformarea funcției de transformare permite modificarea formei unui tablou, cu posibila permutare a
subindicelor.

reshape (sursă, formă [,pad] [,order] ) returnează o matrice cu forma dată de forma matricei întregi de
rangul unu și parametrii de tip și tastați cei ai
Machine Translated by Google

178 Fortran modern explicat

sursa matricei. Mărimea formei trebuie să fie constantă, iar elementele sale nu trebuie să fie
negative. Dacă pad este prezent, trebuie să fie o matrice de același tip și parametri de tip ca
sursa. Dacă pad-ul este absent sau are dimensiunea zero, dimensiunea rezultatului nu trebuie
să depășească dimensiunea sursei. Dacă ordinea este absentă, elementele rezultatului, în
ordinea elementelor de matrice, sunt elementele sursei în ordinea elementelor de matrice,
urmate de copiile padului în ordinea elementelor de matrice. Dacă ordinea este prezentă,
trebuie să fie un tablou întreg de rang unu cu o valoare care este o permutare a lui (1,2,...,n);
elementele r(s1,...,sn) ale rezultatului, luate în indice sau der pentru tabloul având elementele
r(order(1),..., sorter(n)), sunt cele de sursă în element de matrice ordine urmată de copiile pad-
ului în ordinea elementelor de matrice. De exemplu, dacă ordinea are valoarea (3,1,2),
elementele r(1,1,1), r(1,1,2), ..., r(1,1,k), r (2,1,1), r(2,1,2), ... corespund elementelor sursei și pad în ordinea e

8.13.4 Funcția de transformare pentru replicare

spread (sursă, dim, ncopies) returnează o matrice de parametri de tip și tip cei de sursă și de rang
măriți cu unu. Sursa argumentului poate fi scalară sau cu valori matrice. Argumentele dim și
ncopies sunt scalari întregi. Rezultatul conține max(ncopii, 0) copii ale sursei, iar elementul
(r1,...,rn+1) al rezultatului este sursă(s1,...,sn) unde (s1,...,sn) este (r1,...,rn+1) cu indicele dim
omis (sau sursa însăși dacă este scalară).

8.13.5 Funcții de schimbare a matricei

cshift (matrice, shift [,dim] ) returnează o matrice de același tip, parametri de tip și formă ca matrice.
Argumentul shift este de tipul întreg și trebuie să fie scalar dacă tabloul este de rang unu. Dacă
deplasarea este scalară, rezultatul este obținut prin deplasarea fiecărei secțiuni de rang unu
care se extinde de-a lungul timpilor de deplasare circular dim dim. Argumentul dim este un
scalar întreg și, dacă este omis, este ca și cum ar fi prezent cu valoarea 1. Direcția deplasării
depinde de semnul deplasării, fiind la stânga pentru o valoare pozitivă și la corect pentru o
valoare negativă. Astfel, pentru cazul cu shift=1 și matrice de rang unu și dimensiune m,
elementul i al rezultatului este array(i+1), unde i = 1,2,...,m 1 și elementul m este matrice (1).
Dacă shift este o matrice, trebuie să aibă aceeași formă ca cea a matricei cu dimensiunea omisă
și furnizează o valoare separată pentru fiecare schimbare. De exemplu, dacă matricea este de
rangul trei și forma (k,l,m) și dim are valoarea 2, shift trebuie să aibă forma (k,m) și furnizează o
schimbare pentru fiecare dintre secțiunile de rang unu k×m în a doua dimensiune a matricei.

eoshift (array, shift [,boundary] [,dim] ) este identic cu cshift, cu excepția faptului că se efectuează o
schimbare finală și valorile limită sunt inserate în golurile astfel create. Limita argumentului
poate fi omisă atunci când tabloul are tip intrinsec, caz în care valoarea zero este inserată
pentru cazurile întreg, real și complex; fals în cazul logic; și spații libere în cazul caracterelor.
Dacă limită este prezentă, trebuie să aibă aceiași parametri de tip și tip ca și matrice; poate fi
scalar și poate furniza toate valorile necesare sau poate fi o matrice a cărei formă este cea a
matricei cu dimensiunea omisă și poate furniza o valoare separată pentru fiecare schimbare.
Machine Translated by Google

Proceduri intrinseci 179

8.13.6 Transpunerea matricei

Funcția de transpunere realizează o transpunere matriceală pentru orice matrice de rangul doi.

transpose (matrice) returnează o matrice de același tip și parametri de tip ca și matricea de rang doi. Elementul
(i, j) al rezultatului este matricea (j, i).

8.14 Funcții de transformare pentru locație geometrică


Există două funcții de transformare care găsesc locațiile valorilor maxime și minime ale unui număr întreg sau
al unui tablou real.

cu maxloc (matrice [ , rangul matricei.


mask ] )11
Valoarea
returnează
sa este
o matrice
succesiunea
întregă
de implicită
indice alede
unui
rang
element
1 de dimensiune
de valoareegală
maximă (dintre cele corespunzătoare valorilor adevărate ale logicii conforme). masca de matrice dacă
este prezentă), ca și cum toate limitele inferioare declarate ale matricei ar fi 1. Dacă există mai mult de
un astfel de element, este luat primul în ordinea elementelor de matrice. Dacă nu există niciunul,
rezultatul este dependent de procesor.12

maxloc (matrice, dim [ , cea a matricei cumask


dimensiunea
] )11 returnează
dim omise,
o matrice
unde întregă
dim esteimplicită
un întregdescalar
formăcu
egală
valoare
cu în
intervalul 1 dim rang(matrice) sau un scalar dacă rangul inițial este unul. Valoarea fiecărui element
al rezultatului este poziția primului element de valoare maximă în secțiunea corespunzătoare de rang-un
care se întinde dimensiunea dim, printre acele elemente care corespund valorilor adevărate ale măștii
matricei logice conforme. când este prezent.

Dacă nu există, rezultatul este dependent de procesor.12

mask
că se obține poziția unui element de ]valoare
)11 esteminimă.
identic cu maxloc (array [ , minloc (array [ , masca ] ) cu exceptia

minloc (array, dim [, mask ] )11 este identic cu maxloc (array, dim [,mask]) cu excepția faptului că se obțin poziții
ale elementelor de valoare minimă.

8.15 Funcția de transformare pentru disocierea pointerului

Funcția null este disponibilă pentru a da starea dezasociată entităților pointer.

null ( [ mold ] ) returnează un pointer disociat. Argumentul matriță este un indicator de orice tip și poate
avea orice statut de asociere, inclusiv nedefinit. Tipul, parametrul tip și rangul rezultatului sunt
cele ale mucegaiului dacă este prezent și, în caz contrar, sunt cele ale obiectului cu care este
asociat. Într-un argument real asociat cu un argument inactiv de lungime presupusă a caracterului,
mucegaiul trebuie să fie prezent.

11În Fortran 2003, matricea poate fi de tipul caracter. Există, de asemenea, un argument final opțional care trebuie să fie a
expresie constantă a numărului întreg scalar și controlează tipul rezultatului.
12În Fortran 2003, rezultatul are toate elementele zero.
Machine Translated by Google

180 Fortran modern explicat

8.16 Subrutine intrinseci non-elementale


Există, de asemenea, în Fortran subrutine intrinseci non-elementale, care au fost alese să fie mai degrabă
subrutine decât funcții din cauza necesității de a returna informații prin argumente.

8.16.1 Ceas în timp real

Există două subrutine care returnează informații de la ceasul în timp real, prima bazată pe standardul
ISO IS 8601 (Reprezentarea datelor și orelor). Se presupune că există un ceas de sistem de bază care
este incrementat cu unul pentru fiecare număr de ceas până când este atins un maxim count_max și la
următoarea contorizare este setat la zero. Valorile implicite sunt returnate pe sistemele fără ceas. Toate
argumentele au intenție.

call date_and_time ( [ data ] [ , time ] [ , zone ] [ , values ] ) returnează următoarele (cu valorile implicite
goale sau -huge(0), după caz, când nu există ceas).

data este o variabilă cu caracter scalar cu lungimea de 8 sau mai mult. Primele sale 8 caractere sunt setate la
13
secolul, anul, luna și ziua sub forma ccyymmdd.

timpul este o variabilă cu caracter scalar cu lungimea de 10 sau mai mult. Primele 10 caractere
ale sale sunt setate la ora ca ore, minute, secunde și milisecunde sub forma 13 hhmmss.sss.

zone este o variabilă cu caracter scalar de lungime 5 sau mai mare. Primele 5 caractere ale sale
sunt setate la diferența dintre ora locală și UTC (cunoscută și sub numele de Greenwich
Mean Time) sub forma Shhmm, corespunzătoare semnului, orei și minutelor. De exemplu,
13
un procesor din New York în timpul iernii ar returna valoarea -0500.

values este o matrice întregă implicită de rang unu cu dimensiunea de cel puțin 8 care conține
secvența de valori: anul, luna anului, ziua lunii, diferența de timp în minute față de UTC, ora
zilei , minutele orei, secundele minutei și milisecundele secundei.

apelul system_clock ( [ count ] [ , count_rate ] [ , count_max ] ) returnează următoarele.

count este un număr întreg scalar implicit14 care deține o valoare dependentă de procesor
bazată pe valoarea curentă a ceasului procesorului sau -huge(0) dacă nu există un ceas. La
primul apel, procesorul poate seta o valoare inițială care poate fi zero. count_rate este un

număr întreg scalar implicit14 care conține numărul de contorizări de ceas pe secundă sau zero
dacă nu există un ceas.

count_max este un întreg scalar implicit14 care deține valoarea maximă pe care o poate lua
contor sau zero dacă nu există un ceas.

13În Fortran 2003, variabila poate fi mai scurtă; în acest caz, caracterele cele mai din stânga ale valorii sunt atribuite
variabil. Dacă variabila este mai lungă decât valoarea, caracterele rămase sunt setate la gol.
14În Fortran 2003, orice fel de număr întreg.
Machine Translated by Google

Proceduri intrinseci 181

8.16.2 Timp CPU

Există o subrutină intrinsecă non-elementală care returnează timpul procesorului.

call cpu_time (timp) returnează următoarele:

timpul este un real scalar căruia i se atribuie o aproximare dependentă de procesor față de timpul
procesorului în secunde sau o valoare negativă dependentă de procesor dacă nu există un ceas.

Definiția exactă a timpului este lăsată imprecisă din cauza variabilității a ceea ce diferite procesoare sunt
capabile să ofere. Scopul principal este de a compara diferiți algoritmi pe același computer sau de a descoperi
care părți ale unui calcul de pe un computer sunt cele mai scumpe.
Ora de pornire este lăsată imprecisă deoarece scopul este de a cronometra secțiunile de cod, ca în exemplu

real :: t1, t2
:
apelați cpu_time(t1)
: ! Codul de cronometrat.
apelați cpu_time(t2)
scrieți (*,*) „Timpul luat de cod a fost „, t2-t1, „secunde”

8.16.3 Numere aleatorii

O secvență de numere pseudoaleatoare este generată dintr-o sămânță care este deținută ca o matrice de
numere întregi de rangul unu. Subrutina random_number returnează numerele pseudoaleatoare, iar
subrutina random_seed permite să se facă o întrebare despre dimensiunea sau valoarea matricei de semințe
și să fie resetat sămânța. Subrutinele oferă o interfață portabilă unei secvențe dependente de procesor.

call random_number (recolta) returnează un număr pseudoaleator din distribuția uniformă în intervalul 0
x < 1 sau o matrice de astfel de numere. harvest are intentie, poate fi un scalar sau o matrice și trebuie
să fie de tip real.

call random_seed ([size] [put] [get] ) are următoarele argumente.

size are intentie și este un întreg scalar implicit pe care procesorul îl setează la dimensiunea n a
matricei de semințe.

put are intent in si este o matrice intreaga implicita de rang unu si dimensiune n care este folosita de
procesor pentru a reseta seed-ul. Un procesor poate seta aceeași valoare de semințe pentru
mai mult de o valoare de put.

get are intentia si este o matrice intreaga implicita de rang unu si dimensiunea n pe care procesorul o
seteaza la valoarea curenta a seedului. Această valoare poate fi folosită mai târziu pentru a reda
secvența din acel punct sau într-o execuție ulterioară a programului pentru a continua din acel
punct.
Machine Translated by Google

182 Fortran modern explicat

Nu poate fi specificat mai mult de un argument; dacă nu este specificat niciun argument, seed este
setat la o valoare dependentă de procesor. Astfel, această valoare poate fi identică pentru fiecare apel
sau diferită.

8.17 Rezumat
În acest capitol, am introdus cele patru categorii de proceduri intrinseci, am explicat declarația intrinsecă și
am oferit descrieri detaliate ale tuturor procedurilor.
Machine Translated by Google

Proceduri intrinseci 183

Exerciții

1. Scrieți un program pentru a calcula rădăcinile reale sau perechile de rădăcini complexe-conjugate ale ecuației
pătratice ax2 + bx + c = 0 pentru orice valori reale ale a,b și c. Programul ar trebui să citească aceste trei valori
și să imprime rezultatele. Ar trebui să se folosească funcțiile intrinseci adecvate.

2. Repetați exercițiul 1 din capitolul 5, evitând utilizarea constructelor do.

3. Având în vedere regulile explicate în Secțiunile 3.12 și 8.2, care sunt valorile imprimate de următoarele
program?

program principal
real, țintă :: a(3:10) real, pointer ::
p1(:), p2(:) p1 => a(3:9:2) p2 => a(9:3:-2)
print *, asociat(p1, p2) print *, asociat(p1,
p2(4:1:-1)) final program principal

4. În programul următor, două atribuiri de pointer, una către o matrice și cealaltă către o secțiune de matrice, sunt
urmate de un apel de subrutină. Ținând cont de regulile prezentate în Secțiunile 3.12, 6.3 și 8.12.2, ce valori
imprimă programul?

program principal
real, țintă :: a(5:10) real, pointer ::
p1(:), p2(:) p1 => a p2 => a(:) print *,
lbound (a), lbound (a( :)) print *, lbound
(p1), lbound (p2) numiți ceea ce conține
(a, a(:))

subrutine what (x, y) real,


intent (in) :: x(:), y(:) print *, lbound (x),
lbound (y) end subroutine what

termina programul principal


Machine Translated by Google

Această pagină a fost lăsată goală în mod intenționat


Machine Translated by Google

9. Transfer de date

9.1 Introducere

Fortran are, în comparație cu multe alte limbaje de programare de nivel înalt, un set deosebit de bogat
de facilități pentru intrare/ieșire (I/O), dar este o zonă a Fortran în care nu toți programatorii trebuie să
aprofundeze foarte mult. Pentru majoritatea programelor la scară mică este suficient să știți să citiți
câteva înregistrări de date care conțin variabile de intrare și să transmiteți la un ecran sau imprimantă
rezultatele unui calcul. În procesarea datelor pe scară largă, pe de altă parte, programele trebuie adesea
să se ocupe de fluxuri uriașe de date către și dinspre multe fișiere; în aceste cazuri este esențial să se
acorde o mare atenție modului în care I/O este proiectat și codificat, deoarece în caz contrar atât timpul
de execuție, cât și timpul real petrecut în program pot avea de suferit dramatic. Termenul fișier este
folosit pentru o colecție de date în afara memoriei principale și un fișier este întotdeauna organizat într-
o secvență de înregistrări.
Acest capitol începe prin a discuta diferitele forme de I/O formatate, adică I/O care se ocupă de
înregistrări care nu folosesc reprezentarea numerică internă a computerului, ci mai degrabă un șir de
caractere care poate fi afișat. Este, de asemenea, forma necesară de obicei pentru transmiterea datelor
între diferite tipuri de computere. Apoi sunt explicați așa-numiții descriptori de editare, care sunt utilizați
pentru a controla traducerea dintre reprezentarea numerelor interne și formatul extern. În cele din
urmă, sunt acoperite subiectele I/O neformatate (sau binare) și fișierele cu acces direct.

9.2 Conversia numerelor

Modul în care numerele sunt stocate intern de către un computer nu sunt preocuparea nici a standardului
Fortran, nici a acestei cărți. Totuși, dacă dorim să scoatem valori – pentru a le afișa pe un ecran sau
pentru a le imprima – atunci reprezentările lor interne trebuie convertite într-un șir de caractere care
poate fi citit într-un mod normal. De exemplu, conținutul unui anumit cuvânt de calculator poate fi (în
hexazecimal) be1d7dbf și corespunde valorii -0,000450. Pentru scopul nostru special, este posibil să
dorim să afișam această cantitate ca -.000450 sau ca -4.5E-04 sau rotunjită la o cifră semnificativă ca
-5E-04. Conversia de la forma internă la forma externă se realizează conform informațiilor specificate de
un descriptor de editare conținut într-o specificație de format. Acestea vor fi ambele tratate pe deplin
mai târziu în acest capitol; pentru moment, este suficient să dam câteva exemple. De exemplu, pentru a
imprima o valoare întreagă într-un câmp cu lățimea de 10 caractere, am folosi descriptorul de editare
i10, unde i reprezintă conversia întregului, iar 10 specifică lățimea câmpului de ieșire. Pentru a imprima
o cantitate reală într-un
Machine Translated by Google

186 Fortran modern explicat

câmp de 10 caractere, dintre care 5 sunt rezervate pentru partea fracționară a numărului, precizăm
f10.5. Descriptorul de editare f reprezintă conversia în virgulă mobilă (reala), 10 este lățimea totală a
câmpului de ieșire și 5 este lățimea părții fracționale a câmpului. Dacă numărul dat mai sus ar fi convertit
în conformitate cu acest descriptor de editare, ar apărea ca bb-0,00045, unde b reprezintă un gol. Pentru
a imprima o variabilă caracter într-un câmp de 10 caractere, am specifica a10, unde a reprezintă conversia
alfanumerică.
O specificație de format constă dintr-o listă de descriptori de editare încadrați în paranteze și poate fi
codificată fie ca expresie de caracter implicită, de exemplu

„(i10, f10.3, a10)”

sau ca o instrucțiune de format separată, la care se face referire printr-o etichetă de instrucțiune, de exemplu

format 10 (i10, f10.3, a10)

Pentru a tipări variabilele scalare j, b și c, de tipuri întreg, real și, respectiv, caracter, putem scrie fie

tipăriți „(i10, f10.3, a10)”, j,b,c


sau

formatul tipărire 10,


j,b,c 10 (i10, f10.3, a10)

Prima formă este utilizată în mod normal atunci când într-o unitate de definire există doar o singură
referință la o specificație de format dată, iar a doua când există mai multe sau când formatul este
complicat. Partea din declarație care desemnează cantitățile de tipărit este cunoscută sub denumirea de
listă de ieșire și face obiectul următoarei secțiuni.

9.3 Liste I/O

Cantitățile care trebuie citite sau scrise de un program sunt specificate într-o listă de I/O. Pentru ieșire
pot fi expresii, dar pentru intrare trebuie să fie variabile. În ambele cazuri, articolele din listă pot fi liste
implicite de cantități. Exemple sunt prezentate în Figura 9.1, unde observăm utilizarea unui număr de
repetări în fața acelor descriptori de editare care sunt solicitați în mod repetat. Un număr de repetări
trebuie să fie o constantă literală întreg pozitiv și nu trebuie să aibă un parametru de tip tip. Referințele
de funcții sunt permise într-o listă de I/O, cu condiția ca ele însele să nu provoace apariția altor I/E.1 În
toate aceste exemple, cu excepția ultimului, expresiile constau din variabile individuale și

ar fi la fel de valide în instrucțiunile de intrare folosind instrucțiunea read, de exemplu

citiți „(i10)”, i

Astfel de instrucțiuni pot fi folosite pentru a citi valori care sunt apoi alocate variabilelor din lista de
intrare.
Dacă o matrice apare ca element, este tratată ca și cum elementele ar fi fost specificate în ordinea
elementelor de matrice. De exemplu, a treia dintre declarațiile de tipărire din Figura 9.1 ar fi putut fi scrisă

1Această restricție a fost ridicată în Fortran 2003 pentru fișierele interne și în Fortran 2008 pentru fișierele externe, vezi
Secțiunile 17.7 și 20.7.1.
Machine Translated by Google

Transfer de date 187

Figura 9.1 Exemple de ieșire formatată. întreg :: i


real, dimensiune(10) :: a caracter(len=20)
cuvânt ::
print '(i10)', i print '(10f10.3)', a print
'(3f10.3)', a (1),a(2),a(3) print '(a10)',
word(5:14) print '(5f10.3)', (a(i),print
i=1,9,2)
„(2f10.3)”, a(1)*a(2)+i, sqrt(a(3))

tipăriți „(3f10.3)”, a(1:3)

Cu toate acestea, niciun element al matricei nu poate apărea de mai multe ori într-un element de intrare. Astfel,
cazul din Figura 9.2 nu este permis.

Figura 9.2 Un element de intrare ilegal (elementul matricei apare de două


ori). întreg :: j(10), k(3)
:
k = (/ 1, 2, 1 /) se citește
„(3i10)”, j(k) ! Ilegal deoarece j(1) apare de două ori

Dacă o matrice alocabilă apare ca element, aceasta trebuie să fie alocată în prezent.
Orice pointer dintr-o listă I/O trebuie să fie asociat cu o țintă, iar transferul are loc între
fișierul și țintele.
Un articol de tip derivat fără componente alocabile sau pointer la orice nivel de selecție a componentelor este
tratat ca și cum componentele ar fi fost specificate în aceeași ordine ca și în declarația de tip. Această regulă se
aplică în mod repetat pentru componentele de tip derivat, astfel încât este ca și cum am precizat lista articolelor
de tip intrinsec care constituie componentele sale ultime.
De exemplu, dacă p și t sunt de tipul punct și triunghi din figura 2.1, afirmația

citiți „(8f10.5)”, p, t

are același efect ca declarația

citiți „(8f10.5)”, p%x, p%y, t%a%x, t%a%y, t%b%x, t%b%y, t%c%x, t%c %y &

Fiecare componentă finală trebuie să fie accesibilă (s-ar putea să nu fie, de exemplu, o componentă privată de tip
public).
Este convenabil să se extindă termenul de componentă finală pentru a include cazul care se termină cu o
componentă de tip derivat care este alocabilă sau este un pointer. De exemplu, în Figura 9.3 componentele finale
ale tipului doi sunt obișnuite%comp, alloc și point, în timp ce alloc%comp și point%comp nu sunt. Obiectul părinte
(obj în exemplu) poate fi alocabil sau un pointer.
Machine Translated by Google

188 Fortran modern explicat

Figura 9.3 Tipuri imbricate.


type one integer ::
comp end tip unu tip
doi :: tip obișnuit(unul)
tip(unul), allocabil :: alloc(:)
tip doi tip(două) :: obj tip(unul), pointer :: punct final

Un obiect dintr-o listă I/O nu este permis să fie de tip derivat care are o componentă alocabilă sau pointer la
orice nivel de selecție a componentei. Un motiv pentru această restricție este din cauza problemelor asociate cu
structurile de date recursive. De exemplu, presupunând că lanțul este un obiect de date de tipul intrării din figura
2.3 (în secțiunea 2.13), ar putea deține un lanț de lungime trei, chain%index, chain%next%index,
chain%next%next%index cu lanț %next%next%next un pointer disociat. Un alt motiv este că Fortran 2003 permite
definirea descriptorilor de editare pentru structurile de date (vezi Secțiunea 17.2). Programatorii pot scrie
proceduri care sunt apelate ca parte a procesării I/O. O astfel de procedură este mult mai capabilă să gestioneze
structurile a căror dimensiune și compoziție variază dinamic, cazul obișnuit pentru componentele alocabile sau
pointer.

O listă de I/O poate include o listă de activități implicite, așa cum este ilustrat de a cincea instrucțiune de
tipărire din Figura 9.1. Forma generală este

(do-obiect-list, do-var = expr, expr [, expr])

unde fiecare obiect-do este o variabilă (pentru intrare), o expresie (pentru ieșire) sau este el însuși o listă de
activități implicite; do-var este o variabilă întreagă scalară numită și fiecare expr este o expresie întregă scalară.
Inițializarea și execuția buclei sunt aceleași ca pentru un set (posibil imbricat) de constructe do (Secțiunea 4.4).
Într-o listă de intrare, o variabilă care este un element dintr-o listă de obiecte-do nu trebuie să fie un do-var al
oricărei liste de lucru implicite în care este conținută și nici să fie asociată2 cu o astfel de do-var. Într-o listă de
intrare sau de ieșire, niciun do-var nu poate fi un do-var al oricărei liste de do-do implicite în care este conținut
sau să fie asociat cu o astfel de do-var.

Rețineți că o matrice de dimensiune zero sau o listă de activități implicite cu un număr de iterații zero poate apărea ca
un articol dintr-o listă I/O. Un astfel de articol nu corespunde unui transfer efectiv de date.

9.4 Definirea formatului

În declarațiile de tipărire și citire din secțiunea anterioară, specificația formatului a fost dată de fiecare dată sub
forma unei constante de caractere imediat după cuvântul cheie. De fapt, există trei moduri în care poate fi dată o
specificație de format. Ele sunt după cum urmează.

2O astfel de asociere ilegală ar putea fi stabilită prin asociere pointer.


Machine Translated by Google

Transfer de date 189

O expresie de caracter implicită a cărei valoare începe cu o specificație de format în


paranteze:

tipăriți „(f10.3)”, q

sau

caracter(len=*), parametru :: form='(f10.3)'


:
formular de tipărire, q

sau

caracter :: carray(7)=(/ '(','f','1','0','.','3',')' /)


:
print carray, q ! Elemente ale unei expresii matrice
! sunt concatenate.

sau

caracterul(4) :: carr1(10)
caracterul(3) :: carr2(10) întreg :: i, j

:
carr1(10) = '(f10' carr2(3)
= '.3)'
:
i = 10
j=3
:
tipăriți carr1(i)//carr2(j), q

Din aceste exemple se poate observa că este posibil să se programeze formate într-un mod flexibil
și, în special, că este posibil să se utilizeze matrice, expresii și, de asemenea, subșiruri într-un mod
care să permită ca un anumit format să fie construit dinamic în timpul execuției. din diverse
componente. Orice date de caractere care ar putea urma paranteza din dreapta sunt ignorate și
pot fi nedefinite. În cazul unei matrice, elementele sale sunt concatenate în ordinea elementelor
de matrice. Cu toate acestea, la intrare nicio componentă a specificației de format nu poate
apărea și în lista de intrare sau nu poate fi asociată cu aceasta. Acest lucru se datorează faptului
că standardul cere ca întreaga specificație a formatului să fie stabilită înainte ca orice I/O să aibă loc.
În plus, nu este permisă nicio redefinire sau nedefinire a oricărui caracter al formatului în timpul
execuției instrucțiunii I/O.

Un asterisc Acesta este un tip de I/O cunoscut sub numele de list-directed I/O, în care formatul este
definit de sistemul informatic în momentul executării instrucțiunii, în funcție atât de tipul, cât și
de magnitudinea entităților implicate. Această facilitate este deosebit de utilă pentru introducerea
și ieșirea unor cantități mici de valori, în special în codul temporar care este utilizat în scopuri de
testare și care este eliminat din versiunea finală a programului:
Machine Translated by Google

190 Fortran modern explicat

print *, 'Rădăcină pătrată a lui q = ', sqrt(q)

Acest exemplu produce o constantă de caractere care descrie expresia care urmează să fie scoasă,
urmată de valoarea expresiei investigate. Pe ecran, aceasta poate apărea ca

Rădăcină pătrată a lui q = 4,392246

formatul exact fiind dependent de sistemul informatic utilizat. Șirurile de caractere din această
formă de ieșire sunt în mod normal nelimitate, ca și cum ar fi folosit un descriptor de editare, dar
o opțiune din declarația deschisă (Secțiunea 10.3) poate fi folosită pentru a solicita ca acestea să fie
delimitate prin apostrofe sau ghilimele. Constantele complexe sunt reprezentate ca două valori
reale separate prin virgulă și cuprinse între paranteze. Variabilele logice sunt reprezentate ca T
pentru adevărat și F pentru fals. Cu excepția șirurilor adiacente nelimitate, valorile sunt separate
prin spații sau virgule. Procesorul poate reprezenta o succesiune de r valori identice c sub forma
r*c. Detalii suplimentare despre intrarea/ieșirea direcționată pe listă sunt amânate până la
Secțiunea 9.9.

O etichetă de instrucțiune care se referă la o instrucțiune de format care conține specificația relevantă
între paranteze:

tipăriți 100, q
:
format 100 (f10.3)

Declarația de format trebuie să apară în aceeași unitate de definire a domeniului, înaintea


instrucțiunii contains dacă are una. Se obișnuiește fie să se plaseze fiecare instrucțiune de format
imediat după prima instrucțiune care face referire la aceasta, fie să le grupeze pe toate chiar
înainte de instrucțiunea contains sau end. De asemenea, este obișnuit să existe o secvență separată
de numere pentru etichetele instrucțiunilor utilizate pentru instrucțiunile de format. O anumită
instrucțiune de format poate fi utilizată de orice număr de instrucțiuni I/O formatate, fie pentru
intrare, fie pentru ieșire.

Caracterele goale pot preceda paranteza din stânga a unei specificații de format și pot apărea în orice
moment în cadrul unei specificații de format fără niciun efect asupra interpretării, cu excepția unui
descriptor de editare a unui șir de caractere (Secțiunea 9.12.3).

9.5 Numerele unităților

Operațiile de intrare/ieșire sunt folosite pentru a transfera date între variabilele unui program în execuție,
așa cum sunt stocate în computer, și un mediu extern. Există multe tipuri de suporturi externe: ecranul,
imprimanta, hard discul, stick-ul de memorie și CD-ul sunt probabil cele mai familiare.
Indiferent de dispozitiv, un program Fortran îl privește pe fiecare din care citește sau în care scrie ca o
unitate, iar fiecare unitate, cu două excepții, are asociat un număr de unitate.
Acest număr nu trebuie să fie negativ. Astfel, ne-am putea asocia cu un CD de pe care citim unitatea cu
numărul 10 și cu un hard disc pe care scriem unitatea cu numărul 11. Toate
Machine Translated by Google

Transfer de date 191

unitățile de program ale unui program executabil care se referă la un anumit număr de unitate fac referire la
același fișier. Multe dispozitive, cum ar fi un hard disc, pot fi menționate prin mai multe numere de unitate,
deoarece pot conține multe fișiere diferite.
Există două instrucțiuni I/O, print și o variantă de citire, care nu fac referire la niciun număr de unitate;
acestea sunt afirmațiile pe care le-am folosit până acum în exemple, de dragul simplității. O instrucțiune de
citire fără un număr de unitate se așteaptă, în mod normal, să fie citită de la tastatură, cu excepția cazului în
care programul funcționează în modul lot (non-interactiv), caz în care va exista un fișier disc cu un nume
rezervat din care citește. În mod normal, o instrucțiune de tipărire se așteaptă să iasă pe ecran, cu excepția
cazului în care programul este în modul lot, caz în care va fi folosit un alt fișier disc cu un nume rezervat.
Astfel de fișiere sunt de obicei potrivite pentru ieșirea ulterioară pe un dispozitiv de ieșire fizic. Sistemul
asociază numere de unități acestor unități implicite (de obicei 5 pentru intrare și 6 pentru ieșire).3

În afară de aceste două cazuri speciale, toate instrucțiunile I/O trebuie să se refere în mod explicit la o
unitate pentru a identifica dispozitivul către care sau de la care urmează să fie transferate datele. Unitatea
poate fi dată în una dintre cele trei forme. Acestea sunt prezentate în următoarele exemple care utilizează o
altă formă de citire care conține un specificator de unitate, u și un specificator de format, fmt, între paranteze
și separate prin virgulă, unde fmt este o specificație de format așa cum este descris în secțiunea anterioară:

citiți (u, fmt) lista

Cele trei forme ale lui u sunt după cum urmează.

O expresie întreagă scalară care dă numărul unității:

citește (4, '(f10.3)') q citește


(nunit, '(f10.3)') q citește (4*i+j, 100) a

unde valoarea poate fi orice număr întreg nenegativ permis de sistem în acest scop.

Un asterisc De exemplu

citește (*, '(f10.3)') q

unde asteriscul implică unitatea de intrare standard desemnată de sistem, aceeași cu cea utilizată
pentru citirea fără un număr de unitate.

O variabilă caracter implicită care identifică un fișier intern (vezi secțiunea următoare).

9.6 Fișiere interne

Fișierele interne permit conversia formatului între diferite reprezentări care urmează să fie efectuată de
program într-o zonă de stocare definită în cadrul programului însuși. Există două aplicații deosebit de utile,
una pentru citirea datelor al căror format nu este cunoscut în mod corespunzător dinainte și

3În Fortran 2003, aceste valori pot fi accesate dintr-un modul intrinsec, vezi Secțiunea 16.5.
Machine Translated by Google

192 Fortran modern explicat

altele pentru a pregăti liste de ieșire care conțin caractere mixte și date numerice, toate acestea trebuie pregătite
sub formă de caractere, probabil pentru a fi afișate ca o legendă pe un afișaj grafic.
Datele de caractere trebuie să fie de tip implicit. Prima aplicație va fi acum descrisă; al doilea va fi tratat în Secțiunea
9.8.

Imaginați-vă că trebuie să citim un șir de 30 de cifre, care ar putea corespunde la 30 de numere întregi de o
cifră, 15 numere întregi de două cifre sau 10 numere întregi de trei cifre. Informațiile despre ce tip de date sunt
implicate sunt date de valoarea unei cifre suplimentare, care are valoarea 1, 2 sau 3, în funcție de numărul de cifre
pe care le conține fiecare număr întreg. Un fișier intern ne oferă un mecanism prin care cele 30 de cifre pot fi citite
într-o zonă tampon de caractere. Valoarea cifrei finale poate fi testată separat, iar 30, 15 sau 10 valori pot fi citite
din fișierul intern, în funcție de această valoare. Codul de bază pentru a realiza acest lucru ar putea fi după cum
urmează (nu este inclusă nicio recuperare a erorilor sau validarea datelor, pentru simplitate):

întreg :: ival(30), cheie, i caracter (30):: caracter


tampon (6) :: form(3) = (/ '(30i1)', '(15i2)', '(10i3)' /)
citire (*, '(a30,i1)') buffer, citire cheie (tampon, formular (cheie)) (ival(i), i=1,30/cheie)

Aici, ival este o matrice care va primi valorile, va stoca o variabilă caracter de o lungime suficientă pentru a conține
cele 30 de cifre de intrare și va forma o matrice de caractere care conține cele trei formate posibile cărora le-ar
putea corespunde datele de intrare. Prima instrucțiune citită citește 30 de cifre în buffer ca date de caractere și o
cifră finală în cheia variabilă întreagă. A doua instrucțiune de citire citește datele din buffer în ival, folosind conversia
corespunzătoare, așa cum este specificată de descriptorul de editare selectat de cheie. Numărul de variabile citite
din buffer în ival este definit de bucla implicită-do, al cărei al doilea specificator este o expresie întreagă care
depinde și de cheie. După executarea acestui cod, ival va conține 30/valori cheie, numărul și formatul exact al
acestora nefiind cunoscute dinainte.

Dacă un fișier intern este scalar, acesta are o singură înregistrare a cărei lungime este cea a scalarului. Dacă
este o matrice, elementele sale, în ordinea elementelor de matrice, sunt tratate ca înregistrări succesive ale
fișierului și fiecare are lungimea egală cu cea a unui element de matrice. Este posibil să nu fie o secțiune de matrice
cu un indice vectorial.
O înregistrare devine definită atunci când este scrisă. Numărul de caractere trimise nu trebuie să depășească

lungimea înregistrării. Poate fi mai puțin, caz în care restul înregistrării este umplut cu spații libere. Pentru ieșirea
direcționată pe listă (Secțiunea 9.4), constantele de caractere nu sunt delimitate. O înregistrare poate fi citită numai
dacă este definită (care nu trebuie să fie doar printr-o instrucțiune de ieșire).
Înregistrările sunt umplute cu spații libere, dacă este necesar.
Un fișier intern este întotdeauna poziționat la începutul primei înregistrări înainte de transferul de date (notația
secțiunii matrice poate fi folosită pentru a începe în altă parte dintr-o matrice). Desigur, dacă un fișier intern este o
matrice sau un pointer alocabil, acesta trebuie să fie alocat sau asociat cu o țintă.
De asemenea, niciun element din lista de intrare/ieșire nu poate fi în fișier sau asociat cu fișierul.
Un fișier intern trebuie să fie de tip de caracter implicit, iar elementele de caractere care nu sunt implicite nu
sunt permise în listele de intrare/ieșire. Poate fi folosit pentru I/O direcționate pe listă (Secțiunea 9.9), dar nu pentru
I/O listă de nume (Secțiunea 9.10).4

4Această restricție a fost ridicată în Fortran 2003.


Machine Translated by Google

Transfer de date 193

9.7 Intrare formatată

În secțiunile anterioare am oferit descrieri complete ale modurilor în care pot fi specificate formatele și unitățile,
folosind forme simplificate ale declarațiilor de citire și tipărire ca exemple.
Există, de fapt, două forme ale declarației de citire formatate. Fără unitate, are forma

citește fmt [,listă]

iar cu o unitate poate lua forma

citiți ([unit=]u, [fmt=]fmt [,iostat=ios] [, err=error-label] &

[,end=end-label]) [listă]

unde u și fmt sunt specificatorii de unitate și format descriși în secțiunile 9.4 și 9.5, iostat=, err= și end= sunt
specificatori opționali care permit unui utilizator să specifice modul în care o instrucțiune de citire se va recupera
din diferite condiții excepționale, iar lista este o listă de variabile și liste de variabile implicite. Elementele cheie
pot fi specificate în orice ordine, deși este obișnuit să păstrați numărul unității și specificația formatului ca
primele două. Numărul unității trebuie să fie primul dacă nu are cuvântul său cheie. Dacă formatul nu are
cuvântul său cheie, acesta trebuie să fie al doilea, după numărul unității fără cuvântul său cheie.

Pentru simplitatea expunerii, ne-am limitat până acum la formatele care corespund unei singure înregistrări
din dosar, dar vom întâlni mai târziu în acest capitol cazuri care duc la introducerea unei părți dintr-o înregistrare
sau a mai multor înregistrări succesive.
Semnificațiile specificatorilor opționali sunt după cum urmează.

• Dacă este specificat iostat=, atunci ios trebuie să fie o variabilă întreg scalară de tip implicit care, după
executarea instrucțiunii de citire, are o valoare negativă dacă se întâlnește o condiție de sfârșit de
înregistrare în timpul intrării fără avansare (Secțiunea 9.11) , o valoare negativă diferită dacă a fost
detectată o condiție de fișier final pe dispozitivul de intrare (Secțiunea 10.2.3), o valoare pozitivă dacă a
fost detectată o eroare (de exemplu, o eroare de formatare) sau valoarea zero în caz contrar. Valorile
reale atribuite lui ios în cazul apariției unei excepții nu sunt definite de standard, ci doar semnele.

• Dacă este specificat end=, atunci end-label trebuie să fie o etichetă de instrucțiune a unei instrucțiuni
din aceeași unitate de acoperire, căreia îi va fi transferat controlul în cazul în care se ajunge la sfârșitul
fișierului.

• Dacă se specifică err=, atunci error-label este o etichetă de instrucțiune în aceeași unitate de acoperire,
căreia i se va transfera controlul în cazul oricărei alte excepții. Etichetele eticheta de eroare și eticheta
finală pot fi aceleași. Dacă nu sunt specificate și apare o excepție, execuția se va opri, cu excepția cazului
în care este specificat iostat. Un exemplu de instrucțiune de citire cu recuperarea erorii asociată este dat
în Figura 9.4, în care error și last_file sunt subrutine pentru a face față excepțiilor. În mod normal,
acestea vor fi dependente de sistem.

Dacă la intrare apare o eroare sau o condiție de sfârșit de fișier, instrucțiunea se termină și toate elementele
din listă și orice variabile implicite de acțiune devin nedefinite. Dacă apare o condiție de sfârșit de fișier pentru
un fișier extern, fișierul este poziționat după înregistrarea fișierului final (Secțiunea 10.2.3); în cazul în care există
Machine Translated by Google

194 Fortran modern explicat

Figura 9.4 Testarea unei erori sau a sfârșitului fișierului. citiți (nunit,
'(3f10.3)', iostat=ios, err=110, end=120) a,b,c
! Citire reușită - continuați execuția.
:

Eroare de apel 110 (ios)! Condiție de eroare - luați măsurile corespunzătoare.


întoarcere

120 apel last_file ! Sfârșitul fișierului - testați pentru mai multe fișiere.
:

În caz contrar, este o condiție de eroare, poziția fișierului este nedeterminată. O condiție de sfârșit de fișier apare și
dacă se încearcă citirea dincolo de sfârșitul unui fișier intern.
Este o bună practică să includeți un fel de recuperare a erorilor în toate instrucțiunile citite care
sunt incluse permanent într-un program. Pe de altă parte, intrarea în scopuri de testare este în mod
normal suficient de bine gestionată de forma simplă de citire fără un număr de unitate și fără
recuperarea erorilor.

9.8 Ieșire formatată

Există două tipuri de instrucțiuni de ieșire formatate, instrucțiunea print care a apărut în multe dintre exemplele de
până acum din acest capitol și instrucțiunea write a cărei sintaxă este similară cu cea a instrucțiunii read:

print fmt [,listă]

și

scrieți ([unit=]u, [fmt=]fmt [,iostat=ios] [,err=eroare-etichetă] ) [listă]

unde toate componentele au aceleași semnificații ca cele descrise pentru declarația citită (Secțiunea 9.7). Rețineți că
opționalul fmt= poate fi omis numai dacă opționalul unit= este de asemenea omis. Un asterisc pentru u specifică
unitatea de ieșire standard, așa cum este utilizată de print. Dacă apare o condiție de eroare la ieșire, execuția instrucțiunii
se termină, orice variabile implicite-do devin nedefinite și poziția fișierului devine nedeterminată.

Un exemplu de declarație de scriere este

scrie (nout, '(10f10.3)', iostat=ios, err=110) a

Un exemplu de utilizare a unui fișier intern este dat în Figura 9.5, care construiește un șir de caractere din componente
numerice și de caractere. Ultimul șir de caractere poate fi transmis către o altă subrutină pentru ieșire, de exemplu ca o
legendă pe un afișaj grafic.
În acest exemplu, declarăm o variabilă caracter care este suficient de lungă pentru a conține textul
care urmează să fie transferat în ea. (Instrucțiunea de scriere conține o specificație de format cu
descriptori de editare fără lățime de câmp. Acestea presupun o lățime de câmp corespunzătoare lungimii
reale a șirurilor de caractere care trebuie convertite.) După executarea instrucțiunii de scriere, linia poate
conține șirul de caractere
Machine Translated by Google

Transfer de date 195

Figura 9.5 Scrierea într-un fișier intern.


întreg real :: ziua ::
numerar
caracter(len=50) :: linie
:
! scrieți în linie scrieți (linia,
'(a, i2, a, f8.2, a)') &

„Prețuri pentru zi”, zi,” sunt „, numerar, „dolari”

Încasările pentru ziua 3 sunt de 4329,15 dolari

iar acesta ar putea fi folosit ca șir pentru procesarea ulterioară.


Numărul de caractere scrise pe rând nu trebuie să depășească lungimea acesteia.

9.9 I/E direcționate pe listă

În Secțiunea 9.4, a fost introdusă facilitatea de ieșire direcționată pe listă folosind un asterisc ca specificator
de format. Am presupus că lista era suficient de scurtă pentru a se încadra într-o singură înregistrare, dar
pentru liste lungi procesorul este liber să scoată mai multe înregistrări. Constantele de caractere pot fi
împărțite între înregistrări, iar constantele complexe care sunt la fel de lungi sau mai lungi decât o înregistrare
pot fi împărțite după virgula care separă cele două părți. În afară de aceste cazuri, o valoare se află
întotdeauna într-o singură înregistrare. De dragul controlului transportului (care este descris în apendicele
C.3), primul caracter al fiecărei înregistrări este gol, cu excepția cazului în care se continuă o constantă de
caractere delimitate. Rețineți că atunci când o constantă de caractere nelimitată este continuată, primul
caracter al înregistrării de continuare este necompletat. Singurele spații libere permise într-o constantă
numerică sunt într-o constantă complexă împărțită după virgulă.
Această facilitate este la fel de utilă pentru introducerea, în special pentru cantități mici de date de testare.
Pe înregistrarea de intrare, diferitele constante pot apărea în majoritatea formelor lor obișnuite, la fel ca și
cum ar fi citite sub descriptorii obișnuiți de editare, așa cum sunt definiți în Secțiunea 9.12. Excepțiile sunt că
valorile complexe trebuie să fie formate din două valori numerice separate prin virgulă și cuprinse între
paranteze, constantele de caractere pot fi delimitate, un spațiu liber nu trebuie să apară decât într-o constantă
de caractere delimitată sau într-o constantă complexă înainte sau după un câmp numeric, spații libere. nu
sunt niciodată interpretate ca zerouri, iar caracterele opționale care sunt permise într-o constantă logică (cele
care urmează t sau f, vezi Secțiunea 9.12.2) nu trebuie să includă nici virgulă, nici bară oblică. O constantă
complexă răspândită pe mai mult de o înregistrare trebuie să aibă orice capăt de înregistrare după partea
reală sau înaintea părții imaginare.
Constantele de caractere care sunt incluse în apostrofe sau ghilimele pot fi împărțite pe câte înregistrări
este necesar pentru a le conține, cu excepția faptului că un ghilimele sau un apostrof dublu nu trebuie
împărțiți între înregistrări. Delimitatorii pot fi omiși pentru o constantă de caractere implicită dacă:

• este de lungime diferită de zero;

• constanta nu conține spațiu liber, virgulă sau bară oblică;


Machine Translated by Google

196 Fortran modern explicat

• este cuprinsă într-o singură înregistrare;

• primul caracter nu este nici ghilimele, nici apostrof; și

• caracterele de început nu sunt numerice urmate de un asterisc.

În acest caz, constanta se termină atunci când se întâlnește un spațiu liber, virgulă, bară oblică sau sfârșitul
înregistrării, iar apostrofele sau ghilimelele care apar în cadrul constantei nu trebuie dublate.

Ori de câte ori o valoare de caracter are o lungime diferită față de elementul de listă corespunzător,
valoarea este trunchiată sau completată în dreapta cu spații libere, ca în instrucțiunea de atribuire a caracterelor.
Este posibil să se utilizeze un număr de repetări pentru o constantă dată, de exemplu 6*10 pentru a
specifica șase apariții ale valorii întregi 10. Dacă este posibil să se interpreteze constanta fie ca o constantă
literală, fie ca o constantă de caracter nelimitată, prima corespunzătoare elementul din listă determină care
este.
Constantele (opțional repetate) sunt separate în intrare prin separatoare. Un separator
este unul dintre următoarele, care apare altfel decât într-o constantă de caractere:

• o virgulă, opțional precedată și opțional urmată de una sau mai multe învecinate
semifabricate;

• o bară oblică (/), op ional precedată i, op ional, urmată de una sau mai multe învecinate
semifabricate; sau

• unul sau mai multe spa ii libere învecinate între două valori neblank sau după ultima
valoare non-blank.

Un sfârșit de înregistrare care nu se află într-o constantă de caractere este privit ca un gol și, prin urmare,
face parte dintr-un separator. Un gol încorporat într-o constantă complexă sau într-o constantă de caractere
delimitate nu este un separator. O înregistrare de intrare poate fi terminată printr-un separator slash, caz în
care toate valorile următoare din înregistrare sunt ignorate, iar instrucțiunea de intrare se termină.
Dacă nu există valori între două separatoare succesive sau între începutul primei înregistrări și primul
separator, aceasta este considerată ca fiind o valoare nulă și elementul corespunzător din lista de intrare
este lăsat neschimbat, definit sau nedefinit după caz. fi. O valoare nulă nu trebuie utilizată pentru partea
reală sau imaginară a unei constante complexe, dar o singură valoare nulă poate fi utilizată pentru întreaga
valoare complexă. O serie de valori nule poate fi reprezentată printr-un numărător repetat fără constantă:
,6*,. Când se întâlnește un separator bară oblică, valorile nule sunt date tuturor elementelor rămase din listă.

Un exemplu de această formă a declarației citite este:

întreg real :: i
:: A

caracter :: câmp(2) ::
logic steag
complex (len=12) :: caracter de titlu
(len=4) :: cuvânt
:
citește *, i, a, câmp, steag, titlu, cuvânt
Machine Translated by Google

Transfer de date 197

Dacă aceasta citește înregistrarea de intrare

10b6.4b(1.,0.)b(2.,0.)btbtest/

(în care b reprezintă un spațiu liber, iar spațiile sunt folosite ca separatori), atunci i, a, câmpul, steagul și
titlul vor dobândi valorile 10, 6.4, (1.,0.) și (2.,0. ), .true. și, respectiv, test, în timp ce cuvântul rămâne
neschimbat. Pentru înregistrările de intrare

10,.64e1,2*,.adevărat.
'histogramb10'/val1

(în care virgulele sunt folosite ca separatori), variabilele i, a, flag și title vor dobândi valorile 10, 6.4, .true.
și, respectiv, histogramb10. Câmpul și cuvântul variabilelor rămân neschimbate, iar șirul de intrare val1
este ignorat deoarece urmează o bară oblică. (Rețineți apostrofele, care sunt necesare deoarece șirul
conține un spațiu liber. Fără delimitatori, acest șir ar părea a fi un șir urmat de valoarea întreagă 10.) Din
cauza acestei bare oblice, instrucțiunea de citire nu continuă cu următoarea înregistrare și lista nu este
astfel pe deplin satisfăcută.

9.10 I/O liste de nume

Poate fi util, mai ales pentru testarea programelor, introducerea sau ieșirea unei liste adnotate de valori.
Valorile necesare sunt specificate într-un grup de liste de nume (Secțiunea 7.15), iar I/O este efectuată
printr-o instrucțiune de citire sau scriere care nu are o listă de I/O și în care fie

• formatul este înlocuit cu un nume de listă-grup ca al doilea parametru pozițional; sau

• specificatorul fmt= este înlocuit cu un specificator nml= cu acel nume.

La citire, devin definite numai acele obiecte care sunt specificate în înregistrarea de intrare și care nu au o
valoare nulă. Toate celelalte elemente din listă rămân în starea lor existentă de definire sau nedefinire. Este
posibil să se definească valoarea unui element de matrice sau a unei secțiuni fără a afecta celelalte porțiuni
ale matricei. La scriere, toate elementele din grup sunt scrise în fișierul specificat. Această formă de I/O nu
este disponibilă pentru fișierele interne.5
Valoarea pentru un obiect scalar sau o listă de valori pentru o matrice este precedată în înregistrări de
numele sau desemnatorul și un semn egal care poate fi precedat sau urmat opțional de spații libere. Forma
listei de valori și a valorilor nule din înregistrările de intrare și de ieșire este aceeași pentru I/O direcționată
pe listă (Secțiunea 9.9), cu excepția faptului că constantele de caractere trebuie întotdeauna delimitate în
înregistrările de intrare și constantele logice nu trebuie să conțină un egal. semn. O instrucțiune de
introducere a listei de nume se termină la apariția unei bare oblice în listă în afara unei constante de
caractere. Un exemplu simplu este

întreg :: nr_of_eggs, litres_of_milk, kilos_of_butter namelist/food/no_of_eggs,


litres_of_milk, kilos_of_butter read (5, nml=aliment)

pentru a citi înregistrarea

5Dar în Fortran 2003 este disponibil.


Machine Translated by Google

198 Fortran modern explicat

&alimentare litri_de_lapte=5, nr_de_ouă=12 /

unde observăm că ordinea celor două valori date nu este aceeași cu ordinea lor în grupul listei de nume – ordinele
nu trebuie neapărat să se potrivească. Valoarea kilos_of_butter rămâne neschimbată. Primul element care nu este
gol din înregistrare este un ampersand urmat fără un spațiu liber intermediar de numele grupului. Slash-ul este
obligatoriu ca terminator. La ieșire, este produsă o listă de valori adnotată similară, începând cu numele grupului
și terminând cu o bară oblică. Aici, ordinea este aceea a grupului de liste de nume. Astfel, declarațiile

întreg :: număr, listă (10) listă de nume/out/


număr, scriere listă (6, nml=out)

ar putea produce înregistrarea

&OUT NUMBER=1, LIST=14, 9*0 /

La ieșire, numele sunt întotdeauna în majuscule.


În cazul în care un desemnator de subobiect apare într-o înregistrare de intrare, toate expresiile subșirurilor,
indicele și pașii trebuie să fie constante literale scalare întregi fără parametri de tip specificați. Toate numele de
grup, numele obiectelor și numele componentelor sunt interpretate fără a ține cont de majuscule. Spațiile libere
pot precede sau urmează numele sau desemnatorul, dar nu trebuie să apară în el.

Dacă obiectul este scalar și de tip intrinsec, semnul egal trebuie să fie urmat de o valoare.
Dacă este de tip derivat sau este o matrice, semnul egal trebuie să fie urmat de o listă de valori de tip intrinsec
corespunzătoare înlocuirii fiecărei valori de tip derivat cu componentele sale finale și fiecare matrice cu elementele
sale în ordinea elementelor de matrice.
Lista de valori nu trebuie să fie prea lungă, dar poate fi prea scurtă, caz în care valorile nule finale sunt
considerate ca fiind adăugate. Dacă un obiect este de tip caracter, articolul corespunzător trebuie să fie de același
fel.
Obiectele de dimensiune zero nu trebuie să apară într-o înregistrare de intrare a listei de nume. În orice apariție multiplă
a unui obiect dintr-o succesiune de înregistrări de intrare, se ia valoarea finală.
Înregistrările de intrare pentru introducerea listei de nume pot avea un comentariu după un separator nume/
valoare al obiectului, altul decât o bară oblică. Acest lucru permite programatorilor să documenteze structura unui
fișier de intrare cu listă de nume linie cu linie. Comentariul este în formatul obișnuit pentru comentarii. Înregistrarea
de intrare a acestei secțiuni poate fi documentată astfel:

&alimentare litri_de_lapte=5, ! Pentru vacanță în camping


nr_de_ouă=12 /

O linie de comentariu, cu ! este, de asemenea, permis ca primul caracter neblank dintr-o înregistrare de intrare,
dar poate să nu apară într-un context de caracter.

9.11 I/O fără avansare


Până acum am considerat fiecare instrucțiune de citire sau scriere pentru a efectua intrarea sau ieșirea unei
înregistrări complete. Există, totuși, multe aplicații, în special în managementul ecranului,
Machine Translated by Google

Transfer de date 199

unde aceasta ar deveni o restricție supărătoare. Ceea ce este necesar este capacitatea de a citi și scrie fără a
avansa întotdeauna poziția fișierului înaintea următoarei înregistrări. Această facilitate este furnizată de I/O
fără avansare. Pentru a avea acces la această facilitate, specificatorul opțional advance= trebuie să apară în
instrucțiunea de citire sau scriere și să fie asociat cu un avans al expresiei de caractere implicite scalare care
evaluează, după eliminarea oricăror spații libere și conversia oricăror litere mari în minuscule. , la valoarea
nr. Singura altă valoare permisă este da, care este valoarea implicită dacă specificatorul este absent; în acest
caz, apare I/O normală (avansare).

Următorii specificatori opționali sunt disponibili pentru o declarație de citire care nu este avansată:

eor=eor-label
mărime = mărime

unde eor-label este o etichetă de instrucțiune în aceeași unitate de acoperire și dimensiunea este o variabilă
scalară întreagă implicită. Eticheta eor poate fi aceeași cu eticheta finală sau eticheta de eroare a instrucțiunii
citite.
O instrucțiune I/O avansată repoziționează întotdeauna fișierul după ultima înregistrare accesată.
O instrucțiune I/O care nu avansează lasă fișierul poziționat în înregistrare, cu excepția faptului că, dacă
încearcă să transfere date dincolo de sfârșitul înregistrării curente, apare o condiție de sfârșit de înregistrare
și fișierul este repoziționat pentru a urma înregistrarea. Variabila iostat, dacă este prezentă, va dobândi o
valoare negativă diferită de cea care indică o condiție de sfârșit de fișier; și, dacă specificatorul eor= este
prezent, controlul este transferat instrucțiunii specificate prin eticheta eor asociată. Pentru a oferi un mijloc
de control al acestui proces, specificatorul size=, atunci când este prezent, stabilește dimensiunea la numărul
de caractere citite efectiv. Un exemplu complet este astfel

caracter(len=3) :: cheie întreg ::


unitate, dimensiune citită „(a3)”,
(unitate,
avans=”nu”,
dimensiune=dimensiune, eor=66) cheie
:
! cheia nu este într-o înregistrare 66
''
cheie(dimensiune+1:) =
:

În ceea ce privește condițiile de eroare și de sfârșit de fișier, programul se termină la sfârșitul înregistrării
condiția apare dacă nu este specificat nici eor=, nici iostat=.
Dacă întâmpinarea unui sfâr it de înregistrare la citire are ca rezultat ca lista de intrare să nu fie
satisfăcută, specificatorul pad= descris în Sec iunea 10.3 va determina dacă are loc o completare cu caractere
goale. Spațiile goale inserate ca umplutură nu sunt incluse în dimensiunea = număr.
Este posibil să efectuați I/O avansate și neavansare pe aceeași înregistrare sau fișier.
De exemplu, o citire care nu este avansată poate citi primele caractere ale unei înregistrări, iar o citire
avansată poate citi restul.
O aplicație specială a acestei facilități este de a scrie un prompt pe un ecran și de a citi din următoarea
poziție a caracterului de pe ecran fără un avans de linie intermediar:

scrieți (*, '(a)', avans='nu') 'introduceți următorul număr prim:' citiți (*, '(i10)')
număr_prim
Machine Translated by Google

200 Fortran modern explicat

I/O fără avansare poate fi efectuată numai pe un fișier extern și nu poate fi utilizat pentru lista de
nume sau I/O direcționată pe listă. Rețineți că, ca și pentru avansarea intrării/ieșirii, mai multe înregistrări
pot fi procesate de o singură instrucțiune.

9.12 Editați descriptori

În descrierea formelor posibile ale unei specificații de format din Secțiunea 9.4, au fost date câteva
exemple de descriptori de editare. După cum s-a menționat acolo, descriptorii de editare oferă o
specificație precisă a modului în care valorile trebuie convertite într-un șir de caractere pe un dispozitiv
de ieșire sau fișier intern sau convertite dintr-un șir de caractere pe un dispozitiv de intrare sau fișier
intern în reprezentări interne.
Cu anumite excepții menționate în textul următor, descriptorii de editare dintr-o listă sunt separați prin
virgule și numai în cazul în care o listă de intrare/ieșire este goală sau specifică numai matrice de dimensiune
zero, este posibil să nu existe deloc un descriptor de editare în specificația formatului .
Pe un procesor care acceptă litere mari și mici,6 descriptorii de editare sunt interpretați fără a ține
cont de majuscule. Acest lucru este valabil și pentru câmpurile de intrare numerice și logice; un exemplu
este 89AB ca valoare de intrare hexazecimală. În câmpurile de ieșire, orice caractere alfabetice sunt în sus
caz.

9.12.1 Numărări repetate

Editarea descriptorilor se împart în trei clase: date, control și caractere-șir. Descriptorii de editare a
datelor pot fi precedați de un număr de repetare (o constantă literală implicită întreg fără semn), ca în
exemplu

10f12.3

Dintre descriptorii de editare rămași, numai descriptorul de editare slash (Secțiunea 9.12.4) poate avea
asociat un număr de repetări. Un număr de repetări poate fi aplicat unui grup de descriptori de editare,
încadrați în paranteze:

printează '(4(i5,f8.2))', (i(j), a(j), j=1,4)

(pentru întregul i și real a). Acest lucru este echivalent cu scrisul

tipăriți „(i5,f8.2,i5,f8.2,i5,f8.2,i5,f8.2)”, (i(j), a(j), j=1,4)

Numărări repetate ca acesta pot fi imbricate:

tipăriți „(2(2i5,2f8.2))”, i(1),i(2),a(1),a(2),i(3),i(4),a(3),a (4)

Dacă o specificație de format fără componente între paranteze este utilizată cu o listă de I/O care
conține mai multe elemente decât numărul de descriptori de editare, ținând cont de numărul de repetare,
atunci va începe o nouă înregistrare și specificația de format va fi repetată. Înregistrările ulterioare încep
în același mod până la epuizarea listei. Pentru a imprima o matrice de 100 de elemente întregi, 10
elemente pe o linie, se poate folosi următoarea instrucțiune:

6Aproape toate sistemele acceptă ambele cazuri în prezent și aceasta este o cerință în Fortran 2008.
Machine Translated by Google

Transfer de date 201

tipăriți „(10i8)”, i(1:100)

În mod similar, la citirea dintr-un fișier de intrare, înregistrările noi vor fi citite până când lista este satisfăcută,
o înregistrare nouă fiind preluată din fișierul de intrare de fiecare dată când specificația este repetată, chiar
dacă înregistrările individuale conțin mai multe date de intrare decât cele specificate de specificația formatului .
Aceste date superflue vor fi ignorate. De exemplu, citirea celor două înregistrări (b reprezintă din nou un gol)

bbb10bbb15bbb20
bbb25bbb30bbb35

sub controlul declarației citite

citește „(2i5)”, i,j,k,l

va avea ca rezultat ca cele patru variabile întregi i, j, k și l să dobândească valorile 10, 15, 25 și, respectiv, 30.

Dacă un format conține componente între paranteze, ca în

„(2i5, 3(i2,2(i1,i3)), 2(2f8.2,i2))”

ori de câte ori formatul este epuizat, se ia o nouă înregistrare și controlul formatului revine la factorul de
repetare care precede paranteza din stânga corespunzătoare ultimei paranteze din dreapta, aici 2(2f8.2,i2), sau
la paranteza în sine dacă nu are factor de repetare. Aceasta o numim reversiune.

9.12.2 Descriptori de editare a datelor

Valorile tuturor tipurilor de date intrinseci pot fi convertite de descriptorul de editare g. Cu toate acestea, din
motive de claritate, este descris ultimul. Nicio formă de valoare de intrare sau de ieșire nu poate avea un
parametru de tip tip. Pentru toți descriptorii de editare numerică, dacă un câmp de ieșire este prea îngust
pentru a conține numărul de ieșit, acesta este completat cu asteriscuri.7

Valorile întregi pot fi convertite prin intermediul descriptorului i edit. Forma sa de bază este iw, unde w este o
constantă literală implicită întreg fără semn care definește lățimea câmpului. Valoarea întreagă va fi
citită sau scrisă în acest câmp, ajustată în partea dreaptă. Dacă desemnăm din nou o poziție goală prin
b, atunci valoarea 99 imprimată sub controlul descriptorului de editare i5 va apărea ca bb-99, semnul
numărând ca o singură poziție în câmp.

Pentru ieșire, o formă alternativă a acestui descriptor de editare permite specificarea exactă a numărului
de cifre care urmează să fie tipărite, chiar dacă unele sunt zerouri înainte. Formularul iw.m specifică
lățimea câmpului, w, și că cel puțin m cifre trebuie să fie scoase la ieșire, unde m este o constantă literală
întregă implicită fără semn. Valoarea 99 tipărită sub controlul descriptorului de editare i5.3 ar apărea ca
bb099. Valoarea lui m este chiar permisă

7Formulare suplimentare permise de Fortran 2003 apar în Secțiunea 17.5.


Machine Translated by Google

202 Fortran modern explicat

fi zero, iar câmpul va fi apoi umplut cu spații libere dacă valoarea imprimată este 0. La intrare,
iw.m este interpretat exact în același mod ca iw.

Pentru a permite înregistrărilor de ieșire să conțină cât mai puțin spațiu neutilizat posibil,
descriptorul de editare i poate specifica w ca fiind zero, ca în i0. Aceasta nu denotă un câmp cu
lățime zero, ci un câmp care are lățimea minimă necesară pentru a conține valoarea de ieșire în
cauză. Programatorul nu trebuie să-și facă griji că un câmp cu o lățime prea îngustă va cauza
depășirea unui câmp de ieșire și va conține doar asteriscuri.

Valorile întregi pot fi convertite și de scriptorii de editare bw, bw.m, ow, ow.m, zw și zw.m.
Acestea sunt similare cu forma i, dar sunt destinate numerelor întregi reprezentate în sistemele
de numere binar, octal și, respectiv, hexazecimal (Secțiunea 2.6.1). Forma externă nu conține litera
de început (b, o sau z) sau delimitatorii. Forma wm, cu m egal cu w, este recomandată la ieșire,
astfel încât orice zerouri de început să fie vizibile.

Valorile reale pot fi convertite fie prin descriptori de editare e, en, es sau f. Descriptorul f pe care l-am
întâlnit în exemplele anterioare. Forma sa generală este fw.d, unde w și d sunt constante literale
întregi implicite fără semn care definesc, respectiv, lățimea câmpului și, respectiv, numărul de
cifre care urmează să apară după punctul zecimal în câmpul de ieșire. Pentru intrare, w nu trebuie
să fie zero. Punctul zecimal contează ca o singură poziție în câmp. La intrare, dacă șirul de intrare
are un punct zecimal, valoarea lui d este ignorată. Citirea șirului de intrare b9.3729b cu descriptorul
de editare f8.3 ar determina transferul valorii 9.3729.
Toate cifrele sunt folosite, dar rotunjirea poate fi inevitabilă din cauza stocării fizice efective
rezervate pentru valoarea de pe computerul utilizat.

Există, în plus, alte două forme de șir de intrare care sunt acceptabile pentru descriptorul de
editare f. Primul este un șir de cifre cu semn opțional, fără virgulă. În acest caz, cele d cifre din
dreapta vor fi considerate parte fracțională a valorii. Astfel, b-14629 citit sub controlul descriptorului
de editare f7.2 va transfera valoarea -146.29. A doua formă este forma reală standard implicită a
constantei literale, așa cum este definită în Secțiunea 2.6.2, și varianta în care exponentul este
semnat și e este omis. În acest caz, partea d a descriptorului este din nou ignorată. Astfel, valoarea
14.629e-2 (sau 14.629-2), sub controlul descriptorului de editare f9.1, va transfera valoarea
0,14629. Litera exponentului poate fi scrisă și cu litere mari.

Valorile sunt rotunjite la ieșire urmând regulile normale de aritmetică. Astfel, valoarea 10.9336,
când iese sub controlul descriptorului de editare f8.3, va apărea ca bb10.934, iar sub controlul lui
f4.0 ca b11. Pentru ieșire, dacă w este zero, ca în f0.3, aceasta denotă un câmp care are lățimea
minimă necesară pentru a conține valoarea de ieșire în cauză.

Descriptorul de editare e are două forme, ew.d și ew.dee, și este mai potrivit pentru numere cu o
magnitudine sub aproximativ 0,01 sau peste 1000. Valoarea lui w nu trebuie să fie zero. Regulile
pentru aceste două forme de intrare sunt identice cu cele pentru descriptorul de editare fw.d.
Pentru ieșirea cu forma ew.d a descriptorului, va fi transferat un șir de caractere diferit, care
conține o semnificație cu valoare absolută mai mică de 1 și un câmp exponent de patru caractere
care constă fie din E urmat de un semn și
Machine Translated by Google

Transfer de date 203

două cifre sau de un semn și trei cifre. Astfel, pentru 1.234 × 1023 convertit de descriptorul de
editare e10.4, va fi transferat șirul b.1234E+24 sau b.1234+024. Forma care conține litera
exponentului E nu este utilizată dacă mărimea exponentului depășește 99. De exemplu, e10,4 ar
determina ca valoarea 1,234 × 10 150 să fie transferată ca b.1234-149. Unele procesoare
imprimă un zero înainte de virgulă zecimală.

În cea de-a doua formă a descriptorului de editare e, ew.dee, e este o constantă literală integrală
implicită, fără semnătură, diferită de zero, care determină numărul de cifre care urmează să
apară în câmpul exponent. Această formă este obligatorie pentru exponenții a căror magnitudine
este mai mare de 999. Astfel, valoarea 1,234 × 101234 cu descriptorul de editare e12.4e4 este
transferată ca șirul b.1234E+1235. Un număr tot mai mare de computere sunt capabile să facă
față acestor intervale foarte mari de exponenți. Poate fi folosit și dacă se dorește o singură cifră
exponent. De exemplu, valoarea 1.211 cu descriptorul de editare e9.3e1 este transferată ca șirul
b0.121E+1.

Descriptorul de editare (de inginerie) este identic cu descriptorul de editare e, cu excepția faptului
că la ieșire exponentul zecimal este divizibil cu trei, o semnificație diferită de zero este mai mare
sau egală cu 1 și mai mică de 1000 și factorul de scară (Secțiunea 9.12.4). ) nu are efect. Astfel,
valoarea 0,0217 transferată sub un descriptor de editare en9.2 ar apărea ca 21.70E-03 sau
21.70-003.

Descriptorul de editare es (științific) este identic cu descriptorul de editare e, cu excepția faptului


că la ieșire valoarea absolută a unei semnificații diferite de zero este mai mare sau egală cu 1 și
mai mică de 10, iar factorul de scară (Secțiunea 9.12.4) nu are efect. Astfel, valoarea 0,0217
transferată sub un descriptor de editare es9.2 ar apărea ca 2.17E-02 sau 2.17-002.

Valorile complexe pot fi editate sub controlul perechilor de descriptori de editare f, e, en sau es. Cei doi
descriptori nu trebuie să fie identici. Valoarea complexă (0,1,100.) convertită sub controlul lui
f6.1,e8.1 ar apărea ca bbb0.1b0.1E+03. Cei doi descriptori pot fi separați prin șir de caractere și
descriptori de editare de control (care vor fi descriși în Secțiunile 9.12.3 și, respectiv, 9.12.4).

Valorile logice pot fi editate folosind descriptorul de editare lw. Aceasta definește un câmp de lățime w
care la intrare constă din spații libere opționale, urmate opțional de un punct zecimal, urmat de
t sau f (sau T sau F), urmat opțional de caractere suplimentare. Astfel, un câmp definit de l7
permite șirurile .true. iar .fals. să fie introduse. Caracterele t sau f vor fi transferate ca valori
adevărate sau, respectiv, false. La ieșire, caracterul T sau F va apărea în poziția cea mai din
dreapta în câmpul de ieșire.

Valorile caracterelor pot fi editate folosind descriptorul de editare a într-una din cele două forme ale
sale, fie a sau aw. În prima dintre cele două forme, lățimea câmpului de intrare sau de ieșire este
determinată de lățimea reală a articolului din lista I/O, măsurată în număr de caractere de orice
fel. Astfel, o variabilă caracter de lungime 10, care conține
Machine Translated by Google

204 Fortran modern explicat

valoarea STATEMENTS, atunci când este scrisă sub controlul unui descriptor de editare, ar apărea
într-un câmp cu lățime de 10 caractere, iar variabila caracter non-implicit de lungime 4 care conține
valoarea ar apărea într-un câmp cu lățime
sub unde 4 caractere.
descriptor Dacă, totuși,
de editare prima variabilă
a11, aceasta ar fi cu
ar fi tipărită convertită
un
gol: bSTATEMENTS. Sub controlul lui a8, vor fi scrise numai cele opt caractere din stânga: OAMENI
DE STATUT.

Dimpotrivă, cu aceeași variabilă la intrare, un descriptor de editare a11 ar determina transferul


celor 10 caractere din dreapta din câmpul de intrare de 11 caractere, astfel încât bSTATEMENTS să
fie transferat ca STATEMENTS. Descriptorul de editare a8 ar face ca cele opt caractere din câmp să
fie transferate în cele opt poziții cele mai din stânga ale variabilei, iar celelalte două ar fi umplute cu
spații libere: STATEMEN ar fi transferat ca STATEMENbb.

Toate caracterele transferate sub controlul unui descriptor de editare a sau aw au tipul de element
de listă I/O și observăm că acest descriptor de editare este singurul care poate fi folosit pentru a
transmite caractere non-implicite către sau de la o înregistrare . În cazul non-implicit, caracterul
de completare gol este dependent de procesor.

Orice valoare intrinsecă a tipului de date poate fi editată cu descriptorul de editare gw.d și gw.dee
(general) . Când este utilizat pentru tipuri reale sau complexe, este identic cu descriptorul de editare
e, cu excepția faptului că o valoare de ieșire cu magnitudinea n în interval

0,1 0,5×10 d 1 n < 10d 0,5

sau zero când d = 0 este convertit ca și cum ar fi un descriptor de editare f și urmat de un număr de
spații libere egal cu lățimea părții exponent așa cum ar fi specificat de un descriptor de editare e.
Descriptorul de editare f echivalent este fw .d unde w = w , gw.dee
4 pentru gw.d
și dsau
= d w k ecând
2 pentru
n se află
în interval

10k 1 (1 0.5×10 d) n < 10k (1 0.5×10 d)

pentru k = 0,1,...,d și d = d 1 când n = 0 și d > 0. Această formă este utilă pentru tipărirea valorilor
ale căror mărimi nu sunt bine cunoscute în prealabil și unde este preferată o conversie f acolo unde
este posibil, iar un e altfel.

Când descriptorul de editare g este utilizat pentru tipuri întregi, logice sau de caractere, acesta
urmează regulile descriptorilor de editare iw, lw și, respectiv, aw (orice d sau e este ignorat).

Valorile tipului derivat sunt editate de secvența corespunzătoare de descriptori de editare corespunzătoare
tipurilor intrinseci ale componentelor finale ale tipului derivat.8 Un exemplu este:

tip șir întreg ::


lungime caracter(len=20) :: cuvânt

8Fortran 2003 oferă facilități îmbunătățite pentru intrare/ieșire de tip derivat (Secțiunea 17.2).
Machine Translated by Google

Transfer de date 205

end type string


type(string) :: text read (*, '(i2,
a)') text

9.12.3 Descriptor de editare a șirului de caractere

O constantă literală de caracter implicită fără un parametru de tip specificat poate fi transferată într-un fișier de
ieșire prin încorporarea acestuia în specificația formatului în sine, ca în exemplu

print "(' Aceasta este o instrucțiune de format')"

Șirul va apărea de fiecare dată când este întâlnit în timpul procesării formatării. În acest descriptor, cazul este
semnificativ. Descriptorii de editare a șirurilor de caractere nu trebuie utilizați la introducere.

9.12.4 Descriptori de editare de control

Uneori este necesar să se dea și alte instrucțiuni unui dispozitiv I/O decât doar lățimea câmpurilor și modul în care
trebuie interpretat conținutul acestor câmpuri. De exemplu, este posibil ca cineva să dorească să poziționeze câmpuri
la anumite coloane sau să înceapă o nouă înregistrare fără a lansa o nouă comandă de scriere. Pentru acest tip de
scop, descriptorii de editare de control oferă un mijloc de a spune procesorului ce acțiune trebuie să ia. Unii dintre
acești descriptori de editare conțin informații care sunt utilizate pe măsură ce sunt procesate; altele sunt ca niște
comutatoare, care schimbă condițiile în care I/O are loc din punctul în care sunt întâlnite, până la sfârșitul procesării
instrucțiunii I/O care le conține (inclusiv reversiunile, Secțiunea 9.12.1). De acești din urmă descriptori ne vom ocupa
mai întâi.

Condiții de stabilire a descriptorilor de editare de control

Spațiile încorporate în câmpurile de introducere numerică sunt tratate în unul din două moduri, fie ca zero, fie ca
caractere nule care sunt stoarse prin mutarea celorlalte caractere din câmpul de introducere la dreapta și
adăugând spații libere de început în câmp (cu excepția cazului în care câmpul este complet gol, caz în care
este interpretat ca zero). Valoarea implicită este dată de specificatorul blank= (Secțiunea 10.3) în vigoare în
prezent pentru unitate sau este nulă pentru un fișier intern. Oricare ar fi valoarea implicită pentru un fișier,
acesta poate fi suprascris în timpul unei conversii de format dat de descriptorii de editare bn (spații nule) și
bz (spații zero). Să presupunem că modul este că spațiile sunt tratate ca zerouri. Șirul de intrare bb1b4
convertit de descriptorul de editare i5 ar transfera valoarea 104. Același șir convertit de bn,i5 ar da 14. Un
descriptor de editare bn sau bz schimbă modul pentru restul specificației de format sau până când alt bn sau
descriptorul de editare bz este îndeplinit. Descriptorii de editare bn și bz nu au niciun efect asupra ieșirii.

Semnele de început sunt întotdeauna scrise pentru valori numerice negative la ieșire. Pentru cantități pozitive, altele
decât exponenți, dacă semnele sunt scrise depinde de procesor. Descriptorul de editare ss (suprimarea
semnelor) suprimă semnele plus înainte, adică valoarea 99 tipărită de i5 este bbb99 și 1.4 este tipărită de
e10.2 ca bb0.14E+01. Pentru a porni plus
Machine Translated by Google

206 Fortran modern explicat

tipărirea semnelor, pot fi utilizați descriptorii de editare sp (imprimare a semnelor); aceleași


numere scrise zece de sp,i5,e10.2 devin bb+99 și b+0.14E+01. Descriptorul de editare s restabilește
opțiunea procesorului. Un ss, sp sau s vor rămâne în vigoare pentru restul specificației de format,
cu excepția cazului în care este îndeplinit un alt descriptor de editare ss, sp sau s. Acești descriptori
de editare oferă control complet asupra tipăririi semnelor și sunt utili pentru producerea de ieșiri
codificate care trebuie comparate automat, pe două computere diferite.

Factorii de scară se aplică la introducerea cantităților reale sub descriptorii de editare e, f, en, es și g și
sunt un mijloc de scalare a valorilor de intrare. Forma lor este kp, unde k este o constantă literală
întreagă implicită care specifică factorul de scară. Valoarea este zero la începutul execuției
instrucțiunii. Efectul este că orice mărime care nu are un câmp exponent va fi redusă cu un factor
de 10k. Mărimile cu exponent sunt
neafectat.

Factorul de scară kp afectează de asemenea ieșirea cu editarea e, f sau g, dar nu are niciun efect
cu editarea en sau es. Sub controlul unui descriptor de editare f, cantitatea va fi înmulțită cu un
factor 10k. Astfel, numărul 10.39 scos de un descriptor de editare f6.0 după factorul de scară 2p
va apărea ca b1039.. Cu descriptorul de editare e și cu g unde este luată editarea stilului e,
cantitatea este transferată cu exponentul redus cu k, iar semnificația înmulțită cu 10k. Astfel, 0,31
× 103, scris după un descriptor de editare 2p sub controlul e9.2, va apărea ca 31.00E+01. Acest
lucru oferă un control mai bun asupra stilului de ieșire al cantităților reale care altfel nu ar avea
cifre semnificative înainte de virgulă zecimală.

Virgula dintre un factor de scară și un descriptor de editare imediat următor f, e, en, es sau g
(fără o numărare repetată) poate fi omisă, dar nu recomandăm această practică deoarece
sugerează că factorul de scară se aplică numai la următoarea editare a descriptorului, în timp ce
de fapt se aplică pe tot formatul până când este întâlnit un alt factor de scară.

Controlați descriptorii de editare pentru procesare imediată

Tabelarea într-un câmp de intrare sau de ieșire poate fi realizată utilizând descriptorii de editare tn, trn
(și nx) și tln, unde n este o constantă literală întreagă implicită pozitivă. Acestea afirmă, respectiv,
că următoarea parte a I/O ar trebui să înceapă la poziția n în înregistrarea curentă (unde limita
din stânga a filei este poziția 1), sau la n poziții la dreapta poziției curente, sau la n poziții la stânga
poziției curente (limita filei din stânga dacă poziția curentă este mai mică sau egală cu n). Să
presupunem că, în urma unei citiri avansate, citim o înregistrare de intrare bb9876 cu următoarea
declarație:

citește (*, '(t3, i4, tl4, i1, i2)') i, j, k

Specificația formatului va muta un indicator noțional mai întâi în poziția 3, de unde voi fi citit.
Variabila i va dobândi valoarea 9876, iar indicatorul noțional se află atunci în poziția 7. Descriptorul
de editare tl4 o mută în stânga patru poziții, înapoi în poziția 3.
Se citesc apoi mărimile j și k și dobândesc valorile 9 și, respectiv, 87.
Machine Translated by Google

Transfer de date 207

Acești descriptori de editare provoacă înlocuirea la ieșire sau citirea multiplă a acelorași articole
într-o înregistrare la intrare. La ieșire, orice goluri dinaintea ultimului caracter scris efectiv sunt
umplute cu spații. Dacă orice caracter care este omis de unul dintre descriptori este de alt tip
decât implicit, poziționarea depinde de procesor.

Dacă înregistrarea curentă este prima procesată de instrucțiunea I/O și urmează I/O
neavansată care a lăsat fișierul poziționat într-o înregistrare, următorul caracter este limita de
filă din stânga; în caz contrar, primul caracter al înregistrării este limita filei din stânga.

Descriptorul de editare nx este echivalent cu descriptorul de editare trn. Este adesea folosit pentru a
plasa spații într-o înregistrare de ieșire. De exemplu, pentru a începe o înregistrare de ieșire cu un gol
prin această metodă, se scrie

fmt= '(1x,....)'

Spații ca acesta pot precede un descriptor de editare a datelor, dar 1x,i5 nu este, de exemplu,
exact echivalent cu i6 la ieșire, deoarece orice valoare care necesită toate cele șase poziții din
câmp nu le va avea disponibile în primul caz.

Descriptorii de editare t și x nu provoacă niciodată înlocuirea unui caracter deja într-o


înregistrare scoasă, ci doar provoacă o schimbare a poziției în cadrul înregistrării, astfel încât o
astfel de înlocuire ar putea fi cauzată de un descriptor de editare ulterior.

Înregistrările noi pot fi pornite în orice moment într-o specificație de format prin intermediul descriptorului
de editare slash (/). Acest descriptor de editare, deși este descris aici, poate avea de fapt numărări
repetate; pentru a sări peste, să zicem, trei înregistrări se pot scrie fie /,/,/ sau 3/. La intrare, o nouă
înregistrare va fi pornită de fiecare dată când se întâlnește un /, chiar dacă conținutul înregistrării
curente nu a fost transferat în totalitate. Citirea celor două înregistrări

bbb99bbb10
bb100bbb11

cu afirmatia

citiți „(bz,i5,i3,/,i5,i3,i2)”, i, j, k, l, m

va face ca valorile 99, 0, 100, 0 și 11 să fie transferate la cele cinci variabile întregi, respectiv.
Acest descriptor de editare nu trebuie să fie separat printr-o virgulă de un descriptor de editare
precedent, cu excepția cazului în care are un număr de repetări; nu trebuie să fie niciodată
separate printr-o virgulă de un descriptor de editare care urmează.

Rezultatul scrierii cu un format care conține o secvență de, să zicem, patru bare oblice, așa cum
este reprezentat de

tipăriți „(i5,4/,i5)”, i, j

este de a separa cele două valori prin trei înregistrări goale (ultima bară oblică începe
înregistrarea care conține j); dacă i și j au valorile 99 și 100, ar apărea ca
Machine Translated by Google

208 Fortran modern explicat

bbb99
bbb
bb100

Un descriptor de editare slash scris într-un fișier intern va face ca următoarele valori să fie scrise
în următorul element al matricei de caractere specificate pentru fișier. Fiecare astfel de element
corespunde unei înregistrări, iar numărul de caractere scrise într-o înregistrare nu trebuie să
depășească lungimea acesteia.

Editarea prin două puncte este un mijloc de a termina controlul formatului dacă nu există alte elemente într-o
listă de I/O. În special, este util pentru a preveni ieșirea ulterioară a șirurilor de caractere utilizate
pentru adnotare dacă lista de ieșiri este epuizată. Luați în considerare următoarea instrucțiune de
ieșire, pentru o matrice l(3):

print '(" l1 = ", i5, :, " l2 = ", i5, :," l3 = ", i5)', &
(l(i), i=1,n)

Dacă n are valoarea 3, atunci sunt imprimate trei valori. Dacă n are valoarea 1, atunci, fără două
puncte, va fi tipărit următorul șir de ieșire:

l1 = 59 l2 =

Colonele, totuși, oprește procesarea formatului, astfel încât adnotarea pentru valoarea secundă
absentă nu este tipărită. Acest descriptor de editare nu trebuie separat de un vecin prin virgulă.
Nu are niciun efect dacă există alte elemente în lista I/O.

9.13 I/O neformatat

Întregul capitol s-a ocupat până acum de I/O formatate. Reprezentarea internă a unei valori poate diferi
de forma externă, care este întotdeauna un șir de caractere conținut într-o înregistrare de intrare sau de
ieșire. Utilizarea I/O formatate implică o suprasarcină pentru conversia dintre cele două formulare și
adesea o eroare de rotunjire. Există și dezavantajul că reprezentarea externă ocupă de obicei mai mult
spațiu pe un mediu de stocare decât reprezentarea internă. Aceste trei dezavantaje sunt toate absente
atunci când se utilizează I/O neformatat. În această formă, reprezentarea internă a unei valori este scrisă
exact așa cum este pe suportul de stocare și poate fi citită înapoi direct fără rotunjire sau suprasarcină
de conversie. Aici, o valoare de tip derivat este tratată ca un întreg și nu este echivalentă cu o listă a
componentelor sale finale. Acesta este un alt motiv pentru regula (Secțiunea 9.3) că nu trebuie să aibă o
componentă alocabilă sau pointer la niciun nivel de selecție a componentei.

Acest tip de I/O ar trebui utilizat în toate cazurile în care înregistrările sunt generate de un program
pe un computer, pentru a fi citite înapoi pe același computer sau pe alt computer folosind aceleași
reprezentări numerice interne. Doar atunci când nu este cazul sau când datele trebuie să fie
Machine Translated by Google

Transfer de date 209

vizualizate într-o formă sau alta, ar trebui să fie utilizate I/O formatate. Înregistrările unui fișier trebuie să fie toate
formatate sau toate neformatate (în afară de înregistrarea fișierului final).
I/O neformatat are avantajul incidental de a fi mai simplu de programat, deoarece nu sunt necesare specificații
complicate de format. Formele instrucțiunilor de citire și scriere sunt aceleași ca pentru I/O formatate, dar fără
niciun specificator fmt= sau nml=:

citește (4) q
scrie (nout, iostat=ios, err=110) a

Interpretarea specificatorilor iostat=, err= și end= este ca pentru I/O formatat.


I/O fără avansare nu este disponibil (de fapt, un specificator advance= nu este permis). Fiecare declarație de
citire sau scriere transferă exact o înregistrare. Fișierul trebuie să fie un fișier extern. La ie irea la un fi ier
conectat (Sec iunea 10.1) pentru acces secven ial, este creată o înregistrare de lungime suficientă. La intrare,
tipul și parametrii de tip ai fiecărei entități din listă trebuie să fie în acord cu cei ai valorii din înregistrare, cu
excepția faptului că două reale pot corespunde unui complex atunci când toate trei au același parametru de tip.
Numărul de valori specificat de lista de intrare a unei instrucțiuni citite nu trebuie să depășească numărul de
valori disponibile în înregistrarea curentă.

9.14 Fișiere cu acces direct

Singurul tip de organizare a fișierelor de care ne-am ocupat până acum este fișierul secvențial, care are un început
și un sfârșit și care conține o succesiune de înregistrări, una după alta.
Fortran permite un alt tip de organizare a fișierelor cunoscut sub numele de acces direct (sau uneori ca acces
aleatoriu sau indexat). Toate înregistrările au aceeași lungime, fiecare înregistrare este identificată printr-un
număr de index și este posibil să scrieți, citiți sau rescrieți orice înregistrare specificată, indiferent de poziție. (Într-
un fișier secven ial, numai ultima înregistrare poate fi rescrisă fără a pierde alte înregistrări; în general,
înregistrările din fișierele secven iale nu pot fi înlocuite.) Înregistrările sunt fie toate formatate, fie toate
neformatate.

În mod implicit, orice fișier utilizat de un program Fortran este un fișier secven ial. Un fișier cu acces direct
trebuie declarat ca atare în declarația sa deschisă (descrisă în capitolul următor) cu specificatorii access= 'direct' și
recl=rl (rl este lungimea unei înregistrări din fișier). Odată ce această declarație a fost făcută, citirea și scrierea,
indiferent dacă sunt formatate sau neformatat, procedează așa cum este descris pentru fișierele secvențiale, cu
excepția adăugării unui specificator rec=i la instrucțiunile de citire și scriere, unde i este o expresie întreagă
scalară a cărei valoare este numărul index al înregistrării în cauză. Nu este permis un specificator end=. De obicei,
o instrucțiune de transfer de date pentru un fișier cu acces direct accesează o singură înregistrare, dar în timpul I/
O formatate, orice descriptor de editare oblică mărește numărul înregistrării cu unul și determină continuarea
procesării la începutul acestei înregistrări. O secvență de declarații pentru a scrie, a citi și a înlocui o înregistrare
dată este dată în Figura 9.6.

Fișierul trebuie să fie un fișier extern și formatare listă de nume, formatare direcționată pe listă și
I/O care nu avansează sunt toate indisponibile.
Fișierele cu acces direct sunt deosebit de utile pentru aplicațiile care implică o mulțime de sărituri în interiorul
unui fișier sau în care înregistrările trebuie înlocuite, de exemplu în aplicațiile de baze de date. Un punct slab este
că lungimea tuturor înregistrărilor trebuie să fie aceeași9, totuși,

9Această deficiență este evitată în Fortran 2003 cu accesul la flux, Secțiunea 17.6.
Machine Translated by Google

210 Fortran modern explicat

Figura 9.6 Scrieți, citiți și înlocuiți înregistrarea 14. Declarațiile de deschidere și întrebare sunt explicate în Secțiunile
10.3 și 10.5. întreg, parametru :: nunit=2, len=100 întreg :: i, lungime reală :: a(len), b(len+1:2*len)

întrebați (iolength=lungime) a deschis


(nunit, access='direct', recl=lungime)
:

! Scrieți tabloul B în fișierul cu acces direct din înregistrarea 14 scrieți (nunit, rec=14)
b
:

citeste (nunit, rec=14) a ! Citiți matricea înapoi în matricea a


:
do i = 1, len/2 a(i) = i

sfâr itul face

scrie (nunit, rec=14) a ! Înlocuiți înregistrarea modificată

la ieșirea formatată, înregistrarea este umplută cu spații libere, dacă este necesar. Pentru ieșirea neformatată,
dacă înregistrarea nu este completată, restul este nedefinit.
Această facilitate simplă și puternică permite să fie scrisă o logică de control mult mai clară decât este cazul unui
fișier secven ial care este citit în mod repetat, deplasat înapoi sau derulat înapoi. Numai atunci când fișierele cu
acces direct devin mari pot deveni evidente problemele de timp lungi de acces pe unele sisteme informatice, iar
acest punct trebuie investigat întotdeauna înainte de a se face investiții mari în programarea aplicațiilor mari de
fișiere cu acces direct.
Unele sisteme informatice permit ca același fișier să fie considerat acces secven ial sau direct, conform
specifica iilor din declara ia deschisă sau implicită. Prin urmare, standardul consideră aceasta ca o proprietate a
conexiunii (Secțiunea 10.1) mai degrabă decât a fișierului. În acest caz, ordinea înregistrărilor, chiar și pentru I/O
secvențial, este cea determinată de numerotarea înregistrărilor cu acces direct.

9.15 Executarea unei declarații de transfer de date

Până acum, am folosit ilustrații simple ale declarațiilor de transfer de date fără dependențe.
Cu toate acestea, unele forme de dependență sunt permise și pot fi foarte utile. De exemplu, cel
afirmație

citiți (*, *) n, a(1:n) ! n este un număr întreg

permite ca lungimea unei secțiuni de matrice să facă parte din date.


Având în vedere dependențe, ordinea în care sunt executate operațiunile este importantă. Este după cum urmează:

i) identifica unitatea;
Machine Translated by Google

Transfer de date 211

ii) stabilirea formatului (dacă există);

iii) poziționați fișierul pregătit pentru transfer (dacă este necesar);

iv) transferul de date între fișier și lista I/O sau lista de nume;

v) poziționați fișierul în urma transferului (dacă este necesar);

vi) determina definirea variabilelor iostat și dimensiune (dacă sunt prezente).

Ordinea de transfer a intrării listei de nume este cea din înregistrările de intrare. În caz contrar, ordinea este cea
a listei I/O sau a listei de nume. Fiecare element de intrare este procesat pe rând și poate afecta subobiectele
ulterioare și indicii implicați. Toate expresiile dintr-un articol de listă I/O sunt determinate la începutul procesării
articolului. Dacă o entitate este specificată de mai multe ori în timpul execuției unei instrucțiuni de intrare a listei
de nume, valoarea ulterioară suprascrie valoarea anterioară. Orice matrice de dimensiune zero sau listă de
activități implicite de lungime zero este ignorată.
Când un element de intrare este o matrice, niciun element al matricei nu este permis să afecteze valoarea
unei expresii din elementul respectiv. De exemplu, cazurile prezentate în Figura 9.7 nu sunt permise.
Acest lucru previne apariția dependențelor în cadrul articolului în sine.

Figura 9.7 Dependențe nu sunt permise într-un articol de intrare.


întreg :: j(10)
:
citește *, j(j) ! Nu sunt acceptate !
citește *, j(j(1):j(10)) Nu sunt acceptate

În cazul unui fișier intern, un element I/O nu trebuie să fie în fișier sau asociat cu acesta. Nici
poate un element de intrare să con ină sau să fie asociat cu orice parte a formatului stabilit.
În cele din urmă, o referință de funcție nu trebuie să apară într-o expresie oriunde într-o instrucțiune I/O dacă
determină executarea unei alte instrucțiuni I/O sau a unei instrucțiuni stop.

9.16 Rezumat
Acest capitol a început descrierea facilităților I/O extinse ale Fortran. Acesta a acoperit instrucțiunile I/O formatate
și specificațiile de format asociate acestora, apoi s-a orientat către fișiere I/O neformatate și cu acces direct.

Sintaxa instrucțiunilor de citire și scriere a fost introdusă treptat. Sintaxa completă este

citiți (lista de control) [listă de intrare]

și

scrie (lista de control) [listă de ieșire]

unde lista de control conține unul sau mai multe dintre următoarele:
Machine Translated by Google

212 Fortran modern explicat

unitate= u err= etichetă de eroare

fmt= fmt sfâr it= etichetă finală

nml= nml-name advance= advance


rec= i dimensiune = dimensiune

iostat= ios eor= eor-etichetă

O listă de control trebuie să includă un specificator de unitate și nu trebuie să includă niciun


specificator de mai multe ori. Variabilele iostat și dimensiunea nu trebuie să fie asociate între ele (de
exemplu, să fie identice), nici cu nicio entitate care este transferată, nici cu vreo do-var dintr-o listă de
acțiuni implicite a aceleiași instrucțiuni. Dacă oricare dintre aceste variabile este un element de matrice,
valoarea indicelui nu trebuie să fie afectată de transferul de date, procesarea implicită sau de evaluarea
oricărui alt specificator din instrucțiune.

Exerciții

1. Scrieți instrucțiuni de tipărire adecvate pentru a tipări numele și conținutul fiecăreia dintre următoarele matrice:

i) real :: grid(10,10), zece elemente pe o linie (presupunând că valorile sunt între 1,0 și
100,0);

ii) întreg :: list(50), numai elementele impare; iii)

caracter(len=10) :: titluri(20), două elemente la o linie;

iv) real :: putere(10), cinci elemente la o linie în notație inginerească;

v) logic :: flags(10), pe o linie;

vi) complex :: plan(5), pe o linie.

2. Scrieți enunțuri pentru a afișa starea unui joc de tic-tac-toe (nu și cruci) către o unitate
desemnat de unitatea variabilă.

3. Scrieți un program care citește o înregistrare de intrare de până la 132 de caractere într-un fișier intern și o clasifică
ca o linie de comentariu Fortran fără instrucțiune, o linie inițială fără etichetă de instrucțiune, o linie inițială cu o
etichetă de instrucțiune, o linie de continuare. , sau o linie care conține mai multe instrucțiuni.

4. Scrieți instrucțiuni de intrare separate, direcționate pe listă, pentru a umple fiecare dintre tablourile din Exercițiul 1. Pentru fiecare
instrucțiune scrieți un eșantion de primă înregistrare de intrare.

5. Scrieți o subrutină get_char(unit,c,end_of_file) pentru a citi un singur caracter c dintr-o unitate de fișier formatată,
secven ial, ignorând orice structură de înregistrare; end_of_file este o variabilă logică căreia i se dă valoarea .true.
dacă se ajunge la sfârșitul fișierului și valoarea .false. in caz contrar.
Machine Translated by Google

10. Operatii pe fisiere externe

10.1 Introducere

Până acum am discutat subiectul fișierelor externe într-un mod destul de superficial. În exemplele diferitelor
instrucțiuni I/O din capitolul anterior, s-a făcut întotdeauna o presupunere implicită că fișierul specificat a fost
efectiv disponibil și că înregistrările ar putea fi scrise și citite din el. Pentru fișierele secvențiale, instrucțiunile de
control al fișierelor descrise în secțiunea următoare presupun în continuare că poate fi poziționat. De fapt, aceste
presupuneri nu sunt neapărat valide.
Pentru a defini în mod explicit și pentru a testa starea fișierelor externe, sunt furnizate trei instrucțiuni de stare a
fișierului: deschidere, închidere și interogare. Înainte de a începe descrierea lor, totuși, sunt necesare două definiții
noi.
Un sistem informatic conține, printre alte componente, un procesor și un sistem de stocare.
Sistemele moderne de stocare se bazează de obicei pe o anumită formă de disc, care este folosit pentru a stoca
fișiere pentru perioade lungi sau scurte de timp. Execuția unui program de calculator este, prin comparație, un
eveniment tranzitoriu. Un fișier poate exista de ani de zile, în timp ce programele rulează doar câteva secunde sau
minute. În terminologia Fortran, se spune că un fișier există nu în sensul pe care tocmai l-am folosit, ci în sensul
restrâns că există ca fișier la care programul ar putea avea acces. Cu alte cuvinte, dacă programului i se interzice
utilizarea fișierului din cauza unui sistem de protecție cu parolă sau pentru că nu a fost întreprinsă o altă acțiune
necesară, fișierul „nu există”.
Un fișier care există pentru un program care rulează poate fi gol și poate fi sau nu conectat la acel program.
Fișierul este conectat dacă este asociat cu un număr de unitate cunoscut programului. O astfel de conexiune se
realizează de obicei prin executarea unei instrucțiuni deschise pentru fișier, dar multe sisteme informatice vor
preconecta anumite fișiere pe care este de așteptat să le folosească orice program, cum ar fi intrarea și ieșirea
terminalului. Astfel, vedem că un fișier poate exista, dar nu poate fi conectat.
Poate fi, de asemenea, conectat, dar nu există. Acest lucru se poate întâmpla pentru un fișier nou preconectat.
Fișierul va intra în existență (va fi creat) numai dacă este întreprinsă o altă acțiune asupra fișierului: executarea unei
instrucțiuni de deschidere, scriere, tipărire sau finalizare. O unitate nu trebuie conectată la mai mult de un fișier
deodată și un fișier nu trebuie conectat la mai mult de o unitate deodată.
Există o serie de alte puncte de reținut în ceea ce privește fișierele.

• Setul de nume permise pentru un fișier depinde de procesor.

• Atât accesul secven ial, cât i cel direct pot fi disponibile pentru unele fi iere, dar în mod normal un
fi ier este limitat la unul sau altul.

• Un fișier nu conține niciodată atât înregistrări formatate, cât și neformatate.


Machine Translated by Google

214 Fortran modern explicat

În cele din urmă, observăm că nicio declarație descrisă în acest capitol nu se aplică fișierelor interne.

10.2 Declarații de poziționare pentru fișiere secvențiale

Când citiți sau scrieți un fișier extern care este conectat pentru acces secvențial, indiferent dacă este formatat sau
neformatat, uneori este necesar să efectuați alte funcții de control asupra fișierului în plus față de intrare și ieșire.
În special, se poate dori să se modifice poziția curentă, care poate fi într-o înregistrare, între înregistrări, înaintea
primei înregistrări (la punctul inițial) sau după ultima înregistrare (la punctul său terminal). Următoarele trei
declarații sunt furnizate în aceste scopuri.

10.2.1 Instrucțiunea backspace

Se poate întâmpla într-un program să fie scrisă o serie de înregistrări și ca, din anumite motive, ultima înregistrare
scrisă să fie suprascrisă cu una nouă. În mod similar, atunci când citiți înregistrări, poate fi necesar să recitiți
ultima înregistrare citită sau să verificați și să citiți o înregistrare care tocmai a fost scrisă. În acest scop, Fortran
oferă instrucțiunea backspace, care are sintaxa

backspace u
sau

backspace ([unit=]u [,iostat=ios] [,err=eroare-etichetă])

unde u este o expresie întreagă scalară a cărei valoare este numărul unității, iar ceilalți specificatori opționali au
aceeași semnificație ca și pentru o instrucțiune citită. Din nou, specificatorii de cuvinte cheie pot fi în orice ordine,
dar specificatorul de unitate trebuie să fie primul ca specificator de poziție.
Acțiunea acestei instrucțiuni este de a poziționa fișierul înaintea înregistrării curente dacă este poziționat într-
o înregistrare sau înaintea înregistrării precedente dacă este poziționat între înregistrări. O încercare de backspace
atunci când este deja poziționat la începutul unui fișier nu duce la nicio modificare a poziției fișierului. Dacă fișierul
este poziționat după o înregistrare a fișierului final (Secțiunea 10.2.3), acesta devine poziționat înaintea acelei
înregistrări. Nu este posibil să backspace un fișier care nu există, nici să backspace peste o înregistrare scrisă
printr-o instrucțiune de ieșire cu listă direcționată sau listă de nume (Secțiunile 9.9 și 9.10). O serie de instrucțiuni
backspace vor trece înapoi peste numărul corespunzător de înregistrări. Această afirmație este adesea foarte
costisitoare în resursele computerului și ar trebui folosită cât mai puțin posibil.

10.2.2 Declarația de derulare înapoi

Într-un mod analog cu recitirea, rescrierea sau citirea prin verificare a unei înregistrări, o operație similară poate
fi efectuată pe un fișier complet. În acest scop, declarația rewind,

derulează-te înapoi

sau

înapoi ([unit=]u [,iostat=ios] [,err=eroare-etichetă])


Machine Translated by Google

Operații pe fișiere externe 215

poate fi folosit pentru a repoziționa un fișier, al cărui număr de unitate este specificat de expresia întreg scalar u.
Din nou, specificatorii de cuvinte cheie pot fi în orice ordine, dar specificatorul de unitate trebuie să fie primul ca
specificator de poziție. Dacă fișierul este deja la început, nu există nicio modificare a poziției sale. Declarația este
permisă pentru un fișier care nu există și nu are efect.

10.2.3 Declarația endfile

Sfârșitul unui fișier conectat pentru acces secvențial este în mod normal marcat de o înregistrare specială care
este identificată ca atare de hardware-ul computerului, iar sistemele computerizate asigură că toate fișierele
scrise de un program sunt terminate corect de o astfel de înregistrare a fișierului final. În situații îndoielnice sau
când un pas ulterior al programului va reciti fișierul, este posibil să scrieți o înregistrare a fișierului final în mod
explicit folosind instrucțiunea fișierului final:

endfile u
sau

fișier final ([unit=]u [,iostat=ios] [,err=eroare-etichetă])

unde u, din nou, este o expresie întreagă scalară care specifică numărul unității. Din nou, specificatorii de cuvinte
cheie pot fi în orice ordine, dar specificatorul de unitate trebuie să fie primul ca specificator de poziție. Fișierul
este apoi poziționat după înregistrarea fișierului final. Această înregistrare a fișierului final, dacă este citită ulterior
de un program, trebuie gestionată utilizând specificatorul iostat=ios sau end=end-label al instrucțiunii de citire,
altfel executarea programului se va termina în mod normal. Înainte de transferul de date, un fișier nu trebuie să
fie poziționat după o înregistrare a fișierului final, dar este posibil să faceți înapoi sau să derulați înapoi peste o
înregistrare a fișierului final, ceea ce permite transferul suplimentar de date.
O înregistrare de fișier final este scrisă automat ori de câte ori o operație de backspace sau de derulare înapoi
urmează o operație de scriere ca următoarea operație pe unitate, sau fișierul este închis prin executarea unei
instrucțiuni de închidere (Secțiunea 10.4), printr-o instrucțiune de deschidere pentru aceeași unitate ( Secțiunea
10.3), sau prin terminarea normală a programului.
Dacă fișierul poate fi conectat și pentru acces direct, numai înregistrările dinaintea înregistrării fișierului final
sunt considerate a fi scrise și numai acestea pot fi citite în timpul unei conexiuni ulterioare cu acces direct.

Rețineți că dacă un fișier este conectat la o unitate, dar nu există pentru program, acesta va fi creat
să existe prin executarea unei instrucțiuni endfile pe unitate.

10.2.4 Declarații de transfer de date

Executarea unei instrucțiuni de transfer de date (citire, scriere sau imprimare) pentru un fișier secven ial
afectează, de asemenea, poziția fișierului. Dacă este între înregistrări, este mutat la începutul următoarei înregistrări.
Apoi are loc transferul de date, care de obicei mută poziția. Nu mai are loc nicio mișcare pentru acces fără
avansare. Pentru avansarea accesului, poziția se mută în cele din urmă pentru a urma ultima înregistrare
transferată.
Machine Translated by Google

216 Fortran modern explicat

10.3 Declarația deschisă

Declarația open este folosită pentru a conecta un fișier extern la o unitate, pentru a crea un fișier care este
preconectat, pentru a crea un fișier și pentru a-l conecta la o unitate sau pentru a modifica anumite proprietăți
ale unei conexiuni. Sintaxa este

deschide ([unit=]u [,olist])

unde u este o expresie întreagă scalară care specifică numărul unității de fișier extern, iar olist este o listă de
specificatori opționali. Dacă unitatea este specificată cu unit=, aceasta poate apărea în olistă. Un specificator
nu trebuie să apară de mai multe ori. În specificatori, toate entitățile sunt scalare și toate caracterele sunt de
tip implicit. În expresiile de caractere, orice spații libere de la urmă sunt ignorate și, cu excepția fișierului=,
orice litere mari sunt convertite în litere mici. Specificatorii sunt după cum urmează.

iostat= ios, unde ios este o variabilă întreagă implicită care este setată la zero dacă instrucțiunea este
executată corect și la o valoare pozitivă în caz contrar.

err= eticheta de eroare, unde eticheta de eroare este eticheta unei instrucțiuni din aceeași unitate de
acoperire la care va fi transferat controlul în cazul apariției unei erori în timpul execuției instrucțiunii.

file= fln, unde fln este o expresie de caractere care furnizează numele fișierului. Dacă acest specificator este
omis și unitatea nu este conectată la un fișier, specificatorul status= trebuie specificat cu valoarea
scratch și fișierul conectat la unitate va depinde apoi de sistemul computerizat. Dacă interpretarea
este sensibilă la majuscule și minuscule, variază de la sistem la sistem.

status= st, unde st este o expresie de caracter care furnizează valoarea vechi, nou, înlocuire, zero sau
necunoscut. Specificatorul fișier= trebuie să fie prezent dacă este specificat nou sau înlocuit sau dacă
este specificat vechi și unitatea nu este conectată; specificatorul fișier= nu trebuie să fie prezent dacă
este specificat scratch. Dacă este specificat vechi, fișierul trebuie să existe deja; dacă se specifică nou,
dosarul nu trebuie să existe deja, ci va fi adus în existență prin acțiunea declarației deschise. Starea
fișierului devine atunci veche. Dacă este specificat înlocuire și fișierul nu există deja, fișierul este creat;
dacă fișierul există, fișierul este șters și este creat un fișier nou cu același nume. În fiecare caz, starea
este schimbată în vechi. Dacă este specificată valoarea scratch, fișierul este creat și devine conectat,
dar nu poate fi păstrat după finalizarea programului sau execuția unei instrucțiuni close (Secțiunea
10.4). Dacă este specificat necunoscut, starea fișierului depinde de sistem. Aceasta este valoarea
implicită a specificatorului, dacă este omisă.

access= acc, unde acc este o expresie de caracter care furnizează una dintre valorile secven ial sau direct.
Pentru un fișier care există deja, această valoare trebuie să fie o valoare permisă. Dacă fișierul nu
există deja, acesta va fi adus în existență cu metoda de acces adecvată. Dacă acest specificator este
omis, se va presupune valoarea secven ială.

form= fm, unde fm este o expresie de caracter care furnizează valoarea formatată sau neformatată și
determină dacă fișierul trebuie conectat pentru format sau
Machine Translated by Google

Operații pe fișiere externe 217

I/O neformatat. Pentru un fișier care există deja, valoarea trebuie să fie o valoare permisă.
Dacă fișierul nu există deja, acesta va fi adus în existență cu un set permis de formulare care include
formularul specificat. Dacă acest specificator este omis, implicit este formatat pentru acces secven ial
i neformatat pentru conexiunea cu acces direct.

recl= rl, unde rl este o expresie întreagă a cărei valoare trebuie să fie pozitivă. Pentru un fișier cu acces direct,
se specifică lungimea înregistrărilor și este obligatoriu. Pentru un fișier secven ial, acesta specifică
lungimea maximă a unei înregistrări și este op ional cu o valoare implicită care depinde de procesor.
Pentru fișierele formatate, lungimea este numărul de caractere pentru înregistrările care conțin doar
caractere implicite; pentru fișierele neformatate este dependent de sistem, dar instrucțiunea inquire
(Secțiunea 10.5) poate fi folosită pentru a găsi lungimea unei liste I/O.
În ambele cazuri, pentru un fișier care există deja, valoarea specificată trebuie să fie permisă pentru acel
fișier. Dacă fișierul nu există deja, fișierul va fi adus în existență cu un set permis de lungimi de
înregistrare care include valoarea specificată.

blank= bl, unde bl este o expresie de caracter care furnizează valoarea nulă sau zero.
Această conexiune trebuie să fie pentru I/O formatat. Acest specificator stabilește valoarea implicită
pentru interpretarea spațiilor libere în câmpurile de introducere numerică, așa cum se discută în
descrierea descriptorilor de editare bn și bz (Secțiunea 9.12.4, Spații încorporate). Dacă valoarea este
nulă, astfel de spații libere vor fi ignorate (cu excepția faptului că un câmp complet gol este interpretat
ca zero). Dacă valoarea este zero, astfel de spații libere vor fi interpretate ca zerouri. Dacă specificatorul
este omis, valoarea implicită este nulă.

position= pos, unde pos este o expresie de caracter care furnizează valoarea asis, rewind sau append. Metoda
de acces trebuie să fie secven ială, iar dacă specificatorul este omis, se va presupune valoarea implicită
asis. Un fișier nou este poziționat în punctul său inițial. Dacă este specificat asis și fișierul există și este
deja conectat, fișierul este deschis fără a-și schimba poziția; dacă se specifică rewind, fișierul este
poziționat în punctul său inițial; dacă se specifică append și fișierul există, acesta este poziționat înaintea
înregistrării fișierului final dacă are una (și în caz contrar la punctul său terminal). Pentru un fișier care
există, dar nu este conectat, efectul specificatorului asis asupra poziției fișierului este nespecificat.

action= act, unde act este o expresie de caracter care furnizează valoarea read, write sau readwrite. Dacă este
specificat read, instrucțiunile write, print și endfile nu trebuie utilizate pentru această conexiune; dacă
scrieți este specificat, instrucțiunea read nu trebuie utilizată (și backspace și position='append' pot eșua
pe unele sisteme); dacă este specificată readwrite, nu există nicio restricție. Dacă specificatorul este
omis, valoarea implicită depinde de procesor.

delim= del, unde del este o expresie de caracter care furnizează citatul valoric, apostrof sau niciunul. Dacă este
specificat apostrof sau ghilimeleu, caracterul corespunzător va fi folosit pentru a delimita constantele
de caractere scrise cu formatare listă sau listă de nume și va fi dublat acolo unde apare într-o astfel de
constantă de caractere; de asemenea, valorile caracterelor care nu sunt implicite vor fi precedate de
valori tip. Nu se folosește niciun caracter de delimitare dacă nu este specificat niciunul și nici nu are loc
nicio dublare. Valoarea implicită dacă specificatorul este omis este niciuna. Acest specificator poate
apărea numai pentru fișierele formatate.
Machine Translated by Google

218 Fortran modern explicat

pad= pad, unde pad este o expresie de caracter care furnizează valoarea da sau nu. Dacă este specificat
da, o înregistrare de intrare formatată va fi considerată completată cu spații libere ori de câte ori o
listă de intrare și formatul asociat specifică mai multe date decât apar în înregistrare. (Dacă nu este
specificat, lungimea înregistrării de intrare nu trebuie să fie mai mică decât cea specificată de lista
de intrare și formatul asociat, cu excepția prezenței unui specificator advance='nu' și fie o
specificație eor= sau iostat= .) Valoarea implicită dacă specificatorul este omis este da. Pentru
caracterele care nu sunt implicite, caracterul de completare gol este dependent de procesor.

Un exemplu de declarație deschisă este

deschide (2, iostat=ios, err=99, file='orașe', &

status='nou', acces='direct', recl=100)

care aduce la existență un fișier nou, cu acces direct, neformatat, numit orașe, ale cărui înregistrări au
lungimea de 100. Fișierul este conectat la unitatea numărul 2. Neexecutarea corectă a instrucțiunii va face
ca controlul să fie transmis instrucțiunii etichetate 99, unde valoarea lui ios poate fi testată.

Declarațiile deschise dintr-un program sunt cel mai bine adunate într-un singur loc, astfel încât orice
modificări care ar putea trebui făcute la ele la transportul programului de la un sistem la altul pot fi
efectuate fără a fi nevoie să le căutați. Indiferent de locul în care apar, conexiunea poate fi referită în orice
unitate de program a programului.
Scopul declarației deschise este de a conecta un fișier la o unitate. Dacă unitatea este, totuși, deja
conectată la un fișier, atunci acțiunea poate fi diferită. Dacă specificatorul fișier= este omis, implicit este
numele fișierului conectat. Dacă fișierul în cauză nu există, dar este preconectat la unitate, atunci toate
proprietățile specificate de declarația deschisă devin parte a conexiunii. Dacă fișierul este deja conectat la
unitate, atunci dintre atributele existente doar specificatorii blank=, delim=, pad=, err= și iostat= pot avea
valori diferite de cele deja în vigoare. Dacă unitatea este deja conectată la un alt fișier, efectul declarației
deschise include acțiunea unei instrucțiuni anterioare de închidere asupra unității (fără un specificator
status=, vezi secțiunea următoare).

Un fișier deja conectat la o unitate nu trebuie specificat pentru conectarea la o altă unitate.
În general, prin executarea repetată a instrucțiunii deschise pe aceeași unitate, este posibil să se
proceseze în succesiune un număr arbitrar de mare de fișiere, indiferent dacă acestea există sau nu, atâta
timp cât sunt respectate restricțiile tocmai notate.

10.4 Declarația de închidere

Scopul declarației close este de a deconecta un fișier de la o unitate. Forma sa este

close ([unit=]u [,iostat=ios] [,err=eroare-etichetă] [,status=st])

unde u, ios și error-label au aceleași semnificații ca cele descrise în secțiunea anterioară pentru declarația
deschisă. Din nou, specificatorii de cuvinte cheie pot fi în orice ordine, dar specificatorul de unitate trebuie
să fie primul ca specificator de poziție.
Funcția specificatorului status= este de a determina ce se va întâmpla cu fișierul odată ce acesta este
deconectat. Valoarea lui st, care este o expresie de caracter implicită scalară, poate fi oricare
Machine Translated by Google

Operații pe fișiere externe 219

păstrați sau ștergeți, ignorând orice spații libere și conversia orice literă majuscule în minuscule. Dacă
valoarea este păstrată, un fișier care există continuă să existe după executarea instrucțiunii close și mai
târziu poate fi conectat din nou la o unitate. Dacă valoarea este ștearsă, fișierul nu mai există după
executarea instrucțiunii. În ambele cazuri, unitatea este liberă să fie conectată din nou la un fișier.
Instrucțiunea close poate apărea oriunde în program și, dacă este executată pentru o unitate inexistentă
sau neconectată, acționează ca o instrucțiune „nu face nimic”. Valoarea keep nu trebuie specificată pentru
fișierele cu starea scratch.
Dacă specificatorul status= este omis, valoarea sa implicită este păstrată, cu excepția cazului în care
fișierul are status scratch, caz în care valoarea implicită este ștearsă. La terminarea normală a execuției,
toate unitățile conectate sunt închise, ca și cum ar fi executate instrucțiuni close cu specificatorii status=
omis.
Un exemplu de afirmație apropiată este

close (2, iostat=ios, err=99, status='delete')

10.5 Declarația de întrebare

Starea unui fișier poate fi definită de sistemul de operare înainte de execuția programului, sau de programul
însuși în timpul execuției, fie printr-o instrucțiune deschisă, fie printr-o acțiune asupra unui fișier preconectat
care îl aduce în existență. În orice moment în timpul execuției unui program, este posibil să se întrebe
despre starea și atributele unui fișier folosind instrucțiunea de interogare. Folosind o variantă a acestei
declarații, este în mod similar posibil să se determine starea unei unități, de exemplu dacă numărul unității
există pentru acel sistem (adică dacă este un număr de unitate permis), dacă numărul unității are un fișier
conectat lui și, dacă da, ce atribute are acel fișier. O altă variantă permite o interogare despre lungimea
unei liste de ieșire atunci când este utilizată pentru a scrie o înregistrare neformatată.

Unele dintre atributele care pot fi determinate prin utilizarea declarației de întrebare depind de altele.
De exemplu, dacă un fișier nu este conectat la o unitate, nu este semnificativ să întrebați despre formularul
utilizat pentru acel fișier. Dacă se încearcă totuși acest lucru, specificatorul relevant este nedefinit.

Cele trei variante sunt cunoscute ca interogare prin fișier, interogare după unitate și interogare prin listă
de ieșire. În descrierea declarației de întrebare care urmează, primele două variante vor fi descrise
împreună. Formele lor sunt

întrebați ([unit=]u, ilist)

pentru întrebarea după unitate, unde u este o expresie întreagă scalară care specifică o unitate externă și

întreabă (fișier=fln, ilist)

pentru interogare prin fișier, unde fln este o expresie de caracter scalară a cărei valoare, ignorând orice
spații libere, furnizează numele fișierului în cauză. Dacă interpretarea este sensibilă la majuscule, depinde
de sistem. Dacă unitatea sau fișierul este specificat prin cuvânt cheie, acesta poate apărea în ilist. Un
specificator nu trebuie să apară de mai multe ori în lista de specificatori opționali, ilist. Toate atribuirile au
loc urmând regulile obișnuite și toate valorile de tip caracter, în afară de aceasta
Machine Translated by Google

220 Fortran modern explicat

pentru name= specifier, sunt în majuscule. Specificatorii, în care toate variabilele sunt scalare și de tip implicit,1
sunt după cum urmează.

iostat= ios și err= error-label, au semnificațiile descrise pentru ele în declarația deschisă din Secțiunea 10.3.
Variabila iostat= este singura care este definită dacă apare o condiție de eroare în timpul execuției
instrucțiunii.

exist= ex, unde ex este o variabilă logică. Valoarea true este atribuită ex dacă fișierul (sau unitatea)
există și fals în caz contrar.

deschis= deschis, unde deschis este o variabilă logică. Valoarea true este atribuită pentru deschidere dacă
fișierul (sau unitatea) este conectat la o unitate (sau fișier) și false în caz contrar.

număr= num, unde num este o variabilă întreagă căreia i se atribuie valoarea numărului unității
conectat la fișier sau -1 dacă nicio unitate nu este conectată la fișier.

named= nmd și name= nam, unde nmd este o variabilă logică căreia i se atribuie valoarea true dacă fișierul
are un nume și false în caz contrar. Dacă fișierul are un nume, variabilei caracter nam i se va atribui
numele. Această valoare nu este neapărat aceeași cu cea dată în specificatorul de fișier, dacă este
utilizată, dar poate fi calificată într-un fel. Cu toate acestea, în toate cazurile, este un nume care este
valabil pentru utilizare într-o declarație deschisă ulterioară și, astfel, interogarea poate fi utilizată pentru
a determina numele real al unui fișier înainte de a-l conecta.
Dacă numele fișierului este sensibil la majuscule, depinde de sistem.

access= acc, unde acc este o variabilă caracter căreia i se atribuie una dintre valorile SECVENTIAL sau DIRECT
în funcție de metoda de acces pentru un fișier care este conectat și NEDEFINIT dacă nu există conexiune.

secvențial= seq și direct= dir, unde seq și dir sunt variabile de caracter cărora li se atribuie valoarea DA, NU sau
NECUNOSCUT, în funcție de dacă fișierul poate fi deschis pentru acces secvențial sau direct, sau dacă
acest lucru nu poate fi determinat.

form= frm, unde frm este o variabilă de caracter căreia i se atribuie una dintre valorile FORMATTED sau
NEFORMATATE, în funcție de forma la care fișierul este conectat efectiv, și NEDEFINIT dacă nu există
nicio conexiune.

formated= fmt și unformatted= unf, unde fmt și unf sunt variabile de tip caracter cărora li se atribuie valoarea
YES, NO sau NESKNOWN, în funcție de dacă fișierul poate fi deschis pentru acces formatat sau
neformatat, sau dacă acest lucru nu poate fi determinat.

recl= rec, unde rec este o variabilă întreagă căreia i se atribuie valoarea lungimii înregistrării unui fișier conectat
pentru acces direct sau lungimea maximă de înregistrare permisă pentru un fișier conectat pentru
acces secven ial. Lungimea este numărul de caractere pentru înregistrările formatate care conțin
doar caractere de tip implicit și care depind de sistem în caz contrar. Dacă nu există nicio conexiune,
rec devine nedefinit.

1 Cele de tip întreg sau logic pot fi de orice fel în Fortran 2003.
Machine Translated by Google

Operații pe fișiere externe 221

nextrec= nr, unde nr este o variabilă întreagă căreia i se atribuie valoarea numărului ultimei înregistrări
citite sau scrise, plus unu. Dacă nicio înregistrare nu a fost încă citită sau scrisă, i se atribuie valoarea
1. Dacă fișierul nu este conectat pentru acces direct sau dacă poziția este nedeterminată din cauza
unei erori anterioare, nr devine nedefinit.

blank= bl, unde bl este o variabilă tip caracter căreia i se atribuie valoarea NULL sau ZERO, în funcție de
faptul că spațiile libere din câmpurile numerice sunt interpretate implicit ca câmpuri nule sau,
respectiv, zerouri, și UNDEFINED dacă fie nu există nicio conexiune, sau dacă conexiunea nu este
pentru I/O formatat.

poziție= pos, unde pos este o variabilă caracter căreia i se atribuie valoarea REWIND, APPEND sau ASIS, așa
cum este specificat în instrucțiunea deschisă corespunzătoare, dacă fișierul nu a fost repoziționat
de când a fost deschis. Dacă nu există nicio conexiune sau dacă fișierul este conectat pentru acces
direct, valoarea este NEDEFINITĂ. Dacă fișierul a fost repoziționat de când a fost stabilită conexiunea,
valoarea este dependentă de procesor (dar nu trebuie să fie REWIND sau APPEND decât dacă
aceasta corespunde cu poziția adevărată).

action= act, unde act este o variabilă caracter căreia i se atribuie valoarea READ, WRITE sau READWRITE, în
funcție de conexiune. Dacă nu există nicio conexiune, valoarea atribuită este NEDEFINIȚĂ.

read= rd, unde rd este o variabilă caracter căreia i se atribuie valoarea YES, NO sau NECUNOSCUT, în funcție
de faptul că citirea este permisă, nu este permisă sau este nedeterminată pentru fișier.

write= wr, unde wr este o variabilă caracter căreia i se atribuie valoarea YES, NO sau NECUNOSCUT, în
funcție de faptul că scrierea este permisă, nu este permisă sau este nedeterminată pentru fișier.

readwrite= rw, unde rw este o variabilă caracter căreia i se atribuie valoarea YES, NO sau NECUNOSCUT, în
funcție de dacă citirea/scrierea este permisă, nu este permisă sau este nedeterminată pentru fișier.

delim= del, unde del este o variabilă de caracter căreia i se atribuie valoarea CITAT, APOSTROF sau NIMIC,
așa cum este specificat de instrucțiunea deschisă corespunzătoare (sau implicit). Dacă nu există
nicio conexiune sau dacă fișierul nu este conectat pentru I/O formatat, valoarea atribuită este
NEDEFINIT.

pad= pad, unde pad este o variabilă de caracter căreia i se atribuie valoarea YES sau NO, așa cum este
specificat de instrucțiunea deschisă corespunzătoare (sau implicit). Dacă nu există nicio conexiune
sau dacă fișierul nu este conectat pentru I/O formatat, valoarea atribuită este NEDEFINITĂ.

O variabilă care este un specificator într-o instrucțiune inquire sau este asociată cu una nu trebuie să
apară într-un alt specificator în aceeași instrucțiune.
A treia variantă a instrucțiunii de interogare, interogare prin listă I/O, are forma

inquire (iolength=lungime) olist

unde lungimea este o variabilă întregă scalară de tip implicit și este utilizată pentru a determina lungimea
unei liste de ieșire neformatată în unități dependente de procesor și poate fi utilizată pentru a stabili dacă,
de exemplu, o listă de ieșiri este prea lungă pentru lungimea înregistrării dată în recl=
Machine Translated by Google

222 Fortran modern explicat

specificatorul unei instrucțiuni deschise sau să fie utilizat ca valoare a lungimii care trebuie furnizată unui
specificator recl= (vezi Figura 9.6 din Secțiunea 9.14).
Un exemplu de declarație de întrebare, pentru fișierul deschis ca exemplu de deschidere
declarația din Secțiunea 10.3, este

logic :: ex, caracter op (len=11) :: nam, acc,


seq, frm integer :: irec, nr inquire (2, err=99, exist=ex,
opened=op, name=nam, access=acc
form=frm,
, & secven
recl=irec,ial=seq,
nextrec=nr)

După executarea cu succes a acestei instrucțiuni, variabilelor furnizate li se vor fi atribuite următoarele valori:

ex .Adevărat.

op .Adevărat.

nam orasebbbbb
conform DIRECTbbbbb
secv NUbbbbbbbbb
frm NEFORMATATE
irec 100
nr 1

(presupunând că nu intervin operațiuni de citire sau scriere).


Cele trei instrucțiuni de stare I/O tocmai descrise sunt poate cele mai indigeste dintre toate instrucțiunile
Fortran. Ele oferă, totuși, o facilitate puternică și portabilă pentru alocarea și dealocarea dinamică a fișierelor,
complet sub controlul programului, ceea ce este cu mult înaintea celui găsit în orice alt limbaj de programare
adecvat aplicațiilor științifice.

10.6 Rezumat

Acest capitol a completat descrierea caracteristicilor de intrare/ieșire începută în capitolul anterior și, împreună,
oferă o referință completă la toate facilitățile disponibile.

Exerciții

1. Un fișier cu acces direct trebuie să conțină o listă de nume și inițiale, fiecăruia dintre care îi corespunde un
număr de telefon. Scrieți un program care deschide un fișier secvențial și un fișier cu acces direct și copiază
lista din fișierul secvențial în fișierul cu acces direct, închizându-l pentru utilizare într-un alt program.
Scrieți un al doilea program care citește o înregistrare de intrare care conține fie un nume, fie un număr de
telefon (de la un terminal, dacă este posibil) și imprimă intrarea (sau intrările) corespunzătoare în fișierul cu
acces direct, dacă este prezent, și un mesaj de eroare în caz contrar. Amintiți-vă că numele sunt la fel de
diverse precum Wu, O'Hara și Trevington-Smythe și că este jignitor ca un program de calculator să corupă
sau să prescurteze numele oamenilor. Formatul numerelor de telefon ar trebui să corespundă numerelor
dvs. locale, dar formatul efectiv utilizat ar trebui să fie ușor de modificat la altul.
Machine Translated by Google

11. Gestionarea excepțiilor în virgulă mobilă

11.1 Introducere

Gestionarea excepțiilor este necesară pentru dezvoltarea unui software numeric robust și eficient, o
aplicație principală a Fortran. Într-adevăr, existența unei astfel de facilități face posibilă dezvoltarea unui
software mai eficient decât ar fi posibil altfel. Nevoia clară de tratare a excepțiilor, ceva care a fost lăsat
în afara Fortran 95, a condus la dezvoltarea unei facilitati pe o „căială rapidă” ca Raport tehnic,1 adecvată
pentru implementare imediată ca o extensie a Fortran 95. În acest capitol, descriem extensiile la Fortran
95 care au fost detaliate în acest Raport și care sunt toate incluse în Fortran 2003. De asemenea, descriem
câteva caracteristici legate de Fortran 2003 care nu erau în Raport, cu o indicație clară a acestui lucru în
fiecare caz.

Cele mai multe computere din zilele noastre au hardware bazat pe standardul IEEE pentru aritmetica
binară în virgulă mobilă,2 care a devenit ulterior un standard ISO.3 Prin urmare, caracteristicile de
gestionare a excepțiilor Fortran se bazează pe capacitatea de a testa și seta cele cinci steaguri pentru
excepțiile în virgulă mobilă care standardul IEEE specifică. Cu toate acestea, computerele non-IEEE nu au
fost ignorate; acestea pot oferi suport pentru unele dintre caracteristici, iar programatorul este capabil
să afle ce este acceptat sau să afirme că anumite caracteristici sunt esențiale.
Puține computere (dacă există) acceptă fiecare detaliu al standardului IEEE. Acest lucru se datorează
faptului că economii considerabile în construcție și creșteri ale performanței de execuție sunt disponibile
prin omiterea suportului pentru caracteristici considerate a fi necesare pentru puțini programatori. Prin
urmare, s-a decis să se includă facilități de interogare pentru amploarea suportului standardului și pentru
ca programatorul să poată preciza care caracteristici sunt esențiale.
Mecanismul ales în cele din urmă de comitete se bazează pe un set de proceduri de setare și testare a
steaguri și de a se întreba despre caracteristici, colectate într-un modul intrinsec numit ieee_exceptions.

Având în vedere că erau furnizate proceduri pentru steagurile IEEE, părea logic să se prevadă proceduri
pentru alte aspecte ale standardului IEEE. Acestea sunt colectate într-un modul intrinsec separat,
ieee_arithmetic, care conține o instrucțiune de utilizare pentru ieee_exceptions.
Pentru a oferi control asupra caracteristicilor esențiale, există un al treilea modul intrinsec,
ieee_features care conține constante numite corespunzătoare caracteristicilor. Dacă o constantă numită
este accesibilă într-o unitate de acoperire, caracteristica corespunzătoare trebuie să fie disponibilă acolo.

1Raport tehnic ISO/IEC TR 15580 : 1998(E).


2IEEE 754-1985, Standard pentru aritmetica binară în virgulă mobilă.
3IEC 559: 1989, Aritmetică binară în virgulă mobilă pentru sisteme cu microprocesoare.
Machine Translated by Google

224 Fortran modern explicat

11.2 Standardul IEEE

În această secțiune, explicăm acele aspecte ale standardului IEEE pe care cititorul trebuie să le cunoască
pentru a înțelege caracteristicile acestui capitol. Nu încercăm să oferim o descriere completă a standardului.

Sunt specificate două formate de date în virgulă mobilă, unul pentru precizie reală și unul pentru precizie dublă
aritmetic. Sunt superseturi ale modelului Fortran, repetate aici (vezi Secțiunea 8.7.1),

x=0

și
p
x = s×fi ×
fk ×b k
k=1

unde s este ±1, p și b sunt numere întregi care depășesc unu, e este un număr întreg într-un interval emin
e emax și fiecare fk este un număr întreg în intervalul 0 fk < b, cu excepția faptului că f1 este, de
asemenea, diferit de zero. Ambele formate IEEE sunt binare, cu b = 2. Preciziile sunt p = 24 și p = 53, iar
intervalele exponenților sunt 125 e 128 și 1021 e 1024, pentru precizie reală și, respectiv, dublă.
În plus, există numere cu e = emin și f1 = 0, care sunt cunoscute ca numere denormalizate; rețineți că toate
au valori absolute mai mici decât cele returnate de minuscul intrinsec, deoarece ia în considerare numai
numerele din modelul Fortran. De asemenea, zero are un semn și ambele 0 și 0 au inverse, și . În
Fortran, 0 este tratat ca un zero în toate operațiile și comparațiile intrinseci, dar poate fi detectat de funcția
semn și este respectat la ieșirea formatată.

Standardul IEEE specifică, de asemenea, că unele dintre modelele binare care nu se potrivesc
modelului să fie utilizate pentru rezultatele operațiilor excepționale, cum ar fi 0/0. Un astfel de număr
este cunoscut sub numele de NaN (Nu este un număr). Un NaN poate fi semnal sau silentios. Ori de câte
ori un NaN de semnalizare apare ca operand, excepția nevalidă semnalează și rezultatul este un NaN
liniștit. NaN-urile silențioase se propagă prin aproape fiecare operație aritmetică fără a semnala o excepție.
Standardul specifică patru moduri de rotunjire:

cea mai apropiată rotunjește rezultatul exact la cea mai apropiată valoare

reprezentabilă. la zero rotunjește rezultatul exact spre zero la următoarea valoare

reprezentabilă. în sus rotunjește rezultatul exact spre + la următoarea valoare

reprezentabilă. în jos rotunjește rezultatul exact spre la următoarea valoare reprezentabilă.

Unele calculatoare efectuează împărțirea prin inversarea numitorului și apoi înmulțirea cu numărătorul.
Rotunjirea suplimentară pe care aceasta o implică înseamnă că o astfel de implementare nu este conformă cu
standardul IEEE. Standardul IEEE specifică, de asemenea, că sqrt rotunjește corect rezultatul exact și returnează
0 pentru 0. Facilitățile Fortran includ funcții de interogare pentru divizia IEEE și sqrt.

Prezența lui 0, , și NaNs permite aritmetica IEEE să fie închisă, adică fiecare operație are un
rezultat. Acest lucru este foarte util pentru optimizarea hardware-ului modern, deoarece mai multe
operațiuni, niciuna care nu are nevoie de rezultatul niciunuia dintre celelalte, pot fi de fapt să progreseze
în paralel. Dacă apare o excepție, execuția continuă cu semnalizarea corespunzătoare a steagului, iar
steag-ul rămâne semnalizat până când programul îl setează în mod explicit silențios. Prin urmare,
steagurile sunt numite lipicioase.
Machine Translated by Google

Gestionarea excepțiilor în virgulă mobilă 225

Există cinci steaguri:

overflow are loc dacă rezultatul exact al unei operații cu două valori normale este prea mare pentru formatul de date.
Rezultatul stocat este , huge(x), -huge(x) sau , în funcție de modul de rotunjire în funcționare,
întotdeauna cu semnul corect.

divide_by_zero apare dacă o valoare finită diferită de zero este împărțită la zero. Rezultatul stocat este sau
cu semnul corect.

invalid apare dacă operația este invalidă, de exemplu, ×0, 0/0 sau când un operand este un
semnalizarea NaN.

underflow apare dacă rezultatul unei operații cu două valori finite diferite de zero nu poate fi reprezentat exact și
este prea mic pentru a fi reprezentat cu precizie deplină. Rezultatul stocat este cel mai bun disponibil, în
funcție de modul de rotunjire în funcțiune. inexact apare dacă rezultatul exact al unei operații nu poate fi

reprezentat în formatul de date fără rotunjire.

Standardul IEEE specifică posibilitatea ca excepțiile să fie captate de handlere scrise de utilizator, dar acest lucru
inhibă optimizarea și nu este acceptat de Fortran. În schimb, Fortran acceptă posibilitatea de a opri execuția
programului după semnalele unei excepții. De dragul optimizării, o astfel de oprire nu trebuie să aibă loc imediat.

Standardul IEEE specifică câteva funcții care sunt implementate în Fortran ca ieee_copy_sign, ieee_logb,
ieee_next_after, ieee_rem, ieee_rint, ieee_scalb și ieee_unordered și sunt descrise în Secțiunea 11.9.3.

11.3 Accesul la funcții

Pentru a accesa caracteristicile acestui capitol, recomandăm utilizatorului să folosească instrucțiuni de utilizare pentru
unul sau mai multe module intrinseci ieee_exceptions, ieee_arithmetic (care conține o instrucțiune de utilizare pentru
ieee_exceptions) și ieee_features. Dacă procesorul nu acceptă un modul accesat într-o instrucțiune de utilizare,
compilarea, desigur, eșuează.
Dacă o unitate de definire a domeniului nu accesează ieee_exceptions sau ieee_arithmetic, nivelul de suport
depinde de procesor și nu trebuie să includă suport pentru nicio excepție. Dacă un steag semnalizează la intrarea
într-o astfel de unitate de delimitare, procesorul se asigură că semnalează la ieșire.
Dacă un flag este silențios la intrarea într-o astfel de unitate de scoping, dacă este semnalizat la ieșire depinde de
procesor.
Modulul ieee_features conține tipul derivat

ieee_features_type

pentru identificarea unei anumite caracteristici. Singurele valori posibile pe care le pot lua obiectele de acest tip sunt
cele ale constantelor numite definite în modul, fiecare corespunzând unei caracteristici IEEE.
Dacă o unitate de definire a domeniului are acces la una dintre aceste constante, compilatorul trebuie să accepte
caracteristica în unitatea de definire a domeniului sau să respingă programul. De exemplu, unele componente
hardware sunt mult mai rapide dacă numerele denormalizate nu sunt acceptate și, în schimb, toate valorile depășite
sunt eliminate la zero. Într-un astfel de caz, declarația

utilizare, intrinsecă :: ieee_features, numai: ieee_denormal


Machine Translated by Google

226 Fortran modern explicat

se va asigura că unitatea de definire a domeniului este compilată cu cod (mai lent) care suportă numere denormalizate.
Această formă a instrucțiunii de utilizare este mai sigură deoarece asigură că, în cazul în care există un alt modul cu

același nume, se folosește cel intrinsec. Este descris pe deplin în Secțiunea 16.5.

Modulul este neobișnuit prin faptul că tot ceea ce face vreodată un cod este să îl acceseze cu instrucțiuni de utilizare,
care afectează modul în care codul este compilat în unitățile de scoping cu acces la una sau mai multe dintre constantele
modulului. Nu există niciun scop în declararea datelor de tip ieee_features_type, deși este permis; componentele tipului
sunt private, nu este definită nicio operațiune pentru acesta și numai atribuirea intrinsecă este disponibilă pentru
acesta. Într-o unitate de definire a domeniului care conține o instrucțiune de utilizare, efectul este cel al unei directive
de compilator, dar celelalte proprietăți de utilizare fac caracteristica mai puternică decât ar fi posibil cu o directivă.

Setul complet de constante numite din modul și efectul accesibilității acestora este:

ieee_datatype Unitatea de definire a domeniului trebuie să furnizeze aritmetica IEEE pentru cel puțin un tip de real.

ieee_denormal Unitatea de acoperire trebuie să accepte numere denormalizate pentru cel puțin un fel
de real.

ieee_divide Unitatea de acoperire trebuie să accepte IEEE divide pentru cel puțin un tip de real.

ieee_halting Unitatea de stabilire a domeniului trebuie să suporte controlul opririi pentru fiecare semnalizare acceptată.

ieee_inexact_flag Unitatea de acoperire trebuie să accepte excepția inexactă pentru cel puțin un fel
de real.

ieee_inf Unitatea de acoperire trebuie să suporte și pentru cel puțin un tip de real.

ieee_invalid_flag Unitatea de acoperire trebuie să accepte excepția nevalidă pentru cel puțin un fel
de real.

ieee_nan Unitatea de definire a domeniului trebuie să accepte NaN pentru cel puțin un fel de real.

ieee_rounding Unitatea de acoperire trebuie să suporte controlul modului de rotunjire pentru toate cele patru
moduri de rotunjire pe cel puțin un fel de real.

ieee_sqrt Unitatea de acoperire trebuie să accepte rădăcină pătrată IEEE pentru cel puțin un tip de real.

ieee_underflow_flag Unitatea de acoperire trebuie să accepte excepția de subflow pentru cel puțin una
cam real.

Execuția poate fi încetinită pe unele procesoare de suportul unor caracteristici. Dacă ieee_exceptions este accesat,
dar ieee_features nu este accesat, furnizorul este liber să aleagă ce subset să accepte. Cel mai deplin suport al
procesorului este oferit atunci când sunt accesate toate ieee_features:

utilizare, intrinsecă :: ieee_aritmetică utilizare,


intrinsecă :: ieee_features

dar execuția poate fi apoi încetinită de prezența unei caracteristici care nu este necesară. În toate cazurile, amploarea
sprijinului poate fi determinată de funcțiile de anchetă din Secțiunile 11.8.2 și 11.9.2.
Machine Translated by Google

Gestionarea excepțiilor în virgulă mobilă 227

11.4 Steagulele Fortran

Există cinci steaguri de excepție Fortran, care corespund celor cinci steaguri IEEE. Fiecare are o valoare care
este fie silentioasa, fie semnalizatoare. Valoarea poate fi determinată de funcția ieee_get_flag (Secțiunea
11.8.3). Valoarea sa inițială este silențioasă și semnalează când apare excepția asociată într-o operațiune
reală sau complexă. Starea sa poate fi modificată și de subprogramul ieee_set_flag (Secțiunea 11.8.3) sau
subrutina ieee_set_status (Secțiunea 11.8.4). Odată semnalizat, acesta rămâne semnal, cu excepția cazului
în care este oprit printr-o invocare a subrutinei ieee_set_flag sau a subrutinei ieee_set_status. Pentru
invocarea unei proceduri elementare, este ca și cum procedura ar fi invocată o dată pentru fiecare set de
elemente corespunzătoare; dacă oricare dintre invocări revine cu o semnalizare de tip steag, va fi
semnalizată în apelant la finalizarea apelului.

Dacă un semnalizare semnalează la intrarea într-o procedură, procesorul îl va seta pe silentios la intrare și îl va
restabili la semnalizare la întoarcere. Acest lucru permite gestionarea excepțiilor în cadrul procedurii să fie independentă
de starea steagurilor la intrare, păstrând în același timp proprietățile „lipicioase” ale acestora: într-o unitate de definire
a domeniului, un semnalizator de semnalizare rămâne semnalizat până când este stabilit în mod explicit silentios.
Evaluarea unei expresii de specificație poate determina semnalarea unei excepții.
Dacă o unitate de definire are acces la ieee_exceptions și face referire la o procedură intrinsecă care se
execută normal, valorile flag-urilor de depășire, împărțire la zero și invalide sunt ca la intrarea în procedura
intrinsecă, chiar dacă unul sau mai multe semnale în timpul calculului. Dacă un rezultat real sau complex
este prea mare pentru ca procedura intrinsecă să fie gestionată, se poate semnala depășirea. Dacă un
rezultat real sau complex este un NaN din cauza unei operații nevalide (de exemplu, log(-1.0)), poate
semnala invalid. Reguli similare se aplică procesării formatului și operațiunilor intrinseci: niciun semnal de
semnalizare nu va fi setat în mod silențios și nicio semnalizare silențioasă nu va fi setat pentru semnalizare
din cauza unui calcul intermediar care nu afectează rezultatul.
O implementare poate oferi versiuni alternative ale unei proceduri intrinseci; de exemplu, unul ar putea
fi destul de lent, dar poate fi potrivit pentru un apel de la o unitate de scoping cu acces la ieee_exceptions,
în timp ce o alternativă mai rapidă ar putea fi potrivită pentru alte cazuri.
Dacă se știe că o procedură intrinsecă nu va trebui niciodată să semnaleze o excepție, nu există nicio
cerință ca aceasta să fie gestionată - la urma urmei, nu există nicio modalitate ca programatorul să poată
face diferența. Același principiu se aplică unei secvențe de cod în linie fără invocări ale ieee_get_flag,
ieee_set_flag, ieee_get_status, ieee_set_status sau ieee_set_halting. Dacă codul, așa cum este scris, include
o operație care ar semnala un flag, dar după executarea secvenței nicio valoare a unei variabile nu depinde
de acea operație, dacă semnalele de excepție sunt dependente de procesor. Astfel, este permisă o
implementare pentru a optimiza o astfel de operațiune. De exemplu, când y are valoarea zero, indiferent
dacă codul

x = 1,0/yx =
3,0

Împărțirea la zero a semnalelor este dependentă de procesor. Un alt exemplu este:

real, parametru :: x=0,0, y=6,0


:
if (1.0/x == y) print *,'Hello world'
Machine Translated by Google

228 Fortran modern explicat

unde procesorului i se permite să renunțe la instrucțiunea if, deoarece expresia logică nu poate fi niciodată
adevărată și nicio valoare a unei variabile nu depinde de aceasta.
O excepție nu semnalează dacă acest lucru ar putea apărea numai în timpul execuției codului care nu este necesar
sau permise de standard. De exemplu, afirmația

dacă (f(x) > 0,0) y = 1,0/z

nu trebuie să semnaleze împărțirea la zero când ambele f(x) și z sunt zero și instrucțiunea

unde(a > 0,0) a = 1,0/a

nu trebuie să semnaleze împărțirea la zero. Pe de altă parte, când x are valoarea 1,0 și y are valoarea 0,0,
expresia

x > 0,00001 .sau. x/y > 0,00001

este permis să provoace semnalizarea împărțirii la zero.


Procesorul nu trebuie să accepte excepțiile invalide, subflow și inexacte. Dacă o excepție nu este acceptată,
steag-ul său este întotdeauna liniștit. Funcția ieee_support_flag (Secțiunea 11.8.2) poate fi folosită pentru a
întreba dacă este suportat un anumit indicator. Dacă este acceptat invalid, semnalează în cazul conversiei
într-un număr întreg (prin atribuire sau printr-o procedură intrinsecă) dacă rezultatul este prea mare pentru
a fi reprezentabil.

11.5 Oprire

Unele procesoare permit controlul în timpul execuției programului dacă să se abandoneze sau să continue
execuția după ce a apărut o excepție. Un astfel de control este exercitat prin invocarea subrutinei
ieee_set_halting_mode (Secțiunea 11.8.3). Oprirea nu este precisă și poate apărea oricând după apariția
excepției. Funcția ieee_support_halting (Secțiunea 11.8.2) poate fi utilizată pentru a întreba dacă această
facilitate este disponibilă. Modul de oprire inițial depinde de procesor.

Într-o procedură diferită de ieee_set_halting_mode, procesorul nu schimbă modul de oprire la intrare, iar
la întoarcere se asigură că modul de oprire este același ca la intrare.

11.6 Modul de rotunjire

Unele procesoare acceptă modificarea modului de rotunjire în timpul execuției. În acest caz, subrutina
ieee_set_rounding_mode (Secțiunea 11.9.4) poate fi folosită pentru a o modifica. Funcția ieee_support_rounding
(Secțiunea 11.9.2) poate fi utilizată pentru a întreba dacă această facilitate este disponibilă pentru un anumit
mod.
Într-o procedură diferită de ieee_set_rounding_mode, procesorul nu modifică modul de rotunjire la intrare,
iar la întoarcere se asigură că modul de rotunjire este același ca la intrare.

Rețineți că valoarea unei constante literale nu este afectată de modul de rotunjire.


Machine Translated by Google

Gestionarea excepțiilor în virgulă mobilă 229

11.7 Modul underflow (numai Fortran 2003)

Unele procesoare acceptă modificarea modului underflow în timpul execuției, adică dacă valorile mici sunt
reprezentate ca valori denormalizate sau sunt setate la zero. Motivul este probabil că un astfel de procesor
se execută mult mai rapid fără valori denormalizate. Se spune că modul underflow este gradual dacă sunt
folosite valori denormalizate. Dacă modul underflow poate fi modificat în timpul rulării, subrutina
ieee_set_underflow_mode (Secțiunea 11.9.4) poate fi utilizată pentru a-l modifica. Funcția
ieee_support_underflow_control (Secțiunea 11.9.2) poate fi utilizată pentru a întreba dacă această facilitate
este disponibilă pentru un anumit tip de real.
Într-o procedură diferită de ieee_set_underflow_mode, procesorul nu modifică
modul underflow la intrare, iar la retur se asigură că este la fel ca la intrare.

11.8 Modulul ieee_exceptions

Când modulul ieee_exceptions este accesibil, steagurile de depășire și împărțire la zero sunt acceptate în
unitatea de acoperire pentru toate tipurile de date reale și complexe disponibile. Acest nivel minim de
suport a fost conceput pentru a fi posibil și pe un computer non-IEEE. Ce alte excepții sunt acceptate pot
fi determinate de funcția ieee_support_flag, vezi Secțiunea 11.8.2. Dacă controlul opririi este suportat
poate fi determinat de funcția ieee_support_halting, vezi Secțiunea 11.8.2. Amploarea suportului celorlalte
excepții poate fi influențată de accesibilitatea constantelor numite ieee_inexact_flag, ieee_invalid_flag și
ieee_underflow_flag ale modulului ieee_features, vezi Secțiunea 11.3.

Modulul conține două tipuri derivate (Secțiunea 11.8.1), constante numite ale acestor tipuri (Secțiunea
11.8.1) și o colecție de proceduri generice (Secțiunile 11.8.2, 11.8.3 și 11.8.4).
Niciuna dintre proceduri nu este permisă ca argument real.

11.8.1 Tipuri derivate

Modulul ieee_exceptions conține două tipuri derivate.

ieee_flag_type pentru identificarea unui anumit flag de excepție. Singurele valori care pot fi luate de
obiectele de acest tip sunt cele ale constantelor numite definite în modul

ieee_overflow ieee_divide_by_zero ieee_invalid ieee_underflow


ieee_inexact

iar acestea sunt folosite în modul pentru a defini constantele matricei numite

tip(ieee_flag_type), parametru :: ieee_usual(3) = (/ &

ieee_overflow, ieee_divide_by_zero, ieee_invalid/), &

&
ieee_all(5) = (/ieee_usual, ieee_underflow, ieee_inexact/)

Aceste constante de matrice sunt convenabile pentru a se întreba despre starea mai multor
steaguri simultan, folosind proceduri elementare. Pe lângă comoditate, astfel de apeluri elementare
pot fi mai eficiente decât o secvență de apeluri pentru un singur steag.
Machine Translated by Google

230 Fortran modern explicat

ieee_status_type pentru salvarea stării curente în virgulă mobilă. Include valorile tuturor flag-urilor
acceptate, precum și modul curent de rotunjire dacă este acceptat controlul dinamic al rotunjirii
și modul de oprire dacă este acceptat controlul dinamic al opririi.

Componentele ambelor tipuri sunt private. Nu este definită nicio operațiune pentru ei și numai
alocarea intrinsecă este disponibilă pentru ei.

11.8.2 Funcții de interogare pentru excepțiile IEEE

Modulul ieee_exceptions conține două funcții de interogare, ambele fiind pure. Argumentul lor trebuie
să fie de tipul tip(ieee_flag_type) cu una dintre valorile ieee_invalid, ieee_overflow, ieee_divide_by_zero,
ieee_inexact și ieee_underflow. Întrebările se referă la suportul pentru tipuri de reale și același nivel de
suport este oferit pentru tipurile corespunzătoare de tip complex.

ieee_support_flag (flag [,x]) returnează .true. dacă procesorul acceptă parametrul de excepție pentru
toate realele (x absent) sau pentru parametrul real de același tip ca și argumentul real x. În caz
contrar, returnează .false..

ieee_support_halting (steagul) returnează .true. dacă procesorul acceptă abilitatea de a schimba modul
prin apelul ieee_set_halting_mode(flag, stoping). În rest, se întoarce .false..

11.8.3 Subrutine pentru steaguri și moduri de oprire

Modulul ieee_exceptions conține următoarele subrutine elementare.

apelați ieee_get_flag (flag, flag_value) unde:

flag este de tip tip (ieee_flag_type). Specifică un steag.

flag_value este de tip implicit logic și are intent out. Dacă valoarea flag este ieee_invalid,
ieee_overflow, ieee_divide_by_zero, ieee_underflow sau ieee_inexact, flag_value primește
valoarea true dacă indicatorul de excepție corespunzător semnalează și fals în caz contrar.

apelați ieee_get_halting_mode (steagul, oprirea) unde:

flag este de tip tip (ieee_flag_type). Trebuie să aibă una dintre valorile ieee_invalid, ieee_overflow,
ieee_divide_by_zero, ieee_underflow sau ieee_inexact.

oprirea este de tipul implicit logic și are intenție. Dacă excepția specificată de flag va determina
oprirea, stoparea primește valoarea true; în caz contrar, i se dă valoarea false.

Subrutinele elementare nu ar fi adecvate pentru acțiunile „set” corespunzătoare, deoarece o invocare


ar putea cere ca un flag sau un mod să fie setat de mai multe ori. Prin urmare, modulul conține
următoarele subrutine care sunt pure, dar nu elementare:
Machine Translated by Google

Gestionarea excepțiilor în virgulă mobilă 231

apelați ieee_set_flag (flag, flag_value) unde:

flag este de tip tip (ieee_flag_type). Poate fi scalar sau cu valori matrice. Dacă este o matrice, două
elemente nu pot avea aceeași valoare. flag_value este de tipul implicit logic. Trebuie să fie
conform cu steag. Fiecare flag specificat de flag este setat să semnalizeze dacă flag_value
corespunzătoare este adevărat și să fie silentios dacă este fals.

apelați ieee_set_halting_mode (flag, stoping) care poate fi apelat numai dacă valoarea returnată de
ieee_support_halting(flag) este adevărată:

flag este de tip tip (ieee_flag_type). Poate fi scalar sau cu valori matrice. Dacă este o matrice, două
elemente nu pot avea aceeași valoare.
oprirea este de tipul implicit logic. Trebuie să fie conform cu steag. Fiecare excepție specificată de
flag va provoca oprirea dacă valoarea corespunzătoare a opririi este adevărată și nu va
provoca oprirea dacă valoarea este falsă.

11.8.4 Subprograme pentru întreaga stare în virgulă mobilă

Modulul ieee_exceptions conține următoarele subrutine non-elementale.

apelați ieee_get_status (status_value) unde:

status_value este scalar și de tip tip (ieee_status_type) și are intent out.


Returnează starea în virgulă mobilă, inclusiv toate steagurile de excepție, modul de rotunjire
și modul de oprire.

apelați ieee_set_status (status_value) unde:

status_value este scalar și de tip tip (ieee_status_type). Valoarea sa trebuie să fi fost setată într-o
invocare anterioară a ieee_get_status. Starea în virgulă mobilă, inclusiv toate steagurile de
excepție, modul de rotunjire și modul de oprire, este resetat la fel ca atunci.

Figura 11.1 Efectuarea unui calcul subsidiar cu un set independent de steaguri. utilizare,
intrinsec :: tip ieee_exceptions(ieee_status_type) :: status_value

:
apelați ieee_get_status(status_value)! Obțineți apelul steaguri
ieee_set_flag(ieee_all,.false.)! Pune steaguri în liniște
: ! Calcul care implică gestionarea excepțiilor
apelați ieee_set_status(status_value)! Restaurați steagurile

Aceste subrutine au fost incluse pentru comoditate și eficiență atunci când urmează să fie efectuat un
calcul secundar și se dorește reluarea calculului principal cu exact același mediu, așa cum se arată în
Figura 11.1. Nu există facilități pentru găsirea directă a valorii deținute într-o astfel de variabilă a unui
anumit indicator, mod de rotunjire sau mod de oprire.
Machine Translated by Google

232 Fortran modern explicat

11.9 Modulul ieee_arithmetic

Modulul ieee_arithmetic se comportă ca și cum ar conține o instrucțiune de utilizare pentru modulul


ieee_exceptions, deci toate caracteristicile ieee_exceptions sunt, de asemenea, caracteristici ale ieee_arithmetic.

Modulul conține două tipuri derivate (Secțiunea 11.9.1), constante numite ale acestor tipuri (Secțiunea 11.9.1) și
o colecție de proceduri generice (Secțiunile 11.9.2, 11.9.3, 11.9.4 și 11.9.5) . Niciuna dintre proceduri nu este permisă
ca argument real.

11.9.1 Tipuri derivate

Modulul ieee_arithmetic conține două tipuri derivate.

ieee_class_type pentru identificarea unei clase de valori în virgulă mobilă. Singurele valori pe care le pot lua
obiectele de acest tip sunt cele ale constantelor numite definite în modul

ieee_signaling_nan ieee_quiet_nan
ieee_negative_inf ieee_negative_normal
ieee_negative_denormal ieee_negative_zero ieee_pozitive_zero
ieee_pozitive_denormal ieee_pozitive_normal ieee_pozitive_inf

cu semnificații evidente și (numai Fortran 2003)

ieee_other_value

pentru orice cazuri care nu pot fi astfel identificate, de exemplu, dacă un fișier neformatat a fost scris cu
underflow treptat activat și citit cu acesta dezactivat.

ieee_round_type pentru identificarea unui anumit mod de rotunjire. Singurele valori posibile pe care le pot lua
obiectele de acest tip sunt cele ale constantelor numite definite în modul

ieee_nearest ieee_to_zero ieee_up


ieee_down

pentru modurile IEEE și

ieee_altul

pentru orice alt mod.

Componentele ambelor tipuri sunt private. Singurele operații definite pentru acestea sunt == și /= pentru
compararea valorilor unuia dintre tipuri; returnează o valoare de tipul implicit logic.
Este disponibilă și atribuirea intrinsecă.

11.9.2 Funcții de interogare pentru aritmetica IEEE

Modulul ieee_arithmetic conține următoarele funcții de interogare, toate fiind pure. Solicitările se referă la suportul
realului și același nivel de suport este oferit pentru tipurile corespunzătoare de tip complex. Argumentul x poate fi
un scalar sau o matrice.
Machine Translated by Google

Gestionarea excepțiilor în virgulă mobilă 233

ieee_support_datatype ([x]) returnează .true. dacă procesorul acceptă aritmetica IEEE pentru toate reale
(x absent) sau pentru reale de același tip de parametru ca argumentul real x. În caz contrar,
returnează .false.. Conformitatea completă cu standardul IEEE nu este necesară pentru .true. să
fie returnate, dar numerele normalizate trebuie să fie exact cele ale IEEE single sau IEEE double;
operatorii aritmetici binari și * trebuie să fie implementați cu cel puțin unul dintre modurile de
ieee_rem
rotunjire
și ieee_unordered
IEEE; +, - și funcțiile
trebuie ieee_copy_sign,
să implementezeieee_scalb,
funcțiile IEEE
ieee_logb,
corespunzătoare.
ieee_next_after,

ieee_support_denormal ([x]) returnează .true. dacă procesorul acceptă numerele denormalizate IEEE
pentru toate reale (x absent) sau pentru reale de același tip parametru ca argumentul real x. În
caz contrar, returnează .false..

ieee_support_divide ([x]) returnează .true. dacă procesorul acceptă împărțirea cu acuratețea specificată
de standardul IEEE pentru toate reale (x absent) sau pentru parametru real de același tip ca și
argumentul real x. În caz contrar, returnează .false..

ieee_support_inf ([x]) returnează .true. dacă procesorul acceptă facilitatea infinită IEEE pentru toate reale
(x absent) sau pentru reale de același tip parametru ca argumentul real x. În caz contrar,
returnează .false..

ieee_support_io ([x]) returnează .true. dacă rezultatele de intrare/ieșire formatate îndeplinesc cerințele
standardului IEEE pentru toate cele patru moduri de rotunjire IEEE pentru toate reale (x absent)
sau pentru reale de același tip de parametru ca argumentul real x.
În caz contrar, returnează .false..

ieee_support_nan ([x]) returnează .true. dacă procesorul acceptă facilitatea IEEE Not-A Number pentru
toate reale (x absent) sau pentru reale de același tip de parametru ca argumentul real x. În caz
contrar, returnează .false..

ieee_support_rounding (round_value [,x]) pentru o round_value de tipul ieee_round_type returnează .true.


dacă procesorul acceptă acel mod de rotunjire pentru toate reale (x absent) sau pentru reale de
același tip de parametru ca argumentul x.
În caz contrar, returnează .false.. Aici, suportul include posibilitatea de a schimba modul prin
invocare

apelați ieee_set_rounding_mode (round_value)

ieee_support_sqrt ([x]) returnează .true. dacă sqrt implementează rădăcina pătrată IEEE pentru toate
reale (x absent) sau pentru reale de același tip de parametru ca argumentul real x.
În caz contrar, returnează .false..

ieee_support_standard ([x]) returnează .true. dacă procesorul suportă toate facilitățile IEEE definite în
acest capitol pentru toate realele (x absent) sau pentru reale de același tip parametru ca
argumentul real x. În caz contrar, returnează .false..

ieee_support_underflow_control ([x]) (numai Fortran 2003) returnează .true. dacă procesorul acceptă
controlul modului underflow pentru toate reale (x absent) sau pentru reale de același tip de
parametru ca argumentul real x. În caz contrar, returnează .false..
Machine Translated by Google

234 Fortran modern explicat

11.9.3 Funcții elementare

Modulul ieee_arithmetic conține următoarele funcții elementare pentru realele x și y pentru care valorile
ieee_support_datatype(x) și ieee_support_datatype(y) sunt adevărate. Dacă x sau y este un infinit sau un NaN,
comportamentul este în concordanță cu regulile generale ale standardului IEEE pentru operații aritmetice. De
exemplu, rezultatul pentru un infinit este construit ca caz limită al rezultatului cu o valoare de mărime arbitrar
mare, atunci când există o astfel de limită.

ieee_class (x) este de tip type(ieee_class_type) și returnează clasa IEEE a argumentului real x. Valorile posibile
sunt explicate în Secțiunea 11.9.1.

ieee_copy_sign (x, y) returnează un real cu același parametru de tip ca x, păstrând valoarea lui x cu semnul lui
y. Acest lucru este valabil chiar și pentru valorile speciale IEEE, cum ar fi NaN și (pe procesoare care
acceptă astfel de valori).

ieee_is_finite (x) returnează valoarea .true. dacă ieee_class (x) are unul dintre
valorile

ieee_negative_normal ieee_negative_denormal ieee_negative_zero


ieee_pozitive_zero ieee_pozitive_denormal ieee_pozitive_normal

iar .fals. in caz contrar.

ieee_is_nan (x) returnează valoarea .true. dacă valoarea lui x este un IEEE NaN și
.fals. in caz contrar.

ieee_is_negative (x) returnează valoarea .true. dacă ieee_class (x) are unul dintre
valorile

ieee_negative_normal ieee_negative_denormal ieee_negative_zero


ieee_negative_inf

iar .fals. in caz contrar.

ieee_is_normal (x) returnează valoarea .true. dacă ieee_class (x) are unul dintre
valorile

ieee_negative_normal ieee_negative_zero ieee_pozitive_zero


ieee_pozitive_normale

iar .fals. in caz contrar.

ieee_logb (x) returnează un real cu același parametru de tip ca x. Dacă x nu este nici zero, nici infinit, nici NaN,
valoarea rezultatului este exponentul imparțial al lui x, adică exponentul(x)-1. Dacă x==0, rezultatul este
dacă ieee_support_inf(x) este adevărat și -huge(x); altfel, ieee_divide_by_zero semnale. Dacă
ieee_support_inf(x) este adevărat și x este infinit, rezultatul este +infinit. Dacă ieee_support_nan(x) este
adevărat și x este un NaN, rezultatul este un NaN.
Machine Translated by Google

Gestionarea excepțiilor în virgulă mobilă 235

ieee_next_after (x, y) returnează un real cu același parametru de tip ca x. Dacă x==y, rezultatul este x,
fără nicio excepție semnalizarea vreodată. În caz contrar, rezultatul este vecinul lui x pe direcția
lui y. Vecinii lui zero (a oricărui semn) sunt ambele nenule. Depășirea este semnalată când x este
finit, dar ieee_next_after (x, y) este infinit; underflow este semnalat când ieee_next_after (x, y) este
denormalizat; în ambele cazuri, semnale ieee_inexacte.

ieee_rem (x, y) returnează un real cu parametrul tip al oricărui argument are precizia și valoarea mai
mare exact xy*n, unde n este numărul întreg cel mai apropiat de valoarea exactă x/y; ori de câte
ori |n x/y| = 1/2, n este par. Dacă valoarea rezultată este zero, semnul este cel al lui x.

ieee_rint (x, y) returnează un real cu același parametru de tip ca x a cărui valoare este cea a lui x rotunjită
la o valoare întreagă conform modului de rotunjire curent.

ieee_scalb (x, i) returnează un real cu același parametru de tip ca x a cărui valoare este 2ix dacă aceasta
se află în intervalul numerelor normale. Dacă 2ix este prea mare, semnalele ieee_overflow; dacă
ieee_support_inf(x) este adevărată, valoarea rezultată este infinită cu semnul lui x; în caz contrar,
este semn(uriaș(x),x). Dacă 2ix este prea mic și nu poate fi reprezentat exact, ieee_underflow
semnalează; rezultatul este cel mai apropiat număr reprezentabil cu semnul lui x.

ieee_unordered (x, y) returnează .adevărat. dacă x sau y este un NaN sau ambele sunt și .false.
in caz contrar.

ieee_value (x, class) returnează un real cu același parametru de tip ca x și o valoare specificată de clasă.
Clasa de argument este de tip tip(ieee_class_type) și poate avea valoare

ieee_signaling_nan sau ieee_quiet_nan dacă ieee_support_nan(x) este adevărat,

ieee_negative_inf sau ieee_positive_inf dacă ieee_support_inf(x) este adevărat,

ieee_negative_denormal sau ieee_positive_denormal dacă valoarea de


ieee_support_denormal(x) este adevărat sau

ieee_negative_normal, ieee_negative_zero, ieee_pozitive_zero sau


ieee_pozitive_normale.

Deși în cele mai multe cazuri valoarea este dependentă de procesor, ea nu variază între invocări
pentru un anumit tip de parametru al lui x și valoarea clasei.

11.9.4 Subrutine non-elementale

Modulul ieee_arithmetic conține următoarele subrutine non-elementale.

apelați ieee_get_rounding_mode (round_value) unde:


Machine Translated by Google

236 Fortran modern explicat

round_value este scalar, de tip tip(ieee_round_type) și are intent out.


Returnează modul de rotunjire în virgulă mobilă, cu valoarea ieee_nearest, ieee_to_zero, ieee_up
sau ieee_down dacă unul dintre modurile IEEE este în funcțiune și ieee_other altfel.

apelați ieee_get_underflow_mode (gradual) (numai Fortran 2003) unde:

gradual este scalar, de tip implicit logic și are intent out. Se întoarce .adevărat. dacă
depășirea treptată este în vigoare și .false. in caz contrar.

apelați ieee_set_rounding_mode (round_value) unde:

round_value este scalar, de tip tip(ieee_round_type). Specifică modul de


fi setat.

Subrutina nu trebuie apelată decât dacă valoarea lui ieee_support_rounding (round_value, x) este
adevărată pentru un x astfel încât valoarea ieee_support_datatype(x) este adevărată.

apelați ieee_set_underflow_mode (gradual) (numai Fortran 2003) unde:

gradual este scalar, de tip implicit logic. Dacă valoarea sa este adevărată, intră în vigoare treptat subflux;
în caz contrar, subfluxarea treptată încetează să mai fie în vigoare.

Subrutina nu trebuie apelată decât dacă ieee_support_underflow_control (x)


este valabil pentru unele x.

Exemplul din Figura 11.2 arată utilizarea acestor subrutine pentru a stoca modul de rotunjire,
efectuați un calcul cu rotunjirea la cea mai apropiată și restabiliți modul de rotunjire.

Figura 11.2 Stocați modul de rotunjire, efectuați un calcul cu un alt mod și restabiliți modul anterior. utilizare,
intrinsec :: ieee_arithmetic type(ieee_round_type) round_value

:
apelați ieee_get_rounding_mode(round_value)! Stocați apelul modului de rotunjire
ieee_set_rounding_mode(ieee_nearest)
: ! Calcul cu rotunjire la cea mai apropiată

apelați ieee_set_rounding_mode(round_value)! Restabiliți modul de rotunjire

11.9.5 Funcția de transformare pentru valoarea de natură

Modulul ieee_arithmetic conține următoarea funcție de transformare care este permisă într-o expresie constantă
(Secțiunea 7.4):

ieee_selected_real_kind ([p] [, r]) este similar cu selected_real_kind (Secțiunea 8.7.4), cu excepția faptului că
rezultatul este valoarea tipului unui x real pentru care ieee_support_datatype(x) este adevărat.
Machine Translated by Google

Gestionarea excepțiilor în virgulă mobilă 237

11.10 Exemple

11.10.1 Produs punctual

Primul nostru exemplu, Figura 11.3, este al unui modul pentru produsul punctual a două matrice reale de rang 1.
Conține un dot_error scalar logic, care acționează ca un indicator de eroare. Dacă dimensiunile matricelor
sunt diferite, apare o revenire imediată cu dot_error true. Dacă depășirea are loc în timpul calculului
propriu-zis, indicatorul de depășire va semnala și dot_error este setat adevărat. Dacă totul este bine,
valoarea sa este neschimbată.

Figura 11.3 Modul pentru produsul punctual a două tablouri reale de rang
1. modul punct
! Apelantul trebuie să se asigure că excepțiile nu cauzează oprire.
utilizare, intrinsecă :: ieee_exceptions implicit
niciunul interfață logică privată operator(.dot.)
:: mult multiplă
procedură de modul interfață
:: dot_error = .false.

con ine
funcția reală mult(a, b) real,
intent(in) :: a(:), b(:) întreg logic dacă
(size(a)/=size(b)) atunci :: i
:: revărsare

dot_error = .true.
întoarcere
sfâr itul dacă

! Procesorul asigură că ieee_overflow este silențios mult = 0.0

do i = 1, size(a) mult =
mult + a(i)*b(i)
sfâr itul face

apelați ieee_get_flag(ieee_overflow, overflow) dacă


(overflow) dot_error = .true.
end function mult
punct modul final

11.10.2 Apelarea procedurilor alternative

Să presupunem că funcția fast_inv este un cod pentru inversarea matricei care „trăiește periculos” și
poate determina semnalarea unei stări. Funcția alternativă slow_inv este mult mai puțin probabil să provoace
Machine Translated by Google

238 Fortran modern explicat

o condiție de semnalizat, dar este mult mai lentă. Următorul cod, Figura 11.4, încearcă fast_inv și, dacă este necesar,
face o altă încercare cu slow_inv. Dacă aceasta încă nu reușește, este tipărit un mesaj și programul se oprește.
Rețineți, de asemenea, că este important să setați steaguri în liniște înainte de a doua încercare. Starea tuturor
steagurilor este stocată și restaurată.

Figura 11.4 Încercați un algoritm rapid și, dacă este necesar, încercați din nou cu un algoritm mai lent, dar mai fiabil.
utilizare, intrinsecă :: ieee_exceptions utilizare, intrinsecă :: ieee_features, numai: ieee_invalid_flag ! Celelalte excepții
ale ieee_usual (ieee_overflow și ! ieee_divide_by_zero) sunt întotdeauna disponibile cu ieee_exceptions
type(ieee_status_type) :: status_value logic, dimension(3) :: flag_value

apelați ieee_get_status(status_value) apelați


ieee_set_halting_mode(ieee_usual,.false.) ! Necesar în cazul în care ! implicit pe procesor este oprirea la
excepții.

apelați ieee_set_flag(ieee_usual,.false.) ! Elementar

! Încercați mai întâi algoritmul „rapid” pentru inversarea unei matrice: matrix1 =
fast_inv(matrix) ! Acest lucru nu trebuie să modifice matricea. apelați ieee_get_flag(ieee_usual,
flag_value) dacă (any(flag_value)) atunci! Algoritmul „rapid” a eșuat; încercați! unul
Elementar
„lent”:
apelați ieee_set_flag(ieee_usual,.false.) matrix1 = slow_inv(matrix) apelați
ieee_get_flag(ieee_usual, flag_value) dacă (any(flag_value)) atunci

scrieți (*, *) „Nu se poate inversa matricea” stop end if

sfâr itul dacă

apelați ieee_set_status(status_value)

11.10.3 Apelarea unui cod alternativ în linie

Acest exemplu, Figura 11.5, este similar cu partea interioară a celui precedent, dar aici codul pentru inversarea
matricei este în linie, știm că numai overflow poate semnala, iar transferul este făcut mai precis prin adăugarea de
teste suplimentare ale steagului .

11.10.4 Funcție de ipotenuză sigură

Cea mai importantă utilizare a unei facilitati de tratare a excepțiilor în virgulă mobilă este de a face posibilă
dezvoltarea unui software mult mai eficient decât este posibil altfel. Codul din figura
Machine Translated by Google

Gestionarea excepțiilor în virgulă mobilă 239

Figura 11.5 Ca și în Figura 11.4, dar cu cod în linie. utilizare,


intrinsecă :: ieee_exceptions logic :: flag_value

apelați ieee_set_halting_mode(ieee_overflow,.false.) apelați


ieee_set_flag(ieee_overflow,.false.)
! Încercați mai întâi un algoritm rapid pentru inversarea unei matrice. face k = 1, n

apelați ieee_get_flag(ieee_overflow, flag_value) if (flag_value) exit end


do

dacă (flag_value) atunci ! Cod


alternativ care știe că k-1 pași au ! executate normal.

:
sfâr itul dacă

11.6 pentru funcția „ipotenuză”, x2 +y2, ilustrează utilizarea facilității în dezvoltarea unui software eficient.

Se încearcă evaluarea directă a acestei funcții în cel mai rapid mod posibil. Acest lucru va funcționa aproape de
fiecare dată, dar dacă apare o excepție în timpul acestui calcul rapid, un mod sigur, dar mai lent evaluează funcția.
Această evaluare mai lentă poate implica scalare și descalare, iar în cazuri extreme (foarte rare) această descalare
poate provoca depășire (la urma urmei, rezultatul adevărat s-ar putea depăși dacă x și y sunt ambele aproape de
limita de depășire). Dacă indicatorul de depășire sau de depășire este semnalizat la intrare, acesta este resetat la
întoarcere de către procesor, astfel încât excepțiile anterioare să nu se piardă.

11.10.5 Acces la valorile aritmetice IEEE

Programul din Figura 11.7 ilustrează modul în care modulul ieee_arithmetic poate fi utilizat pentru a testa valorile
IEEE speciale. Se dublează în mod repetat a și înjumătățit b, testând valorile depășite, denormalizate și zero. Utilizează
ieee_set_halting_mode pentru a preveni oprirea. Sunt afișate începutul și sfârșitul unui eșantion de ieșire. Notați
mesajele de avertizare; procesorului trebuie să producă o astfel de ieșire dacă se semnalează excepții la terminare.
Machine Translated by Google

240 Fortran modern explicat

Figura 11.6 O funcție de ipotenuză sigură. funcția reală


ipoteză (x, y)

! În cazuri rare, acest lucru poate duce la semnalizarea ! ieee_overflow.

! Apelantul trebuie să se asigure că excepțiile nu cauzează oprire.


utilizare, intrinsecă :: ieee_exceptions utilizare,
intrinsecă :: ieee_features, numai: ieee_underflow_flag
! ieee_overflow este întotdeauna disponibil cu ieee_exceptions

implicit nici unul real


:: x, y ::
real scaled_x, scaled_y, scaled_result logic, dimensiune(2) ::
tip de steaguri(ieee_flag_type), parametru, dimensiune(2) ::
&

out_of_range = (/ ieee_overflow, ieee_underflow /)


intrinsec :: sqrt, abs, exponent, max, cifre, scară
! Procesorul șterge steaguri la intrare
apelați ieee_set_halting_mode(out_of_range, .false.) ! Necesar în cazul în care implicit pe procesor
! este oprirea la excepții.
! Încercați mai întâi un algoritm rapid
hypot = sqrt( x**2 + y**2 ) apelați
ieee_get_flag(out_of_range, flags) if ( any(flags) ) apoi apelați
ieee_set_flag(out_of_range, .false.) if ( x==0.0 .or. y== 0,0 )
atunci hipot = abs(x) + abs(y)

altfel, dacă ( 2*abs(exponent(x)-exponent(y)) > digits(x)+1 ) then hypot = max( abs(x), abs(y) )!
Putem ignora unul dintre x și y! Scalați astfel încât abs(x) să fie aproape de 1 restul

scaled_x = scale( x, -exponent(x) ) scaled_y = scale( y,


-exponent(x) ) scaled_result = sqrt( scaled_x**2 +
scaled_y**2 ) hypot = scale(scaled_result, exponent(x)) ! Poate cauza

sfâr itul dacă ! revărsare


sfâr itul dacă

! Procesorul resetează orice semnalizare care semnala la ipoteza funcției de sfârșit de intrare
Machine Translated by Google

Gestionarea excepțiilor în virgulă mobilă 241

Figura 11.7 Testarea valorilor depășite, denormalizate și zero. testul


programului folosește ieee_arithmetic; folosește ieee_features :: a=1.0, b=1.0
integer :: apelez ieee_set_halting_mode(ieee_overflow, .false.) do i =
real 1,1000 a = a*2.0 b = b/2.0 if (.nu. ieee_is_finite(a)) atunci

scrieți (*, *) „2,0**”, i, „este infinit” a = 0,0

sfâr itul dacă

dacă (.nu. ieee_is_normal(b)) și scrieți (*, *)


„0,5**”, i, „este denormal”
dacă (b==0,0) ieșire
sfâr itul face

scrieți (*, *) „0,5**”, i, „este zero” la finalul testului


programului

0,5** 127 este denormal 2,0**


128 este infinit 0,5** 128 este
denormal
0,5** 129 este denormal
:
0,5** 148 este denormal
0,5** 149 este denormal
0,5** 150 este zero
Avertisment: Floating overflow a avut loc în timpul execuției
Avertisment: în timpul execuției s-a produs un depășire flotantă
Machine Translated by Google

Această pagină a fost lăsată goală în mod intenționat


Machine Translated by Google

12. Interoperabilitate cu C

12.1 Introducere

Fortran 2003 oferă un mecanism standardizat pentru interoperarea cu C. În mod clar, orice entitate
implicată trebuie să fie astfel încât să poată fi făcute declarații echivalente în cele două limbi. Acest lucru
este aplicat în cadrul programului Fortran prin solicitarea tuturor acestor entități să fie interoperabile.
Vom explica pe rând ce necesită acest lucru pentru tipuri, variabile și proceduri.
Toate sunt cerințe pentru sintaxă, astfel încât compilatorul să știe în momentul compilării dacă o entitate
este interoperabilă. Continuăm cu examinarea interoperabilității pentru datele globale și apoi discutăm
câteva exemple. Încheiem cu o nouă sintaxă pentru definirea unor seturi de constante întregi care este
utilă în acest context.

12.2 Interoperabilitatea tipurilor intrinseci

Există un modul intrinsec numit iso_c_binding care conține constante denumite de tipul întreg implicit
care deține valori ale parametrilor tip tip pentru tipurile intrinseci. Numele lor sunt prezentate în Tabelul
12.1, împreună cu tipurile C corespunzătoare. Procesorul trebuie să accepte doar int. Lipsa suportului
este indicată cu o valoare negativă a constantei. Dacă valoarea este pozitivă, indică faptul că parametrul
de tip Fortran și tipul de tip interoperează cu tipul C corespunzător.

Valorile negative sunt după cum urmează. Pentru tipurile întregi, valoarea este -1 dacă există un astfel
de tip C, dar nu există un fel Fortran interoperabil sau -2 dacă nu există un astfel de tip C. Pentru tipurile
reale, valoarea este 1 dacă tipul C nu are o precizie egală cu precizia niciunuia dintre tipurile reale
Fortran, 2 dacă tipul C nu are o gamă egală cu domeniul vreunuia dintre tipurile reale. Tipuri reale
Fortran, 3 dacă tipul C nu are nici precizia, nici intervalul niciunuia dintre tipurile reale Fortran și egal
cu 4 dacă nu există un fel Fortran interoperabil din alte motive. Valorile lui c_float_complex,
c_double_complex și c_long_double_complex sunt aceleași cu cele ale c_float, c_double și, respectiv,
c_long_double. Pentru logic, valoarea lui c_bool este -1 dacă nu există niciun fel de Fortran care să
corespundă tipului C _Bool. Pentru caracter, valoarea lui c_char este -1 dacă nu există niciun fel Fortran
care să corespundă tipului C char.
Pentru tipul de caractere, interoperabilitatea necesită, de asemenea, ca parametrul tipul de lungime
să fie omis sau să fie specificat printr-o expresie constantă a cărei valoare este una. Sunt furnizate
următoarele constante numite (cu semnificațiile evidente): c_null_char, c_alert, c_backspace, c_form_feed,
c_new_line, c_carriage_return, c_horizontal_tab, c_vertical_tab.
Machine Translated by Google

244 Fortran modern explicat

Toate sunt de tip caracter cu lungimea unu și tipul c_char (sau tipul implicit dacă c_char are valoarea
-1).

Tabelul 12.1. Constante denumite pentru tipuri interoperabile de tipuri intrinseci Fortran.
Constanta numita Tipul sau tipurile C
Tastați întreg c_int int
c_scurt scurt int
c_long long int
c_long_long long long int
c_signed_char caracter semnat, caracter nesemnat
c_size_t c_int8_t size_t int8_t

c_int16_t int16_t
c_int32_t int32_t
c_int64_t int64_t
c_int_least8_t int_least8_t
c_int_least16_t int_least16_t
c_int_least32_t int_least32_t
c_int_least64_t int_least64_t
c_int_fast8_t int_fast8_t
c_int_fast16_t int_fast16_t
c_int_fast32_t int_fast32_t
c_int_fast64_t int_fast64_t
c_intmax_t intmax_t
c_intptr_t intptr_t
real c_float pluti

c_dublu dubla
c_long_double long double
complex c_float_complex float _Complex
c_double_complex dublu _Complex
c_long_double_complex long double _Complex
logic c_bool _Bool
caracterul c_char char
Machine Translated by Google

Interoperabilitate cu C 245

12.3 Interoperabilitate cu tipuri de pointer C

Pentru interoperarea cu pointerii C (care sunt doar adrese), modulul conține tipurile derivate c_ptr
și c_funptr care sunt interoperabile cu tipurile de pointer C obiect și, respectiv, funcție.
Componentele lor sunt private. Există constante numite c_null_ptr și c_null_funptr pentru valorile
nule corespunzătoare ale lui C.
Modulul conține, de asemenea, următoarele proceduri.

c_loc (x) este o funcție de interogare care returnează un scalar de tip c_ptr care deține C
adresa argumentului său x, care trebuie

i) să aibă parametrii de tip și tip interoperabili și să fie

a) o variabilă care are atributul target și este interoperabilă; b) o


variabilă alocabilă alocată care are atributul țintă și nu este o matrice de dimensiune
zero; sau c) un indicator scalar asociat;

sau

ii) să fie un scalar nepolimorf, să nu aibă parametri de tip lungime și să fie

a) o variabilă nealocabilă, non-pointer, care are atributul target; b) o variabilă


alocabilă alocată care are atributul target; sau c) un pointer asociat.

c_funloc (x) este o funcție de interogare care returnează adresa C a unei proceduri. Argumentul x
este permis să fie o procedură care este interoperabilă (vezi Secțiunea 12.7) sau un pointer
asociat cu o astfel de procedură.

c_associated (c_ptr1 [, c_ptr2]) este o funcție de interogare pentru scalari de tip c_ptr sau pentru
scalari de tip c_funptr. Returnează un scalar logic implicit. Are valoarea false dacă c_ptr1
este un pointer C nul sau dacă c_ptr2 este prezent cu o valoare diferită; în caz contrar, are
valoarea adevărată.

c_f_pointer (cptr, fptr [, shape]) este o subrutină cu argumente

cptr este un scalar de tip c_ptr cu intent in. Valoarea sa este fie i)

adresa C a unei entitati de date interoperabile; sau ii)


rezultatul unei referiri la c_loc cu un argument neinteroperabil.
Nu trebuie să fie adresa C a unei variabile Fortran care nu are atributul target.

fptr este un pointer cu intent out.

i) Dacă cptr este adresa C a unei entități interoperabile, fptr trebuie să fie un pointer
de date de tipul și parametrii de tip ai entității și devine pointer asociat cu ținta
cptr. Dacă este o matrice, forma sa este specificată de formă și fiecare limită
inferioară este 1.
Machine Translated by Google

246 Fortran modern explicat

ii) Dacă cptr a fost returnat printr-un apel de c_loc cu un argument neinteroperabil x, fptr
trebuie să fie un pointer scalar nepolimorf de tipul și parametrii de tip ai lui x. x sau
ținta sa, dacă este un pointer, nu trebuie să fi fost dealocate sau să fi devenit nedefinită
din cauza executării unei instrucțiuni return sau end. fptr devine pointer asociat cu x
sau ținta acestuia. shape (opțional) este o matrice de rang unu de tipul întreg cu

intenție. Dacă este prezentă, dimensiunea sa este egală cu rangul fptr. Trebuie să fie prezent
dacă fptr este o matrice.

c_f_procpointer (cptr, fptr) este o subrutină cu argumente

cptr este un scalar de tip c_funptr cu intent in. Valoarea sa este adresa C a unei proceduri care
este interoperabila.
fptr este un indicator de procedură cu intent out. Interfața sa trebuie să fie interoperabilă cu
ținta cptr și devine asociată cu pointer-ul acelei ținte.

Un pointer Fortran sau o variabilă alocabilă și majoritatea tablourilor Fortran nu interoperează direct
cu nicio entitate C deoarece C nu are aceleași concepte; de exemplu, spre deosebire de un pointer de
matrice Fortran, un pointer de matrice C nu poate descrie o secțiune de matrice necontiguă. Cu toate
acestea, acest lucru nu împiedică astfel de entități să fie transmise la C prin asociere de argument,
deoarece compilatoarele Fortran efectuează deja copiere în copiere atunci când acest lucru este necesar.
De asemenea, funcția c_loc poate fi folosită pentru a obține adresa C a unui tablou alocabil alocat, ceea
ce este util dacă partea C a programului dorește să mențină un pointer către această matrice.
În mod similar, adresa unui tablou alocat în C poate fi transmisă la Fortran și c_f_pointer utilizat pentru a
construi un pointer Fortran a cărui țintă este tabloul C. Există o ilustrare în acest sens în Secțiunea 12.9.

Cazul ii) al lui c_loc permite programului C să primească un pointer către un scalar Fortran care nu
este interoperabil. Nu se intenționează ca vreo utilizare a acestuia să fie făcută în C, decât pentru a-l
transmite înapoi către Fortran, unde c_f_pointer este disponibil pentru a reconstrui pointerul Fortran.
Există o ilustrare în acest sens în Secțiunea 12.10.

12.4 Interoperabilitatea tipurilor derivate

Pentru ca un tip derivat să fie interoperabil, trebuie să aibă atributul bind:

tip, bind(c) :: tipul meu


:
tipul meu tipul meu

Nu trebuie să fie un tip de secvență (Anexa B.2.1), să aibă parametri de tip, să aibă atributul extins
(Secțiunea 14.2) sau să aibă proceduri legate de tip (Secțiunea 14.6). Fiecare componentă trebuie să aibă
parametrii de tip și tip interoperabili, nu trebuie să fie o matrice de dimensiune zero, nu trebuie să fie un
pointer și nu trebuie să fie alocabil.
Aceste restricții permit tipului să interopereze cu un tip de structură C care are același număr de
componente. Componentele corespund prin poziție în definițiile lor. Fiecare componentă Fortran trebuie
să fie interoperabilă cu componenta C corespunzătoare. Iată un exemplu simplu:
Machine Translated by Google

Interoperabilitate cu C 247

typedef struct { int m,


n; float r; } myctype;

este interoperabil cu
utilizare, intrinsec :: iso_c_binding type,
bind(c) :: myftype
integer(c_int) :: i, j real(c_float) ::
s end type myftype

Numele tipului și numele componentelor nu sunt semnificative pentru interoperabilitate. Dacă două
definiții echivalente ale unui tip derivat interoperabil sunt făcute în unități de acoperire separate, ele
interoperează cu același tip C (dar de obicei este de preferat să definiți un tip într-un modul și să îl accesați
prin instrucțiuni de utilizare).
Niciun tip Fortran nu este interoperabil cu un tip union C, un tip struct C care conține un bit
câmp sau un tip de structură C care conține un membru flexibil al matricei.

12.5 Interoperabilitatea variabilelor

O variabilă scalară Fortran este interoperabilă dacă este de tip și parametri de tip interoperabili și nu este
nici pointer, nici alocabilă. Este interoperabil cu un scalar C dacă tipul și parametrii de tip Fortran sunt
interoperabili cu tipul C.
O variabilă matrice Fortran este interoperabilă dacă dimensiunea sa este diferită de zero, este de tip interoperabil
și parametrii de tip și are o formă explicită sau o dimensiune presupusă (Anexa B.3).
Pentru ca o matrice Fortran de rang unu să interoperaze cu o matrice C, elementele matricei Fortran trebuie
să fie interoperabile cu elementele matricei C. Dacă matricea Fortran este de dimensiune explicită, matricea C
trebuie să aibă aceeași dimensiune. Dacă matricea Fortran are o dimensiune presupusă, matricea C nu trebuie să
aibă o dimensiune specificată.
O matrice Fortran a de rang mai mare de unu și de formă (e1, e2,..., er) este interoperabilă cu o matrice
C de dimensiune er cu elemente care sunt interoperabile cu o matrice Fortran de același tip ca a și de
formă (e1,e2,..., er 1). Pentru rangurile mai mari de două, această regulă se aplică recursiv.
De exemplu, tablourile Fortran declarate ca
întreg(c_int) :: fa(18, 3:7), fb(18, 3:7, 4)

sunt interoperabile cu tablourile C declarate ca


int ca[5][18], cb[4][5][18];

iar elementele corespund. Rețineți că ordinea indicelui este inversată.


O matrice Fortran de dimensiune presupusă de rang mai mare decât unu este interoperabilă cu o
matrice C de dimensiune nespecificată dacă elementele sale sunt legate de tabloul Fortran în același mod
ca în cazul dimensiunii explicite. De exemplu, tablourile Fortran declarate ca
integer(c_int) :: fa(18, *), fb(18, 3:7, *) sunt interoperabile cu

tablourile C declarate ca
int ca[ ][18], cb[ ][5][18];
Machine Translated by Google

248 Fortran modern explicat

12.6 Atributul valoare

De dragul interoperabilității, a fost introdus un nou atribut, valoarea, pentru argumentele simulate
scalare. Poate fi specificat într-o declarație de tip pentru argument sau separat într-o declarație de valoare:

funcția func(a, i, j) bind(c) real(c_float) func,


a integer(c_int), value :: i, j value :: a

Când procedura este invocată, se face o copie a argumentului propriu-zis. Argumentul dummy este o
variabilă care poate fi modificată în timpul execuției procedurii, dar la returnare nu are loc nicio copiere
înapoi. Singura restricție asupra tipului este că, dacă este caracter, lungimea caracterului trebuie să fie
cunoscută în momentul compilării. Argumentul nu trebuie să fie un pointer, să fie alocabil, să aibă intent
out sau inout, să fie o procedură sau să aibă atributul volatil (Secțiunea 16.3).
Atributul value nu se limitează la procedurile cu atributul bind; poate fi folosit în
orice procedura. Acest lucru este util pentru un anumit stil de programare; de exemplu, în

funcția întreg nth_word_position(șir, n) rezultat(pos) caracter(*), intent(in) :: șir


întreg, valoare logică în_word = .false. do pos = 1, len(string) if
:: n else if (.not.in_word) atunci in_word
(string(pos:pos)==' ')then in_word = .false.
= .true. :: în cuvânt

! La primul caracter al unui cuvânt.


n=n-1
dacă (n==0) revine ! A găsit a n-a, poziție de întoarcere.
sfâr itul dacă

sfâr itul face

pos = 0 ! n cuvinte nu au fost găsite, returnează zero.


funcția finală

argumentul n este micsorat local pana cand ajunge la zero, fara a afecta argumentul real sau a necesita
o variabila temporara suplimentara. Deoarece atributul modifică mecanismul de transmitere a
argumentelor, o procedură cu un argument fals de valoare este necesară pentru a avea o interfață
explicită.
În contextul unui apel de la C, absența atributului value indică faptul că se așteaptă ca argumentul
actual să fie un indicator de obiect către un obiect de tipul specificat sau un pointer de funcție a cărui
țintă are un prototip care este interoperabil cu interfața specificată. (vezi secțiunea următoare).
Machine Translated by Google

Interoperabilitate cu C 249

12.7 Interoperabilitatea procedurilor

O procedură Fortran este interoperabilă dacă are o interfață explicită și este declarată cu atributul bind:

funcția func(i, j, k, l, m) bind(c) subrutine subr () bind(c)

Rețineți că pentru o subrutină fără argumente, sunt necesare parantezele. Procedura poate fi o procedură
externă sau modulară, dar nu este permisă să fie o procedură internă.
Toate argumentele fictive trebuie să fie neopționale și interoperabile. Pentru o funcție, rezultatul trebuie să fie
scalar și interoperabil.
Procedura are de obicei o etichetă obligatorie, care are un domeniu de aplicare global și este numele prin
care este cunoscută procesorului C. În mod implicit, este versiunea cu minuscule a numelui Fortran. De
exemplu, funcția din paragraful anterior are eticheta obligatorie func. Se poate specifica o etichetă alternativă
obligatorie:

funcția func(i, j, k, l, m) bind(c, name='c_func')

Valoarea care urmează numelui= trebuie să fie o expresie constantă de caractere implicite scalară.
Ignorând spațiile de început și de final, acesta trebuie să fie un identificator C valid și majusculele sunt semnificative.
O etichetă obligatorie nu este un alias pentru numele procedurii pentru o invocare Fortran obișnuită.
Este pentru utilizare numai de la C. Două entități diferite nu trebuie să aibă aceeași etichetă obligatorie.
Dacă expresia caracterului are lungime zero sau este total necompletat, nu există nicio etichetă obligatorie.
Procedura poate fi încă invocată din C printr-un pointer de procedură și, dacă acesta este singurul mod în care
va fi invocată, nu este adecvat să-i dea o etichetă obligatorie. În special, o procedură de modul privat nu
trebuie să aibă o etichetă obligatorie.
O interfață de procedură Fortran interoperabilă este interoperabilă cu un prototip de funcție C care are
același număr de argumente și nu are argumente variabile notate cu punctele de suspensie (...). Pentru o
funcție, rezultatul trebuie să fie interoperabil cu rezultatul prototipului.
Pentru o subrutină, prototipul trebuie să aibă un rezultat nul. Un argument fals cu atributul value trebuie să
fie interoperabil cu parametrul formal corespunzător. Un argument fals fără atributul value trebuie să
corespundă unui parametru formal de tip pointer și să fie interoperabil cu o entitate de tipul referit al
parametrului formal. Rețineți că un tablou Fortran nu are permisiunea de a avea atributul value, dar poate
interopera cu un tablou C, deoarece acesta este automat de tip pointer.

Iată un exemplu de interoperabilitate a interfeței procedurii. Interfața Fortran din Figura 12.1 este
interoperabilă cu prototipul funcției C

short int func(int i, double *j, int *k, int l[10], void *m);

Dacă o funcție C cu acest prototip urmează să fie apelată din Fortran, codul Fortran trebuie să acceseze o
interfață ca aceasta. Apelul în sine este gestionat în același mod ca și cum ar fi apelată o procedură externă
Fortran cu o interfață explicită. Aceasta înseamnă, de exemplu, că secțiunea matrice larray(1:20:2) ar putea fi
argumentul propriu-zis corespunzător matricei fictive l; în acest caz, are loc copy-in copy-out.

În mod similar, dacă urmează să fie apelată o funcție Fortran cu interfața din paragraful anterior
din C, codul C trebuie să aibă un prototip precum cel de la paragraful anterior.
Machine Translated by Google

250 Fortran modern explicat

Figura 12.1 O interfață Fortran pentru o funcție C.


interfata
funcția func(i, j, k, l, m) bind(c) utilizare, intrinsec ::
iso_c_binding integer(c_short) :: func integer(c_int),
valoare :: i
real(c_double) :: j integer(c_int) valoare
:: k, l(10) tip(c_ptr),

:: m
funcția finală
interfață finală

Dacă o funcție C este apelată de la Fortran, aceasta nu trebuie să folosească semnal (standard C,
7.14.1) pentru a schimba gestionarea oricărei excepții care este gestionată de procesorul Fortran și nu
trebuie să modifice starea virgulă mobilă (secțiunea 11.8.4) altfel decât prin stabilirea unui flag de
excepție pentru semnalizare. Valorile steagurilor de excepție în virgulă mobilă la intrarea într-o funcție
C sunt dependente de procesor.

12.8 Interoperabilitatea datelor globale

O variabilă de modul interoperabil (sau un bloc comun, apendicele B.2.3, cu membri interoperabili)
poate primi atributul bind într-o declarație de tip sau într-o instrucțiune bind:

folosește iso_c_binding
integer(c_int), bind(c) :: c_extern integer(c_long) ::
c2 bind(c, name='myvariable') :: c2 common /
com/ r, s real(c_float) :: r, s bind(c):: /com/

Are o etichetă obligatorie definită de aceleași reguli ca și pentru proceduri și interoperează cu o variabilă
C cu legătură externă care este de tip corespunzător. Dacă o etichetă de legare este specificată într-o
instrucțiune, instrucțiunea trebuie să definească o singură variabilă.
O variabilă cu atributul de legare are și atributul de salvare (care poate fi confirmat în mod explicit).
O modificare a variabilei în oricare limbă afectează valoarea variabilei corespunzătoare în cealaltă
limbă. Variabila AC nu are permisiunea de a interopera cu mai mult de o variabilă Fortran.

Declarația obligatorie este disponibilă numai în acest scop; nu este disponibil, de exemplu, pentru a
specifica atributul bind pentru o procedură de modul. De asemenea, atributul bind nu trebuie specificat
pentru o variabilă care nu este o variabilă de modul (adică nu este disponibil pentru a confirma că o
variabilă este interoperabilă) și nu trebuie specificat pentru o variabilă de modul care se află într-o
variabilă comună. bloc.
Machine Translated by Google

Interoperabilitate cu C 251

Dacă un bloc comun este specificat într-o instrucțiune de legare, acesta trebuie specificat într-o
instrucțiune de legare cu aceeași etichetă de legare în fiecare unitate de acoperire în care este declarat.
Interoperează cu o variabilă de tip struct ale cărei componente sunt fiecare interoperabilă cu membrul
corespunzător al blocului comun. Dacă blocul comun are un singur membru, acesta interoperează și cu
o variabilă care este interoperabilă cu membrul.
Declarația de echivalență (Anexa B.2.2) nu este permisă pentru a specifica o variabilă care
are atributul bind sau este membru al unui bloc comun care are atributul bind.
Două două puncte într-o instrucțiune de legătură este opțională.

12.9 Invocarea unei funcții C din Fortran

Dacă o funcție C urmează să fie invocată din Fortran, aceasta trebuie să aibă o legătură externă și să fie
descrisă de un prototip C care este interoperabil cu o interfață Fortran accesibilă care are aceeași etichetă
de legare.
Dacă este necesară transmiterea unui tablou Fortran către C, interfața poate specifica ca matricea să
fie de dimensiune explicită sau presupusă, iar mecanismele obișnuite Fortran, care implică probabil
copierea în copiere, se asigură că o matrice contiguă este primită de codul C. . Iată un exemplu care
implică atât o matrice de dimensiune presupusă, cât și o matrice alocabilă. Prototipul C este

int c_library_function(int expl[100], float alloc[], int len_alloc);

iar codul Fortran este prezentat în Figura 12.2.

Figura 12.2 Trecerea matricelor Fortran la o funcție C.


utilizați interfața iso_c_binding

întreg (c_int) funcție c_library_function (expl, alloc, len_alloc) &

bind(c)
utilizați iso_c_binding
integer(c_int) real(c_float) :: expl(100) ::
integer(c_int), value :: alloc(*)
len_alloc
end function c_library_function end interface

integer(c_int):: expl(100), len_alloc, x1 real(c_float), allocabil :: alloc(:)

:
len_alloc = 200
alocare (alloc(len_alloc))
:
x1 = c_library_function(expl, alloc, len_alloc)
:

Regulile privind dezacordul privind forma și lungimea caracterului (Anexa B.3) permit entităților
specificate ca character(kind=c_char) de orice lungime să fie asociate cu o dimensiune presupusă
Machine Translated by Google

252 Fortran modern explicat

sau matrice de formă explicită, și astfel să fie transmisă la și de la C. De exemplu, funcția C cu prototip

void Copy(car in[], char out[]);

poate fi invocat de codul Fortran din Figura 12.3.


Acest cod funcționează deoarece Fortran permite ca variabila caracter digit_string să fie asociată cu
matricea simulată de dimensiune presupusă. De asemenea, am profitat de această ocazie pentru a
ilustra folosirea unei etichete de legare pentru a apela o procedură C al cărei nume include o literă
majusculă. .

Figura 12.3 Transmiterea șirurilor de caractere Fortran unei funcții


C. utilizare, intrinsecă :: iso_c_binding, numai: interfață c_char, c_null_char

copierea subrutinei (in, out) bind (c, name='Copy')


utilizare, intrinsecă :: iso_c_binding, numai: caracter
c_char(kind=c_char), dimensiune(*) :: in, out
sfârșitul subrutinei copiere
sfârșitul interfeței
character(len=10, kind=c_char) :: & digit_string =
c_char_'123456789' // c_null_char character(kind=c_char) ::
digit_arr(10) call copy(digit_string, digit_arr) print '(1x, a1)', digit_arr(1:9)

Sfâr it

12.10 Invocarea Fortran de la C


O referire în C la o procedură care are atributul de legare, are aceeași etichetă de legare și este definită
prin intermediul Fortran, face ca procedura Fortran să fie invocată.
Figura 12.4 prezintă un exemplu de procedură Fortran care este apelată din C și utilizează o structură
pentru a permite accesarea matricelor alocate în C în Fortran. Declarația C struct corespunzătoare este:

struct pass { int


lenc, lenf; plutire *c,
*f;
};

prototipul funcției C este:

simulare void(struct pass *arrays);

iar instrucțiunea de apelare C ar putea fi:

simulare(&arrays);
Machine Translated by Google

Interoperabilitate cu C 253

Figura 12.4 Accesarea în Fortran a unei matrice care a fost alocată în C.


simulare subrutinei(matrice) bind(c) folosește iso_c_binding type,
bind(c) :: pass integer (c_int) :: lenc, lenf type (c_ptr) :: c, f tip final
tip de trecere (trecere), intent (în) :: matrice real (c_float),
pointer :: c_array(:)

...
! asociați c_array cu o matrice alocată în apelul C
c_f_pointer(arrays%c, c_array, (/arrays%lenc/) )
...
terminați simularea subrutinei

Nu este neobișnuit ca un modul de bibliotecă Fortran să aibă o procedură de inițializare care stabilește
o structură de date pentru a păstra toate datele pentru o anumită problemă care urmează să fie rezolvată.
Apelurile ulterioare la alte proceduri din modul oferă date despre problemă sau primesc date despre
soluția acesteia. Structura de date este probabil să fie de un tip care nu este interoperabil, de
exemplu, deoarece are componente care sunt matrice alocabile.
Procedurile c_loc și c_f_pointer au fost concepute pentru a sprijini această situație. Codul Fortran
din Figura 12.5 ilustrează acest lucru. Tipul problem_struct deține o matrice alocabilă de dimensiunea
problemei și multe altele. Când codul C apelează new_problem, trece dimensiunea. Codul Fortran
alocă o structură și o componentă de matrice în cadrul acesteia de dimensiunea relevantă; apoi
returnează un pointer către structură. Codul C apelează ulterior add și transmite date suplimentare
împreună cu pointerul pe care l-a primit de la new_problem. Procedura Fortran add folosește
c_f_pointer pentru a stabili un pointer Fortran pentru structura relevantă și efectuează calcule
folosindu-l. Rețineți că codul C poate apela new_problem de mai multe ori dacă dorește să funcționeze
simultan cu mai multe probleme; fiecare va avea o structură separată de tip problem_struct și va fi
accesibil prin propriul său „mâner” de tip (c_ptr). Când o problemă este completă, codul C își dă rămas
bun pentru a-și dealoca structura.

12.11 Enumerări

O enumerare este un set de constante întregi (enumeratoare) care este adecvat pentru interoperarea
cu C. Tipul de enumeratori corespunde tipului întreg pe care C l-ar alege pentru același set de
constante. Iată un exemplu:
enum, bind(c)
enumerator :: roșu = 4, albastru = 9
enumerator galben
end enumerare

Aceasta declară constantele numite roșu, albastru și galben cu valorile 4, 9 și, respectiv, 10.
Machine Translated by Google

254 Fortran modern explicat

Figura 12.5 Oferirea accesului în C la o structură Fortran care nu este interoperabilă. modul
lib_code
utilizați tipul
iso_c_binding :: problem_struct
real, alocabil :: a(:)
: ! Mai multe lucruri
de tip final
con ine
tip(c_ptr) funcție new_problem(problem_size) bind(c)
integer(c_size_t), valoare :: problem_size
type(problem_struct), pointer :: problem_ptr
allocate(problem_ptr) allocate(problem_ptr%a(problem_size))
new_problem = c_loc(problem_ptr) funcția finală new_problem
subrutine,... add(problem) (c)

type(c_ptr), intent(in) :: tipul problemei


(problem_struct), pointer :: problem_ptr
:
apelați c_f_pointer(problemă, problem_ptr)
:
termina subrutina adauga
subrutina la revedere(problema) bind(c)
type(c_ptr), intent(in) :: tip de problemă
(problem_struct), pointer :: problem_ptr apel c_f_pointer(problem,
problem_ptr) deallocate(problem_ptr)

termina subrutinei la revedere


terminați modulul lib_code

Dacă o valoare nu este specificată pentru un enumerator, aceasta este considerată cu unu mai mare decât
enumeratorul anterior sau zero dacă este primul.

Pentru a declara o variabilă de tip enumerare, utilizați funcția intrinsecă kind pe una dintre
constante. Un exemplu care utilizează definiția enumerată de mai sus este:

întreg (tip (roșu)) :: culoare_fond

Exerciții

1. Scrieți un bloc de interfață generic Fortran pentru funcțiile standard de eroare C libm erf și erff.

2. Scrieți funcții Fortran pentru a calcula produsul scalar a doi vectori, potrivite pentru a fi apelate din C.
Machine Translated by Google

13. Introduceți parametri și indicatorii de


procedură

13.1 Introducere

Acest capitol combină subiectele separate despre extensiile parametrilor de tip și indicatorii de procedură.

Extensiile de parametri de tip constau în adăugarea unor parametri de tip amânat, interogare a
parametrilor de tip și capacitatea de a parametriza tipurile derivate.
Extensia pointer de procedură oferă posibilitatea de a asocia un pointer cu o procedură, similar
modului în care procedurile fictive devin asociate cu procedurile reale.

13.2 Parametri de tip amânat

O valoare a parametrului de tip len este permisă să fie două puncte într-o declarație de tip, cum ar fi

caracter(len=:), pointer :: varchar

pentru un pointer sau o entitate alocabilă. Indică un parametru de tip amânat; un astfel de parametru
de tip nu are o valoare definită până când i se dă una prin alocare sau atribuire pointer. De exemplu, în

character(:), pointer :: varchar character(100),


target :: name character(200), target :: address

:
varchar => nume
:
varchar => adresa

lungimea caracterului varchar după fiecare atribuire de pointer este aceeași cu cea a țintei sale; adică
100 după prima atribuire de indicator și 200 după a doua.
Pentru tipurile intrinseci, numai lungimea caracterelor poate fi amânată. Tipuri derivate care sunt
parametrizat poate avea parametri de tip care pot fi amânați, vezi Secțiunea 13.4.2.
Parametrilor de tip amânat li se pot da valori prin instrucțiunea alocare; consultați Secțiunea 15.4.1
pentru detalii. Pentru variabilele alocabile li se pot da valori și prin atribuire; consultați Secțiunea 15.5.2
pentru detalii.
Machine Translated by Google

256 Fortran modern explicat

13.3 Interogare tip parametru

Valoarea (actuală) a unui parametru de tip al unei variabile poate fi descoperită printr-o interogare de parametru
de tip. Aceasta folosește aceeași sintaxă ca și pentru accesul la componente, dar valoarea este întotdeauna
scalară, chiar dacă obiectul este un tablou; de exemplu, în

real(tip_real_selectat(10,20)) :: z(100)
:
print *,z%kind

este tipărită o singură valoare, aceasta fiind rezultatul executării referinței la funcția intrinsecă
select_real_kind. Acest caz particular este echivalent cu kind(z). Cu toate acestea, interogarea parametrului
de tip poate fi utilizată chiar și atunci când funcția intrinsecă nu este disponibilă; de exemplu, în

subrutină write_centered(ch, len)


caracter(*), intent(inout) :: ch integer, intent(in) ::
len integer do i=1, (len-ch%len)/2
:: i

nu ar fi posibil să se înlocuiască interogarea parametrului de tip ch%len cu referința la funcția intrinsecă


len(ch) deoarece len este numele unui argument fals.
Rețineți că această sintaxă nu trebuie utilizată pentru a modifica valoarea unui parametru de tip, să spunem de
care apare în partea stângă a unei declarații de atribuire.

13.4 Tipuri derivate parametrizate

Parametrii de tip au fost introduși pentru tipurile derivate, în exactă analogie cu parametrii de tip
ai tipurilor intrinseci. La fel ca parametrii de tip intrinseci, parametrii de tip derivat vin în două
variante; cele care trebuie cunoscute la momentul compilării (cum ar fi parametrul kind pentru
tipul real) și cele a căror evaluare poate fi amânată până la timpul de execuție (cum ar fi parametrul
len pentru tipul caracterului). Primii sunt cunoscuți ca parametri de tip tip (deoarece, pentru
tipurile intrinseci, toate sunt denumite kind), iar cei din urmă ca parametri de tip lungime (prin
analogie cu lungimea caracterului).

13.4.1 Definirea unui tip derivat parametrizat

Pentru a defini un tip derivat care are parametri de tip, parametrii de tip sunt listați în instrucțiunea de
definire a tipului și trebuie, de asemenea, declarați în mod explicit la începutul definiției de tip. De
exemplu,

matrice de tip(real_kind, n, m)
integer, kind :: real_kind intreg, len
real(real_kind) :: value(n,:: m)
n, m
end type
matrice
Machine Translated by Google

Parametrii de tip și indicatorii de procedură 257

definește o matrice de tip derivat cu un parametru de tip tip numit real_kind și doi parametri de tip lungime
numiți n și m. Toți parametrii de tip trebuie declarați în mod explicit ca fiind de tip întreg cu atributul kind
sau len pentru a indica un parametru de tip sau, respectiv, de lungime. În definiția tipului, un parametru
de tip tip poate fi utilizat atât în expresiile constante, cât și în expresiile de specificație, dar un parametru
de tip lungime poate fi utilizat numai într-o expresie de specificație (adică pentru limitele matricei și pentru
alți parametri de tip lungime, cum ar fi lungimea caracterului) . Cu toate acestea, nu există nicio cerință ca
un parametru de tip să fie utilizat deloc. De exemplu, vezi Figura 13.1.

Figura 13.1 Un tip derivat parametrizat valid și unul nevalid. tip


goodtype(p1, p2, p3, p4) integer, kind :: p1, p3 integer, len :: p2, p4
real(kind=p1) :: c1 ! ok, p1 este
(len=p2)
un parametru
:: c2 ! ok, acesta
de tip tip
estecaracter
un
complex de expr de specificație
oriunde
:: c3(p3)
intreg! ok,
:: c4p3
= poate
p1 ! ok,fi p1
folosit
poate fi folosit oriunde! p4 goodtype
nu a fost folosit, dar e ok. tipul final

tip badtype(p5) integer,


len :: p5 real(kind=p5) :: x
integer end type badtype ! Invalid, p5 nu este un parametru de tip tip :: y = p5 ! Invalid,
p5 nu este un parametru de tip tip

Dacă o componentă este inițializată implicit, parametrii de tip și limitele matricei trebuie să fie constante
expresii. De exemplu, dacă o componentă este declarată ca

caracter (n) :: ch(m) = 'xyz'

atât n cât și m trebuie să fie denumite constante sau parametri de tip fel.
Când se declară o entitate de tip derivat parametrizat, numele acesteia este calificat de tip
parametrii într-o declarație de tip de formular

tip(spec-tip-derivat)

unde este derivat-type-spec

nume-tip-derivat(listă-tip-param-spec)

în care derived-type-name este numele tipului derivat și type-param-spec este

[keyword =] tip-param-valoare

Cuvântul cheie trebuie să fie numele unuia dintre parametrii de tip ai tipului. La fel ca argumentele
cuvântului cheie în apelurile de procedură, după un tip-param-spec care include un cuvânt cheie = clauză, orice
Machine Translated by Google

258 Fortran modern explicat

specificațiile suplimentare ale parametrilor de tip trebuie să includă un cuvânt cheie. Rețineți că acest lucru este
în concordanță cu sintaxa pentru specificarea parametrilor de tip pentru tipurile intrinseci. Iată câteva exemple
de variabile ale matricei noastre de tip:

tip(matrice(tip(0,0), 10, 20)) :: x


tip(matrice(tip_real=tip(0d0), n=n1, m=n2)) :: y

13.4.2 Parametri de tip presupus și amânat

În ceea ce privește un argument dummy al caracterului de tip intrinsec, se poate presupune un parametru
de tip lungime pentru un argument dummy de tip derivat. În acest caz, valoarea sa este indicată printr-
un tip-param-value care este un asterisc și este luată din cea a argumentului real, ca în exemplu:

subrutina print_matrix(z)
tip(matrice(tip_real_selectat(30.999), n=*, m=*)) :: z
:

Un asterisc poate fi folosit și pentru un parametru de tip presupus în instrucțiunea de alocare (vezi
Secțiunea 15.4) și instrucțiunea de tip select (vezi Secțiunea 14.5).
În ceea ce privește caracterul de tip intrinsec, o lungime tip-param-valoare pentru un tip derivat poate
fi amânată. De exemplu, în

tip(matrice(selected_real_kind(30.999), n=:, m=:)), pointer :: mp


type(matrix(selected_real_kind(30.999), n=100, m=200)), target :: x mp => x

valorile atât pentru n cât și pentru m sunt amânate până la asociere sau alocare. După executarea
atribuirii pointerului, valorile parametrilor de tip n și m ale lui mp sunt egale cu cele ale lui x (100 și,
respectiv, 200).

13.4.3 Valori implicite ale parametrilor de tip

Toți parametrii de tip pentru tipurile intrinseci au valori implicite. În mod similar, un parametru de tip pentru
un tip derivat poate avea o valoare implicită; aceasta este declarată folosind aceeași sintaxă ca și pentru
inițializarea implicită a componentelor, de exemplu

tip char_with_max_length(maxlen, fel) întreg, len ::


maxlen = 255 întreg, fel caracter întreg
fel) ::(maxlen,
valoare
:: fel = fel('a')
:: len

tipul final char_with_max_length

Când se declară obiecte de tip char_with_max_length, nu este necesar să se specifice parametrii tip sau
maxlen dacă valorile implicite sunt acceptabile. Acest lucru ilustrează, de asemenea, că, în multe cazuri
simple care au un singur parametru de tip tip, numele natural pentru
Machine Translated by Google

Parametrii de tip și indicatorii de procedură 259

parametrul tip poate fi amabil (la fel cum este pentru tipurile intrinseci). Acest nume a fost ales în acest exemplu
special deoarece char_with_max_length a fost menit să fie cât mai asemănător cu caracterul de tip intrinsec
posibil. Rețineți că această alegere nu intră în conflict cu cuvântul cheie tip atribut și nici nu intră în conflict cu
utilizarea tipului funcției intrinseci în definiția tipului.

13.4.4 Interogarea parametrului tip derivat

Valoarea unui parametru de tip al unei variabile poate fi descoperită printr-o interogare de parametru de tip,
ca și în cazul tipurilor intrinseci (vezi Secțiunea 13.3). De exemplu, în

tip(car_cu_lungime_max.(...,...)) :: x, y(100)
:
print *,x%kind print
*,y%maxlen

vor fi tipărite valorile parametrului de tip kind al lui x și ale parametrului tip maxlen al lui y.
Deoarece sintaxa componentelor este utilizată pentru a accesa valoarea unui parametru de tip, un tip nu este
permis să aibă o componentă al cărei nume este același cu unul dintre parametrii tipului.

13.5 Interfețe abstracte

În Fortran 95, pentru a declara un dummy sau o procedură externă cu o interfață explicită, trebuie să utilizați un
bloc de interfață. Acest lucru este bine pentru o singură procedură, dar este oarecum verbos pentru declararea
mai multor proceduri care au aceeași interfață (în afară de numele procedurilor). Mai mult, în Fortran 2003, există
mai multe situații în care acest lucru devine imposibil (componente pointer de procedură sau proceduri abstracte
legate de tip).
Din aceste motive, interfața abstractă este introdusă în Fortran 2003. O interfață abstractă dă un nume unui
set de caracteristici și nume de cuvinte cheie argumente care ar constitui o interfață explicită pentru o procedură,
fără a declara că vreo procedură reală are acele caracteristici. Acest nume abstract de interfață poate fi utilizat în
instrucțiunea de procedură pentru a declara proceduri care ar putea fi proceduri externe, proceduri fictive,
indicatoare de procedură sau proceduri legate de tip amânat.

Un bloc de interfață abstractă conține cuvântul cheie abstract și fiecare corp de procedură declarat în acesta
definește o nouă interfață abstractă. De exemplu, având în vedere blocul de interfață abstractă

interfață abstractă

subrutine boring_sub_with_no_args end subroutine


boring_sub_with_no_args funcția reală r2_to_r(a, b) real,
intent(in) :: a, b end function r2_to_r

interfață finală

declarațiile de declarație
Machine Translated by Google

260 Fortran modern explicat

procedura(boring_sub_with_no_args) :: sub1, sub2 procedure(r2_to_r) ::


modulus, xyz

declară sub1 și sub2 ca fiind subrutine fără argumente reale, iar modulul și xyz ca fiind funcții reale ale a
două argumente reale. Numele boring_sub_with_no_args și r2_to_r sunt locale pentru unitatea de
acoperire în care este declarat blocul abstract de interfață și nu reprezintă proceduri sau alte entități
globale în sine.
La fel ca și cu interfețele abstracte, instrucțiunea de procedură poate fi utilizată cu orice specific
procedură care are o interfață explicită. De exemplu, dacă distracția are o interfață explicită,

procedura(distractie) :: fun2

declară fun2 ca fiind o procedură cu o interfață identică cu cea a fun.


Declarația de procedură nu este disponibilă pentru un set de proceduri generice, dar poate fi utilizată
pentru o procedură specifică care este membru al unui set generic. Toate procedurile intrinseci sunt
generice, dar câteva au și versiuni specifice care pot fi transmise ca argument real și sunt enumerate în
Tabelul B.2. Un intrinsec poate fi denumit într-o instrucțiune de procedură numai dacă numele apare în
acest tabel.
În plus, instrucțiunea de procedură poate fi folosită pentru a declara proceduri care au interfețe
implicite; în loc să puneți numele unei proceduri în paranteze, nu se folosește nimic, fie o specificație de
tip. De exemplu,

procedura() x
procedura(real) y
procedura(complex(tip(0.0d0))) z

declară x o procedură (care ar putea fi o subrutină sau o funcție), y o funcție reală și z o funcție complexă
(dublă). Acest lucru este exact echivalent cu

extern :: x
real, extern :: y
complex(kind(0.0d0)), extern :: z

Pentru aceste cazuri, instrucțiunea de procedură nu oferă nicio funcționalitate utilă față de instrucțiunea
de declarație externă sau de tip; într-adevăr își iese în sine numai atunci când se declară pointerii de
procedură (vezi secțiunea următoare).
Sintaxa completă a instrucțiunii de procedură este

procedura ( [ proc-interface ] ) [[, proc-attr-spec] ... ::] proc-decl-list

unde un proc-attr-spec este unul dintre

public
private
bind ( c [, nume=șir de caractere] ) intent
( inout ) pointer opțional

salva

iar un proc-decl este


Machine Translated by Google

Parametrii de tip și indicatorii de procedură 261

nume-procedură [ => null-init ]


unde null-init este o referință la funcția intrinsecă null fără argumente. (Atributul bind pentru
proceduri este descris în Secțiunea 12.7.)
Fiecare proc-attr-spec dă tuturor procedurilor declarate în acea instrucțiune atributul
corespunzător. Inițializarea (pentru a fi un pointer nul) poate apărea numai dacă o procedură este
un pointer.

13.6 Indicatori de procedură

Un pointer de procedură este un pointer care, în loc să fie asociat cu un obiect de date, este asociat
cu o procedură. Poate avea o interfață explicită sau implicită și asocierea sa cu o țintă este ca
pentru o procedură inactivă, astfel încât interfața sa nu este permisă să fie generică sau elementară.

13.6.1 Variabile pointer de procedură

Un pointer de procedură este declarat prin specificarea că este atât o procedură, cât și are atributul
pointer. De exemplu,

pointer :: interfață
sp
subrutina sp(a, b) real,
intent(inout) :: a real, intent(in) ::
b
end subrutine sp end
interfață
real, extern, pointer :: fp

declară sp ca fiind un pointer către o subrutină cu interfața explicită specificată și declară fp ca


fiind un pointer către o funcție reală scalară cu o interfață implicită. Mai de obicei, un pointer de
procedură este declarat cu instrucțiunea de procedură care specifică atributul pointerului:

procedura(sp), pointer :: p1 ! Pointer cu interfata sp procedure(), pointer :: p2 ! Pointer


cu o interfață implicită

Dacă un pointer de procedură este asociat în prezent (nu este nici dezasociat, nici nedefinit),
ținta sa poate fi invocată prin referirea pointerului. De exemplu,

fp => fun sp
=> sub print
*, fp(x) ! afișează fun(x) call sp(a, b)
! apeluri sub

13.6.2 Componentele indicatorului de procedură

O componentă a unui tip derivat este permisă să fie un pointer de procedură. Acesta trebuie declarat
folosind instrucțiunea de procedură. De exemplu, pentru a defini un tip pentru reprezentarea unei liste de
Machine Translated by Google

262 Fortran modern explicat

proceduri (fiecare cu aceeași interfață) care urmează să fie apelate la un moment dat, poate fi utilizată o
componentă pointer de procedură, vezi Figura 13.2.

Figura 13.2 Un tip cu o componentă pointer de procedură.


tastați listă_procese
procedura(interfata_proces), pointer :: tip proces(lista_proces), tipul
final pointer lista_proces interfata abstracta :: next => null()

subrutină interfață_proces(...)
:
end subrutine proces_interfață final interfață

O componentă pointer de procedură poate fi atribuită unui pointer de procedură, transmisă ca


argument real sau invocată direct. De exemplu,

tip(listă_proces) :: x, y(10)
procedură(interfață_proces), pointer :: p
:
p => apelul
x%process another_subroutine(x%process) apel
y(i)%process(...)

Rețineți că, la fel ca și în cazul unei componente pointer de date, într-o referință la o componentă pointer
de procedură, obiectul căruia pointer este o componentă trebuie să fie scalar (deoarece nu există
tablouri de pointeri în Fortran).
Când o procedură este apelată printr-o componentă pointer a unui obiect, este adesea nevoie
pentru a accesa obiectul în sine; acesta este subiectul secțiunii 13.6.3.

13.6.3 Atributul de trecere

Când o componentă pointer de procedură (sau o procedură legată de tip, Secțiunea 14.6) este invocată,
obiectul prin care este invocată este în mod normal trecut procedurii ca prim argument real, iar
elementele din lista între paranteze sunt celelalte argumente reale. Acest lucru ar putea fi nedorit; de
exemplu, s-ar putea dori să se treacă obiectul unui argument fals, altul decât primul, sau să nu-l transmită
deloc.
Pentru a trece obiectul invocat la un alt argument fals, este folosit atributul pass. Un exemplu este
prezentat în Figura 13.3. Argumentul dummy la care urmează să fie transmis obiectul este cunoscut sub
numele de argument dummy al obiectului transmis.
Cu excepția cazului în care tipul are atributul secvență (Anexa B.2.1) sau atributul bind (Secțiunea
12.4), este extensibil și argumentul real poate fi de tip extins. Pentru a permite acest lucru, argumentul
dummy al obiectului transmis este necesar să fie declarat cu clasa de cuvinte cheie în loc de tip, vezi
Figura 13.3. Extensia de tip este discutată pe deplin în Capitolul 14.
Machine Translated by Google

Parametrii de tip și indicatorii de procedură 263

Rețineți că atributul pass se aplică componentei pointer de procedură și nu procedurii cu care este
asociat. De exemplu, indicatorul de procedură poate fi asociat din când în când cu două proceduri
diferite; obiectul poate fi transmis ca prim argument în primul caz și ca al doilea argument în al doilea
caz. Cu toate acestea, dacă procedura asociată este invocată prin alte mijloace, nu există niciun argument
dummy pentru obiectul trecut, deci trebuie furnizat un argument real explicit în referință (ca în „call
my_obp_sub(32, a)” în Figura 13.3).

Figura 13.3 Utilizarea atributului pass pentru a asocia obiectul invocat cu argumentul fals x. tip t
procedura(obp), pointer, pass(x) :: p

tip final
interfață abstractă
subrutină obp(w, x) import ::
t întreg :: w clasă(t) :: x

sfâr itul subrutinei


interfață finală
:
tip(t) aa%p
=> my_obp_sub
:
sunați la un%p(32) ! echivalent cu „call my_obp_sub(32, a)”

Atributul pass poate fi folosit și pentru a confirma implicit (de trecere a obiectului invocat
la primul argument dummy), prin utilizarea numelui primului argument dummy.
Dacă nu se dorește deloc să se treacă obiectul care invocă procedurii, se folosește atributul nopass.

Exerciții

1. Scrieți un înlocuitor pentru complexul de tip intrinsec, care este opac (are componente private), utilizează
reprezentarea polară în interior și are un singur parametru de tip care are același implicit ca și tipul intrinsec.

2. Scrieți înlocuitori pentru operatorul de concatenare a caracterelor (//) și indexul funcției intrinseci care funcționează
pe tipul char_with_max_length (definit în Secțiunea 13.4.3).

3. Scrieți o coadă de evenimente (structură de date) și un dispecer de evenimente (procedură) folosind componente
pointer de procedură. Fiecare eveniment ar trebui să aibă un timp și o acțiune (procedură care trebuie invocată);
procedurile de acțiune ar trebui să ia timpul ca argument. Ar trebui să existe o procedură de programare care,
având în vedere o oră și o procedură, pune la coadă un eveniment pentru acel moment. Dacă timpul a trecut
deja, procedura ar trebui să fie încă pusă în coadă pentru activare imediată. Procedura dispecerului în sine ar trebui,
Machine Translated by Google

264 Fortran modern explicat

la invocare, procesați fiecare eveniment din coadă în ordine de timp (inclusiv evenimente suplimentare programate în
timpul acestui proces) până când coada este goală.
Machine Translated by Google

14. Programare orientată pe obiecte

14.1 Introducere

Abordarea orientată pe obiect a programării și proiectării este caracterizată prin concentrarea sa pe


structurile de date ale unui program, mai degrabă decât pe proceduri. Adesea, invocarea unei proceduri
cu un obiect de date ca argument principal este considerată ca „trimiterea unui mesaj” către obiect.
În mod obișnuit, este disponibil suport special de limbaj pentru colectarea acestor proceduri (uneori
cunoscute sub numele de „metode”) împreună cu definirea tipului de obiect.
Această abordare este susținută în Fortran 2003 prin extensie de tip, variabile polimorfe și proceduri
legate de tip.

14.2 Extensie de tip

Extensia de tip creează noi tipuri derivate prin extinderea tipurilor derivate existente. Pentru a crea un tip
nou care extinde unul vechi, atributul extends este utilizat în instrucțiunea de definire a tipului.
De exemplu, dat un tip vechi, cum ar fi

tip person
character(len=10) :: nume
real ::
tipul de varsta :: id
sfârșit întreg persoană

aceasta poate fi extinsă pentru a forma un nou tip cu

tip, extinde(persoana) :: angajat


întreg :: număr_asigurare_națională real :: salariu

angajat de tip final

Noul tip moștenește toate componentele vechiului tip și poate avea componente suplimentare. Deci, o
variabilă angajat are componentele moștenite de nume, vârstă și id și componente suplimentare de număr
și salariu. Acolo unde ordinea contează, adică într-un constructor de structură care nu folosește cuvinte
cheie1 și implicit tipul derivat input/output (Capitolul 9), componentele moștenite vin pe primul loc în
ordinea lor, urmate de noile componente în ordinea lor.

1Utilizarea cuvintelor cheie în constructorii de structură este nouă în Fortran 2003 și este descrisă în Secțiunea 15.3.
Machine Translated by Google

266 Fortran modern explicat

În plus, un tip extins are o componentă părinte; aceasta este o componentă care are parametrii tip și
tip ai tipului vechi și numele ei este cel al tipului vechi. Permite referirea porțiunii moștenite ca întreg.
Astfel, o variabilă angajat are o componentă numită persoană de tip persoană, asociată cu componentele
moștenite. De exemplu, dat

tip(angajat) :: director

componenta director%name este aceeași cu director%person%name și așa mai departe. Componenta


părinte este deosebit de utilă atunci când se invocă proceduri care operează pe tipul părinte, dar care nu
au fost scrise cu extensia de tip în minte. De exemplu, procedura
subrutine display_older_people(parray, min_age) tip(persoană),
intent(in) :: pararay(:) întreg, intent(in) :: min_age intrinsec ::
dimensiune

do i=1, dimensiune (parare)


if (parray(i)%age >= min_age) print *, parray(i)%name end do

termina subrutina display_older_people

poate fi folosit cu o matrice de tip (angajat) trecându-i componenta părinte a matricei, de exemplu

tip(angajat):: lista_personal(:)
:
!
! Arătați angajații eligibili pentru pensionare anticipată!

apelați display_older_people(staff_list%person, 55)

Componenta părinte este ea însăși moștenită dacă tipul este extins în continuare (devenind a
„componenta bunicilor”); de exemplu, cu
tip, extinde(angajat) :: vânzător real :: comision_rate
tip final tip vânzător (vânzător) :: călător

călătorul are atât componenta angajat, cât și componentă persoană, iar traveller%person este exact
același cu traveller%employee%person.
Un tip poate fi extins fără a adăuga componente, de exemplu
type, extends(employee) :: clerical_staff_member end type
clerical_staff_member Deși un clerical_staff_member are aceleași

componente finale ca un angajat, este totuși considerat a fi un tip diferit.

Extinderea unui tip fără a adăuga componente poate fi utilă în mai multe situații, în special:

• pentru a crea un tip cu operații suplimentare (ca proceduri specifice sau generice legate de tip,
vezi Secțiunea 14.6);
Machine Translated by Google

Programare orientată pe obiecte 267

• să creeze un tip cu efecte diferite pentru operațiunile existente, prin depășirea procedurilor
specifice de tip; și

• pentru clasificare, adică atunci când singura informație suplimentară despre noul tip este
faptul că este de tipul respectiv (de exemplu, ca în tipul clerical_staff_member de mai sus).

Un tip derivat este extensibil (poate fi extins) cu condiția să nu aibă atributul de secvență (a se
vedea apendicele B.2.1) sau atributul de legătură (a se vedea secțiunea 12.4). Un tip extins nu
trebuie să primească atributul secvență sau bind.

14.2.1 Extensie de tip și parametri de tip

Când un tip este extins, noul tip moștenește toți parametrii de tip. Pot fi adăugați și noi parametri
de tip, de exemplu:

matrice de tip(real_kind, n, m)
integer, kind :: real_kind intreg, len
:: n,m)
real(real_kind) :: value(n, mtip final
tip matrice, extins(matrix) ::
labeled_matrix(max_label_length) integer,
len :: max_label_length character(max_label_length) :: label = tip final tip
labeled_matrix(labelled_matrix(kind(0.0), 10, 20, 200)) :: x
''

Variabila x are patru parametri de tip: real_kind, n, m și max_label_length.

14.3 Entități polimorfe

O variabilă polimorfă este o variabilă al cărei tip de date poate varia în timpul rulării. Trebuie să
fie un pointer sau o variabilă alocabilă sau un obiect de date inactiv și este declarat folosind
cuvântul cheie class în locul cuvântului cheie type. De exemplu,

tastați punct
real :: x, y tip
punctul final
class(punct), pointer :: p

declară un pointer p care poate indica orice obiect al cărui tip este în clasa de tipuri constând din
tip(punct) și toate extensiile sale.
Spunem că obiectul polimorf este compatibil de tip cu astfel de obiecte.2 Un pointer polimorf
poate fi asociat doar cu o țintă compatibilă cu tipul, o variabilă alocabilă polimorfă poate fi alocată
doar pentru a avea o alocare compatibilă cu tipul (vezi Secțiunea 15.4). ), iar un argument inactiv
polimorf poate fi asociat doar cu un argument real compatibil cu tipul. În plus, dacă un argument
dummy polimorf este alocabil

2Un obiect nepolimorf este compatibil de tip numai cu obiecte de același tip declarat.
Machine Translated by Google

268 Fortran modern explicat

sau un pointer, argumentul real trebuie să fie de același tip declarat; aceasta este pentru a se
asigura că relația de compatibilitate tip este aplicată.
Tipul numit în atributul de clasă trebuie să fie un tip derivat extensibil – nu poate fi un tip derivat
din secvență, un tip derivat de legare sau un tip intrinsec. Acest tip se numește tipul declarat al
entității polimorfe, iar tipul obiectului la care se referă se numește tip dinamic.

Cu toate acestea, chiar și atunci când o entitate polimorfă se referă la un obiect de tip extins, ea
oferă acces prin notație pentru componente numai la componente, parametri de tip și legături (vezi
Secțiunea 14.6) ale tipului declarat. Acest lucru se datorează faptului că compilatorul știe doar
despre tipul declarat al obiectului, nu poate ști despre tipul dinamic (care poate varia în timpul
rulării). Accesul la componentele etc. care sunt în tipul dinamic, dar nu în tipul declarat este asigurat
de constructia tip select (vezi Secțiunea 14.5).
Un argument inactiv polimorf care nu este nici alocabil, nici un pointer își asumă tipul dinamic
din argumentul actual. Aceasta oferă un mijloc convenabil de a scrie o funcție care se aplică oricărei
extensii a unui tip, de exemplu

funcția reală distanță(a, b)


clasă(punct) :: a, b distanță =
sqrt((a%xb%x)**2 + (a%yb%y)**2)
distanța funcției de final

Această funcție va funcționa neschimbată, de exemplu, nu numai pe un scalar de tip punct, ci și pe


un scalar de tip

tip, extinde(punct) :: data_point


real, alocabil :: data_value(:)
tipul final data_point

14.3.1 Stabilirea tipului dinamic

O variabilă dummy polimorfă are tipul său dinamic stabilit doar prin asocierea argumentelor, ceea
ce înseamnă că nu variază în timpul unei singure execuții a procedurii, deși poate fi diferită la
diferite invocări.
Cu toate acestea, tipul dinamic al unei variabile polimorfe alocabile sau pointer poate fi modificat
în orice moment, după cum urmează:

• poate fi alocat să fie de un tip (și parametri de tip) specificati în instrucțiunea de alocare, vezi
Secțiunea 15.4;
• folosind specificatorul source= din instrucțiunea alocare, acesta poate fi alocat să aibă același
tip, parametri de tip și valoare ca o altă variabilă;

tipul dinamic al unei variabile polimorfe alocabile poate fi modificat:

• când o alocare este transferată de la o variabilă alocabilă la alta folosind subrutina intrinsecă
move_alloc (vezi Secțiunea 15.5.3), variabila receptoare ia tipul dinamic pe care l-a avut
emițătorul;

iar tipul dinamic al unei variabile pointer polimorfe poate fi modificat:


Machine Translated by Google

Programare orientată pe obiecte 269

• prin asociere de pointer deoarece un pointer polimorf are tipul dinamic al țintei sale.

Rețineți că o instrucțiune de alocare care nu are atât o specificație de tip, cât și sursa=
specificatorul va aloca variabila pentru a fi de tipul ei declarat.
Tipul dinamic al unui pointer disociat sau al variabilei alocabile nealocate este tipul declarat. Un
pointer cu statut de asociere nedefinit nu are un tip dinamic definit: nu este permis să fie utilizat în niciun
context în care tipul său dinamic ar fi relevant.
În Fortran 2008, tipul dinamic al unei variabile alocabile se poate modifica și datorită
realocare automată, vezi Secțiunea 20.6.2.

14.3.2 Limitări ale utilizării unei variabile polimorfe

O variabilă polimorfă poate apărea într-o listă de intrare/ieșire numai dacă este procesată de tipul de
intrare/ieșire derivat (Secțiunea 17.2).
Variabila dintr-o instrucțiune de atribuire intrinsecă nu este permisă să fie polimorfă (acest lucru este
relaxat în Fortran 2008 pentru variabilele alocabile). Totuși, dacă este asociată cu o variabilă nepolimorfă,
poate prin tipul este guard într-o instrucțiune select type (vezi Secțiunea 14.5), atribuirea variabilei
nepolimorfe va avea efectul dorit.
O variabilă polimorfă nu este permisă să fie un argument real corespunzător unui
intenționați argumentul dummy de dimensiune presupusă (a se vedea Secțiunea B.3).

14.3.3 Rețele polimorfe și scalari

O variabilă polimorfă poate fi fie o matrice, fie un scalar (inclusiv un scalar alocabil, vezi Secțiunea 15.5.1).

O matrice polimorfă este întotdeauna omogenă; adică fiecare element de matrice are același tip
dinamic. Aceasta este prin construcție: fiecare metodă de stabilire a tipului dinamic al unei variabile
polimorfe oferă un singur tip pentru întregul tablou. Motivul pentru aceasta este atât de a simplifica
raționamentul despre programe, cât și de a se asigura că accesarea unui element dintr-o matrice
polimorfă este rezonabil de eficientă.
Dacă este necesară o matrice polimorfă eterogenă, se poate folosi circumlocuția obișnuită de utilizare
a unei matrice de tip derivat cu un pointer polimorf scalar sau o componentă alocabilă.

14.3.4 Entități polimorfe nelimitate

Uneori, cineva dorește să aibă un pointer care se poate referi nu doar la obiecte dintr-o clasă de tipuri
extinse, ci și la obiecte de orice tip, poate chiar incluzând tipuri neextensibile sau intrinseci. De exemplu,
s-ar putea dori să aibă o listă „universală” de variabile (ținte pointer), fiecare dintre acestea putând fi de
orice tip.
Acest lucru se poate face cu un indicator polimorf nelimitat. Acestea sunt declarate folosind * ca
specificatorul de clasă, de exemplu

clasa (*), indicator :: sus


declară până la un pointer polimorf nelimitat. Acest lucru ar putea fi asociat cu o țintă reală, de exemplu:
Machine Translated by Google

270 Fortran modern explicat

real, tinta :: x
:

sus => x

Un obiect polimorf nelimitat nu poate fi referit în niciun mod normal; poate fi folosit doar ca argument real, ca
indicator sau țintă în atribuirea pointerului, sau ca selector într-o instrucțiune de tip select (vezi Secțiunea 14.5).

Informațiile de tip sunt menținute pentru un pointer polimorf nelimitat în timp ce sunt asociate cu un tip intrinsec
sau un tip derivat extensibil, dar nu atunci când este asociat cu un tip derivat neextensibil. (Acest lucru se datorează
faptului că diferite tipuri neextensibile sunt considerate a fi aceleași dacă au aceeași structură și nume.) Pentru a
preveni ca un pointer de tip intrinsec sau extensibil să devină asociat cu o țintă incompatibilă, un astfel de pointer nu
este permis să fie partea stângă a unei atribuiri de indicator dacă ținta este polimorfă nelimitată. De exemplu,

utilizați tipul iso_c_binding,


bind(c) :: triplet
real(c_double) :: values(3) end type triplet
class(*), pointer :: univp type(triplet), pointer ::
tripp real, pointer :: realp

univp => tripp univp ! Valabil

=> realp ! Valabil


:

tripp => univp realp ! Valabil când tipul dinamic se potrivește! Întotdeauna
=> univp invalid

În loc de atribuirea de pointer nevalidă, trebuie utilizat un construct tip select pentru a asocia un pointer de tip
intrinsec sau extensibil cu o țintă polimorfă nelimitată. Un exemplu mai lung care arată utilizarea de pointeri
polimorfi nelimitați, împreună cu tipul de selectare, este prezentat în Figura 14.2.

Când este alocat un pointer polimorf nelimitat, tipul și parametrul de tip necesar
valorile trebuie specificate în instrucțiunea de alocare (Secțiunea 15.4).

14.3.5 Entități polimorfe și rezoluție generică

Deoarece un argument dummy polimorf poate fi asociat cu un argument real de tip extins, un argument dummy
polimorf nu se distinge de un argument dummy de tip extins în regulile de distincție a procedurilor dintr-un set
generic (Secțiunea 5.18). De exemplu, procedura

funcția reală data_distance(a, b) class(data_point) ::


a, b data_distance = end function data_distance
...
Machine Translated by Google

Programare orientată pe obiecte 271

nu este permisă în același set generic ca distanța funcției definită la începutul acestei secțiuni (14.3).
Acolo unde este necesar un astfel de efect, pot fi utilizate proceduri de tip (secțiunea 14.6.3).

În cazul unui argument dummy polimorf nelimitat, deoarece este compatibil cu tipul
cu orice tip nu se distinge de orice argument de acelasi rang.3

14.4 Constructul asociat

Construcția asociată permite asocierea unui nume fie cu o variabilă, fie cu valoarea unei expresii, pe
durata unui bloc. Orice entitate cu acest nume în afara constructului este separată și inaccesibilă în
interiorul acestuia. În timpul execuției blocului, numele asociatului rămâne asociat cu variabila (sau
păstrează valoarea) specificată și își ia tipul, parametrii de tip și rangul din asociere. Acest construct este
util pentru simplificarea acceselor multiple la o variabilă care are o descriere lungă (subscripte și nume
de componente). De exemplu, având în vedere un set imbricat de definiții de tip derivat, dintre care cea
mai interioară este

tip unu
real, alocabil, dimensiune(:) :: xvec, niveluri logice :: trasare tip final
unu

apoi asocierea așa cum este specificat în

asociat(point_qfstate => master_list%item(n)%qfield%posn(i, j)%state) point_qfstate%xvec =


matmul(transpose_matrix, point_qfstate%xvec) point_qfstate%levels = timestep(point_qfstate%levels,
if input_field) %tracing) apelează show_qfstate(point_qfstate, stepno) final asociat

ar fi și mai greu de înțeles dacă point_qfstate ar fi scris în întregime în fiecare


apariția.

Formal, sintaxa este

[ nume: ] asociat ( lista de asociații )


asociat
final bloc [nume]

unde se află fiecare asociație

nume-asociat => selector

iar selectorul este fie o variabilă, fie o expresie. Ca și în cazul altor constructe, constructul asociat poate
fi numit; dacă nume: apare pe mențiunea de asociat, același nume trebuie să apară pe declarația de
asociat final.
Dacă asocierea este cu o variabilă, numele asociat poate fi folosit ca variabilă în bloc. Asocierea este
ca și pentru asocierea argumentului unui argument fals care nu

3Fortran 2008 permite utilizarea unor atribute suplimentare pentru rezoluția generică chiar și în acest caz, vezi Secțiunea 20.5.7.
Machine Translated by Google

272 Fortran modern explicat

au pointerul sau atributul alocabil, dar numele asociat are atributul țintă dacă variabila are. Dacă
asocierea este cu o expresie, numele asociat poate fi folosit doar pentru valoarea acesteia. Dacă
asocierea este cu o matrice, limitele numelui-asociat sunt date de intrinsecile lbound și ubound
aplicate matricei.
Dacă selectorul este polimorf, numele asociat este, de asemenea, polimorf. Dacă selectorul este
un pointer sau are atributul țintă, numele-asociat are atributul țintă. Singurele alte atribute pe care
numele-asociat le primește de la selector sunt atributele asincrone și volatile; în special, dacă
selectorul are atributul opțional, numele-asociat nu și așadar selectorul trebuie să fie prezent
atunci când constructul este executat.
Pot fi stabilite mai multe asociații în cadrul unui singur construct asociat. De exemplu, în

asociați ( x => arg(i)%coordonate%sol(1), & y => arg(i)


%coordonate%sol(2) ) distanță =
sqrt((myloc%xx)**2+(myloc%yy )**2) rulment = atan2(myloc%yy,
myloc%xx) end associate

denumirile simplificatoare x și y îmbunătățesc lizibilitatea codului.


Fără acest construct, pentru a face acest tip de cod lizibil, ar trebui utilizată fie o procedură, fie
pointeri (care necesită, în plus, atributul țintă pe variabilele afectate).
Acest lucru ar putea afecta negativ performanța programului (și, într-adevăr, probabil că încă nu
ar atinge lizibilitatea prezentată aici).
Construcția poate fi imbricată cu alte constructe în mod obișnuit.

14.5 Construcția tip select

Pentru a executa cod alternativ în funcție de tipul dinamic al unei entități polimorfe și pentru a
obține acces la părțile dinamice, este furnizat constructul de tip select. Dacă entitatea nu este
polimorfă nelimitată, acest construct ia forma

[ name: ] select type ( [ associate-name =>] selector) [ type-guard-


stmt [ name ] block ]... end select [ name ]

unde fiecare declarație de gardă de tip este una dintre

tipul este (spec-tip-derivat) tipul


este (tip-intrinsec [ (listă-valoare-parametru-de-tip) ] ) clasa este
(spec-tip-derivat) clasa implicită

unde derivated-type-spec este definit în secțiunea 13.4.1. O protecție de tip care specifică un tip
intrinsec este permisă numai dacă selectorul este polimorf nelimitat. Specificul de tip derivat
trebuie să fie un tip extensibil compatibil cu selectorul. Ca și în cazul altor constructe, constructul
de tip select poate fi numit; if name: apare pe instrucțiunea de tip select, același nume trebuie să
apară pe fiecare gardă de tip și pe instrucțiunea de selectare finală.
Machine Translated by Google

Programare orientată pe obiecte 273

Selectorul este o variabilă sau o expresie și numele asociat este asociat cu acesta în cadrul blocului
exact în același mod ca pentru un construct asociat (secțiunea anterioară).
Cu toate acestea, corpul este acum împărțit în părți, dintre care cel mult una este executată după cum urmează:

i) Blocul care urmează unui tip este gard este executat dacă tipul dinamic al selectorului este exact
tipul derivat specificat, iar valorile parametrului tip tip se potrivesc.

ii) În caz contrar, blocul care urmează unei clase este gard este executat dacă este singurul pentru
care tipul dinamic este tipul derivat specificat, sau o extensie a acestuia, iar valorile parametrului
tip tip se potrivesc. Dacă există mai mult de o astfel de gardă, una dintre ele trebuie să fie de un
tip care este o extensie a tipurilor tuturor celorlalte, iar blocarea lui este executată.

iii) În caz contrar, blocarea care urmează unei paznici implicite de clasă este executată.

În cazul (care apare frecvent) în care selectorul este un nume simplu și același nume
este potrivit pentru numele asociat, „nume asociat =>” poate fi omis.
Exemplul din Figura 14.1 arată o utilizare tipică a tipului select. Fiecare instrucțiune de tip guard care
specifică un tip extins oferă acces prin notația componentelor la componentele extinse. Rețineți că în
cadrul unui tip este bloc, numele asociat nu este polimorf, deoarece se știe că tipul său dinamic este
exact același cu tipul declarat în instrucțiunea tip is.

Figura 14.1 Utilizarea constructului de tip select pentru obiecte polimorfe ale particulelor de clasă.
subrutină describe_particle(p) clasă(particulă) :: p

! Aceste atribute sunt comune tuturor particulelor. call


describe_vector('Poziție:',p%position) apel
descrie_vector('Velocity:',p%velocity) print *,'Mass:',p%mass

! Verificați alte atribute.

select type (p) tipul


este (charged_particle)
print *,'Taxă:',p% taxă
clasa este (particulă_încărcată)
print *,'Charge:',p%charge print *,'...
poate avea alte atribute (necunoscute).'
tipul este (particulă)
! Doar tipul de particule de bază, nu există nimic în plus. implicit de clasă

print *,'... poate avea alte atribute (necunoscute).'


final select
sfâr itul subrutinei describe_particle
Machine Translated by Google

274 Fortran modern explicat

Dacă derivat-type-spec conține o listă type-param-spec-list, valorile corespunzătoare parametrilor tip


tip trebuie să fie expresii constante, iar cele pentru parametrii tip lungime trebuie să fie asteriscuri. Acest
lucru este astfel încât parametrii de tip lungime să nu participe la potrivirea parametrilor de tip, ci sunt
întotdeauna presupuși din selector.
Dacă selectorul este polimorf nelimitat, o instrucțiune de gardă de tip este permisă pentru a specifica
un tip intrinsec, dar tot nu poate specifica o secvență sau un tip derivat de legare. De exemplu, dacă
indicatorul polimorf nelimitat în sus este asociat cu ținta reală x, execuția lui

select type(up) type


is (real) up = 3.5 rp
=> up end select

atribuie lui x valoarea 3,5 și asociază indicatorul real rp cu x. (Atribuirea pointerului nu ar fi fost permisă
în afara construcției tip select.)
Un exemplu mai lung, care arată utilizarea polimorfului nelimitat în construirea unui generic
pachetul de listă vectorială, este prezentat în Figura 14.2.

14.6 Proceduri legate de tip

Adesea, în programarea orientată pe obiecte, se dorește să se invoce o procedură pentru a efectua o


sarcină a cărei natură variază în funcție de tipul dinamic al unui obiect polimorf.
Acesta este scopul procedurilor legate de tip. Acestea sunt proceduri care sunt invocate printr-un
obiect, iar procedura efectivă executată depinde de tipul dinamic al obiectului.

Ele sunt numite tip-legate deoarece selecția procedurii depinde de tipul obiectului, spre deosebire de
componentele pointer-ului de procedură care depind de valoarea obiectului (s-ar putea numi pe aceasta
din urmă obiect-bound).
În unele alte limbi, procedurile legate de tip sunt cunoscute ca metode și invocare a
o metodă este considerată ca „trimiterea unui mesaj” către obiect.
Cu toate acestea, procedurile legate de tip pot fi utilizate chiar și atunci când nu există intenția de a
extinde tipul. Vom descrie mai întâi cum să definim și să folosim proceduri legate de tip în cazul simplu,
iar mai târziu vom explica cum sunt afectate de extensia de tip.

14.6.1 Proceduri specifice legate de tip

Secțiunea de procedură legată de tip a unei definiții de tip este separată de secțiunea de componentă
prin instrucțiunea contains, în mod analog cu modul în care variabilele modulului sunt separate de
procedurile modulului. Accesibilitatea implicită a procedurilor legate de tip este separată de accesibilitatea
implicită pentru componente; adică, chiar și cu componente private, fiecare procedură legată de tip este
publică, cu excepția cazului în care o declarație privată apare în secțiunea procedurii legate de tip sau
dacă nu este declarată în mod explicit ca fiind privată.
Fiecare declarație de procedură legată de tip specifică numele legării și numele procedurii efective la
care este legată. (Acesta din urmă poate fi omis dacă este același cu numele procedurii legate de tip.) De
exemplu, în Figura 14.3 obiectele de tip mytype au
Machine Translated by Google

Programare orientată pe obiecte 275

Figura 14.2 Lista de vectori generici și selecția tipului.


tastați generic_vector_pointer_list_elt
class(*), pointer :: element_vector(:) => null() procedura(gvp_processor), pointer ::
default_processor => null() tip(generic_vector_pointer_list_elt), pointer :: next => null()

tip final generic_vector_pointer_list_elt interfață abstractă


subrutină gvp_processor(gvp)

import :: generic_vector_pointer_list_elt
class(generic_vector_pointer_list_elt) :: gvp end subroutine
gvp_processor end interface

tip(generic_vector_pointer_list_elt), pointer :: p
:
do
if (.not.associated(p)) exit select type(q
=> p%element_vector) tipul este
(integer(selected_int_kind(9))) apel special_process_i9(q)
tipul este (real) apel special_process_default_real(q)
tipul este ( precizie dublă) apel
special_process_double_precision (q)

tipul este (caracter(*)) call


special_process_character(q) clasa implicită

if (asociat(p%default_processor)) apelează p%default_processor


final select
p => p%next
end do

două proceduri legate de tip, scriere și resetare. Acestea sunt invocate ca și cum ar fi indicatorii de
procedură componente ale obiectului, iar obiectul care invocă este transmis în mod normal procedurii ca
prim argument. De exemplu, referirile la procedura
apelați x%write(6)
apelează x%reset

sunt echivalente
cu apelul write_mytype(x,6) call
reset(x)

Cu toate acestea, deoarece sunt publice, procedurile legate de tip (scriere și resetare) pot fi referite oriunde
în program care are o variabilă tip(mytype), în timp ce, deoarece procedurile modulului (write_mytype și
reset) sunt private, ele pot doar fie referit direct din interiorul mytype_module.
Machine Translated by Google

276 Fortran modern explicat

Figura 14.3 Un tip cu două proceduri legate de tip.


modul mytype_module type
mytype private real ::
myvalue(4) = 0.0

con ine
procedura :: scrie => tipul_meu procedura ::
resetare tip final tipul meu privat :: tipul_meu,
resetare

con ine
subrutine write_mytype(this, unit) class(mytype) ::
acest număr întreg, opțional
(prezent(unit))
:: unit if
apoi scrieți (unitate, *) this%myvalue else

print *,această%valoarea mea se


termină dacă

final subrutine write_mytype subrutine


resetare(variabilă)
class(mytype) :: variabilă
variabilă%myvalue = 0.0 final
resetarea subrutinei
termina modul mytype_module

Sintaxa completă a instrucțiunii care declară o procedură specifică legată de tip este

procedura [ (nume-interfață) ][[ , lista-atr-binding ] :: ] nume-tbp [=> nume-proc ]

unde fiecare binding-attr este unul dintre

public sau privat amânat

non_overridable
nopass sau pass [ (nume-arg) ]

iar nume-interfață sau nume-proc este numele unei proceduri cu o interfață explicită. Atributele
publice și private sunt permise numai în partea de specificații a unui modul. Atributele pass și nopass
sunt descrise în Secțiunea 13.6.3. (nume-interfață) apare dacă și numai dacă apare și atributul amânat;
acestea sunt descrise în Secțiunea 14.7. Un exemplu de caz în care nu se dorește trecerea obiectului
invocator este prezentat în Figura 14.4.

Dacă apare atributul non_overridable, procedura legată de tip nu poate fi suprascrisă în timpul
extinderii tipului (vezi Secțiunea 14.6.3). Rețineți că non_overridable este incompatibil cu deferred,
deoarece aceasta necesită ca procedura de tip legat să fie suprascrisă.
Machine Translated by Google

Programare orientată pe obiecte 277

Figura 14.4 Două proceduri legate de tip cu atributul nopass. modul


utility_module tip privat, public :: utility_access_type conține

procedura, nopass :: procedura de


pornire, nopass :: oprire
tipul de capăt
con ine
subrutine startup print
*,'Procesul început' sfâr itul
subrutinei
oprirea subrutinei

stop 'Proces oprit' final subrutine

modul final
:
utilizați tipul
utility_module(utility_access_type) :: control_proces apel
process_control%startup

14.6.2 Proceduri generice legate de tip

Procedurile legate de tip pot fi generice. O procedură generică legată de tip este definită cu instrucțiunea
generică din partea de procedură legată de tip. Această afirmație ia forma

generic [ [ , access-spec ] :: ] generic-spec => lista-nume-tbp

și poate fi utilizat pentru generice numite, precum și pentru operatori, atribuire și specificații de intrare/
ieșire de tip derivat definite de utilizator. Fiecare nume-tbp specifică o procedură individuală (specifică)
legată de tip care urmează să fie inclusă în setul generic.
De exemplu, în Figura 14.5 extrasul procedurii de tip-legat este generic, fiind rezolvat la una dintre
procedurile specifice tip-legat xi sau xc, în funcție de tipul de date al argumentului.

Astfel, în

utilizați tipul container_module


(container) v întreg ix complex
cx

:
apelați v%extract(ix)
apelați v%extract(cx)

una dintre procedurile „extract_something_from_container” va fi invocată.


Machine Translated by Google

278 Fortran modern explicat

Figura 14.5 O procedură de tip generic denumită. modul


container_module tip privat, public :: container

întreg, privat ::i=0 complex, privat ::


c = (0.,0.)
con ine
procedură
privată :: xi => extrage_integer_din_container procedura :: xc =>
extract_complex_from_container generic, public :: extract => xi, tip
final xc

con ine
subrutine extract_integer_from_container(this, val) class(container),
intent(in) :: this integer, intent(out) val = this%i
:: val

final subrutine extract_integer_from_container subrutine


extract_complex_from_container(this, val) class(container), intent(in) :: this
complex, intent(out) val = this%c
:: val

sfârșitul subrutinei extract_complex_from_container


modul final container_module

O procedură generică legată de tip nu trebuie să fie numită; poate fi un operator, o atribuire sau o
specificație de intrare/ieșire de tip derivat, definită de utilizator. În acest caz, obiectul prin care este
invocată procedura legată de tip este oricare dintre operanzi corespunde argumentului dummy al
obiectului transmis. Din acest motiv, procedurile specifice legate de tip pentru un generic fără nume nu
trebuie să aibă atributul nopass. Ca și alte proceduri legate de tip, genericele nenumite care sunt publice
sunt accesibile oriunde este accesibil tipul sau un obiect al tipului.
Acest lucru este util pentru împachetarea unui tip și a operațiunilor sale, deoarece singura clauză
a unei instrucțiuni de utilizare nu afectează accesibilitatea operatorilor legați de tip, spre deosebire
de operatorii definiți de un bloc de interfață. Acest lucru previne omiterea accidentală a operatorilor
solicitați prin greșeala în declarația de utilizare. Acest lucru este deosebit de relevant atunci când se
folosește o atribuire definită între obiecte de același tip, deoarece omiterea atribuirii definite ar
determina ca o alocare intrinsecă nedorită să fie utilizată fără avertisment.
De exemplu, Figura 14.6 prezintă supraîncărcarea operatorului (+) pentru operațiuni pe
tip (complexul meu); aceste operațiuni sunt disponibile chiar dacă utilizatorul a făcut

utilizați mycomplex_module, numai: mycomplex


Machine Translated by Google

Programare orientată pe obiecte 279

Figura 14.6 Un operator generic legat de tip.


modul mycomplex_module tip mycomplex
private ! Componentele de date care nu
sunt afișate conține procedura
: privată :: mycomplex_plus_mycomplex
procedura :: mycomplex_plus_real procedure,
pass(b) :: real_plus_mycomplex generic,
public :: operator(+) => mycomplex_plus_mycomplex, &

mycomplex_plus_real, real_plus_mycomplex
: ! multe alte operatii si functii... tipul final

con ine
: ! proceduri care implementează modulul de sfârșit de
operațiuni

14.6.3 Extinderea tipului și procedurile legate de tip

Când un tip este extins, noul tip moștenește de obicei toate procedurile legate de tip ale vechiului tip,
așa cum este ilustrat în Figura 14.7, unde noul tip charged_particle moștenește nu numai componentele
particulei, ci și impulsul procedurilor legate de tip. si energie.

Figura 14.7 Extinderea unui tip cu proceduri legate de tip.


tip particule
tip(vector) :: poziție, viteză reală
:: masa
con ine
procedura :: impuls => particule_moment procedura ::
energie => particule_energie tip final particule

tip, extinde(particulă) :: particule_încărcate real :: tipul final de


încărcare particule_încărcate

Procedurile specifice legate de tip definite de noul tip sunt fie legături suplimentare (cu un nume
nou), fie pot suprascrie procedurile legate de tip care altfel ar fi fost moștenite de la tipul vechi. (Cu toate
acestea, suprascrierea unei proceduri legate de tip nu este permisă dacă cea moștenită are atributul
non_overridable.) O legare de procedură de tip overriding trebuie să aibă exact aceeași interfață ca
procedura suprascrisă, cu excepția
Machine Translated by Google

280 Fortran modern explicat

tipul argumentului dummy pentru obiectul trecut; dacă există un argument dummy pentru obiectul trecut,
procedura de suprascriere trebuie să specifice tipul său pentru a fi class(new-type).
Procedurile legate de tip generic definite de noul tip extind întotdeauna setul generic; setul complet
de legături generice pentru un anumit identificator generic (inclusiv atât legăturile generice moștenite,
cât și cele nou definite) trebuie să îndeplinească regulile obișnuite pentru dezambiguizarea generică
(Secțiunile 5.18 și 14.3.5). O procedură care ar face parte dintr-un set generic moștenit poate fi suprascrisă
folosind numele său specific.
De exemplu, în Figura 14.8, cele trei proceduri specifice legate de tip au fost înlocuite; atunci când
operația generică a lui (+) este aplicată entităților de tip instrumented_mycomplex, va fi invocată una
dintre procedurile de suprascriere.

Figura 14.8 Extinderea unui tip cu suprascrierea procedurilor legate de tip.


tip mycomplex
private contains

procedura :: mycomplex_plus_mycomplex procedura ::


mycomplex_plus_real procedure,
generic
pass(b)
:: operator(+)
:: real_plus_mycomplex
=>
mycomplex_plus_mycomplex, &

mycomplex_plus_real, real_plus_mycomplex
tip final mycomplex

tip, extinde(mycomplex) :: instrumented_mycomplex întreg, public ::


plus_operation_count = 0
con ine
procedura :: mycomplex_plus_mycomplex => instrumented_myc_plus_myc procedura ::
mycomplex_plus_real => instrumented_myc_plus_r procedura :: real_plus_mycomplex =>
instr_r_p_myc tip final instrumented_mycomplex

14.7 Legături amânate și tipuri de abstracte

Uneori, un tip este definit nu în scopul de a crea obiecte de acel tip, ci doar pentru a servi ca tip de bază
pentru extensie. În această situație, o procedură legată de tip în tipul de bază ar putea să nu aibă
implementare implicită sau naturală, ci mai degrabă doar un scop și o interfață bine definite. Acest lucru
este susținut de cuvântul cheie abstract din definiția tipului și cuvântul cheie amânat din instrucțiunea
de procedură.
Iată un exemplu simplu:

tip, abstract :: file_handle con ine

procedura (open_file), amânată, trece :: deschis


:
Machine Translated by Google

Programare orientată pe obiecte 281

tipul final file_handle


interfață abstractă
subrutine open_file(handle) import
class(file_handle), intent(inout) :: :: file_handle
handle
end subrutine open_file final
interfață

Aici, intenția este ca extensiile de tip să aibă componente care să dețină date
despre fișier și deschis ar fi înlocuit de o procedură care utilizează aceste date pentru a-l deschide.
Procedura este cunoscută sub denumirea de procedură de tip amânată. Este necesară o interfață,
care poate fi o interfață abstractă sau cea a unei proceduri cu o interfață explicită.
Nicio variabilă obișnuită nu este permisă să fie de tip abstract, dar o variabilă polimorfă o poate avea
ca tip declarat. Când un tip abstract este extins, noul tip poate fi un tip extins normal sau poate fi el însuși
abstract. Legăturile amânate sunt permise numai în tipurile abstracte.
(Dar un tip abstract nu este necesar să aibă nicio legătură amânată.)
Figura 14.9 prezintă definiția unui tip abstract my_numeric_type și crearea tipului normal
my_integer_type ca o extensie a acestuia. Variabilele care sunt declarate a fi my_numeric_type trebuie să
fie polimorfe, iar dacă sunt pointer sau alocabile instrucțiunea alocare trebuie să specifice un tip normal
(vezi Secțiunea 15.4).
Utilizarea atributelor abstracte și amânate asigură că obiectele de tip insuficient nu pot fi create și că
atunci când extinde tipul abstract pentru a crea un tip normal, programatorul se poate aștepta la un
diagnostic de la compilator dacă el sau ea a uitat să suprascrie orice moștenit. proceduri amânate de tip
limitat.

14.8 Finalizare

Când variabilele sunt dealocate sau în alt mod încetează să existe, uneori este de dorit să se execute o
procedură care „curăță” după variabilă, poate eliberând o anumită resursă (cum ar fi închiderea unui
fișier sau dealocarea unei componente pointer). Acest proces este cunoscut sub numele de finalizare și
este asigurat de „subrutinele finale”. Finalizarea este disponibilă numai pentru tipurile derivate care nu
au atributul de secvență (Anexa B.2.1) sau atributul de legare (Secțiunea 12.4).

Setul de subrutine finale pentru un tip derivat este specificat prin instrucțiuni de formă

final [ :: ] lista-nume-subrutine

în secțiunea privind procedura legată de tip; cu toate acestea, ele nu sunt proceduri legate de tip și nu
au nici un nume care să poată fi accesat printr-un obiect de acest tip. În schimb, ele se execută automat
atunci când un obiect de acest tip încetează să mai existe.
O subrutină finală pentru un tip trebuie să fie o procedură de modul cu un singur argument fals de
acel tip. Toate subrutinele finale pentru acel tip formează un set generic și trebuie să îndeplinească
regulile pentru referințe generice fără ambiguitate; deoarece fiecare dintre ele are exact un argument
inactiv de același tip, aceasta înseamnă pur și simplu că argumentele inactiv trebuie să aibă valori diferite
ale parametrilor de tip tip sau rang. Fiecare astfel de argument inactiv trebuie să fie o variabilă fără
atributul alocabil, intent(out), opțional, pointer sau value și trebuie asumat orice parametru de tip
lungime (valoarea trebuie să fie „*”).
Machine Translated by Google

282 Fortran modern explicat

Figura 14.9 Tip numeric abstract. tip,


abstract :: tipul_numeric_meu conține procedura
privată (op2), amânată :: procedura de adăugare
(op2), amânată :: scade

: ! proceduri pentru alte operațiuni neprezentate


generic, public :: operator(+) => adaugă, ... generic, public ::
operator(-) => scade, ...
: ! specificații generice pentru alte operațiuni care nu sunt afișate
tip final interfața abstractă my_numeric_type

function op2(a, b) result(r) import ::


my_numeric_type
class(my_numeric_type), intent(in) :: a, b class(my_numeric_type),
allocatable :: r end function op2 end interface type,
extends(my_numeric_type) : : my_integer întreg, privat :: valoarea
conține

procedura :: add => adauga_meu_integer


procedura :: subtract => scade_meu_integer
:
tip final my_integer

Un obiect non-pointer este finalizabil dacă tipul său are o subrutină finală al cărei argument fals se
potrivește cu obiectul. Când un obiect finalizabil este pe cale să înceteze să existe (de exemplu, prin
dealocare sau prin executarea unei instrucțiuni return), subrutina finală este invocată cu obiectul ca
argument real. Acest lucru se întâmplă și atunci când obiectul este transmis unui argument fals intent
out sau este variabila din partea stângă a unei instrucțiuni de atribuire intrinsecă.
În acest din urmă caz, subrutina finală este invocată după ce expresia din partea dreaptă a fost evaluată,
dar înainte de a fi atribuită variabilei.
Un exemplu este prezentat în Figura 14.10. Când subrutina s revine, subrutina close_scalar_file_handle
va fi invocată cu x ca argument real, iar close_rank1_file_handle va fi invocat cu y ca argument real.
Ordinea în care acestea vor fi invocate depinde de procesor.

Încheierea unui program printr-o condiție de eroare, prin executarea unei instrucțiuni stop sau a
Instrucțiunea end din programul principal, nu invocă nicio subrutine finale.
Dacă un obiect conține componente finalizabile (non-pointer), obiectul ca întreg va fi finalizat înaintea
componentelor individuale. Adică, în Figura 14.11, când ovalue este finalizată, destroy_outer_ftype va fi
invocat cu ovalue ca argument înainte ca destroy_inner_ftype să fie invocat cu ovalue%ivalue ca argument.
Machine Translated by Google

Programare orientată pe obiecte 283

Figura 14.10 Un exemplu de finalizare. modul


file_handle_module tip file_handle private

:
con ine
final :: close_scalar_file_handle, close_rank1_file_handle
tipul final file_handle conține

subrutină close_scalar_file_handle(h)
tip(file_handle) :: h
:
end subrutine close_scalar_file_handle
:
modul final file_handle_module
:
subrutină s(n)
tip(file_handle) :: x, y(n)
:
sfâr itul subrutinei s

Figura 14.11 Un tip finalizabil cu o componentă finalizabilă.


tastați inner_ftype
:
con ine
final :: destroy_inner_ftype tip final
inner_ftype tip outer_ftype tip(inner_ftype) ::
ivalue

con ine
final :: destroy_outer_ftype tip final
outer_ftype
:
tip(oter_ftype) :: ovalue
Machine Translated by Google

284 Fortran modern explicat

14.8.1 Extensie de tip și subrutine finale

Când un tip este extins, noul tip nu moștenește niciuna dintre subrutinele finale ale vechiului tip. Noul
tip este, totuși, încă finalizabil, iar atunci când este finalizat, orice subrutine finale aplicabile ale vechiului
tip sunt invocate pe componenta părinte.
Dacă noul tip definește orice subrutină finală, aceasta va fi invocată înainte ca orice subrutină finală
de tipul vechi să fie invocată. (Adică obiectul în ansamblu este finalizat, apoi componenta sa părinte este
finalizată etc.) Acest lucru funcționează recursiv, astfel încât atunci când x este dealocat în codul din
Figura 14.12, destroy_bottom_type va fi invocat cu x ca argument. , atunci destroy_top_type va fi invocat
cu x%top_type ca argument.

Figura 14.12 Extensii imbricate ale tipurilor finalizabile.


tip top_type
:
con ine
final :: destroy_top_type end type
type, extinde(top_type) :: middle_type

:
tip tip final,
extinde(tip_mijloc):: tip_inferioară
:
con ine
final :: destroy_bottom_type tip final

tip(tip_jos), indicator :: x alocă (x)

:
dealocarea (x)

14.9 Exemplu de încapsulare a procedurii

O procedură poate cere utilizatorului său să definească problema care trebuie rezolvată prin furnizarea
unei funcții precum și a datelor. Exemplul pe care îl vom considera aici este acea cuadratura
multidimensională, în care trebuie specificată funcția de integrat. Această funcție poate depinde de alte
date într-un mod complicat, care nu a fost anticipat de autorul procedurii de cuadratura.
Soluțiile disponibile anterior pentru probleme de acest gen au fost:

i) ca rutina de cuadratura să accepte un argument suplimentar, de obicei un vector real, și să-l


transmită funcției definite de utilizator atunci când este apelată;

ii) pentru ca programul să transmită informațiile către funcție prin variabile de modul sau comune
blocuri; sau
Machine Translated by Google

Programare orientată pe obiecte 285

iii) utilizarea tehnicilor de „comunicare inversă”, în care programul apelează în mod repetat rutina în
cuadratură, oferindu-i informații suplimentare de fiecare dată, până când rutina în cuadratura este
satisfăcută.

Toate acestea au dezavantaje; primul nu este foarte flexibil (un vector real ar putea fi o modalitate
slabă de a reprezenta datele), al doilea necesită date globale (recunoscute ca fiind o practică slabă)
și nu este sigur pentru fire, în timp ce al treilea este flexibil și sigur pentru fire, dar foarte complicat
de utilizat, în special pentru scriitorul rutinei de cuadratura.

Figura 14.13 Schița unui modul în cuadratura. modul


quadrature_module
întreg, parametru :: wp = tipul select_real_kind(15), abstract :: bound_user_function !
Nu conține componente de date

procedure(interfață_funcție_utilizator), amânat :: eval


tipul final bound_user_function interfață
abstractă

funcția real(wp) user_function_interface(date, coords)


import :: wp, bound_user_function class(bound_user_function) :: data real(wp), intent(in) ::
coords(:) end function user_function_interface

interfață finală
:
con ine

funcția real(wp) ndim_integral(hyper_rect, userfun, opțiuni și


stare)
real(wp), intent(in) :: hyper_rect(:)
class(bound_user_function) :: userfun

tip(quadrature_options), intent(in) :: opțiuni tip(quadrature_status), intent(out) ::


status
:
! Acesta este modul în care funcția utilizator este invocată

single_value = userfun%eval(coordonate)
:

funcția finală ndim_integral


:
modul final

Cu extensia de tip, utilizatorul poate împacheta o procedură cu orice tip de date necesare, iar rutina de cuadratura va
trece datele prin intermediul. Figura 14.13 prezintă definiția tipurilor în cauză și o schiță a rutinei în cuadratura. Detaliile
care nu sunt relevante pentru evaluarea funcției (cum ar fi definirea tipurilor pentru trecerea opțiunilor la rutină și pentru
primirea stării integrării) au fost omise.
Machine Translated by Google

286 Fortran modern explicat

Pentru a utiliza ndim_integral, utilizatorul trebuie să extindă tipul abstract pentru a include orice
componente de date necesare și să-și lege funcția de tip. Figura 14.14 arată cum ar putea utilizatorul
să facă acest lucru pentru o funcție polinomială arbitrară.

Figura 14.14 Extinderea tipului Figura 14.13 pentru integrarea polinomială.


modul polinomial_integration
utilizați modulul quadrature

tip, extinde(funcția_utilizator_limitată) :: întreg polinom_miu_legat :: grad,


dimensionalitate reală(wp),alocabil :: coefici (:,:)

con ine
procedura :: eval => evaluare_polinom
tipul de capăt
con ine
funcția real(wp) polinomial_evaluation(date, coords) rezultat(r)
class(my_bound_polinomal) :: date real(wp),
intent(in) întreg r=0 :: coords(:) :: i, j

do i=1, data%dimensionality r=r+


sum([ (data%coeffs(i, j)*coords(i)**j, & j=1, data%degree) ])

sfâr itul face

funcția finală polinomial_evaluation


modul final polinomial_integration

Pentru a realiza efectiv o integrare, utilizatorul are nevoie doar de o variabilă locală de acest tip
încărcat cu datele necesare și apelează rutina de cuadratura așa cum se arată în Figura 14.15.

14.10 Funcții de interogare tip

Au fost adăugate două noi funcții intrinseci care compară tipurile dinamice. Acestea sunt destinate
utilizării pe variabile polimorfe, dar pot fi utilizate și pe variabile nepolimorfe.

extends_type_of(a, mold) returnează, ca logic implicit scalar, dacă tipul dinamic al lui a este o extensie
a tipului dinamic de matriță. Atât a cât și mucegaiul trebuie să fie polimorfe nelimitate sau de
tip extensibil.

Acest lucru va returna adevărat dacă matrița este polimorfă nelimitată și este fie un pointer
disociat, fie o variabilă alocabilă nealocată; în caz contrar, dacă a este polimorfă nelimitată și
este fie un pointer disociat, fie o variabilă alocabilă nealocabilă, va returna false.
Machine Translated by Google

Programare orientată pe obiecte 287

Figura 14.15 Efectuarea integrării polinomiale. utilizați


tipul de integrare_polynomial(my_bound_polynoal) ::
poly :: integral real(wp) real(wp), alocabil ::
type(quadrature_options) :: opțiuni hyper_rectangle(:)
tip(quadrature_status) :: status

! Citiți datele în variabila locală

citește (...) poly%degree, poly%dimensionality alocare


(poly%coeffs(poli%dimensionality, poly%degree)) citește (...) poly%coeffs

! Citiți informațiile hiper-dreptunghiului alocat


(hyper_rectangle(poli%dimensionalitate)) citiți (...) hiper_dreptunghi !
Setarea opțiunilor a fost omisă
:
! Evaluați integrala integrală =
ndim_integral(hyper_rectangle, poly, options, status)

În caz contrar, dacă atât a cât și mucegaiul sunt polimorfe nelimitate și niciunul nu are tip dinamic
extensibil, rezultatul este dependent de procesor.

same_type_as(a, b) returnează, ca logic implicit scalar, dacă tipul dinamic al lui a este același cu tipul
dinamic al lui b. Atât a cât și b trebuie să fie polimorfe nelimitate sau de tip extensibil.

Dacă ambele a și b sunt polimorfe nelimitate și niciunul nu are tip dinamic extensibil, rezultatul
este dependent de procesor.

Pentru ambele funcții, niciunul dintre argumente nu este permis să fie un pointer cu statut de asociere
nedefinit.
Aceste două funcții nu sunt îngrozitor de utile, deoarece cunoașterea tipului dinamic al lui a (sau
modul în care acesta se raportează la tipul dinamic al lui b sau matriță) nu permite în sine accesul la
componentele extinse. Prin urmare, vă recomandăm ca tipul de selectare să fie utilizat pentru testarea
tipurilor dinamice de entități polimorfe.

Exerciții

1. Definiți un tip de poligon în care fiecare punct este definit de o componentă a punctului de clasă (definit în Secțiunea 14.3).
O funcție pentru a testa dacă o poziție se află în poligon ar fi utilă. O extensie tipică de un astfel de tip ar putea avea o
etichetă și unele date asociate; definiți o astfel de extensie.

2. Definiți un tip de înregistrare a datelor. Acesta ar trebui să conțină proceduri legate de tip pentru a inițializa înregistrarea
într-un anumit fișier și pentru a scrie o intrare de jurnal. Fișierul ar trebui să fie închis automat dacă obiectul încetează să
mai existe.
Machine Translated by Google

Această pagină a fost lăsată goală în mod intenționat


Machine Translated by Google

15. Stabilirea și mutarea datelor

15.1 Introducere

Au fost aduse multe îmbunătățiri relativ minore pentru manipularea obiectelor de date.

15.2 Accesibilitatea componentelor mixte

Acum este posibil ca unele componente ale unui tip să fie private, în timp ce altele rămân publice.
Declarația privată dintr-un tip, care anterior setează toate componentele să fie private, acum setează
doar accesibilitatea implicită a componentelor să fie privată. Accesibilitatea implicită pentru fiecare
componentă poate fi suprascrisă sau confirmată în declarația de definire a componentei, prin specificarea
atributelor publice sau private. De exemplu, în

modul mytype_module
tastați tipul
meu
''
caracter privat(20), public :: debug_tag = !
: componente private omise
tipul meu tipul meu
:
termina modul mytype_module

deși unele dintre componentele mytype sunt private, câmpul debug_tag este public, expunându-se
utilizatorului modulului mytype_module.
Dacă orice componentă a unui tip derivat este privată, constructorul de structură poate fi folosit în exterior
modulul în care este definit numai dacă valoarea pentru acea componentă este omisă.

15.3 Constructori de structură

În Fortran 95, constructorii de structură arată ca apelurile de funcții, cu excepția faptului că argumentele
cuvintelor cheie nu sunt permise. În Fortran 2003, constructorii de structură pot avea argumente cheie
și argumente opționale; în plus, un nume de procedură generică poate fi același cu numele constructorului
de structură (care este același cu numele tipului), orice proceduri specifice din setul generic având
prioritate față de constructorul de structură dacă există vreo ambiguitate. Acesta poate fi folosit eficient
pentru a produce „constructori” suplimentari pentru acest tip, așa cum se arată în Figura 15.1.
Machine Translated by Google

290 Fortran modern explicat

Figura 15.1
modul mycomplex_module tip
mycomplex real :: argument,
modulul tip final interfață mycomplex
modul modul complex_to_mycomplex,
two_reals_to_mycomplex end interface

:
con ine

tip(mycomplex) funcție complex_to_mycomplex(c)


complex, intenție(în) :: c
:

funcția finală complex_to_mycomplex tip(mycomplex)


funcție two_reals_to_mycomplex(x, y)
real, intent(in) real, :: X

intent(in), optional :: y
:

sfârșitul funcției two_reals_to_mycomplex


:

termina modul mycomplex_module


:

utilizați mycomplex_module
type(mycomplex) :: a, b, c
:

a = complexul meu(argument=5,6, modul=1,0) ! Constructorul structurii c = mycomplex(x=0.0, y=1.0)


! O referință de funcție

Dacă o componentă a unui tip are inițializare implicită, valoarea sa poate fi omisă în constructorul structurii ca și cum
ar fi un argument opțional.1 De exemplu, în

tip real_list_element real


:: valoare

type(real_list_element), pointer :: next => null()


tipul final real_list_element
:

tip(element_real_list) :: x = real_list_element(3.5)

valoarea omisă pentru următoarea componentă înseamnă că aceasta preia valoarea implicită de
inițializare – adică un pointer nul.
Dacă tipul derivat are parametri de tip, aceștia sunt specificați în paranteze imediat după numele tipului în constructorul
său de structură. Din nou, dacă parametrii de tip au valori implicite, acestea pot fi omise, ca în exemplul din Figura 15.2.

1Fortran 2008 permite, de asemenea, omiterea valorilor componentelor alocabile, vezi Secțiunea 20.1.4.
Machine Translated by Google

Stabilirea și mutarea datelor 291

Figura 15.2
tip character_with_max_length(maxlen, kind)
întreg, len întreg, :: maxlen
fel :: fel = fel('a') întreg :: lungime = 0 caracter(tip) ::
valoare(maxlen) tip final caracter_cu_lungime_max.

:
tip(caracter_cu_lungime_max.(100)) :: nume
:
nume = character_with_max_length(100)('John Hancock')

15.4 Declarația de alocare

Pe lângă determinarea dimensiunii matricei, instrucțiunea de alocare poate determina acum valorile
parametrului tip, tipul (pentru o variabilă polimorfă) și valoarea. Acest lucru este controlat fie prin
includerea unei specificații de tip în instrucțiunea de alocare:

alocare ( [ tip-spec :: ] lista-alocare [ , stat=stat ] )

unde tip-spec este numele tipului urmat de valorile parametrului de tip în paranteze, dacă există, atât
pentru tipurile intrinseci, cât și pentru cele derivate; sau prin utilizarea clauzei source= pentru un singur obiect:

alocă ( alocare [ , sursă=expr-sursă ] [ , stat=stat ] )

unde sursa-expr este o expresie cu care alocarea este compatibilă cu tipul (vezi Secțiunea 14.3). Dacă
alocarea este pentru o matrice, expr-sursă poate fi o matrice de același rang, altfel expr-sursă trebuie să
fie scalară.
O instrucțiune de alocare cu o specificație de tip este o alocare tipizată și o instrucțiune de alocare
cu o clauză sursă= este o alocare sursă. Acum explicăm noile funcții.

15.4.1 Alocare tip și parametri de tip amânat

Un parametru de tip lungime care este amânat (indicat de două puncte în specificația de tip) nu are o
valoare definită până când nu este dat una printr-o instrucțiune de alocare sau printr-o atribuire de
pointer (un parametru de tip care nu este amânat nu poate fi modificat prin alocare sau pointer misiune).
De exemplu, în
caracter(:), alocabil :: x(:)
:
aloca (caracter(n) :: x(m))

tabloul x va avea m elemente și fiecare element va avea lungimea caracterului n după executarea
instrucțiunii alocare.
Dacă se presupune un parametru de lungime al unui articol care este alocat, acesta trebuie
specificat ca un asterisc în specificația de tip. De exemplu, parametrul de tip string_dim din Figura
15.3 trebuie specificat ca * deoarece se presupune.
Machine Translated by Google

292 Fortran modern explicat

Figura 15.3
tip string_vector(string_dim, space_dim) întreg, len ::
string_dim, space_dim type(string(string_dim))
value(space_dim)
::

tipul de sfârșit string_vector


:
subrutină alocă vectori_șir de caractere (vp, n, m)
tip(vector_șir(*,:)), pointer :: vp(:) întreg, intent (în) alocare
(vector_șir(dim_șir=*, spațiu_dim=n) :: vp(m)) :: n, m

sfâr itul subrutinei allocate_string_vectors

Rețineți că există o singură specificație de tip într-o instrucțiune de alocare, deci trebuie să fie potrivită
pentru toate elementele care sunt alocate. În special, dacă oricare dintre ele este un argument inactiv cu
un parametru de tip presupus, toate trebuie să fie argumente inactiv care presupun acest parametru de tip.
Dacă vreun parametru de tip nu este nici asumat, nici amânat, valoarea specificată pentru el de
tip-spec trebuie să fie aceeași cu valoarea sa curentă. De exemplu, în
subrutina allocate_string3_vectors(vp, n, m) type(string_vector(3,:)),
pointer :: vp(:) întreg, intent(in) allocate
(string_vector(string_dim=3, space_dim=n) :: vp(m):: )n, m

final subrutine allocate_string3_vectors

expresia furnizată pentru parametrul de tip string_dim trebuie să fie egală cu 3.

15.4.2 Variabile polimorfe și alocare tipizată

Pentru variabilele polimorfe, specificația de tip specifică nu numai valorile oricăror parametri de tip
amânat, ci și tipul dinamic de alocat. Dacă un articol este polimorf nelimitat, acesta poate fi alocat pentru
a fi orice tip (inclusiv tipuri intrinseci); în caz contrar, tipul specificat în instrucțiunea de alocare trebuie să
fie o extensie a tipului declarat al articolului.
De exemplu,

class(*), pointer :: ux, uy(:) class(t), pointer ::


x, y(:)
:
alocă (t2 :: ux, x, y(10)) alocă (real ::
uy(100))

alocă ux, x și y să fie de tip t2 (o extensie a lui t), iar uy să fie de tipul implicit real.

15.4.3 Alocarea sursă

În loc să alocați o variabilă cu un tip specificat explicit (și parametri de tip), este posibil să luați tipul,
parametrii de tip și valoarea dintr-o altă variabilă sau expresie.
Machine Translated by Google

Stabilirea și mutarea datelor 293

Acest lucru produce efectiv o „clonă” a expresiei sursă și se face prin utilizarea clauzei source= în
instrucțiunea alocare. De exemplu, în

subrutină s(b)
clasă(t), alocabil :: a clasă(t) :: b alocă
(a, sursă=b)

variabila a este alocată cu aceiași tip dinamic și parametri de tip ca și b și va avea aceeași valoare.

Acest lucru este util pentru copierea structurilor de date eterogene, cum ar fi liste și arbori, ca în
exemplul din Figura 15.4.

Figura 15.4
tastați single_linked_list
class(singly_linked_list), pointer :: next => null()
! Fără date - utilizatorul tipului ar trebui să o extindă pentru a include! datele dorite.

tip de final single_linked_list


:
funcție recursivă sll_copy(sursă) rezultat(copiere)
clasă(singly_linked_list), pointer class(singly_linked_list),:: copie
intent(in) :: sursă alocare (copy, source=source) if (asociat(sursa%next))
copy%next => sll_copy(sursa%next)

sfârșitul funcției sll_copy

Dacă elementul alocat este o matrice, limitele și forma acestuia sunt specificate în mod obișnuit și nu
sunt preluate din sursă. Acest lucru permite sursei să fie un scalar a cărui valoare este dată fiecărui
element al tabloului. Alternativ, poate fi o matrice de aceeași formă.
Deoarece limitele și forma articolului alocat nu sunt preluate din sursă, făcând
o clonare a unui tablou trebuie făcută după cum urmează:

clasa(t), alocabil :: a(:), b(:)


:
alocă (a(lbound(b,1):ubound(b,1)), sursă=b)

15.5 Entități alocabile

Există mai multe extensii ale atributului alocabil în Fortran 2003, dincolo de cele din Raportul Tehnic și
descrise în Capitolele 2 până la 10. Vom descrie acum fiecare dintre acestea pe rând.
Machine Translated by Google

294 Fortran modern explicat

15.5.1 Scalari alocabili

Atributul alocabil (și, prin urmare, funcția alocată) poate fi acum aplicat și variabilelor scalare și
componentelor. Acest lucru este util în special atunci când este combinat cu parametri de tip amânat, de
exemplu, în

character(:), alocabil :: chdata integer :: unit, reclen

:
citire (unitate) reclen
alocare (caracter(reclen) :: chdata) citire (unitate)
chdata

unde reclen permite ca lungimea caracterului să fie specificată în timpul rulării.


Componentele scalare alocabile pot fi, de asemenea, utilizate pentru a construi structuri de date care nu
scurgeri de memorie (pentru că sunt dealocate automat).

15.5.2 Atribuire la o matrice alocabilă

Am explicat în Secțiunea 6.5.6 că atribuirea intrinsecă pentru un obiect care conține componente
alocabile determină alocarea sau realocarea automată a oricărei componente alocabile care nu este
alocată și de formă corectă. În Fortran 2003, pentru coerența cu componentele alocabile, această
realocare automată este extinsă și la variabilele obișnuite alocabile.
Acest lucru simplifică utilizarea funcțiilor matrice care returnează un rezultat de dimensiune variabilă
(cum ar fi funcțiile intrinseci pack și unpack).
De exemplu, în

subrutine process(x) real(wp),


intent(inout) :: x real(wp), alocable ::
nonzero_values(:) nonzero_values = pack(x, x/=0)

variabila nonzero_values este alocată automat pentru a avea lungimea corectă pentru a conține
rezultatele pachetului de funcții intrinseci, în loc ca utilizatorul să fie nevoit să o aloce manual (ceea
ce ar necesita numărarea separată a numărului de nonzero). De asemenea, permite o extensie
simplă a unei matrice alocabile existente ale cărei limite inferioare sunt toate 1. Pentru a adăuga
câteva valori suplimentare la o astfel de matrice întregă a de rang 1, este suficient să scrieți, de exemplu,

a = (/ a, 5, 6 /)

Această realocare automată are loc și dacă variabila alocabilă are un parametru de tip amânat care nu
are deja aceeași valoare cu parametrul corespunzător al expresiei. Acest lucru se aplică scalarilor
alocabili, precum și tablourilor alocabile, ca în

caracter(:), alocabil :: citat


:
citat = 'Acum este iarna nemulțumirii noastre.'
:
citat = "Aceasta nu este vara iubirii."
Machine Translated by Google

Stabilirea și mutarea datelor 295

În fiecare dintre atribuirile la cotație, acesta este realocat pentru a avea lungimea potrivită (cu excepția
cazului în care este deja de această lungime) pentru a păstra cotația dorită. Dacă, în schimb, trunchierea
sau umplutura normală este necesară într-o atribuire unui caracter cu lungime alocabilă, notația subșir
poate fi folosită pentru a suprima realocarea automată. De exemplu,
''
citat(:) =

lasă cotația la lungimea sa actuală, setându-le toate în spații libere.

15.5.3 Transferarea unei alocări

Subrutina intrinsecă move_alloc a fost introdusă pentru a muta o alocare de la un obiect alocabil la altul.

apelați move_alloc (de la, la) unde:

de la este alocabil și de orice tip. Are intentie inout.

to este alocabil și de același tip și rang ca de la. Are intenție.

După apel, starea de alocare și ținta (dacă există) pentru a este cea de din prealabil și de devine
dealocată.

Acesta oferă ceea ce este în esență echivalentul alocabil al atribuirii pointerului: transferul
de alocare. Totuși, spre deosebire de atribuirea pointerului, aceasta menține semantica alocabilă
de a avea cel mult un obiect alocat pentru fiecare variabilă alocabilă. De exemplu,

real, alocabil :: a1(:), a2(:) alocă (a1(0:10)) a1(3)


= 37 apel move_alloc(de la=a1, la=a2) ! a1 este
acum nealocat, ! a2 este alocat cu limite (0:10)
și a2(3)==37.

Aceasta poate fi folosită pentru a minimiza cantitatea de copiere necesară atunci când se dorește să se extindă
sau contractați o matrice alocabilă; secvența canonică pentru aceasta este:

real, alocabil :: a(:,:), temp(:,:)


:
! Măriți dimensiunea a la (n, m) alocare
(temp(n, m)) temp(1:size(a,1), 1:size(a,2)) =
un apel move_alloc(temp, a) ! a acum are formă
(/ n, m /), iar temp nu este alocată

Această secvență necesită doar o operație de copiere în loc de cele două care ar fi fost necesare fără
move_alloc. Deoarece copia este controlată de utilizator, valorile preexistente vor ajunge acolo unde le
dorește utilizatorul (care ar putea fi la aceleași indice, sau toate la început, sau toate la sfârșit etc.).
Machine Translated by Google

296 Fortran modern explicat

15.6 Atribuire pointer

Două îmbunătățiri au fost aduse instrucțiunii de atribuire a indicatorului de matrice. Primul este că acum
este posibil să setați limitele inferioare dorite la orice valoare. Acest lucru poate fi de dorit în situații
precum următoarele. Considera

real, target :: annual_rainfall(1700:2003) real, pointer :: rp1(:),


rp2(:)
:
rp1 => precipitații_anuale rp2
=> precipitații_anuale(1800:1856)

Limitele lui rp1 vor fi (1700:2003); cu toate acestea, cele ale rp2 vor fi (1:57). Pentru a putea avea un
pointer către o subsecțiune a unui tablou să aibă limitele corespunzătoare, acestea pot fi setate pe
alocarea pointerului după cum urmează:

rp2(1800:) => precipitații_anuale(1800:1856)

Această declarație va stabili limitele lui rp2 la (1800:1856).


A doua nouă facilitate pentru atribuirea pointerului de matrice este că ținta unui pointer de
matrice multidimensională poate fi unidimensională. Sintaxa este similară cu cea a specificației
limitelor inferioare de mai sus, cu excepția faptului că în acest caz se specifică fiecare limită
superioară, precum și limita inferioară. Aceasta poate fi folosită, de exemplu, pentru a furniza un
pointer către diagonala unui tablou:

real, pointer :: matrice_bază(:), matrice(:,:), diagonal(:) alocă (matrice_bază(n*n))


matrice(1:n, 1:n) => matrice_bază diagonală => matrice_bază(:: n+1)

După executarea atribuirilor de indicator, diagonala este acum un pointer către elementele diagonale
ale matricei.

15.7 Mai mult control al accesului de la un modul

Uneori este de dorit să se permită utilizatorului unui modul să poată face referire la valoarea unei
variabile de modul fără a permite modificarea acesteia. Un astfel de control este asigurat de atributul
protejat. Acest atribut nu afectează vizibilitatea variabilei, care trebuie să fie în continuare publică pentru
a fi vizibilă, dar conferă aceeași protecție împotriva modificării pe care o face intenția în cazul
argumentelor false.
Atributul protejat poate fi specificat cu cuvântul cheie protejat într-un tip
declarație. De exemplu, în
modulul m
real
public, întreg protejat, :: v
protejat :: i

atât v cât și i au atributul protejat. Atributul poate fi specificat și separat, într-o declarație protejată, la fel
ca și pentru alte atribute (vezi Secțiunea 7.7).
Machine Translated by Google

Stabilirea și mutarea datelor 297

Variabilele cu acest atribut pot fi modificate numai în cadrul modulului de definire. În afara modulului,
acestea nu au voie să apară într-un context în care ar fi modificate, cum ar fi în partea stângă a unei
declarații de atribuire.
De exemplu, în codul din Figura 15.5, atributul protejat permite utilizatorilor termometrului să citească
temperatura fie în Fahrenheit, fie în Celsius, dar variabilele pot fi modificate numai prin subrutinele
furnizate care asigură că ambele valori sunt de acord.

Figura 15.5
termometru modul
real, protejat :: temperature_celsius = 0 real, protejat ::
temperature_fahrenheit = 32 con ine

subrutine set_celsius(new_celsius_value) real, intent(in) ::


new_celsius_value temperature_celsius =
new_celsius_value temperature_fahrenheit =
temperature_celsius*(9.0/5.0) + 32
end subroutine set_celsius subroutine
set_fahrenheit(new_fahrenheit_value) real, intent(in) ::
new_fahrenheit_value temperature_fahrenheit =
new_fahrenheit_value temperature_celsius =
(temperature_fahrenheit - 32)*(5.0/9.0)
sfârșitul subrutinei set_fahrenheit
termometru cu modul final

15.8 Redenumirea operatorilor pe instrucțiunea de utilizare

Operatorii definiți de utilizator pot fi acum redenumiti în instrucțiunea de utilizare, la fel cum pot fi
numele de variabile și proceduri. De exemplu,

utilizați fred, operator(.nurke.) => operator(.banana.)

redenumește .banana. operator situat în modulul fred astfel încât să poată fi referit folosind .nurke. ca
operator.
Cu toate acestea, acest lucru se aplică numai operatorilor definiți de utilizator. Operatorii intrinseci nu pot fi
redenumit, astfel încât toate următoarele sunt invalide:

utilizați fred, numai: operator(.equal.) => operator(.eq.) ! Utilizați


fred invalid, numai: operator(.ne.) => operator(.notequal.) ! Folosește fred invalid, numai:
operator(*) => atribuire(=) ! Invalid

15.9 Sintaxa constructorului de matrice

O deficiență bine-cunoscută a constructorilor de matrice în Fortran 95 este că sunt oarecum incomod de


utilizat pentru tipul de caractere; fiecare element trebuie să aibă exact același caracter
Machine Translated by Google

298 Fortran modern explicat

lungime. Acest lucru este iritant pentru utilizator, căruia i se cere să completeze manual constantele de
caractere cu spații libere, pentru a le face pe toate de aceeași lungime. Pentru constructorii de matrice care
implică variabile, această cerință nu este adesea verificată în timpul compilării, ceea ce duce la potențiale erori
de rulare sau rezultate ciudate.
O altă deficiență este că pentru constructorii de matrice de dimensiune zero, poate fi dificil, dacă nu
imposibil, pentru compilator să deducă valoarea oricăror parametri de tip lungime (în Fortran 95 acest
lucru este limitat la tipul de caractere).
O deficiență mai puțin gravă este aceea că nu se pot amesteca elemente de tip diferit chiar și atunci
când acele elemente ar fi atribuibile unui tip comun (de exemplu, având elemente întregi sau reale într-un
constructor de matrice complex).
În cele din urmă, atunci când expresiile între paranteze sunt elemente constructoare de matrice și când
constructorii de matrice sunt elemente din interiorul expresiilor între paranteze și referințe la funcții, poate
fi dificil să potriviți parantezele, astfel încât constructorii de matrice să se termine cu /).
Toate aceste deficiențe au fost abordate în Fortran 2003. Pentru a facilita potrivirea parantezelor, un
constructor de matrice poate fi între paranteze, [ ], în loc de (/ /).

Pentru a depăși deficiențele de tip, un constructor de matrice poate începe acum cu o specificare
explicită a tipului și a parametrilor de tip. Sintaxa pentru un constructor de matrice cu o specificație de tip
este:

(/ tip-spec :: ac-value-list /) sau [ type-spec :: ac-value-list ]

unde tip-spec este forma scurtă utilizată în instrucțiunea de alocare (Secțiunea 15.4). În acest caz, valorile
constructorului de matrice pot avea orice tip (și parametri de tip) care este compatibil cu alocarea cu
parametrii de tip și tip specificați, iar valorile sunt convertite în acel tip prin conversiile obișnuite de atribuire.

Aici sunt cateva exemple:

[ caracter(len=33) :: „bun”, „rău”, „și” și


„aspectul contestat” ] [ complex(kind(0d0)) ::
1, (0,1), 3.14159265358979323846264338327d0 ]

[ matrix(kind=kind(0.0), n=10, m=20) :: ] ! matrice de dimensiune zero

Dacă specificația de tip este absentă, regulile Fortran 95 continuă să se aplice: toate articolele trebuie să aibă
același tip și parametri de tip. Această regulă se aplică și tipurilor derivate parametrizate.

15.10 Specificații și expresii constante

O expresie de specificație (utilizată pentru un parametru de tip matrice sau lungime) poate face referire
acum la o funcție recursivă, atâta timp cât funcția nu invocă procedura care conține acea expresie de
specificație. Poate conține o întrebare de tip parametru (Secțiunea 13.3) sau o referință la o funcție de
interogare IEEE (Secțiunea 11.9.2). În interiorul unei definiții de tip derivat, o expresie de specificație poate
face referire, de asemenea, la orice parametru de tip al tipului care este definit.

O expresie constantă nu este la fel de restricționată ca în Fortran 95. Poate face referire la orice funcție
intrinsecă standard elementară sau transformațională sau la funcția ieee_selected_real_kind of
Machine Translated by Google

Stabilirea și mutarea datelor 299

modulul intrinsec ieee_arithmetic, atâta timp cât argumentele sale sunt toate expresii constante.
Aceasta include funcțiile matematice intrinseci (sin, cos etc.). De exemplu,

real :: root2 = sqrt(2.0)

este acum o inițializare validă. Operatorul de exponențiere nu se limitează la o putere întreagă.


Toate funcțiile de interogare pot fi referite într-o expresie constantă cu restricțiile privind argumentele lor,
care sunt date la punctul vii) al listei din Secțiunea 7.4. O interogare de parametru de tip (Secțiunea 13.3)
poate fi utilizată atâta timp cât parametrul de tip nu este asumat, amânat sau definit de o altă expresie decât
o expresie constantă.
O expresie constantă poate face referire la funcția intrinsecă nulă atâta timp cât nu are un argument cu
un parametru de tip care este asumat sau definit de o expresie care nu este o expresie constantă.

În cele din urmă, în cadrul unei definiții de tip derivat, o expresie constantă poate face referire la un tip tip
parametrul tipului care se definește.

Exerciții

1. Scrieți o declarație care face un tablou întreg de rang-2 existent b, care are limite inferioare de 1, două rânduri și două
coloane mai mari, cu valorile elementelor vechi reținute în mijlocul matricei.
(Sugestie: utilizați funcția intrinsecă de remodelare.)

2. Scrieți o procedură de introducere care citește un număr variabil de caractere dintr-un fișier, oprindu-se la întâlnirea unui
caracter dintr-un set specificat de utilizator sau la sfârșitul înregistrării, returnând intrarea într-un șir de caractere cu
lungime alocată amânată.
Machine Translated by Google

Această pagină a fost lăsată goală în mod intenționat


Machine Translated by Google

16. Îmbunătățiri diverse

16.1 Introducere

Acest capitol adună împreună o serie de îmbunătățiri diverse făcute în Fortran 2003 care nu
se încadrează în nicio categorie convenabilă.

16.2 Intenția indicatorului

Atributul intent a fost extins pentru a include pointeri. Pentru un pointer, intenția se referă la asocierea
pointerului și nu la valoarea țintei; adică se referă la descriptor.
Un indicator de intent out are statut de asociere nedefinit la intrarea în procedură; o intenție
în pointer nu poate fi anulată sau asociată în timpul executării procedurii; iar argumentul real
pentru un pointer de intrare intenție trebuie să fie o variabilă pointer (adică nu poate fi o
referință la o funcție cu valoare de pointer).
Rețineți că, deși o intenție în pointer nu poate avea statutul de asociere a pointerului
schimbat în cadrul procedurii, dacă este asociată cu o țintă, valoarea țintei sale poate fi modificată.
De exemplu,

subrutina maybe_clear(p)
real, pointer, intent(in) :: p(:) if (asociat(p)) p =
0,0
sfâr itul subrutinei maybe_clear

16.3 Atributul volatil

Atributul volatil este un nou atribut care poate fi aplicat numai variabilelor. Este conferit fie de atributul
volatil dintr-o declarație de tip, fie de declarația volatilă, care are forma

volatil [::] lista-nume-variabilă

De exemplu,

întreg, volatil :: x real


:: y
volatil :: y

declară două variabile volatile x și y.


Machine Translated by Google

302 Fortran modern explicat

16.3.1 Semantică volatilă

A fi volatilă indică compilatorului că, în orice moment, variabila poate fi schimbată și/sau examinată din
afara programului Fortran. Aceasta înseamnă că fiecare referință la variabilă va trebui să-și încarce
valoarea din memoria principală (deci, de exemplu, nu poate fi păstrată într-un registru într-o buclă
interioară). În mod similar, fiecare atribuire a variabilei trebuie să scrie datele în memorie.
În esență, acest lucru dezactivează majoritatea optimizărilor care ar fi putut fi aplicabile obiectului,
făcând programul să ruleze mai lent, dar, se spera, făcându-l să funcționeze cu hardware special sau
software de procesare multiplă.
Cu toate acestea, este responsabilitatea programatorului să efectueze orice sincronizare necesară;
acest lucru este relevant în special pentru sistemele cu mai multe procesoare. Chiar dacă un singur
proces scrie în variabilă și programul Fortran citește din ea, deoarece variabila nu este protejată automat
de o secțiune critică, este posibil să se citească o valoare parțial actualizată (și astfel o valoare
inconsecventă sau imposibilă). De exemplu, dacă variabila este o variabilă în virgulă mobilă IEEE, citirea
unei valori parțial actualizate ar putea returna un NaN de semnalizare; sau dacă variabila este un pointer,
descriptorul ei ar putea fi invalid. În oricare dintre aceste cazuri, programul ar putea fi întrerupt brusc,
așa că această facilitate trebuie utilizată cu grijă.
În mod similar, dacă două procese încearcă ambele să actualizeze o singură variabilă volatilă, efectele
sunt complet dependente de procesor. Variabila ar putea ajunge cu valoarea sa originală, una dintre
valorile dintr-un proces de actualizare, un conglomerat parțial de valori din procesele de actualizare sau
programul s-ar putea chiar bloca.
O simplă utilizare a acestei caracteristici ar putea fi gestionarea unui eveniment extern (controlat de întreruperi), cum ar fi
ca utilizatorul care tastează Control-C, într-un mod controlat. De exemplu,

logic, țintă, volatil :: event_a_occurred


:
evenimentul a avut loc = .false. apelează
register_event_flag(evenimentul_a_a avut loc, ...)
:
do
: ! unele calcule dacă
(event_a_ocurred) ieși! bucla de ieșire dacă a avut loc evenimentul! inca niste calcule!
: Ai terminat încă calculele noastre?
dacă (...) exit end
do
:

unde register_event_flag este o rutină, eventual scrisă într-o altă limbă, care asigură faptul că
event_a_ocurred devine adevărat atunci când are loc evenimentul specificat.
Dacă variabila este un pointer, atributul volatil se aplică atât descriptorului, cât și țintei. Chiar dacă
ținta nu are atributul volatil, este tratată ca având-l atunci când este accesată printr-un pointer care îl are.
Dacă variabila este alocabilă, se aplică atât alocării, cât și valorii. În ambele cazuri, dacă variabila este
polimorfă (Secțiunea 14.3), tipul dinamic se poate schimba prin mijloace non-Fortran.

Dacă o variabilă are atributul volatil, la fel și toate subobiectele sale.


De exemplu, în
Machine Translated by Google

Îmbunătățiri diverse 303

logic, logic țintă, pointer, :: semnal_state(100)


volatil :: signal_flags(:)
:

signal_flags => stare_semnal


:

signal_flags(10) = .adevărat. ! O referință volatilă


:

scrie (20) semnal_state ! O referință nevolatilă

pointerul (descriptorul) semnalului_steaguri este volatil, iar accesul la fiecare element al semnalului_dragurilor este volatil;
totuși, signal_state în sine nu este volatil.
Rațiunea de a fi pentru volatile este interoperarea cu pachete de procesare paralelă, cum ar fi MPI, care au proceduri
pentru transferul asincron al datelor de la un proces la altul. De exemplu, fără atributul volatil de pe datele matricei din
Figura 16.1, o optimizare a compilatorului ar putea muta alocarea înainte de apelul la mpi_wait. Utilizarea mpi_module
oferă acces la constantele MPI și interfețele explicite pentru rutinele MPI; în special, mpi_isend, care necesită atributul
volatil pe primul argument fals.

Figura 16.1 Utilizarea volatilului pentru a evita mișcarea codului.


subrutina transfer_while_producing(...) folosește mpi_module !
Interfețe de acces pentru mpi_isend etc. real, allocabil :: newdata(:) real, allocatable,
volatile :: data(:)

: ! Produceți date aici

apelați mpi_isend(date, dimensiune(date), mpi_real, dest și


tag, comm, request, err)
: ! Produceți noi date aici apelați

mpi_wait(request, status) data = newdata

termina subrutine transfer_while_producing

16.3.2 Scoping volatil

Dacă o variabilă trebuie tratată ca volatilă doar pentru o perioadă scurtă de timp, programatorul are
două opțiuni: fie să o treacă la o procedură pentru a fi acționată într-o manieră volatilă (vezi Secțiunea
16.3.3), fie să o acceseze prin utilizare. sau asociația gazdă, folosind o declarație volatilă pentru a o
declara volatilă numai în domeniul de acces. De exemplu, în codul din Figura 16.2, matricea de date nu
este volatilă în data_processing, ci este în data_transfer. Rețineți că aceasta este o excepție de la regulile
obișnuite de asociere de utilizare, care interzic modificarea altor atribute în domeniul de acces. În mod
similar, declararea unei variabile care este accesată de asociere gazdă ca fiind volatilă este permisă și,
spre deosebire de alte declarații de specificație, nu provoacă crearea unei noi variabile locale.
Machine Translated by Google

304 Fortran modern explicat

Figura 16.2 Utilizarea unei proceduri pentru a limita domeniul de aplicare al volatilității unei variabile.
module data_module
real, alocabil :: date(:,:), date noi(:,:)
:
con ine
subrutină procesare_date
:
end subrutine de procesare_date subrutine
transfer_date
volatile :: date
:
sfâr it subrutină transfer_date sfâr it
modul modul_date

16.3.3 Argumente volatile

Volatilitatea unui argument real și argumentul inactiv asociat acestuia pot diferi. Acest lucru este
important, deoarece volatilitatea poate fi necesară într-unul, dar nu și în celălalt. În special, o variabilă
volatilă poate fi utilizată ca argument real într-un apel la o procedură intrinsecă. Cu toate acestea, în timp
ce o variabilă volatilă este asociată cu un argument fals nevolatil, programatorul trebuie să se asigure că
valoarea nu este modificată prin mijloace non-Fortran. Rețineți că, dacă volatilitatea unui argument real
persistă printr-o referință de procedură, ca de exemplu în apelul MPI din Figura 16.1, aceasta înseamnă
că procedura la care se face referire trebuie să aibă o interfață explicită și argumentul inactiv
corespunzător trebuie declarat ca fiind volatil.
Dacă argumentul dummy este volatil, argumentul actual nu trebuie să fie o secțiune de matrice cu un
indice vectorial; în plus, dacă argumentul real este o secțiune de matrice sau o matrice de formă
presupusă, argumentul inactiv trebuie să fie de formă presupusă și dacă argumentul real este un pointer
de matrice, argumentul inactiv trebuie să fie un pointer sau de formă presupusă. Aceste restricții sunt
concepute pentru a permite ca argumentul să fie transmis prin referință; în special, pentru a evita
necesitatea unei copii locale, deoarece aceasta ar interfera cu volatilitatea.
Un argument fals cu intenție în sau cu atributul value (Secțiunea 12.6) nu este permis să fie volatil.
Acest lucru se datorează faptului că valoarea unui astfel de argument este de așteptat să rămână fixă în
timpul execuției procedurii.
Dacă un argument dummy al unei proceduri este volatil, interfața trebuie să fie explicită ori de câte
ori este apelată și argumentul dummy trebuie declarat ca volatil în orice corp de interfață pentru
procedură.

16.4 Declarația de import

O problemă cu blocurile de interfață de procedură din Fortran 95 este că un corp de interfață nu


accesează mediul său prin asociere gazdă și, prin urmare, nu poate folosi constantele numite și tipurile
derivate definite în acesta.
Machine Translated by Google

Îmbunătățiri diverse 305

În special, este de dorit într-o procedură de modul să se poată descrie o procedură inactivă care
utilizează tipuri definite în modul. De exemplu, în Figura 16.3, corpul interfeței este invalid, deoarece nu
are acces nici la tipul t, nici la constanta wp.

Figura 16.3 Un bloc de interfață nevalid într-o procedură de modul.


modulul m
întreg, parametru :: wp = fel(0.0d0) tip t

:
tipul final t
conține
subrutine aplica (distracție,...)
interfață
funcția tip(t) fun(f) ! Nu este permis real(wp) :: f ! Nu
este permis sfârșitul funcției distracție

interfață finală

sfâr itul subrutinei aplica


sfâr itul modulului m

Această problemă a fost rezolvată de declarația de import. Această instrucțiune poate fi utilizată numai
într-un corp de interfață și oferă acces la entitățile numite ale unității de acoperire care le conține.
Figura 16.4 prezintă un corp de interfață corect pentru a-l înlocui pe cel incorect din Figura 16.3.

Figura 16.4 Blocul de interfață din Figura 16.3 a devenit valid prin adăugarea unei instrucțiuni de import.
funcția de interfață fun(f) import :: t, wp type(t) :: fun real(wp) :: f end function fun

interfață finală

Declarația trebuie plasată după orice instrucțiuni de utilizare, dar înaintea oricăror alte instrucțiuni
a corpului. Are forma generală:

import [ [::] import-name-list ]

unde fiecare nume-import este cel al unei entități care este accesibilă în unitatea de acoperire care conține.
Dacă o entitate importată este definită în unitatea de acoperire care conține, aceasta trebuie să fie declarată în mod
explicit înainte de corpul interfeței.
O instrucțiune de import fără o listă importă toate entitățile din unitatea de acoperire care le conține
care nu sunt declarate a fi entități locale ale corpului interfeței; aceasta funcționează în același mod ca și
asociația gazdă normală.
Machine Translated by Google

306 Fortran modern explicat

16.5 Module intrinseci

La fel ca o funcție intrinsecă, un modul intrinsec este unul care este furnizat de procesorul Fortran și nu de
către utilizator sau o terță parte. Un procesor Fortran 2003 oferă cel puțin cinci module intrinseci:
ieee_arithmetic, ieee_exceptions, ieee_features, iso_c_binding și iso_fortran_env și poate furniza module
intrinseci suplimentare.
De asemenea, ca și procedurile intrinseci, este posibil ca un program să utilizeze un modul intrinsec și
un modul definit de utilizator cu același nume, deși nu pot fi referite ambele din aceeași unitate de definire.
Pentru a utiliza un modul intrinsec în detrimentul unui modul definit de utilizator cu același nume, cuvântul
cheie intrinsec este specificat în instrucțiunea de utilizare, de exemplu

utilizare, intrinsec :: ieee_arithmetic

În mod similar, pentru a vă asigura că un modul definit de utilizator este accesat în detrimentul unui
modul intrinsec, este utilizat cuvântul cheie non_intrinsic, de exemplu:

utilizați, non_intrinseci :: numere_aleatoare

Dacă atât un modul intrinsec, cât și un modul definit de utilizator sunt disponibile cu același nume, o
instrucțiune de utilizare fără niciunul dintre aceste cuvinte cheie accesează modulul definit de utilizator.
Cu toate acestea, dacă compilatorul nu poate găsi modulul utilizatorului, ar accesa cel intrinsec în schimb
fără avertisment; prin urmare, recomandăm ca programatorii să evite utilizarea aceluiași nume pentru un
modul definit de utilizator ca cel al unui modul intrinsec cunoscut (sau să fie folosit cuvântul cheie
non_intrinsic).
Modulele IEEE oferă acces la facilități din standardul aritmetic IEEE și sunt descrise în Capitolul 11.
Modulul intrinsec iso_c_binding oferă suport pentru interoperabilitatea cu C și este descris în Capitolul 12.

Modulul intrinsec iso_fortran_env oferă informații despre mediul Fortran, sub formă de constante
numite, după cum urmează.

character_storage_size Mărimea în biți a unei unități de stocare a caracterelor (aplicabilă numai


la contextele de asociere de stocare, vezi Anexa B.2).

error_unit Numărul unității pentru o unitate de ieșire preconectată adecvată pentru raportarea erorilor.

file_storage_size Mărimea în biți a unei unități de stocare a fișierelor (unitatea de măsură pentru lungimea
înregistrării unui fișier extern, așa cum este utilizată în clauza recl= a unei instrucțiuni deschise sau
interogare).

input_unit Numărul unității pentru unitatea de intrare standard preconectată (același care este utilizat prin
citire fără un număr de unitate sau cu un specificator de unitate de *).

iostat_end Valoarea returnată de iostat= pentru a indica o condiție de sfârșit de fișier.

iostat_eor Valoarea returnată de iostat= pentru a indica o condiție de sfârșit de înregistrare.

numeric_storage_size Mărimea în biți a unei unități de stocare numerică (aplicabilă numai pentru
contexte de asociere de stocare, vezi Anexa B.2).
Machine Translated by Google

Îmbunătățiri diverse 307

output_unit Numărul unității pentru unitatea de ieșire standard preconectată (aceeași care este utilizată prin
tipărire sau prin scriere cu un specificator de unitate de *).

Spre deosebire de numerele de unități normale, numerele de unități speciale ar putea fi negative, dar nu
vor fi 1 (acest lucru se datorează faptului că 1 este folosit de clauza number= a instrucțiunii de investigare
pentru a însemna că nu există un număr de unitate). Unitatea de raportare a erorilor error_unit poate fi aceeași
cu unitatea de ieșire standard output_unit.
Modulele intrinseci ar trebui să fie întotdeauna utilizate cu o singură clauză, deoarece furnizorii sau
standardele viitoare ar putea face completări la modul.

16.6 Accesul la mediul de calcul


Au fost adăugate funcții intrinseci pentru a oferi informații despre variabilele de mediu și despre comanda prin
care a fost executat programul.

16.6.1 Variabile de mediu

Majoritatea sistemelor de operare au un concept de variabilă de mediu, asociind nume cu valori. Accesul la
acestea este asigurat de o subrutină intrinsecă.

apelați get_environment_variable (nume [, valoare ] [, lungime ] [, stare] [, trim_name]) unde argumentele sunt
definite după cum urmează.

name are intentia si este un sir de caractere implicit scalar care contine numele variabilei de mediu
care trebuie recuperata. Spațiile necompletate nu sunt semnificative decât dacă trim_name este
prezent și fals. Cazul poate fi sau nu semnificativ.

value are intent out și este o variabilă scalară caracter implicit; primește valoarea variabilei de mediu
(trunchiată sau completată cu spații libere dacă argumentul valorii este mai scurt sau mai lung
decât valoarea variabilei de mediu). Dacă nu există o astfel de variabilă, există o astfel de
variabilă, dar nu are valoare sau procesorul nu acceptă variabile de mediu, acest argument
este setat la spații libere.

length are intent out și este o variabilă scalară întreagă implicită; dacă este specificat
variabila de mediu există și are o valoare, argumentul lungime este setat la lungimea acelei
valori; altfel este setat la zero.

status are intent out și este un întreg scalar implicit; primește valoarea 1 dacă variabila de mediu
nu există, 2 dacă procesorul nu suportă variabile de mediu, un număr mai mare de 2 dacă
apare o eroare, 1 dacă argumentul valorii este prezent, dar prea scurt și zero în caz contrar
(indicând că nu a apărut nicio eroare sau condiție de avertizare).

trim_name are intent in si este un scalar de tip logic; dacă acest lucru este fals, spațiile de sfârșit
din nume vor fi considerate semnificative dacă procesorul permite ca numele variabilelor
de mediu să conțină spații de sfârșit.
Machine Translated by Google

308 Fortran modern explicat

16.6.2 Informații despre invocarea programului

Sunt furnizate două metode diferite de preluare a informațiilor despre comandă, reflectând cele două abordări
de uz comun.
Metoda asemănătoare Unix este furnizată de două proceduri: o funcție care returnează numărul
de argumente de comandă și o subrutină care returnează un argument individual. Acestea sunt:

command_argument_count () returnează, ca număr întreg scalar implicit, numărul de argumente ale comenzii.
Dacă rezultatul este zero, fie nu au existat argumente, fie procesorul nu acceptă facilitatea. Dacă
numele comenzii este disponibil ca argument, acesta nu este inclus în acest număr.

apelează get_command_argument (număr [, valoare] [, lungime] [, stare] )


unde argumentele sunt definite după cum urmează.

număr are intenția și este un întreg scalar implicit care indică numărul argumentului de returnat.
Dacă numele comenzii este disponibil ca argument, acesta este numărul zero.

value are intent out și este o variabilă scalară caracter implicit; primește valoarea argumentului
indicat (trunchiat sau completat cu spații libere dacă variabila caracter este mai scurtă sau mai
lungă decât argumentul comenzii). length are intent out și este o variabilă scalară întreagă

implicită; primește lungimea


a argumentului indicat.

status are intent out și este o variabilă integrală scalară implicită; primește un pozitiv
valoare dacă acel argument nu poate fi preluat, -1 pentru a indica faptul că variabila valoare a
fost mai scurtă decât argumentul comenzii și zero în caz contrar.

Cealaltă paradigmă pentru procesarea comenzilor oferă o linie de comandă simplă, nu împărțită în
argumente. Acesta este preluat de subrutina intrinsecă

apelați get_command ( [ comandă ] [, lungime ] [, stare ] ) unde

comanda are intent out și este o variabilă scalară caracter implicit; primește valoarea liniei de comandă
(trunchiată sau completată cu spații libere dacă variabila este mai scurtă sau mai lungă decât
linia de comandă reală). length are intent out și este o variabilă scalară întreagă implicită;

primește lungimea liniei de comandă efectivă sau zero dacă lungimea nu poate fi determinată.

status are intent out și este o variabilă integrală scalară implicită; primește o valoare pozitivă dacă linia
de comandă nu poate fi preluată, 1 dacă comanda a fost prezentă, dar variabila a fost mai
scurtă decât lungimea liniei de comandă actuală și zero în caz contrar.

16.7 Sprijin pentru internaționalizare

Capacitățile de internaționalizare au fost îmbunătățite prin cerințe suplimentare privind setul de caractere de
bază al procesorului, controlul simbolului zecimal pentru intrare/ieșire în virgulă mobilă și
Machine Translated by Google

Îmbunătățiri diverse 309

multe îmbunătățiri ale suportului pentru alte seturi de caractere, în special pentru setul de caractere
universal ISO/IEC 10646 (cunoscut și ca Unicode).

16.7.1 Seturi de caractere

Setul de caractere Fortran include acum toate literele mici și multe caractere speciale suplimentare din
setul de caractere ASCII. Caracterele speciale suplimentare sunt
'
~^ \ { } [ ] | # @

Parantezele pătrate pot fi folosite pentru a delimita constructorii de matrice (vezi Secțiunea 15.9); celelalte
pot apărea numai în comentarii, caractere literale și descriptori de editare a șirurilor de caractere.
Funcția intrinsecă achar are acum un argument opțional de tip. Acest argument specifică tipul
rezultatului. De exemplu, dacă procesorul avea un tip de caracter suplimentar 37 pentru EBCDIC,
achar(iachar('h'),37) ar returna caracterul 'h' minuscul EBCDIC.
În mod similar, funcția intrinsecă iachar acceptă acum un caracter de orice fel, returnând codul ASCII
pentru acel caracter dacă se află în setul de caractere ASCII și, în caz contrar, o valoare dependentă de
procesor.
Noua funcție intrinsecă selected_char_kind poate fi utilizată pentru a selecta un anumit set de caractere.

selected_char_kind (nume) returnează valoarea tip pentru setul de caractere al cărui nume este dat de
numele șirului de caractere sau 1 dacă nu este acceptat (sau dacă numele nu este recunoscut).
În special, dacă numele este

DEFAULT, rezultatul este tipul de caracter implicit (egal cu kind('A')); ASCII, rezultatul este tipul
de caracter ASCII; ISO_10646, rezultatul este tipul de caractere ISO/IEC 10646 UCS-4.

Alte nume de seturi de caractere depind de procesor. Numele setului de caractere nu ține cont de
majuscule (minusculele sunt tratate ca majuscule) și orice spații libere de la urmă sunt ignorate.

Rețineți că singurul set de caractere care este garantat a fi acceptat este setul de caractere implicit; nu
este necesar un procesor pentru a suporta ASCII sau ISO 10646.

16.7.2 Setul de caractere ASCII

Dacă setul de caractere implicit pentru un procesor nu este ASCII, dar ASCII este acceptat pe acel
procesor, atribuirea intrinsecă este definită între ele pentru a converti caracterele în mod corespunzător.
De exemplu, pe o mașină EBCDIC, în

întreg, parametru :: ascii = caracterul selectat_char_kind('ASCII').


:: ce
caracter(ascii) ce = :: ca
ascii_'X'
ca = 'X'

prima instrucțiune de atribuire va converti X majuscul ASCII într-un X majuscul EBCDIC, iar a doua
instrucțiune de atribuire va face invers.
Machine Translated by Google

310 Fortran modern explicat

16.7.3 Setul de caractere ISO 10646

ISO/IEC 10646 UCS-4 este un set de caractere de 4 octeți conceput pentru a putea reprezenta fiecare
caracter în fiecare limbă din lume, inclusiv toate caracterele speciale utilizate în alte seturi de caractere
codificate. Este un superset strict de ASCII pe 7 biți; adică primele 128 de caractere ale sale sunt
aceleași cu cele ale ASCII.
Este permisă atribuirea caracterelor implicite sau a caracterelor ASCII la ISO 10646, iar caracterele sunt
convertite corespunzător. De asemenea, este permisă atribuirea caracterelor ISO 10646 la caractere
implicite sau ASCII; totuși, dacă orice caracter ISO 10646 nu este reprezentabil în setul de caractere
destinație, rezultatul este dependent de procesor (informația se va pierde).
De exemplu, în

întreg, parametru :: ascii = selected_char_kind('ASCII') întreg, parametru :: iso10646


= selected_char_kind('ISO_10646') character(ascii) :: x = ascii_'X' character(iso10646) :: y

y=x

variabila caracter ISO 10646 y va fi setată la valoarea corectă pentru litera X majusculă.
Variabilele de caractere ISO 10646 pot fi utilizate ca fișiere interne; valorile numerice, logice, implicite,
ASCII și ISO 10646 pot fi citite sau scrise într-o astfel de variabilă. De exemplu,

subrutină japanese_date_stamp(șir) întreg, parametru ::


ucs4 = selected_char_kind('ISO_10646') character(*, ucs4), intent(out) :: șir întreg :: val(8)
call date_and_time(values=val) write (string, 10) val(1), '', val(2), '', val(3), '

'

10 format(i0,a,i0,a,i0,a) end
subroutine stampila_data_japoneza

Rețineți că, deși citirea dintr-un fișier intern ISO 10646 într-un caracter implicit sau într-o variabilă caracter
ASCII este posibilă, este permisă numai atunci când datele citite sunt reprezentabile în caractere implicite
sau caracter ASCII.

16.7.4 Fișiere UTF-8

Standardul ISO 10646 specifică o codificare standard a caracterelor UCS-4 într-un flux de octeți, numit UTF-8.
Fișierele formatate în format UTF-8 sunt acceptate în Fortran 2003 de specificatorul de codificare= din
declarația deschisă. De exemplu,

deschis (20, name='output.file', action='write', encoding='utf-8')

Specificatorul encoding= din instrucțiunea inquire returnează codificarea unui fișier, care va fi UTF-8 dacă
fișierul este conectat pentru intrare/ieșire UTF-8 sau procesorul poate detecta formatul într-un fel,
NECUNOSCUT dacă procesorul nu poate detecta formatul sau o valoare dependentă de procesor dacă se
știe că fișierul este într-un alt format (de exemplu, UTF-16LE).
Machine Translated by Google

Îmbunătățiri diverse 311

În cea mai mare parte, fișierele UTF-8 pot fi tratate ca fișiere formatate obișnuite. La ieșire, toate
datele sunt convertite efectiv în caractere ISO 10646 pentru codificarea UTF-8.
La intrare, dacă datele sunt citite într-o variabilă de caractere ASCII, fiecare caracter de intrare trebuie
să fie în intervalul 0-127 (subsetul ASCII al ISO 10646); dacă datele sunt citite într-o variabilă caracter
implicită, fiecare caracter de intrare trebuie să fie reprezentabil în setul de caractere implicit.
Aceste condiții vor fi îndeplinite dacă datele au fost scrise prin formatare numerică sau logică, sau prin
formatare a caracterelor dintr-o valoare ASCII sau caracter implicită; altfel, ar fi mai sigur să citiți datele
într-o variabilă de caractere ISO 10646 pentru procesare.
Figura 16.5 prezintă rutinele I/O pentru o aplicație de procesare a datelor care utilizează aceste facilități.

Figura 16.5
subrutine write_id(unitate, nume, id)
caracter(kind=ucs4, len=*), intent(in) :: nume întreg, intent(in) :: id,
unitate scrie (unitate, '(1x,a,i6, 2a)') „Numărul clientului”, id,nume
„este „,

sfârșitul subrutinei write_id


:
subrutine read_id(unitate, nume, id)
caracter(kind=ucs4, len=*), intent(out) :: nume întreg, intent(in) ::
unitate întreg, intent(out) caracter(kind=ucs4, len=20) :: șir întreg
stringlen
::
:: id client
citește (unitate, '(1x,a16)', advance='nu') șir if (șir/=ucs4_'Număr
') stop 'Format rău' do stringlen=1, len(șir)

citește (unitate, '(3x,a)', advance='nu') string(stringlen:stringlen) if


(string(stringlen:stringlen)==ucs4_' ') exit
sfâr itul face

read (șir(1:stringlen), *) id read (unitate, '(3x,a)')


nume sfârșit subrutine read_id

16.7.5 Virgulă zecimală pentru intrare/ieșire

Multe țări folosesc virgulă zecimală în loc de virgulă zecimală. Suport pentru acest lucru este oferit de
specificatorul decimal= input/output și de descriptorii de editare dc și dp. Acestea afectează modul de
editare zecimală pentru unitate. În timp ce modul de editare zecimal este punctul zecimal, punctele
zecimale sunt utilizate în intrare/ieșire la fel ca în Fortran 95.
În timp ce modul este virgulă zecimală, virgulele sunt folosite în locul punctelor zecimale atât pentru
intrare, cât și pentru ieșire. De exemplu,

x = 22./7
Machine Translated by Google

312 Fortran modern explicat

tipăriți „(1x,f6.2)”, x

ar produce rezultatul

3,14

în modul virgulă zecimală.

Clauza decimal= poate apărea pe declarațiile deschise, citite și scrise și are forma

zecimal=caractere-scalar-expr

unde scalar-character-expr evaluează fie la punct, fie la virgulă. Pe instrucțiunea deschisă specifică modul
de editare zecimal implicit pentru unitate. Dacă nu există o clauză zecimal= în declarația deschisă, modul
pentru unitate este implicit la virgulă zecimală. Valoarea implicită pentru fișierele interne este, de
asemenea, punctul zecimal. Pentru instrucțiunile de citire și scriere, clauza decimal= specifică modul
implicit doar pentru durata acelei instrucțiuni de intrare/ieșire.
Descriptorii de editare dc și dp schimbă modul de editare zecimal la virgulă zecimală și, respectiv,
punct zecimal. Acestea intră în vigoare atunci când sunt întâlnite în timpul procesării formatului și
continuă în vigoare până când este întâlnit un alt descriptor de editare dc sau dp sau până la sfârșitul
instrucțiunii de intrare/ieșire curente. De exemplu, scrieți (*,10) x, x, x 10 format(1x,'Default ',f5.2,', English
',dp,f5.2,'Français',dc,f5.2) ar produce valoarea lui x mai întâi cu modul implicit, apoi cu o virgulă
zecimală pentru engleză și o virgulă zecimală pentru franceză.

Dacă modul de editare zecimal este virgulă zecimală în timpul introducerii/ieșirii listei direcționate sau a listei de nume, a
punct și virgulă acționează ca un separator de valori în loc de virgulă.

16.8 Lungimea numelor și declarațiilor

Lungimea maximă pentru nume (Secțiunea 2.7) și jetoane de operator (Secțiunea 3.8) a fost mărită la 63
de caractere.
Declarațiile erau anterior limitate la 40 de rânduri (20 de rânduri în formă fixă, vezi Anexa C.1.1);
lungimea maximă în oricare dintre forme este acum de 256 de linii. Adică sunt permise până la 255 de
linii de continuare.
Unul dintre motivele pentru a permite instrucțiuni mai lungi este gestionarea codului sursă care este
generat automat.

16.9 Constante binare, octale și hexazecimale

Constantele binare, octale și hexazecimale („boz”), permise anterior doar în instrucțiunile de date, sunt
acum permise și ca argument principal într-un apel al funcțiilor intrinseci cmplx, dble, int și real (nu pentru
un argument opțional care precizează felul).
Pentru int, constanta „boz” este tratată ca și cum ar fi o constantă întreagă de tipul cu cel mai mare
interval acceptat de procesor. Prin urmare,
Machine Translated by Google

Îmbunătățiri diverse 313

întreg :: i, j date i/
z'3f7'/ j = int(z'3f7')

dă atât i, cât și j aceeași valoare (în zecimală, 1015).


Pentru dble și real, este tratat ca având valoarea pe care o variabilă de același tip și tip de parametru
ca rezultatul ar avea-o dacă reprezentarea sa internă ar fi modelul de biți specificat. Această interpretare
a modelului de biți este dependentă de procesor. Pentru cmplx cu un rezultat de tip valoare tip, un
argument „boz” fie pentru x, fie pentru y oferă aceeași valoare ca real(x,kind) sau real(y,kind), astfel încât
să specifice reprezentarea internă a unei componente a rezultat.

Avantajul de a permite constantele „boz” în expresii doar ca argumente pentru aceste intrinseci este
că nu există ambiguitate în modul în care sunt interpretate. Există extensii de furnizor care le permit
direct în expresii, dar modurile în care valorile sunt interpretate diferă.

16.10 Alte modificări ale procedurilor intrinseci

Funcțiile intrinseci max, maxval, min și minval pot fi acum utilizate pe valori de tip caracter.

Dacă un set de elemente de matrice examinate de maxloc sau minloc este gol, locația elementului său
maxim sau minim este acum considerată a avea toate indicele zero (era dependentă de procesor în
Fortran 95).
Următoarele funcții intrinseci au acum un argument de tip opțional la sfârșitul listei de argumente:
count, iachar, ichar, index, lbound, len, len_trim, maxloc, minloc, scan, shape, size, ubound și verify. Acest
argument specifică tipul de rezultat întreg pe care îl returnează funcția, în cazul în care un întreg implicit
nu este suficient de mare pentru a conține valoarea corectă (ceea ce poate fi cazul pe mașinile pe 64 de
biți).
De exemplu, în cod

real, alocabil :: a(:,:,:,:) alocă


(a(64,1024,1024,1024))
:
print *, size(a, kind=selected_int_kind(12))

tabloul a are un total de 236 de elemente; pe majoritatea mașinilor, acesta este mai mare decât uriaș (0), deci
este necesar un argument bun pentru a obține răspunsul corect de la referința la dimensiunea intrinsecă a
funcției.
Argumentele count, count_rate și count_max ale subrutinei intrinseci system_clock pot fi acum de
orice fel de număr întreg; aceasta este pentru a găzdui sisteme cu o frecvență de ceas care este prea
mare pentru a fi reprezentată într-un număr întreg implicit. În plus, argumentul count_rate poate fi acum
de tip real, precum și întreg; aceasta este pentru a găzdui sistemele al căror ceas nu bifează un număr
întreg de ori în fiecare secundă.
Argumentelor caracterului date_and_time li se atribuie acum rezultatele și nu
trebuie să fie suficient de lungă pentru a păstra valorile.
Machine Translated by Google

314 Fortran modern explicat

Au fost făcute modificări la funcțiile intrinseci atan2, log și sqrt pentru procesoarele care fac distincția
între zero real pozitiv și negativ (pe majoritatea computerelor, acum că aritmetica IEEE este larg
răspândită). Funcția intrinsecă atan2(y, x) returnează acum o aproximare la π dacă x< 0 și y este un
zero negativ, deoarece aceasta este limita ca y 0 de jos (anterior a returnat o aproximare la π). Din
motive similare, funcția intrinsecă log(x) returnează acum o aproximare la π dacă x este de tip complex
cu o parte reală care este mai mică decât zero și o parte imaginară negativă zero; iar funcția intrinsecă
sqrt(x) pentru complexul x returnează acum un rezultat imaginar negativ dacă partea reală a rezultatului
este zero și partea imaginară a lui x este mai mică decât zero.

16.11 Preluare mesaj de eroare

Dezavantajul utilizării clauzei stat= pe o instrucțiune de alocare sau dealocare este că este imposibil ca
programul să furnizeze un raport sensibil al erorii, deoarece codurile de eroare depind de procesor.

Pentru a depăși acest lucru, la aceste două declarații a fost adăugată clauza errmsg=. Aceasta ia o
variabilă implicită șir de caractere scalară, iar dacă apare o condiție de eroare care este gestionată de
stat=, un mesaj explicativ este atribuit variabilei errmsg=.
De exemplu,

caracterul(200) :: mesaj_eroare ! Probabil suficient de lung


:
alocă (x(n), stat=allocate_status, errmsg=error_message) dacă (allocate_status>0)
atunci
print *, „Alocarea lui X a eșuat:”, trim(error_message)
:
sfâr itul dacă

16.12 Constante complexe îmbunătățite

O constantă complexă poate fi acum scrisă cu o constantă numită de tip real sau întreg pentru partea sa
reală, partea imaginară sau ambele. De exemplu,

real, complex de :: zero = 0, unu = 1


parametri, parametru :: i = (zero, unu)

Cu toate acestea, niciun semn nu este permis cu un nume, așa că, deși (0,-1) este o constantă complexă
perfect bună, (zero,-unu) este invalidă.
Deoarece funcția intrinsecă cmplx este acum permisă să apară într-o expresie constantă și
oferă toată această funcționalitate și mai mult, această caracteristică este foarte puțin utilizată.

16.13 Extensii bloc de interfață

Declarația de procedură a modulului (vezi Secțiunea 5.18) a fost schimbată în Fortran 2003. Cuvântul
cheie modul este acum opțional; de exemplu,
Machine Translated by Google

Îmbunătățiri diverse 315

Interfață gamma
procedura :: sgamma, dgamma end
interface

Dacă cuvântul cheie modul este omis, procedurile numite nu trebuie să fie proceduri de modul, ci pot
fi, de asemenea, proceduri externe, proceduri fictive sau pointeri de procedură. Fiecare procedură
numită trebuie să aibă deja o interfață explicită pentru a fi utilizată în acest mod.
Acest lucru poate fi folosit pentru a evita limitarea Fortran 95 pe care o procedură externă nu ar putea-o
apar în mai multe blocuri de interfață. De exemplu, în

tip șir de biți


:
tipul de capăt
:
operator de interfață(*) tip
elementar( șir de biți) funcție bitwise_and(a, b) import :: tip șir de biți (șir
de biți), intenție (în) :: a, b funcție de sfârșit biți_și interfață de sfârșit

operator de interfață (. și.)


procedura :: bitwise_and end
interface

aceasta permite utilizarea atât a * cât și a .și. operatori pentru „bit și” pe valori de tip bitstring.

Un nume generic este permis în Fortran 2003 să fie același cu un nume de tip. Numele generic are
prioritate asupra numelui tipului; un constructor de structură pentru tip este interpretat ca atare numai
dacă nu poate fi interpretat ca referință la procedura generică.

16.14 Entități publice de tip privat

Entitățile de tip privat nu mai trebuie să fie ele însele private; acest lucru se aplică în mod egal
procedurilor cu argumente care au tip privat. Aceasta înseamnă că un writer de modul poate oferi un
acces foarte limitat la valori sau variabile fără a oferi utilizatorului puterea de a crea noi variabile de acest
tip.
De exemplu, biblioteca LAPACK utilizată pe scară largă necesită argumente de caractere, cum ar fi uplo,
o variabilă caracter căreia trebuie să i se atribuie valoarea „L” sau „U” în funcție de faptul că matricea este
triunghiulară superioară sau inferioară. Valoarea este verificată în timpul execuției și apare o eroare
returnată dacă este invalidă. Aceasta ar putea fi înlocuită cu valori inferioare și superioare de tip privat.
Acest lucru ar fi mai clar și verificarea ar fi făcută în timpul compilării.
Machine Translated by Google

316 Fortran modern explicat

Exerciții

1. Scrieți o funcție care formatează o valoare de intrare reală, de un fel care are o precizie zecimală de 15 sau
mai mult, într-o formă adecvată pentru afișare ca valoare monetară în euro. Dacă mărimea valorii este
astfel încât câmpul „cent” depășește precizia zecimală, ar trebui returnat un șir format din toate asteriscurile.

2. Scrieți un program care să afișeze suma tuturor numerelor de pe linia de comandă.


Machine Translated by Google

17. Îmbunătățiri de intrare/ieșire

17.1 Introducere

În acest capitol, explicăm îmbunătățirile aduse procesării de intrare/ieșire care au fost făcute în Fortran
2003. Intrarea/ieșirea de tip derivat non-implicit (Secțiunea 17.2) permite programatorului să ofere
formatare special adaptată unui tip și să transfere structuri cu componentele pointerului. Intrarea/ieșirea
asincronă (Secțiunile 17.3 și 17.4) a fost disponibilă ca extensii de compilator de mulți ani și este acum
standardizată. De la apariția aritmeticii IEEE multe compilatoare au oferit facilități pentru introducerea/
ieșirea valorilor excepționale; aceasta este acum standardizată (secțiunea 17.5). Accesul la flux (Secțiunea
17.6) permite o mare flexibilitate atât pentru intrare/ieșire formatată, cât și neformatată. Secțiunile rămase
detaliază diverse îmbunătățiri simple.

17.2 Intrare/ieșire de tip derivat non-implicit

Se poate aranja ca, atunci când un obiect de tip derivat este întâlnit într-o listă de intrare/ieșire, să fie apelată
o subrutină Fortran. Acesta fie citește unele date din fișier și construiește o valoare a tipului derivat, fie
acceptă o valoare a tipului derivat și scrie unele date în fișier.
Pentru intrare/ieșire formatate, descriptorul de editare dt specifică un șir de caractere și un număr întreg
matrice pentru a controla acțiunea. Un exemplu este

dt „listă-legată” (10, -4, 2)

Șirul de caractere poate fi omis; acest caz este tratat ca și cum ar fi fost dat un șir de lungime zero. Lista
de numere întregi din paranteză poate fi omisă, caz în care este trecută o matrice de lungime zero.

Astfel de subrutine pot fi legate de tipul ca legături generice (vezi Secțiunea 14.6.2) ale formularelor

generic :: citit(formatat) => r1, r2 generic ::


citire(neformatat) => r3, r4, r5 generic :: scrie(formatat) => w1
generic :: scrie(neformatat) => w2, w3

ceea ce le face accesibile oriunde este accesibil un obiect de acest tip. O alternativă este un bloc de interfață,
cum ar fi
Machine Translated by Google

318 Fortran modern explicat

interfață citită (formatată)


procedura modulului r1, interfața
finală r2

Forma unei astfel de subrutine depinde dacă este pentru I/O formatat sau neformatat:

subrutină formatted_io(dtv,unit,iotype,v_list,iostat,iomsg) subrutine unformatted_io(dtv,unit, iostat,iomsg)

dtv este un scalar de tip derivat. Poate fi polimorf (astfel încât să poată fi numit pentru tipul sau orice extensie
a acestuia). Trebuie asumați toți parametrii de tip lungime. Pentru ieșire, este intenționat și deține
valoarea care urmează să fie scrisă. Pentru intrare, este de intenție inout și este modificată în
conformitate cu valorile citite.

unit este un scalar de intenție în și tipul implicit întreg. Valoarea sa este unitatea pe care
intrare/ieșire are loc sau negativ dacă este pe un fișier intern.

iotip este un scalar de intenție și tip caracter (*). Valoarea sa este „LISTDIRECTED”, „NAMELIST” sau „DT”//șir,
unde șirul este șirul de caractere din descriptorul de editare dt.

v_list este o matrice de intenție cu formă presupusă de rang unu în și tipul întreg implicit. Valoarea acestuia
provine din lista dintre paranteze a descriptorului de editare.

iostat este un scalar de intent out și tipul implicit întreg. Dacă apare o condiție de eroare, trebuie să i se
atribuie o valoare pozitivă. În caz contrar, dacă apare o condiție de sfârșit de fișier sau de sfârșit de
înregistrare, trebuie să i se dea, respectiv, valoarea iostat_end sau iostat_eor a modulului intrinsec
iso_fortran_env (vezi Secțiunea 16.5). În caz contrar, trebuie să i se atribuie valoarea zero.

iomsg este un scalar de intentie inout si tip caracter (*). Dacă iostat primește o valoare diferită de zero, iomsg
trebuie setat la un mesaj explicativ. În caz contrar, nu trebuie modificată.

Numele subrutinei și argumentele sale nu sunt semnificative atunci când sunt invocate ca parte a procesării
de intrare/ieșire.
În cadrul subrutinei, intrarea/ieșirea către fișierele externe este limitată la unitatea specificată și în direcția
specificată. O astfel de declarație de transfer de date se numește declarație de transfer de date copil, iar
declarația originală se numește părinte. Nicio poziționare a fișierului nu are loc înainte sau după executarea
unei instrucțiuni de transfer de date copil (orice avans= specificator este ignorat). I/O într-un fișier intern este
permisă. O listă I/O poate include un descriptor de editare dt pentru o componentă a argumentului dtv, cu
sensul evident. Executarea oricăreia dintre instrucțiunile open, close, backspace, endfile și rewind nu este
permisă. De asemenea, procedura nu trebuie să modifice niciun aspect al instrucțiunii I/O părinte, decât prin
argumentul dtv.
Poziția fișierului la intrare este tratată ca o limită a filei din stânga și nu există nicio încetare a înregistrării
la returnare. Prin urmare, poziționarea cu rec= (pentru un fișier cu acces direct, Secțiunea 9.14) sau pos=
(pentru accesul în flux, Secțiunea 17.6) nu este permisă într-o declarație de transfer de date copil.
Această caracteristică nu este disponibilă în combinație cu intrare/ieșire asincronă (Secțiunea 17.3).
Urmează un exemplu simplu de ieșire formatată de tip derivat. Președintele variabil de tip derivat are
două componente. Tipul și o procedură formatată de scriere asociată sunt definite într-un modul numit
person_module și pot fi invocate așa cum se arată în Figura 17.1.
Machine Translated by Google

Îmbunătățiri de intrare/ieșire 319

Figura 17.1 Un program cu un descriptor de editare dt.


utilizarea
programului
person_module ID întreg,
tipul membrilor (persoană) :: chairman
:
scrieți (6, fmt="(i2, dt(15,6), i5)" ) id, președinte, membri ! Aceasta scrie o înregistrare
cu patru câmpuri, cu lungimi 2, 15, 6, 5, ! respectiv finalul programului

Figura 17.2 Un modul care conține o subrutină de scriere (formatată).


modul person_module type ::
person character
(len=20) :: nume întreg :: vârstă

con ine
procedura :: pwf
generic :: write(formatted) => pwf end type person

con ine
subrutină pwf (dtv, unit, iotype, vlist, iostat, iomsg)
! Argumente
clasa(persoana), intentia(in) intreg, :: dtv
intentia(in) caracter (len=*), :: unitate ::
intentia(in) intreg, intentia(in) ! vlist(1) și iotip :: vlist(:)
(2) vor fi folosite ca lățimi de câmp! dintre
cele două componente ale variabilei de tip derivat. întreg, intent(out) ::
caracter iostat (len=*), intent(inout) :: iomsg ! Variabila locala

caracter (len=9) :: pfmt ! Configurați


formatul de utilizat pentru scrierea de ieșire (pfmt,
'(a,i2,a,i2,a)') &
„(a”, vlist(1), „,i”, vlist(2), „)”
! Acum instrucțiunea de ieșire copil scrie
(unitate, fmt=pfmt, iostat=iostat) dtv%name, dtv%age
end subrutine pwf end
module person_module
Machine Translated by Google

320 Fortran modern explicat

Modulul care implementează acest lucru este prezentat în Figura 17.2. Din descriptorul de editare
dt(15,6), acesta construiește formatul (a15,i 6) în variabila caracter local pfmt și îl aplică. De asemenea, ar
fi posibil să verificați dacă iotipul are într-adevăr valoarea „DT” și să setați iostat și iomsg în consecință.

În exemplul următor, Figura 17.3, ilustrăm ieșirea unei structuri cu o componentă pointer și arătăm o
instrucțiune de transfer de date copil în sine invocând intrare/ieșire de tip derivat.
Aici, arătăm cazul în care aceeași subrutină (recursivă) este invocată în ambele cazuri.
Variabilele nodului de tip derivat formează un lanț, cu o singură valoare la fiecare nod și se termină cu
un pointer nul. Subrutina pwf este folosită pentru a scrie valorile în listă, câte una pe linie.

Figura 17.3 Un modul care conține o subrutină recursivă de scriere (formatată).


modul list_module tip nod
tip întreg (nod),
pointer :: :: valoare = 0
next_node => null ( )
con ine
procedura :: pwf
generic :: write(formatted) => pwf end type nodul
contine

subrutină recursivă pwf (dtv, unit, iotype, vlist, iostat, iomsg)


! Scrieți lanțul de valori, fiecare pe o linie separată în format I9. clasă(nod), intenție(în) întreg,
:: dtv întreg, intent(în) caracter (len=*),
intenție(în) caracter (len=*), intent(în) întreg, intent(în)
:: unit dtv%value
intent(in) : : iomsg scrie (unitate, '(i9,/)', iostat = iostat) :: if (iostat/=0) return if
(asociat(dtv%next_node)) & iotype ::
vlist(:) :: iostat

scrie (unitate, '(dt)', iostat=iostat) dtv%next_node end subroutine pwf


end module list_module

17.3 Intrare/ieșire asincronă

Intrarea/ieșirea poate fi asincronă, adică alte instrucțiuni se pot executa în timp ce o instrucțiune input/
output este în execuție. Este permis numai pentru fișierele externe deschise cu asynchronous='yes' în
instrucțiunea deschisă și este indicată de un specificator asynchronous='yes' în instrucțiunea de citire
sau scriere. În mod implicit, execuția este sincronă chiar și pentru un fișier deschis cu asynchronous='yes',
dar poate fi specificat cu asynchronous='no'.
Machine Translated by Google

Îmbunătățiri de intrare/ieșire 321

Execuția unei instrucțiuni de intrare/ieșire asincronă inițiază o operațiune de intrare/ieșire „în așteptare”, iar
execuția altor instrucțiuni continuă până când ajunge la o instrucțiune care implică o operație de așteptare
pentru fișier. Aceasta poate fi o declarație de așteptare explicită, cum ar fi

asteptati (10)

sau o întrebare, o închidere sau o declarație de poziționare a fișierului pentru fișier. Compilatorului i se permite
să trateze fiecare instrucțiune de intrare/ieșire asincronă ca o instrucțiune de intrare/ieșire obișnuită (acesta,
la urma urmei, este doar cazul limitativ al intrării/ieșirii rapide). Desigur, compilatorul trebuie să recunoască
toată sintaxa nouă.
Iată un exemplu simplu

real :: a(100000), b(100000) open (10,


file='mydata', asynchronous='yes') read (10, '(10f8.3)',
asynchronous='yes') a ! Calcul care implică matricea b wait (10)
:

: ! Calcul care implică tabloul a

Alte instrucțiuni de intrare/ieșire asincrone pot fi executate pentru fișier înainte de a ajunge la instrucțiunea
wait. Instrucțiunile de intrare/ieșire pentru fiecare fișier sunt efectuate în aceeași ordine în care ar fi fost dacă
ar fi fost sincrone.
O execuție a unei instrucțiuni de intrare/ieșire asincronă poate fi identificată printr-o variabilă întreg scalară
într-un specificator id=. Trebuie să fie de tip implicit sau mai lung. Executarea cu succes a instrucțiunii face ca
variabilei să i se dea o valoare dependentă de procesor care poate fi transmisă unei instrucțiuni ulterioare wait
sau inquire ca variabilă întreg scalară într-un specificator id=.

O instrucțiune wait poate avea specificatorii end=, eor=, err= și iostat=. Acestea au aceleași semnificații ca
și pentru o declarație de transfer de date și se referă la situații care apar în timp ce operația de intrare/ieșire
este în așteptare. Dacă există și un specificator id=, numai operațiunea în așteptare identificată este terminată
și ceilalți specificatori se referă la aceasta; în caz contrar, toate operațiunile în așteptare pentru fișier sunt
încheiate pe rând.
O instrucțiune de întrebare este permisă să aibă un specificator pending= pentru o variabilă logică implicită
scalară. Dacă este prezent un specificator id=, variabilei i se dă valoarea true dacă operația de intrare/ieșire
este încă în așteptare și falsă în caz contrar. Dacă nu este prezent nici un specificator id=, variabilei i se dă
valoarea true dacă orice operațiuni de intrare/ieșire pentru unitate sunt încă în așteptare și false în caz contrar.
În cazul „fals”, se efectuează operațiuni de așteptare pentru fișier sau fișiere. Operațiile de așteptare nu sunt
efectuate în cazul „adevărat”, chiar dacă unele dintre operațiunile de intrare/ieșire sunt finalizate.

Este permisă execuția unei instrucțiuni wait care specifică o unitate care nu există, nu are niciun fișier
conectat la ea sau nu a fost deschisă pentru intrare/ieșire asincronă, cu condiția ca instrucțiunea wait să nu
aibă un specificator id=; o astfel de declarație de așteptare nu are efect.
O instrucțiune de poziționare a fișierului (backspace, endfile, rewind) efectuează operațiuni de așteptare pentru
toate operațiunile de intrare/ieșire în așteptare pentru fișier.
Intrarea/ieșirea asincronă nu este permisă împreună cu intrarea/ieșirea de tip derivat definit de utilizator
(secțiunea anterioară), deoarece se anticipează că numărul de caractere scrise efectiv este probabil să depindă
de valorile variabilelor.
Machine Translated by Google

322 Fortran modern explicat

Se spune că o variabilă dintr-o unitate de definire a domeniului este un factor care afectează o operație
de intrare/ieșire în așteptare dacă orice parte a acesteia este asociată cu orice parte a unui articol din lista
de intrare/ieșire, lista de nume sau specificatorul de dimensiune=. În timp ce o operație de intrare/ieșire
este în așteptare, nu este permisă redefinirea, modificarea sau modificarea stării de asociere a pointerului
unui agent de afectare. În timp ce o operațiune de intrare este în așteptare, nu este permisă referirea sau
asociat unui factor de influență cu un argument fals cu atributul value (Secțiunea 12.6).

17.4 Atributul asincron


Atributul asincron pentru o variabilă a fost introdus pentru a avertiza compilatorul că optimizările care
implică mișcarea codului între instrucțiunile wait (sau alte instrucțiuni care provoacă operații de așteptare)
pot duce la rezultate incorecte. Dacă o variabilă apare într-o instrucțiune executabilă sau o expresie de
specificație într-o unitate de definire a domeniului și orice instrucțiune a unității de acoperire este
executată în timp ce variabila este un afectator, aceasta trebuie să aibă atributul asincron în unitatea de
acoperire.
O variabilă primește automat acest atribut dacă ea sau un subobiect al acestuia este un element din lista
de intrare/ieșire, lista de nume sau specificatorul de dimensiune= al unei instrucțiuni de intrare/ieșire
asincrone. O variabilă numită poate fi declarată cu acest atribut:

întreg, asincron :: int_array(10)

sau dat de instrucțiunea asincronă

asincron :: int_array, altul

Această declarație poate fi folosită pentru a da atributul unei variabile care este accesată prin utilizare sau
asociere gazdă.
La fel ca atributul volatil (Secțiunea 16.3), dacă un obiect are atributul asincron poate varia între
unitățile de acoperire. Dacă o variabilă este accesată prin utilizare sau asociere gazdă, aceasta poate
câștiga atributul, dar nu-l pierde niciodată. Pentru argumentele fictive și reale corespunzătoare, nu există
nicio cerință de acord cu privire la atributul asincron. Acest lucru oferă o flexibilitate utilă, dar trebuie
utilizat cu grijă. Dacă programatorul știe că toate acțiunile asincrone vor fi în cadrul procedurii, nu este
nevoie ca argumentul real să aibă atributul asincron. În mod similar, dacă programatorul știe că nicio
operație nu va fi vreodată în așteptare atunci când procedura este apelată, nu este nevoie ca argumentul
dummy să aibă atributul asincron.

Toate subobiectele unei variabile cu atributul asincron au atributul.


Există restricții care evită orice copiere a unui argument real atunci când argumentul fals corespunzător
are atributul asincron: argumentul real nu trebuie să fie o secțiune de matrice cu un indice vectorial;
dacă argumentul real este o secțiune de matrice sau o matrice de formă presupusă, argumentul inactiv
trebuie să fie o matrice de formă presupusă; iar dacă argumentul real este o matrice de pointeri,
argumentul inactiv trebuie să fie o formă presupusă sau o matrice de pointeri.
Machine Translated by Google

Îmbunătățiri de intrare/ieșire 323

17.5 Intrarea și ieșirea valorilor excepționale IEEE

Este specificată intrarea și ieșirea infinitelor și NaN-urilor IEEE, făcute anterior într-o varietate de moduri
ca extensii ale Fortran 95. Toți descriptorii de editare pentru reali tratează aceste valori în același mod și
se ia în considerare numai lățimea câmpului w.
Formele de ieșire, fiecare drept justificat în domeniul său, sunt

i) -Inf sau -Infinit pentru minus infinit;

ii) Inf, +Inf, Infinity sau +Infinity pentru plus infinit; și

iii) NaN, urmat opțional de caractere alfanumerice între paranteze (pentru a menține
informație).

La introducere, literele mari și mici sunt tratate ca echivalente. Formele sunt

i) -Inf sau -Infinit pentru minus infinit;

ii) Inf, +Inf, Infinity sau +Infinity pentru plus infinit; și

iii) NaN, urmat opțional de caractere alfanumerice în paranteze pentru un NaN. Cu


nu există astfel de caractere alfanumerice, este un NaN liniștit.

17.6 Intrare/ieșire acces stream

Accesul în flux este o nouă metodă de accesare a unui fișier extern. Se stabilește prin specificarea
access='stream' pe declarația deschisă și poate fi formatat sau neformatat.
Fișierul este poziționat de „unități de stocare a fișierelor”, în mod normal octeți, începând cu poziția 1.
Poziția curentă poate fi determinată dintr-o variabilă întreagă scalară într-un specificator pos= al unei
instrucțiuni de interogare pentru unitate. Un fișier poate avea capacitatea de a poziționa înainte sau
înapoi, numai înainte sau niciunul. Dacă are capacitatea, o poziție necesară poate fi indicată într-o
instrucțiune de citire sau scriere de către specificatorul pos=, care acceptă o expresie întregă scalară. În
absența unui specificator pos=, poziția fișierului rămâne neschimbată.
Intenția este ca fluxul de intrare/ieșire neformatat să citească sau să scrie doar datele în/din fișier;
adică că nu există informații auxiliare privind lungimea înregistrării (care sunt în mod normal scrise
pentru fișierele neformatate). Acest lucru permite o interoperabilitate ușoară cu fluxurile binare C, dar
facilitatea de a trece peste înregistrări sau nu este disponibilă. Dacă o instrucțiune de ieșire suprascrie o
parte a unui fișier, restul fișierului este neschimbat Iată un exemplu simplu de intrare/ieșire a fluxului
neformatat:

real :: d
întreg :: înainte_d
:
deschis (unitate, ..., access='stream', form='neformatted')
:
întreabă (unitate, pos=înainte de_d)
scrie (unitate) d
:
scrie (unitate, pos=before_d)d+1
Machine Translated by Google

324 Fortran modern explicat

Presupunând că d ocupă 4 octeți, utilizatorul se poate aștepta în mod rezonabil ca prima scriere să scrie exact 4
octeți în fișier. Utilizarea specificatorului pos= asigură că a doua scriere va suprascrie valoarea scrisă anterior a
lui d.
Fișierele stream formatate sunt foarte asemănătoare cu fișierele secvențiale obișnuite (orientate pe
înregistrări); diferența principală este că nu există o lungime maximă prestabilită a înregistrării (recl= specificatorul
în declarațiile deschise sau înquire). Dacă fișierul permite poziționarea relevantă, valoarea unui specificator pos=
trebuie să fie 1 sau o valoare returnată anterior într-o instrucțiune de interogare pentru fișier. În ceea ce privește
un fișier secvențial formatat, o declarație de ieșire părăsește fișierul care se termină cu datele transferate.
O altă diferență față de un fișier secven ial formatat este că este permisă terminarea înregistrării bazată pe
date în stilul fluxurilor de text C. Funcția de interogare intrinsecă new_line(a) returnează caracterul care poate fi
folosit pentru a provoca terminarea înregistrării (acesta este echivalentul caracterului „\n” din limbajul C):

new_line (a) returnează caracterul newline utilizat pentru ieșirea fluxului formatat. Argumentul a trebuie să fie
de tipul caracter. Rezultatul este de tip caracter cu aceeași valoare a parametrului tip tip ca a. În cazul
puțin probabil în care nu există un caracter adecvat pentru noua linie în acel set de caractere, este
returnat un spațiu liber.

Ca exemplu, următorul cod va scrie două linii în fișierul /dev/tty:


deschide (28, fișier='/dev/tty', access='stream', form='formatat') scrie (28, '(a)') 'Hello'//
new_line('x')//'World '

17.7 Intrare/ieșire recursive

O instrucțiune recursivă de intrare/ieșire este una care este executată în timp ce o altă instrucțiune de intrare/
ieșire este în execuție. Am întâlnit acest lucru în legătură cu intrare/ieșire de tip derivat (Secțiunea 17.2); o
instrucțiune de transfer de date copil este recursivă, deoarece se execută întotdeauna în timp ce părintele său
este în execuție. Singura altă situație în care este permisă execuția unei instrucțiuni recursive de intrare/ieșire, și
aceasta este o extensie din Fortran 95, este pentru intrare/ieșire către/din un fișier intern în care instrucțiunea
nu modifică niciun fișier intern, altul decât propriul său .1

17.8 Instrucțiunea de flush

Executarea unei instrucțiuni de ștergere pentru un fișier extern face ca datele scrise în acesta să fie disponibile

pentru alte procese sau determină ca datele plasate în el prin alte mijloace decât Fortran să fie disponibile pentru
o declarație de citire. Sintaxa este la fel ca cea a instrucțiunilor de poziționare a fișierelor.
În combinație cu advance='no' sau stream access (Secțiunea 17.6), acesta permite programului să se asigure
că datele scrise pe o unitate sunt trimise la fișier înainte de a solicita intrarea pe o altă unitate; adică că
„prompturile” apar prompt.

17.9 Virgulă după un descriptor de editare P

Virgula după un descriptor de editare P devine opțională atunci când este urmată de un specificator de repetare.
De exemplu, 1P2E12.4 este permis (cum era în Fortran 66).

1Fortran 2008 permite cazuri suplimentare, vezi Secțiunea 20.7.1.


Machine Translated by Google

Îmbunătățiri de intrare/ieșire 325

17.10 Specificatorul iomsg=

Orice instrucțiune de intrare/ieșire este permisă să aibă un specificator iomsg=. Aceasta identifică o variabilă
scalară de tip caracter implicit în care procesorul plasează un mesaj dacă apare o condiție de eroare, de sfârșit de
fișier sau de sfârșit de înregistrare în timpul execuției instrucțiunii. Dacă nu apare o astfel de condiție, valoarea
variabilei nu este modificată. Rețineți că acest lucru este util numai pentru mesajele referitoare la condițiile de
eroare și este necesar un specificator iostat= sau err= pentru a preveni o eroare care cauzează terminarea imediată.

17.11 Rotunda= specificatorul

Rotunjirea în timpul intrării/ieșirii formatate poate fi controlată de specificatorul round= din instrucțiunea deschisă,
care ia una dintre valorile sus, jos, zero, cel mai apropiat, compatibil sau definit de procesor. Poate fi suprascris de
un specificator round= într-o instrucțiune de citire sau scriere cu una dintre aceste valori. Semnificațiile sunt
evidente, cu excepția diferenței dintre cel mai apropiat și compatibil. Ambele se referă la o valoare reprezentabilă
cea mai apropiată. Dacă două sunt echidistante, ceea ce este luat este dependent de procesor pentru cel mai
apropiat și valoarea departe de zero pentru compatibil.

Modul de rotunjire poate fi, de asemenea, schimbat temporar în cadrul unei instrucțiuni de citire sau scriere la
sus, jos, zero, cel mai apropiat, compatibil sau procesor_defined de descriptorul de editare ru, rd, rz, rn, rc sau,
respectiv, rp.
Există un specificator corespunzător în instrucțiunea de interogare căruia i se atribuie valoarea SUS, JOS, ZERO,
CEL MAI APROPIAT, COMPATIBIL, DEFINIT_PROCESATOR sau NEDEFINIT, după caz.
Procesorul returnează valoarea PROCESSOR_DEFINED numai dacă modul de rotunjire I/O aflat în prezent în
vigoare se comportă diferit față de celelalte moduri de rotunjire.
În Secțiunea 9.12.2, formula pentru n din descriptorul de editare g conține, de două ori, valoarea 0,5.
Această valoare este modificată de unele dintre modurile de rotunjire, devenind 1 pentru sus și pentru zero dacă
valoarea este pozitivă; 0 pentru jos și pentru zero dacă valoarea este negativă; și -0,5 pentru cel mai apropiat dacă
valoarea inferioară este pară.

17.12 Semnul= specificatorul

Specificatorul semn= a fost adăugat la declarația deschisă. Poate lua valoarea suppress, plus sau procesor_defined
și controlează caracterele plus opționale din ieșirea numerică formatată. Poate fi suprascris de un specificator
sign= într-o instrucțiune de scriere cu una dintre aceste valori. Modul poate fi, de asemenea, schimbat temporar
în cadrul unei instrucțiuni de scriere de către descriptorii de editare ss, sp și s, care fac parte din Fortran 95.

Există un specificator corespunzător în declarația de interogare căruia i se atribuie valoarea PLUS, SUPPRESS,
PROCESSOR_DEFINED sau UNDDEFINED, după caz.

17.13 Parametrii de tip tip ai specificatorilor întregi și logici

Specificatorii întregi și logici care returnează o valoare (cum ar fi nextrec=) au fost limitati la tipul implicit în Fortran
95. Orice fel este permis în Fortran 2003.
Machine Translated by Google

326 Fortran modern explicat

17.14 Mai mulți specificatori în declarațiile de citire și scriere

Specificatorii declarației de interogare blank= și pad= sunt acum disponibili și în instrucțiunea de citire, iar delim=
este disponibil în instrucțiunea de scriere.

17.15 Funcții intrinseci pentru testarea stării I/O

Sunt furnizate două noi funcții intrinseci elementare pentru testarea valorii stării I/O returnate prin specificatorul
iostat=. Ambele funcții acceptă un argument de tip întreg și returnează un rezultat logic implicit.

is_iostat_end(i) returnează valoarea true dacă i este o valoare de stare I/O care îi corespunde
o condiție de sfârșit de fișier și falsă în caz contrar.

is_iostat_eor(i) returnează valoarea adevărată dacă i este o valoare de stare I/O care corespunde cu
o condiție de sfârșit de înregistrare și falsă în caz contrar.

17.16 Unele îmbunătățiri ale declarațiilor de întrebare

Am întâlnit deja o serie de specificatori noi pentru declarația de interogare (Secțiunea 10.5): codificare= (Secțiunea
16.7.4), id= și în așteptare= (Secțiunea 17.3), pos= (Secțiunea 17.6), iomsg= (Secțiunea 17.10) , rotund= (Secțiunea
17.11) și semn= (Secțiunea 17.12). Mai mult, un specificator existent, access=, are acum valoarea suplimentară
posibilă pentru acc de STREAM dacă fișierul este conectat pentru accesul la flux.

Următorii specificatori noi, opționali, nu au fost descriși până acum și completează


descrierea îmbunătățirilor de intrare/ieșire.

asincron= asincron, unde asincron este o variabilă caracter căreia i se atribuie valoarea DA dacă fișierul este
conectat și este permisă intrarea/ieșirea asincronă pe unitate; i se atribuie valoarea NO dacă fișierul este
conectat și nu este permisă intrarea/ieșirea asincronă pe unitate. Dacă nu există nicio conexiune, i se
atribuie valoarea UNDDEFINED.

decimal= dec, unde dec este o variabilă de caracter căreia i se atribuie valoarea COMMA sau POINT,
corespunzătoare modului de editare zecimal în vigoare pentru o conexiune pentru intrare/ieșire formatată.
Dacă nu există nicio conexiune sau dacă conexiunea nu este pentru intrare/ieșire formatată, i se atribuie
valoarea NEDEFINITĂ.

dimensiune = dimensiune, unde dimensiunea este o variabilă întreagă căreia i se atribuie dimensiunea fișierului
în unități de stocare a fișierelor. Dacă dimensiunea fișierului nu poate fi determinată, variabilei i se atribuie
valoarea -1. Pentru un fișier care poate fi conectat pentru accesul în flux, dimensiunea fișierului este
numărul unității de stocare a fișierelor cu cea mai mare numărătoare din fișier. Pentru un fișier care poate
fi conectat pentru acces secven ial sau direct, dimensiunea fișierului poate fi diferită de numărul de
unități de stocare implicate de datele din înregistrări; relația exactă este dependentă de procesor.

stream= stm, unde stm este o variabilă caracter căreia i se atribuie valoarea YES dacă STREAM este inclus în setul
de metode de acces permise pentru fișier, NU dacă STREAM nu este inclus în setul de metode de acces
permise pentru fișier și NECUNOSCUT dacă procesorul nu poate
Machine Translated by Google

Îmbunătățiri de intrare/ieșire 327

pentru a determina dacă STREAM este inclus sau nu în setul de metode de acces permise pentru fișier.

17.17 Îmbunătățirile listei de nume

Cele mai multe dintre restricțiile privind variabilele numite într-o declarație de listă de nume (Secțiunea 7.15) au fost
eliminate. Singurul care rămâne este că o matrice de dimensiune presupusă nu este permisă.
I/O Lista de nume (vezi Secțiunea 9.10) este acum disponibilă pentru fișierele interne.

Exerciții

1. Scrieți un program care citește un fișier (presupus a fi un fișier text) ca flux neformatat, verificând
Unix (LF) și DOS/Windows (CRLF) terminatori de înregistrare.

2. Scrieți un program care să afișeze efectele specificatorului semn= și ale descriptorilor de editare ss, sp și s.
La ce ieșire v-ați aștepta dacă fișierul este deschis cu sign='suppress'?
Machine Translated by Google

Această pagină a fost lăsată goală în mod intenționat


Machine Translated by Google

18. Facilități îmbunătățite ale modulelor

18.1 Introducere

Facilitățile modulului Fortran 95, deși sunt adecvate pentru programe de dimensiuni modeste, au unele
deficiențe pentru programele foarte mari. Amploarea acestor deficiențe nu a fost apreciată în mod
corespunzător atunci când au fost alese principalele caracteristici ale Fortran 2003 și nu a fost concepută
o soluție simplă până când dezvoltarea Fortran 2003 a fost aproape completă.
Prin urmare, în loc să riscăm o întârziere pentru întregul Fortran 2003, s-a decis definirea caracteristicii
submodulului ca o extensie într-un Raport Tehnic1, cu promisiunea că următoarea revizuire a Fortranului
o va include, în afară de corectarea oricăror defecte constatate. în câmp.
Întrucât procedurile oficiale de aprobare pentru un Raport Tehnic sunt mai simple decât cele pentru un
Standard, acesta a fost publicat în 2005, cu mult înainte de publicarea Fortran 2008, așteptată în 2010.
Acesta este motivul pentru care acest capitol apare aici. Caracteristicile fac parte din Fortran 2008.
Neajunsurile caracteristicii modulului provin toate din faptul că, deși modulele sunt un ajutor pentru
modularizarea programului, ele sunt ele însele dificil de modularizat. Pe măsură ce un modul crește,
poate pentru că conceptul pe care îl încapsulează este mare, singura modalitate de modularizare este
împărțirea lui în mai multe module. Acest lucru expune structura internă, crescând potențialul pentru
ciocniri inutile de nume globale și oferind utilizatorului modulului acces la ceea ce ar trebui să fie date și/
sau proceduri private. Mai rău, dacă subfuncțiile modulului sunt interconectate, acestea trebuie să
rămână împreună într-un singur modul, oricât de mare este.
Un alt dezavantaj semnificativ este că, dacă se face o modificare a codului în cadrul unei proceduri de
modul, chiar și una privată, utilizarea tipică a make sau a instrumentelor similare are ca rezultat
recompilarea fiecărui fișier care a folosit modulul respectiv, direct sau indirect. (O metodă de a evita
acest lucru pentru unele compilatoare este descrisă în Anexa D.)
Soluția este de a permite modulelor să fie împărțite în unități de program separate numite submodule,
care pot fi în fișiere separate. Procedurile modulului pot fi apoi împărțite astfel încât informațiile de
interfață să rămână în modul, dar corpurile pot fi plasate în submodule. O modificare a unui submodul
nu poate modifica o interfață și, prin urmare, nu provoacă recompilarea unităților de program care
utilizează modulul.
Introducerea submodulelor oferă și alte beneficii, pe care le putem explica mai ușor
odată ce am descris caracteristica.

1Raport tehnic ISO/IEC TR 19767: 2005(E).


Machine Translated by Google

330 Fortran modern explicat

18.2 Submodule

Submodulele oferă o modalitate de structurare a unui modul în părți componente, care pot fi în
fișiere separate. Toate procedurile modulului continuă să aibă interfața definită în modul, dar
implementarea lor poate fi amânată la un submodul. Un submodul are acces prin asociere gazdă
la entitățile din modul și poate avea entități proprii pe lângă furnizarea de implementări ale
procedurilor modulului.

18.2.1 Proceduri separate ale modulelor

Esența caracteristicii este de a separa definiția unei proceduri de modul în două părți: interfața,
care este definită în modul; și corpul, care este definit în submodul.
O astfel de procedură de modul este cunoscută ca o procedură de modul separat. Un exemplu
simplu este prezentat în Figura 18.1. Cuvântul cheie modul din prefixul instrucțiunii de funcție
indică în blocul de interfață că aceasta este interfața cu o procedură de modul, mai degrabă decât
o procedură externă, iar în submodul că aceasta este partea de implementare a unei proceduri de modul.
Submodulul specifică numele părintelui său. Atât interfața, cât și submodulul au acces la punctul
de tip prin asociere gazdă.

Figura 18.1 O procedură separată de modul.


tip de puncte de modul :: punct real :: x,
y interfață punct de tip final

funcția modul real point_dist(a, b) tip(punct),


intent(in) :: a, b end function point_dist end
interface

punctele modulului final

submodul (puncte) puncte_a conține

funcția modul real point_dist(a, b) tip(punct),


intent(in) :: a, b point_dist = sqrt((a%xb%x)**2+
(a%yb%y)**2)
funcția finală point_dist
submodul final puncte_a

Interfața specificată în submodul trebuie să fie exact aceeași cu cea specificată în blocul de
interfață. Pentru o procedură externă, interfața este permisă să difere în ceea ce privește numele
argumentelor, dacă este pură și dacă este recursivă (vezi Secțiunea 5.11); astfel de variații nu sunt
permise pentru un submodul, deoarece intenția este pur și simplu de a separa definiția procedurii
în două părți. Numele variabilei rezultat nu face parte din
Machine Translated by Google

Facilități îmbunătățite ale modulelor 331

interfață și astfel este permis să fie diferit în cele două locuri; în acest caz, numele din blocul de interfață este
ignorat.
Există, de asemenea, o sintaxă care evită cu totul redeclararea:

submodul (puncte) puncte_a conține

procedura modulului point_dist point_dist


= sqrt((a%xb%x)**2+(a%yb%y)**2) procedura finală point_dist

submodul final puncte_a

În acest caz, întreaga interfață este preluată din blocul de interfață, inclusiv dacă este o funcție sau o subrutină și
numele variabilei rezultat dacă este o funcție.

18.2.2 Submodule de submodule

Submodulelor le este permis să aibă submodule, ceea ce este util pentru programe foarte mari. Modulul sau
submodulul al cărui submodul este subsidiar direct se numește părinte și se numește copil al părintelui său. Nu
ne așteptăm ca numărul de niveluri de submodule să depășească adesea două (adică un modul cu submodule
care ele însele au submodule) dar nu există limită și ne referim la strămoși și descendenți cu semnificațiile
evidente. Fiecare modul sau submodul este rădăcina unui arbore ale cărui alte noduri sunt descendenții săi și au
acces la el prin asociere gazdă. Niciun alt submodule nu are un astfel de acces, ceea ce este util pentru dezvoltarea
independentă a părților modulelor mari. În plus, nu există niciun mecanism pentru a accesa ceva declarat într-un
submodul din altă parte – este efectiv privat.

Dacă se face o modificare la un submodul, numai acesta și descendenții săi vor avea nevoie de recompilare.
Un submodul este identificat prin combinația dintre numele modulului său strămoș și numele părintelui său,
de exemplu, points:points_a pentru submodulul din Figura 18.1. Acest lucru permite ca două submodule să aibă
același nume dacă sunt descendenți ai modulelor diferite.
Acest identificator este necesar doar pentru a-l specifica ca părinte în instrucțiunea submodulului unui copil, ca
în

submodul (puncte:puncte_a) puncte_b

18.2.3 Entități submodule

Un submodul poate conține, de asemenea, entități proprii. Acestea nu sunt entități modulare și, prin urmare, nu

sunt nici publice, nici private; ele sunt, totuși, inaccesibile în afara submodulului definitoriu, cu excepția
descendenților acestuia.
De obicei, acestea vor fi variabile, tipuri, constante numite etc., pentru a fi utilizate în implementarea unor
proceduri separate de modul. Conform regulilor obișnuite de asociere a gazdei, dacă orice entitate submodul
are același nume ca o entitate modul, entitatea modul este ascunsă.
Un submodul poate conține și proceduri, pe care le vom numi proceduri submodulului. O procedură de
submodul este accesibilă numai în submodul și descendenții săi și, prin urmare, poate fi invocată doar acolo.
Pentru a asigura această proprietate pentru o procedură de submodul cu bind
Machine Translated by Google

332 Fortran modern explicat

(vezi Secțiunea 12.7), o astfel de procedură nu are o etichetă obligatorie și nu poate avea un specificator
name=. Acest lucru se datorează faptului că un utilizator al unui modul ar putea scrie un submodul cu modulul
ca părinte și care conține o procedură care accesează entitățile module private prin asociere gazdă. Cu toate
acestea, fără o etichetă obligatorie, nu există niciun mecanism pentru ca utilizatorul să invoce o astfel de
procedură.
La fel ca o procedură de modul, o procedură de submodul poate fi, de asemenea, separată; o procedură
separată de submodul are interfața declarată într-un submodul și corpul într-un descendent.

18.2.4 Submodule și asociere de utilizare

Un submodul nu are voie să acceseze modulul său strămoș prin asociere de utilizare; la urma urmei, nu este
nevoie, deoarece are acces prin asociația gazdă. Poate, totuși, să acceseze orice alt modul prin asocierea
utilizării. În particular, este posibil ca un submodul al modulului a să acceseze modulul b i un submodul al
modulului b să acceseze modulul a. Un exemplu simplu este în cazul în care o procedură a modulului a
apelează o procedură a modulului b și o procedură a modulului b apelează o procedură a modulului a.
Deoarece dependențele circulare între module nu sunt permise, fără submodule, acest lucru ar necesita ca a
și b să fie același modul sau ca un al treilea modul c să fie utilizat (conținând acele părți care erau dependente
reciproc).

18.3 Avantajele submodulelor

Un beneficiu major al submodulelor este că, dacă se face o modificare la unul, doar acesta și descendenții săi
sunt afectați. Astfel, un modul mare poate fi împărțit în arbori de submodule mici, îmbunătățind modularitatea
(și astfel mentenabilitatea) și evitând cascadele de recompilare inutile (dar vezi și Anexa D). Acum rezumăm
alte beneficii.
Entitățile declarate într-un submodul sunt private pentru acel submodul și descendenții acestuia, care
controlează gestionarea numelui și utilizarea accidentală într-un modul mare.
Conceptele separate cu dependențe circulare pot fi separate în diferite submodule în cazul obișnuit în care
doar implementările se referă una la alta (deoarece dependențele circulare nu sunt permise între module,
acest lucru era imposibil înainte).
În cazul în care o sarcină mare a fost implementată ca un set de module, poate fi oportun să fie înlocuită
cu un singur modul și o colecție de submodule. Entitățile care au fost publice doar pentru că sunt necesare
altor module ale setului pot deveni private pentru modul sau pentru un submodul și descendenții acestuia.

Odată ce detaliile de implementare ale unui modul au fost separate în submodule, textul modulului în sine
poate fi publicat pentru a oferi documentație autorizată a interfeței fără a expune niciun secret comercial
conținut în implementare.
Pe multe sisteme, fiecare fișier sursă produce un singur fișier obiect care trebuie încărcat în întregime în
programul executabil. Împărțirea modulului în mai multe fișiere va permite încărcarea doar a acelor proceduri
care sunt de fapt invocate într-un program utilizator. Acest lucru face modulele mai atractive pentru construirea
de biblioteci mari.
Machine Translated by Google

19. Coarrays

19.1 Introducere

Modelul de programare coaarray este conceput pentru a oferi o extensie sintactică simplă pentru a
sprijini programarea paralelă atât din punct de vedere al distribuției de lucru, cât și al distribuției de date.

În primul rând, luați în considerare distribuția muncii. Extensia coarray adoptă modelul de programare
cu date multiple cu program unic (SPMD). Un singur program este replicat de un număr fix de ori, fiecare
replicare având propriul set de obiecte de date. Fiecare replicare a programului se numește imagine.
Numărul de imagini poate fi același cu, sau mai mare sau mai mic decât numărul de procesoare fizice. O
anumită implementare poate permite alegerea numărului de imagini la momentul compilării, la
momentul legăturii sau la momentul execuției. Fiecare imagine se execută asincron și regulile normale
ale Fortran se aplică în cadrul fiecărei imagini.1 Secvența de execuție poate diferi de la o imagine la alta,
așa cum este specificat de programator, care, cu ajutorul unui index unic al imaginii, determină calea
reală folosind constructele normale de control Fortran. și sincronizări explicite. Pentru codul dintre
sincronizări, compilatorul este liber să folosească aproape toate tehnicile sale normale de optimizare ca
și cum ar fi prezentă o singură imagine.
În al doilea rând, luați în considerare distribuția datelor. Extensia coarray permite programatorului să
exprime distribuția datelor prin specificarea relației dintre imaginile de memorie într-o sintaxă foarte
asemănătoare cu sintaxa normală a matricei Fortran. Obiectele cu noua sintaxă au o proprietate
importantă: pe lângă faptul că au acces la obiectul local, fiecare imagine poate accesa obiectul
corespunzător de pe orice altă imagine. De exemplu, afirmațiile

real, dimensiune(1000), codimension[*] :: x, y real, codimension[*] ::


z

declara trei obiecte, fiecare ca un coarray. x și y sunt coarray de matrice și z este o matrice scalară.
Un coarray are întotdeauna aceeași formă pe fiecare imagine. În acest exemplu, fiecare imagine are
două matrice reale de dimensiune 1000 și o matrice scalară. Dacă o imagine execută instrucțiunea:

x(:) = y(:)[q]

coarray-ul y pe imaginea q este copiat în co-array-ul x pe imaginea executată.


Indicele dintre paranteze urmează regulile normale Fortran dintr-o singură imagine. Scripturile
Cosub dintre paranteze pătrate oferă o notație la fel de convenabilă pentru accesarea unui obiect pe o
altă imagine. Marginile dintre paranteze pătrate în declarațiile coarray urmează regulile de

1Deși acest lucru nu este necesar, se anticipează că în implementările timpurii fiecare imagine va executa același
fișier executabil pe hardware aproape identic.
Machine Translated by Google

334 Fortran modern explicat

matrice de dimensiune presupusă, deoarece un coarray există întotdeauna pe toate imaginile. Limita
superioară pentru ultima codimensiune nu este niciodată specificată, ceea ce permite programatorului să
scrie cod fără să știe numărul de imagini pe care codul le va folosi în cele din urmă.
Programatorul folosește sintaxa coaarray numai acolo unde este nevoie. O referire la un coarray fără
paranteze pătrate atașate este o referință la obiectul din memoria imaginii în execuție. Deoarece este de dorit
ca majoritatea referințelor la obiectele de date dintr-un program paralel să fie locale, sintaxa coarasei ar
trebui să apară numai în părți izolate ale codului sursă. Sintaxa Coarray acționează ca un semnal vizual pentru
programator că va avea loc comunicarea între imagini.
De asemenea, acționează ca un flag pentru compilator pentru a genera cod care evită latența2 ori de câte ori este posibil.
Deoarece un coarray are aceeași formă pe fiecare imagine și deoarece alocările și dealocarea coarray-urilor
au loc în sincronie între toate imaginile, coarray-urile pot fi implementate în așa fel încât fiecare imagine să
poată calcula adresa unei coarray pe altă imagine. Aceasta este uneori numită memorie simetrică. Pe o mașină
cu memorie partajată, un coarray pe o imagine și coarray-urile corespunzătoare pe alte imagini pot fi
implementate ca o secvență de obiecte cu adrese uniform distanțate. Pe o mașină cu memorie distribuită cu
un procesor fizic pentru fiecare imagine, un coarray poate fi stocat la aceeași adresă în fiecare procesor fizic.
Dacă este o matrice coarray, fiecare imagine poate calcula adresa unui element dintr-o altă imagine în raport
cu adresa de început a matricei de pe acea altă imagine.

Deoarece coarray-urile sunt integrate în limbaj, referințele de la distanță câștigă automat


serviciile de capabilități de date de bază ale Fortran, inclusiv

• sistemul de tip; •
conversii automate în teme; • informații despre
structura structurii; și • caracteristici orientate pe
obiecte cu unele restricții.

19.2 Imagini de referință

Obiectele de date din alte imagini sunt referite prin cosubscripte incluse între paranteze drepte.
Fiecare set valid de cosubscripte se mapează la un index de imagine, care este un număr întreg între unu și
numărul de imagini, în același mod ca un set valid de subscripte de matrice mapează la o poziție în ordinea
elementelor de matrice.
Numărul de imagini este returnat de funcția intrinsecă num_images. Funcția intrinsecă this_image fără
argumente returnează indexul de imagine al imaginii invocate. Setul de cosubscripti care corespunde imaginii
de invocare pentru un coarray z sunt disponibile ca this_image(z). Indexul de imagine care corespunde unui
sub-matrice de cosubscripte valide pentru un co-array z este disponibil ca imagine_index(z, sub).

De exemplu, pe imaginea 5, this_image() are valoarea 5 și pentru matricea coarray declarată


la fel de

real :: z(10, 20)[10, 0:9, 0:*]

this_image(z) are valoarea (/ 5, 0, 0 /), în timp ce pe imaginea 213, this_image(z) are valoarea (/ 3, 1, 2 /). Pe
orice imagine, valoarea lui image_index(z, (/ 5, 0, 0 /)) este 5, iar valoarea image_index(z, (/ 3, 1, 2 /)) este 213.

2 Întârzieți cât imaginea așteaptă transferul datelor către sau de la o altă imagine.
Machine Translated by Google

Coarrays 335

19.3 Proprietățile coarrays

Fiecare imagine are propriul set de obiecte de date, toate putând fi accesate în mod normal Fortran.
Unele obiecte sunt declarate cu codimensiuni între paranteze drepte, de exemplu:
real, dimensiune(20), codimension[20,*] :: a ! O matrice coarray real :: c[*], d[*] caracter ::
b(20)[20,0:*] întreg :: ib(10)[*] tip(interval) :: s[20,* ] ! Coarrays scalare

Cu excepția cazului în care coarray-ul este alocabil (Secțiunea 19.7), forma pentru codimensiunile din
paranteze drepte este aceeași cu cea pentru dimensiunile din paranteze pentru o matrice de dimensiune
presupusă. Numărul total de indice plus cosubscriptii este limitat la 15.
Un subobiect al unui coarray este privit ca un coarray dacă și numai dacă nu are cosubscripti, nici
subindice vectoriale, nicio selecție de componente alocabile și nici o selecție de componente pointer.
De exemplu, a(1) și a(2:10) sunt coarrays dacă a este coarray declarat la începutul acestei secțiuni.
Această definiție înseamnă că trecerea unui subobiect coarray la un coarray inactiv nu implică copiere în
copiere (ceea ce ar fi imposibil având în vedere că coarray-ul există pe toate imaginile).
Termenul întreg coarray este folosit pentru întregul obiect care este declarat ca un coarray sau întregul
component al unei structuri coarray.
Corank-ul unui întreg coarray este determinat de declarația acestuia. Colimitările sale sunt specificate
între paranteze drepte în declarația sau alocarea sa. Orice subobiect al unui coarray întreg care este un
coarray are corank, cobounds și coextents ale întregului coarray. Codimensionarea unui coarray este
întotdeauna egală cu numărul de imagini. Chiar dacă limita superioară finală este specificată ca un
asterisc, o matrice are o coextensie finală și o limită superioară finală, care depind de numărul de imagini.
Colimitarea superioară finală este cea mai mare valoare pe care o poate avea colimitarea finală într-o
referință validă (discutăm acest lucru în continuare în Secțiunea 19.4). De exemplu, când numărul de
imagini este de 128, coarray-ul a declarat astfel
real :: array(10,20)[10,-1:8,0:*]
are rangul 2, corancul 3, forma (/10,20/); colimitele sale inferioare sunt 1, -1, 0, iar colimitele sale
superioare sunt 10, 8, 1.
O coarray nu este permisă să fie o constantă numită, deoarece aceasta ar fi inutilă. Fiecare imagine ar
avea exact aceeași valoare, așa că nu ar exista niciun motiv pentru a-și accesa valoarea pe o altă imagine.

Pentru a vă asigura că fiecare imagine își inițializează numai propriile date, nu sunt permise cosubscripturile
declarații de date. De exemplu:
real :: a(10)[*] date a(1)
/0.0/ ! Date permise a(1)
[2] /0.0/ ! Nepermis Un coarray poate fi alocabil,
vezi Secțiunea 19.7.
Un coarray nu este permis să fie un pointer, dar un coarray poate fi de tip derivat cu pointer sau
componente alocabile, vezi Secțiunea 19.8. În plus, deoarece un obiect de tip c_ptr sau c_funptr are
esența unui pointer, un coarray nu este permis să fie de niciunul dintre aceste tipuri. Deși un coarray
este permis să aibă o componentă de c_ptr sau c_funptr, acestea sunt aproape inutile, vezi Anexa B.10.2.
Machine Translated by Google

336 Fortran modern explicat

19.4 Accesarea coarray-urilor

Un coarray de pe o altă imagine poate fi abordat utilizând cosubscripte în paranteze drepte după orice
subindex în paranteze, de exemplu:

a(5)[3,7] = ib(5)[3] d[3] = ca(:)


[2,3] = c[1]

Numim orice obiect al cărui designator include cosubscripte un obiect coindexat. Numai o imagine
poate fi referită la un moment dat, astfel încât fiecare cosubscript trebuie să fie o expresie întreagă
scalară (cosubscripturile de secțiune nu sunt permise). Indicele sau indicele de secțiune trebuie să fie
utilizați atunci când coarray-ul are rang diferit de zero. De exemplu, a[2,3] nu este permisă ca prescurtare
pentru a(:)[2,3].
Orice referință la obiect fără paranteze pătrate este întotdeauna o referință la obiectul din imaginea
de execuție. De exemplu, în

real :: z(20)[20,*], zmax[*]


:
zmax = maxval(z)

valoarea celui mai mare element al matricei coarray z de pe imaginea de execuție este plasată în
coarrayul scalar zmax de pe imaginea de execuție.
Pentru o referință cu paranteze drepte, lista de cosubscript trebuie să fie mapată la un index de imagine valid.
De exemplu, dacă există 16 imagini și coarray-ul z este declarat astfel

real :: z(10)[5,*]

atunci o referire la z(:)[1,4] este validă, deoarece se referă la imaginea 16, dar o referire la z(:)[2,4] este
invalidă, deoarece se referă la imaginea 17. La fel ca subscriptele matrice, este responsabilitatea
programatorului să se asigure că cosubscripturile sunt în limite și se referă la o imagine validă.
Parantezele pătrate atașate la obiecte avertizează cititorul despre comunicarea probabilă dintre
imagini. Cu toate acestea, comunicarea poate avea loc și în cadrul unei referințe de procedură și aceasta
poate fi printr-o operație definită sau o atribuire definită.
Că o imagine în execuție este selectată între paranteze drepte nu are nicio legătură cu
imaginea de execuție evaluează expresia sau atribuirea. De exemplu, afirmația

z[6] = 1

este executat de fiecare imagine care o întâlnește, nu doar de imaginea 6. Dacă codul urmează să fie
executat selectiv, este nevoie de construcția Fortran if sau case. Un exemplu este

dacă (această_imagine()==6)z=1

Un obiect coindexat este permis în majoritatea contextelor, cum ar fi operațiuni intrinseci, alocare
intrinsecă, liste de intrare/ieșire și ca argument real corespunzător unui argument fals non-coarray.3 Pe
o mașină cu memorie distribuită, trecându-l ca argument real. este probabil să facă o copie locală a
acesteia înainte de a începe execuția procedurii (cu excepția cazului în care are intenția de a ieși) și
rezultatul să fie copiat înapoi la returnare (cu excepția cazului în care are intenția sau valoarea

3Obiectele coindexate polimorfe sunt mult mai restrânse, vezi Secțiunea 19.10.
Machine Translated by Google

Coarrays 337

atribut). Regulile pentru asocierea argumentelor au fost construite cu grijă, astfel încât o astfel de copiere
să fie întotdeauna permisă.
Pointerilor nu li se permite să aibă ținte pe imaginile de la distanță, deoarece acest lucru ar încălca
cerința ca accesul la distanță să fie evident. Prin urmare, ținta unui pointer nu este permisă să fie un obiect
coindexat: p => a(n)[p]
! Nu este permis (constrângere de timp de compilare)
Un obiect coindexat nu este permis ca selector într-o instrucțiune de tip asociat sau select, deoarece
aceasta ar ascunde o referință la o imagine la distanță (numele asociat este fără paranteze drepte). Cu
toate acestea, un coarray este permis ca selector, caz în care entitatea asociată este, de asemenea, un
coarray, iar colimitările sale sunt cele ale selectorului.

19.5 Instrucțiunea sync all

Fiecare imagine se execută singură, indiferent de execuția altor imagini, cu excepția cazului în care
întâlnește instrucțiuni speciale numite instrucțiuni de control al imaginii. Programatorul inserează
instrucțiuni de control al imaginii pentru a se asigura că, ori de câte ori o imagine modifică valoarea unei
variabile coarray sau a unei variabile cu atributul țintă, nicio altă imagine nu dorește încă valoarea veche și
că ori de câte ori o imagine accesează valoarea unei variabile, aceasta primește valoarea dorită – fie
valoarea veche (înainte de actualizare), fie valoarea nouă (din actualizare). În această secțiune, descriem
cele mai simple dintre aceste instrucțiuni de control al imaginii.
Instrucțiunea sync all oferă o barieră în care toate imaginile se sincronizează înainte de a executa
instrucțiuni ulterioare. Toate instrucțiunile executate înaintea barierei pe imaginea P se execută înainte ca
orice instrucțiune să se execute după bariera pe imaginea Q. Dacă valoarea unei variabile este modificată
de imaginea P înaintea barierei, noua valoare este disponibilă pentru toate celelalte imagini după barieră.
Dacă o imagine face referire la valoarea unei variabile înaintea barierei, aceasta obține valoarea înainte de
a trece bariera.

Figura 19.1 Citiți pe imaginea 1 și transmiteți celorlalți.


real :: z[*]
:
sincronizați
totul dacă (this_image()==1) apoi
citiți (*, *) z do imagine = 2,
num_images() z[image] = z

sfâr itul face

se încheie dacă

se sincronizează tot

Figura 19.1 prezintă un exemplu simplu de utilizare a sync all. Imaginea 1 citește datele și le difuzează
către alte imagini. Prima sincronizare asigură că imaginea 1 nu interferează cu nicio utilizare anterioară a
lui z de către o altă imagine. A doua sincronizare asigură că o altă imagine nu accesează z înainte ca noua
valoare să fie setată de imaginea 1.
Machine Translated by Google

338 Fortran modern explicat

Deși, de obicei, sincronizarea va fi inițiată de aceeași instrucțiune sync all pentru toate imaginile,
aceasta nu este o cerință. Flexibilitatea suplimentară poate fi utilă, de exemplu, atunci când imagini
diferite execută cod diferit și trebuie să facă schimb de date.
Toate imaginile sunt sincronizate la inițierea programului ca printr-o instrucțiune sync all. Acest lucru
asigură că coarray-urile inițializate vor avea valorile inițiale pe toate imaginile înainte ca orice imagine să
înceapă să execute instrucțiunile executabile.
Există o barieră implicită ori de câte ori un corray este alocat sau dealocat, vezi Secțiunea 19.7.
Alte instrucțiuni de control al imaginii sunt descrise în Secțiunile 19.13 și o listă completă este găsită în
Secțiunea 19.13.7.

19.6 Coarrays în proceduri

Un argument fals al unei proceduri este permis să fie un coarray. Poate fi un scalar sau o matrice care
are formă explicită, dimensiune presupusă, formă presupusă sau alocabilă, vezi Figura 19.2.

Figura 19.2 Argumente dummy Coarray.


subrutina subr(n, p, u, w, x, y, z, a) întreg :: n, p real ::
u[2, p/2, *] real :: w(n)[p, *] real :: x(n, *)[*] real ::
y(:, :)[*] real, alocabil :: z(:)[:, :] ! Alocabil real,! Scalar
alocabil :: a[:] ! Forma explicita!
Mărimea presupusă
! Forma asumată

! Scalar alocabil

Când procedura este apelată, argumentul actual corespunzător trebuie să fie un coarray. Asocierea
este cu coarray-ul în sine și nu cu o copie; restricțiile de mai jos asigură că nu este niciodată necesară
copierea în copiere. (Efectuarea unei copii ar necesita sincronizare la intrare și întoarcere pentru a se
asigura că referințele de la distanță din cadrul procedurii nu sunt la o copie care nu există încă sau care
nu mai există.) În plus, interfața trebuie să fie explicită, astfel încât compilatorul să știe trece coarray și
nu doar variabila locală. Un exemplu este prezentat în Figura 19.3.

Restricțiile privind argumentele dummy coaarray sunt:


• argumentul real trebuie să fie un coarray (vezi Secțiunea 19.3 pentru regulile dacă a
subobiectul este un coarray);

• dacă argumentul dummy este un tablou, altul decât o matrice de formă presupusă fără atributul
contiguu (vezi Secțiunea 20.4.2), argumentul real trebuie să fie pur și simplu contigu (îndeplinește
condițiile din Secțiunea 20.4.3, care asigură că matricea este cunoscut la momentul compilării ca
fiind învecinat); și
• nu trebuie să aibă atributul value (acest lucru se aplică și unui argument dummy non-coarray
care are o componentă coarray finală alocabilă).

Dacă un argument inactiv este un coarray alocabil, argumentul actual corespunzător trebuie să fie un
coarray alocabil de același rang și corank. În plus, lanțul său de argumentare
Machine Translated by Google

Coarrays 339

Figura 19.3 Apelarea unei proceduri cu argumente dummy coarase.


real, alocabil :: a(:)[:], b(:,:)[:]
:
apelați sub(a(:), b(1,:))
:
conține
subrutina sub(x, y) real :: x(:)
[*], y(:)[*]
:
end subrutine sub

asocierile, poate prin mai multe niveluri de apel de procedură, trebuie să se termine cu același coarray
real pe fiecare imagine. Acest lucru permite coarray-ului să fie alocat sau dealocat în procedură.

Dacă un argument inactiv este un coarray alocabil sau are o componentă care este un coarray alocabil,
acesta nu trebuie să aibă intent out. Acest lucru se datorează faptului că dealocarea coarray-ului ar
necesita o sincronizare implicită.
Coarrayurile automate nu sunt permise. De exemplu, următoarele sunt invalide:

subrutina solve3(n) întreg ::


n real :: lucru(n)[*] ! Nu sunt
acceptate

Dacă ar fi permise coarray-urile automate, ar fi necesar să se solicite sincronizarea, atât după ce memoria
este alocată la intrare, cât și înainte ca memoria să fie dealocată la întoarcere. În plus, ar însemna că
procedura ar trebui apelată pe toate imaginile simultan (a se vedea penultimul paragraf al acestei
secțiuni).
Un rezultat al funcției nu este permis să fie un coarray sau să aibă o componentă finală care este un
coarray. Deoarece funcțiile nu sunt invocate în lockstep pe fiecare imagine, nu ar avea sens să aibă un
rezultat coaarray.
Coarrayurile alocabile pot fi declarate într-o procedură. Acestea sunt discutate în Secțiunea 19.7.
Regulile pentru rezolvarea referințelor de procedură generică nu au fost extinse pentru a permite
supraîncărcarea versiunilor de matrice și coară, deoarece ar fi ambiguă.
O procedură pură sau elementară nu este permisă pentru a defini un obiect coindexat sau pentru a conține
instrucțiuni de control al imaginii (Secțiunea 19.13.7), deoarece acestea implică efecte secundare (definirea
unui obiect coindexat este similară cu definirea unei variabile de la gazdă sau un modul) . Cu toate acestea,
poate face referire la valoarea unui obiect coindexat.
O procedură elementară nu este permisă să aibă un argument dummy coaarray.
Cu excepția cazului în care este alocabil sau un argument inactiv, un obiect care este un coarray sau
care are o componentă coarray este necesar să aibă atributul de salvare. Rețineți că în Fortran 2008,
variabilele declarate în partea de specificații a unui modul sau submodul, precum și variabilele programului
principal, au automat atributul de salvare. Din nou, acest lucru se datorează faptului că un coarray
nealocabil nesalvat ar intra în existență la invocarea procedurii, necesitând sincronizare, iar acest lucru
este inadecvat deoarece procedurile nu sunt invocate în pas de blocare pe fiecare imagine. Un coarray
alocabil nu este necesar pentru a avea atributul de salvare deoarece
Machine Translated by Google

340 Fortran modern explicat

o procedură recursivă poate avea nevoie de coarrays alocabile separate la mai mult de un nivel de
recursivitate.
O procedură cu un argument dummy nealocabil va fi apelată deseori pe toate imaginile în același timp
cu același coarray real, dar aceasta nu este o cerință. De exemplu, imaginile pot fi grupate în două echipe și
imaginile unei echipe pot apela procedura cu un coarray, în timp ce imaginile celeilalte echipe apelează
procedura cu un alt coarray sau execută cod diferit.

Fiecare imagine asociază în mod independent argumentul său inactiv de coarray nealocabil cu un coarray
real, poate prin mai multe niveluri de apel de procedură, și definește din nou corank și cobounds. Le
folosește pentru a interpreta fiecare referință la un obiect coindexat, fără a ține cont de faptul dacă o
imagine la distanță execută aceeași procedură cu
coarray.

19.7 Coarrays alocabile

Un coarray poate fi alocabil. Instrucțiunea de alocare este extinsă, astfel încât cobound-urile să poată fi
specificate, de exemplu,

real, alocabil :: a(:)[:], s[:, :]


:
alocă (a(10)[*], s[-1:34,0:*])

Colimitele trebuie să fie întotdeauna incluse în instrucțiunea de alocare, iar limita superioară pentru
codimensiunea finală trebuie să fie întotdeauna un asterisc. De exemplu, următoarele nu sunt permise
(constrângeri de timp de compilare):

alocă (a(n)) alocă (a(n) ! Nu este permis pentru un coarray (fără cobounds)
[p]) ! Nu este permis (cobound nu *)

De asemenea, valoarea fiecărui parametru de tip legat, colegat sau lungime trebuie să fie aceeași pentru
toate imaginile. De exemplu, următoarele nu sunt permise (constrângere de timp de rulare)

alocă (o(această_imagine())[*])! Nu este permis (limită locală variabilă)

În plus, tipurile dinamice trebuie să fie aceleași pe toate imaginile. Împreună, aceste restricții asigură faptul
că corray-urile există pe fiecare imagine și sunt consecvente.
Există o sincronizare implicită de barieră a tuturor imaginilor în asociere cu fiecare instrucțiune de
alocare care implică una sau mai multe coarrays. Imaginile nu încep să execute instrucțiunile ulterioare
până când toate imaginile termină executarea aceleiași instrucțiuni de alocare (pe aceeași linie a codului
sursă). În mod similar, pentru dealocare, toate imaginile se sincronizează la începutul aceleiași instrucțiuni
de dealocare și nu continuă cu instrucțiunea următoare până când toate imaginile au terminat dealocarea.

Când o imagine execută o instrucțiune de alocare, comunicarea între imagini este necesară doar pentru
sincronizare. Imaginea își alocă coarray-ul local și înregistrează modul în care vor fi abordate coarray-urile
corespunzătoare din alte imagini. Compilatorul nu este obligat să verifice dacă limitele și coboundurile sunt
aceleași pe toate imaginile, deși poate face acest lucru (sau are o opțiune pentru a face acest lucru). Nici
compilatorul nu este necesar să detecteze când a apărut blocajul; de exemplu, atunci când o imagine
execută o instrucțiune de alocare în timp ce alta execută o instrucțiune de alocare.
Machine Translated by Google

Coarrays 341

Dacă un coarray alocabil nesalvat este local pentru o procedură sau construcție bloc (vezi Secțiunea
20.5.3) și este încă alocat atunci când procedura sau constructul bloc finalizează execuția, are loc
dealocarea implicită a coarray-ului și, prin urmare, sincronizarea tuturor imaginilor.
Alocarea unui coarray polimorf nu este permisă pentru a crea un coarray care este de tip
c_ptr, c_funptr sau de un tip cu o componentă coarray finală.
Fortran 2003 permite ca formele sau parametrii de lungime să nu fie de acord pe cele două laturi
ale unei alocări intrinseci de matrice unei matrice alocabile (vezi Secțiunea 15.5.2); sistemul efectuează
realocarea corespunzătoare. Un astfel de dezacord nu este permis pentru un coarray alocabil,
deoarece ar implica sincronizare.
Din același motiv, atribuirea intrinsecă nu este permisă unui coarray polimorf.

19.8 Coarrays cu componente alocabile sau pointer

Un coarray este permis să fie de tip derivat cu componente alocabile sau pointer.

19.8.1 Componente de date

Pentru a partaja structuri de date cu dimensiuni diferite, valori ale parametrilor de lungime sau tipuri
între diferite imagini, putem declara un coarray de tip derivat cu o componentă non-coarray care
este alocabilă sau un pointer. Pe fiecare imagine, componenta este alocată local sau este alocat
pointer unei ținte locale, astfel încât să aibă proprietățile dorite pentru imaginea respectivă (sau nu
este alocat sau alocat pointer dacă nu este necesar pe imaginea respectivă). Este simplu să accesați
astfel de date pe o altă imagine, de exemplu,

x(:) = z[p]%alloc(:)

unde cosubscriptul este asociat cu variabila scalară z, nu cu componenta acesteia. Cu cuvinte, această
declarație înseamnă „Mergeți la imaginea p, obțineți adresa aloc al componentei matricei și copiați
datele din matrice în sine în matricea locală x”.
Dacă coaarray z conține o componentă pointer de date ptr, apariția lui z[q]%ptr într-un context
care se referă la ținta sa este o referință la ținta componentei ptr a lui z pe imaginea q.
Această țintă trebuie să se găsească pe imaginea q și trebuie să fi fost stabilită printr-o instrucțiune de
alocare executată pe imaginea q sau o atribuire de pointer executată pe imaginea q, de exemplu,

z%ptr => r ! Asociația locală

Un pointer local poate fi asociat cu o componentă țintă pe imaginea locală,

r => z%ptr ! Asociația locală

dar poate să nu fie asociat cu o componentă țintă pe altă imagine,

r => z[q]%ptr ! Nu este permis (constrângere de timp de compilare)

Dacă o asociere cu o componentă țintă pe altă imagine ar fi altfel implicată, componenta pointer
devine nedefinită. De exemplu, acest lucru se întâmplă atunci când atribuirile intrinseci de tip derivat

z[q] = z ! Componenta pointer a lui z[q] poate deveni nedefinită z = z[q] ! Componenta
pointer a lui z poate deveni nedefinită
Machine Translated by Google

342 Fortran modern explicat

sunt executate pe o altă imagine decât q. Se poate întâmpla și într-o invocare a unei proceduri dacă z[q]
este un argument real sau z[q]%ptr este asociat cu un argument dummy pointer.
În mod similar, pentru o matrice de tip derivat care are un pointer sau o componentă alocabilă,
alocarea uneia dintre acele componente pe o altă imagine nu este permisă:
tip(ceva), alocabil :: t[:]
:
alocă (t[*]) alocă ! Permis
(t%ptr(n)) alocă (t[q] ! Permis
%ptr(n)) ! Nu este permis (constrângere de timp de compilare)
Într-o atribuire intrinsecă la un obiect coindexat care este o matrice alocabilă, este necesar ca
parametrii de tip de formă și lungime să fie de acord; aceasta împiedică orice posibilitate de alocare la
distanță. Din același motiv, nu este permisă atribuirea intrinsecă la un obiect coindexat polimorf sau la
un obiect coindexat cu o componentă finală alocabilă. În plus, dacă un argument real este un obiect
coindexat cu o componentă finală alocabilă, argumentul inactiv corespunzător trebuie să fie alocabil, un
pointer sau să aibă atributul intent în sau valoare.

19.8.2 Componentele indicatorului de procedură

Un coarray este permis să fie de un tip care are o componentă pointer de procedură sau o procedură
legată de tip. O referință de procedură printr-o componentă pointer de procedură a unui obiect coindexat,
de exemplu,
apelați un[p]%proc(x)! Nepermis nu este
permis, deoarece ținta procedurii de la distanță ar putea fi lipsită de sens pe imaginea în execuție. Cu
toate acestea, o referință printr-o procedură legată de tip (secțiunea 14.6) este permisă cu condiția ca
tipul să nu fie polimorf; acest lucru asigură că tipul și, prin urmare, procedura este aceeași pe toate
imaginile.

19.9 Componente Coarray

O componentă poate fi un coarray și, dacă da, trebuie să fie alocabilă. O variabilă sau componentă a unui
tip care are o componentă coarray finală nu poate fi ea însăși o coarray și trebuie să fie un scalar
nealocabil non-pointer.4.
Dacă un obiect cu o componentă coarray finală alocabilă este declarat fără atributul de salvare într-o
procedură și coarray este încă alocat la returnare, există o dealocare implicită și sincronizare asociată. În
mod similar, dacă un astfel de obiect este declarat în cadrul unui construct de bloc și coaarray-ul este
încă alocat atunci când blocul finalizează execuția, există o dealocare implicită și o sincronizare asociată.

Pentru a evita posibilitatea realocării implicite într-o atribuire intrinsecă pentru un scalar de tip derivat
cu o componentă coaarray alocabilă, nu este permisă nicio dezacord privind starea sau forma de alocare
pentru componenta coară.

4Dacă am permite un coarray de un tip cu componente coarray, ne-am confrunta cu referințe precum z[p]%x[q] O modalitate logică de
a citi o astfel de expresie ar fi: mergeți la imaginea p și găsiți componenta x pe imaginea q.
Aceasta este echivalentă cu z[q]%x.
Machine Translated by Google

Coarrays 343

Nu este permis să se adauge o componentă coarray prin extensia de tip decât dacă tipul este deja
are una sau mai multe componente coarray.

19.10 Referiri la subobiecte polimorfe


Pentru ca implementarea să nu fie nevoie să interogheze tipul dinamic al unui obiect pe o altă imagine, nu
sunt permise referințe la un subobiect polimorf al unui obiect coindexat sau la un obiect coindexat care are o
subcomponentă alocabilă polimorfă.

19.11 Atribute volatile și asincrone


Dacă o matrice simulată este volatilă, la fel trebuie să fie și argumentul real corespunzător și invers. Fără
această restricție, valoarea unui coarray nevolatil ar putea fi modificată printr-o altă imagine prin mijloace
nespecificate de program, adică să se comporte ca volatile.
În mod similar, acordul atributului este necesar atunci când se accesează un coarray prin asociere de
utilizare, asociere gazdă sau într-un construct de bloc (vezi Secțiunea 20.5.3) din domeniul care îl conține. Aici,
restricția este simplă; deoarece atributul este același în mod implicit, nu trebuie să fie respecificat pentru un
coarray accesat.
Din același motiv, acordul atributului volatil este necesar pentru pointer
asociere cu orice parte a unui coarray.
Un obiect coindexat asincron sau volatil nu este permis să fie un argument real care să corespundă unui
argument fals asincron sau volatil. Acest lucru se datorează faptului că mecanismul de copiere în copiere este
interzis atunci când se asociază un argument actual asincron sau volatil cu un argument fals asincron sau
volatil, dar trecerea unui obiect coindexat ca argument real necesită practic să se facă copierea în copiere.

19.12 Interoperabilitate
Coarray-urile nu sunt interoperabile, deoarece C nu are conceptul de obiect de date ca un coarray.
Interoperabilitatea coarrays-urilor cu UPC5 ar putea fi luată în considerare în viitor.

19.13 Sincronizare
Am întâlnit sincronizarea barierelor în secțiunile 19.5 și 19.7. Aici, descriem instrucțiunile de control al imaginii
care oferă sincronizări mai selective și conceptul de segment de execuție care susține comportamentul
programelor care le folosesc.

19.13.1 Segmente de execuție

Pe fiecare imagine, succesiunea de instrucțiuni executate înainte de prima execuție a unei instrucțiuni de
control a imaginii sau între execuția a două instrucțiuni de control a imaginii este cunoscută ca

5Unified Parallel C, o extensie a lui C care este similară cu coarrays din Fortran.
Machine Translated by Google

344 Fortran modern explicat

un segment. Segmentul executat imediat înainte de execuția unei instrucțiuni de control al imaginii include evaluarea
tuturor expresiilor din instrucțiune.
De exemplu, în Figura 19.1, fiecare imagine execută un segment înainte de a executa prima instrucțiune sync all,
execută un segment între executarea celor două instrucțiuni sync all și execută un segment după executarea celei
de-a doua instrucțiuni sync all.
Pe fiecare imagine P, ordinea de execuție a instrucțiunii determină ordinea segmentului, Pi, i=1, 2, ... . Între
imagini, execuția instrucțiunilor corespunzătoare de control a imaginii pe imaginile P și Q la sfârșitul segmentelor Pi
și Qj poate asigura că fie Pi precede Qj+1, fie Qj precede Pi+1, sau ambele.

O consecință este că mulțimea tuturor segmentelor din toate imaginile este parțial ordonată: segmentul Pi
precede segmentul Qj dacă și numai dacă există o secvență de segmente care începe cu Pi și se termină cu Qj astfel
încât fiecare segment al secvenței îl precede pe următorul fie deoarece sunt pe aceeași imagine sau din cauza
executării instrucțiunilor de control a imaginii corespunzătoare.
O pereche de segmente Pi și Qj sunt numite neordonate dacă Pi nici nu precede și nici nu urmează Qj.
De exemplu, dacă segmentul din mijloc din figura 19.1 este Pi pe imaginea 1 și Qj pe o altă imagine Q, Pi 1 precede
Qj+1 și Pi+1 succedează Qj 1, dar Pi și Qj sunt neordonate.
Există restricții cu privire la ceea ce este permis într-un segment care este neordonat în raport cu un alt segment.
Acestea oferă compilatorului spațiu de optimizare. Un coarray poate fi definit și referit în timpul execuției segmentelor
neordonate prin apeluri la subrutine atomice (Anexa B.10.1). În afară de aceasta,

• dacă o variabilă este definită într-un segment dintr-o imagine, nu trebuie să fie referită, definită sau să devină
nedefinită într-un segment dintr-o altă imagine decât dacă segmentele sunt ordonate; • dacă alocarea unui

subobiect alocabil al unui coarray sau asocierea pointerului unui subobiect pointer al unui coarray este
modificată într-un segment dintr-o imagine, acel subobiect nu va fi referit sau definit într-un segment al unei
alte imagini decât dacă segmentele sunt ordonate ; și

• dacă o invocare a unei proceduri pe imaginea P este în execuție în segmentele Pi, Pi+1, ..., Pk și definește un
argument inactiv non-coarray, entitatea asociată cu argumentul nu va fi referită sau definită pe o altă imagine
Q dintr-un segment Qj , cu excepția cazului în care Qj precede Pi sau reușește Pk (deoarece o copie a
argumentului real poate fi transmisă procedurii).

Rezultă că pentru codul dintr-un segment, compilatorul este liber să folosească aproape toate tehnicile sale normale
de optimizare ca și cum ar fi prezentă o singură imagine.

19.13.2 Declarația de sincronizare a imaginilor

Pentru o mai mare flexibilitate, declarația de sincronizare a imaginilor

sincronizați imagini (set de imagini)

realizează o sincronizare a imaginii care o execută cu fiecare dintre celelalte imagini din setul său de imagini. Aici,
imagine-set este fie o matrice întreagă de rangul unu care conține indici de imagine diferiți, fie un asterisc care indică
toate imaginile, cu excepția imaginii invocate.
Executarea unei instrucțiuni de sincronizare a imaginilor pe imaginea P corespunde execuției unei instrucțiuni de
sincronizare a imaginilor pe imaginea Q dacă de numărul de ori imaginea P a executat o imagine de sincronizare
Machine Translated by Google

Coarrays 345

instrucțiunea cu Q în setul său de imagini este același cu numărul de ori imaginea Q a executat o instrucțiune de
sincronizare a imaginilor cu P în setul său de imagini. Segmentele care s-au executat înainte de instrucțiunea de
sincronizare a imaginilor pe oricare imagine preced segmentele care au fost executate după instrucțiunea de
sincronizare a imaginilor corespunzătoare pe cealaltă imagine. Figura 19.4 prezintă un exemplu care impune imaginilor
ordinea fixă 1, 2, ....

Figura 19.4 Utilizarea imaginilor sincronizate pentru a impune o ordine asupra imaginilor.

eu = this_image() ne =
num_images() if (me==1)
then p=1 else

sincronizare imagini (me-1)


p = p[me-1] + 1 sfârșit dacă

dacă (eu<ne) sincronizează imagini (eu+1)

Execuția unei instrucțiuni sync images(*) nu este echivalentă cu executarea unei instrucțiuni sync all. O instrucțiune
sync all face ca toate imaginile să aștepte una pe cealaltă, în timp ce instrucțiunile sync images nu sunt necesare pentru
a specifica același set de imagini pentru toate imaginile care participă la sincronizare. În exemplul din Figura 19.5,
imaginea 1 va aștepta ca fiecare dintre celelalte imagini să ajungă la instrucțiunea sync images(1). Celelalte imagini
așteaptă ca imaginea 1 să configureze datele, dar nu se așteaptă una pe cealaltă.

Figura 19.5 Utilizarea imaginilor sincronizate pentru a face ca alte imagini să aștepte imaginea 1.
if (this_image() == 1) atunci ! Configurați
datele coarray necesare pentru toate celelalte imagini pentru sincronizarea
imaginilor (*)

sincronizați imagini (1)


! Utilizați datele configurate de imaginea 1 sfârșit
dacă

19.13.3 Declarațiile de blocare și deblocare

Blocările oferă un mecanism pentru controlul accesului la datele la care se referă sau sunt definite de mai multe imagini.

O blocare este o variabilă scalară de tip derivat lock_type care este definită în modulul intrinsec iso_fortran_env.
Tipul are componente private care nu sunt pointeri și nu sunt alocabile. Nu are atributul bind sau niciun parametru de
tip și nu este un tip de secvență. Toate componentele au inițializare implicită. O blocare trebuie să fie un coarray sau
un subobiect al unui coarray. Are una dintre cele două stări: blocat și deblocat. Starea deblocată este reprezentată de a
Machine Translated by Google

346 Fortran modern explicat

valoare unică și aceasta este valoarea inițială. Toate celelalte valori sunt blocate. Singura modalitate de a
schimba valoarea unui blocare este prin executarea instrucțiunii de blocare sau deblocare. De exemplu, dacă o
blocare este un argument inactiv sau un subobiect al unui argument inactiv, argumentul inactiv nu trebuie să
aibă intenție. Dacă o variabilă de blocare este blocată, aceasta poate fi deblocată numai de imaginea care a blocat-o.

Figura 19.6 Utilizarea blocării și deblocării pentru a gestiona


stivele. utilizarea modulului stack_manager, intrinsec ::
iso_fortran_env, numai: sarcină de tip lock_type

:
tip final
tip(lock_type), private :: stack_lock[*] tip(sarcină), private ::
stack(100)[*] întreg, private :: stack_size[*]
parametru
tip(sarcină),
:: null =
task( ...) conține

subrutina get_task(job)
! Obțineți o sarcină din tipul meu de
stivă (sarcină), intenție (out) :: blocare job
(stack_lock) dacă (stack_size>0) atunci

job = stack(stack_size) stack_size


= stack_size - 1 else job = final nul dacă

deblocare (stack_lock) final


subrutine get_task subrutine
put_task(job, image)
! Pune o sarcină pe stiva de tip de imagine
(sarcină), intent (în) :: număr întreg de
muncă, intenție (în) :: blocare imagine
(stack_lock[image]) stack_size[image] =
stack_size[image] + 1 stack(stack_size [image])[image] =
deblocare job (stack_lock[image]) termina subrutinei
put_task

sfârșitul modulului stack_manager

Figura 19.6 ilustrează utilizarea instrucțiunilor de blocare și deblocare pentru a gestiona stivele. Fiecare
imagine are propria sa stivă; orice imagine poate adăuga o sarcină la orice stivă. Dacă se execută o
instrucțiune de blocare pentru o variabilă de blocare care este blocată de o altă imagine, imaginea așteaptă
ca blocarea să fie deblocată de acea imagine. Efectul din acest exemplu este că get_task trebuie să aștepte dacă altul
Machine Translated by Google

Coarrays 347

imaginea adaugă o sarcină la stivă și put_task trebuie să aștepte dacă get_task primește o sarcină din stivă sau o
altă imagine execută put_task pentru aceeași stivă.
Există o formă a instrucțiunii de blocare care evită o așteptare când variabila de blocare este blocată:

logical :: succes lock


(stack_lock, acquired_lock=success)

Dacă variabila este deblocată, este blocată și valoarea succesului este setată la adevărat; în caz contrar, succesul
este setat la fals și nu trebuie așteptat.

O condiție de eroare apare pentru o instrucțiune de blocare dacă variabila de blocare este deja blocată de
imaginea în execuție și pentru o instrucțiune de deblocare dacă variabila de blocare nu este deja blocată de
imaginea în execuție. În ceea ce privește instrucțiunile de alocare și dealocare, specificatorul stat= este
disponibil pentru a evita ca aceasta să provoace terminarea erorii.
Orice variabilă de blocare anume este blocată și deblocată succesiv printr-o secvență de instrucțiuni de blocare
și deblocare, fiecare dintre acestea separând două segmente din imaginea în execuție. Dacă execuția unei astfel
de instrucțiuni de deblocare Pu pe imaginea P este urmată imediat în această secvență de execuția unei instrucțiuni
de blocare Ql pe imaginea Q, segmentul care precede execuția lui Pu pe imaginea P precede segmentul care
urmează execuției lui Ql pe imagine Q.

Pentru o alocare sursă a unui coarray (folosind source= pentru a-și lua valoarea dintr-o altă variabilă sau
expresie), expresia sursă nu este permisă să fie de tip lock_type sau să aibă o subcomponentă de acest tip,
deoarece aceasta ar crea o nouă blocare care ar putea fi blocat inițial.

19.13.4 Secțiuni critice

În mod excepțional, poate fi necesar să se limiteze execuția unei bucăți de cod la o singură imagine la un moment
dat. Un astfel de cod se numește o secțiune critică. Există un nou construct pentru a delimita o secțiune critică:

critic
: ! cod care este executat pe o singură imagine odată
sfâr itul critic

Nicio instrucțiune de control al imaginii nu poate fi executată în timpul execuției unui construct critic, adică codul
executat trebuie să fie un singur segment. Nu este permisă ramificarea într-o secțiune critică sau în afara acesteia.

Dacă imaginea Q este următoarea care execută constructul după imaginea P, segmentul este critic
secțiunea din imaginea P precede segmentul din secțiunea critică a imaginii Q.

19.13.5 Instrucțiunea memoriei de sincronizare și subrutinele atomice

Execuția unei instrucțiuni de memorie de sincronizare definește o graniță pe o imagine între două segmente,
fiecare dintre acestea putând fi ordonat într-un mod definit de utilizator în raport cu segmentele din alte imagini.
O modalitate de a efectua ordonarea definită de utilizator între imagini este prin utilizarea subrutinelor atomice,
cărora li se permite să încalce regulile de ordonare a segmentelor din Secțiunea 19.13.1.
Machine Translated by Google

348 Fortran modern explicat

Considerăm că construirea unui cod fiabil și portabil în acest fel este foarte dificilă – este prea ușor să introduceți erori
subtile care se manifestă doar ocazional. Prin urmare, nu recomandăm utilizarea instrucțiunii de memorie de sincronizare
sau a subrutinelor atomice și amânăm descrierea acestora la Anexa B.10.1.

19.13.6 Specificatorii stat= și errmsg= în instrucțiunile de sincronizare

Toate instrucțiunile de sincronizare, adică sincronizați tot, sincronizați imaginile, blocați, deblocați și sincronizați memoria, au
specificatorii opționali stat= și errmsg=. Ei au același rol pentru aceste declarații ca și pentru alocare și dealocare în Fortran
2003 (Secțiunea 16.11).
Dacă oricare dintre aceste instrucțiuni, inclusiv alocare și dealocare, întâlnește o imagine care a executat o instrucțiune
de oprire sau încheiere a programului și are un specificator stat=, variabilei stat= i se dă valoarea constantei stat_stopped_image
din modulul intrinsec iso_fortran_env și efectul executării instrucțiunii este de altfel același cu cel al executării instrucțiunii de
memorie de sincronizare. Fără un specificator stat=, execuția unei astfel de instrucțiuni inițiază terminarea erorii (Secțiunea
19.14).

19.13.7 Instrucțiunile de control al imaginii

Lista completă a instrucțiunilor de control al imaginii este

• sincronizați toate instrucțiunile;

• declarație de sincronizare a imaginilor;

• declarație de blocare sau deblocare;

• instrucțiune de sincronizare a memoriei;

• instrucțiunea de alocare sau dezalocare care implică un coarray; • declarație

critică sau critică finală; • instrucțiune end sau return care implică o dealocare

implicită a unui coarray; • o instrucțiune care finalizează execuția unui bloc (vezi Secțiunea 20.5.3) și are

ca rezultat o dealocare implicită a unui coarray;

• instrucțiunea de oprire sau de terminare a programului.

19.14 Încetarea programului

Pare natural să se permită tuturor imaginilor să continue executarea până când toate au executat o instrucțiune de oprire
sau de terminare a programului, cu condiția ca niciuna dintre ele să nu întâmpine o condiție de eroare care ar putea fi de
așteptat să-și încheie execuția. Aceasta se numește terminare normală. Pe de altă parte, dacă o astfel de condiție de eroare
apare pe o imagine, calculul este greșit și este de dorit să se oprească celelalte imagini cât mai curând posibil. Aceasta se
numește terminarea erorii.
Terminarea normală are loc în trei pași: inițiere, sincronizare și finalizare. O imagine inițiază terminarea normală dacă
execută o instrucțiune de oprire sau terminare a programului. Toate imaginile sincronizează execuția la al doilea pas, astfel
încât nicio imagine nu începe pasul de finalizare până când toate imaginile nu au terminat pasul de inițiere. Pasul de
sincronizare permite datelor sale
Machine Translated by Google

Coarrays 349

rămân accesibile celorlalte imagini până când toate ajung la pasul de sincronizare. Terminarea normală poate fi
inițiată și în timpul executării unei proceduri definite de un procesor C însoțitor.

O imagine inițiază terminarea erorii dacă execută o instrucțiune care ar cauza terminarea unui program cu o
singură imagine, dar nu este o instrucțiune de oprire sau de terminare a programului. Acest lucru face ca toate
celelalte imagini care nu au inițiat deja terminarea erorii să inițieze terminarea erorii. În limitele de performanță
ale capacității procesorului de a trimite semnale către alte imagini, este de așteptat ca toate imaginile să fie
terminate imediat.
Declaratia

oprire eroare [stop-code]

a fost introdus, unde stop-code este un întreg sau o expresie constantă de caracter implicită.
Când este executat pe o imagine, inițiază terminarea erorii acolo și, prin urmare, face ca toate celelalte imagini
care nu au inițiat deja terminarea erorii să inițieze terminarea erorii. Astfel, întregul calcul se oprește de îndată
ce este posibil. Semnificația codului stop este aceeași ca și pentru instrucțiunea stop, vezi secțiunile 5.3 și 20.1.6.

Exemplul din Figura 19.7 ilustrează utilizarea opririi și opririi erorilor într-un model climatic
care folosește două echipe, una pentru ocean și una pentru atmosferă.
Dacă ceva nu merge prost în calculul atmosferei, întregul model este invalid și o repornire este imposibilă,
așa că toate imaginile se opresc cât mai curând posibil, fără a încerca să păstreze date.

Dacă ceva nu merge ușor cu calculul atmosferei, imaginile din echipa de atmosferă își scriu datele în fișiere
și se opresc, dar datele lor rămân disponibile pentru imaginile oceanului, care completează execuția subrutinei
oceanului. La întoarcerea de la rutinele de calcul, dacă ceva nu a mers ușor cu calculul atmosferei, imaginile
oceanului scriu date în fișiere și se opresc, gata pentru o repornire într-o rulare ulterioară.

19.15 Intrare/ieșire

Așa cum fiecare imagine are propriile variabile, la fel are propriile sale unități de intrare/ieșire. Ori de câte ori o
instrucțiune de intrare/ieșire folosește o expresie întreagă pentru a indexa o unitate, se referă la unitatea de pe
imaginea în execuție.
Unitatea implicită pentru intrare (* într-o instrucțiune de citire sau input_unit în modulul intrinsec
iso_fortran_env) este preconectat numai pe imaginea 1.
Unitatea implicită pentru ieșire (* într-o instrucțiune de scriere sau output_unit în modulul intrinsec
iso_fortran_env) și unitatea care este identificată prin error_unit în modulul intrinsec iso_fortran_env sunt
preconectate pe fiecare imagine. Fișierele la care acestea sunt conectate sunt considerate separate, dar este de
așteptat ca procesorul să le îmbine înregistrările într-un singur flux sau într-un flux pentru toate fișierele
output_unit și un flux pentru toate fișierele error_unit. Dacă ordinea scrierilor din imagini este importantă, sunt
necesare sincronizarea și instrucțiunea de ștergere, deoarece imaginii i se permite să rețină datele într-un buffer
și să întârzie transferurile până când fie execută o instrucțiune de ștergere pentru fișier, fie fișierul este închis.

Orice altă unitate preconectată este conectată numai pe imaginea în execuție, iar fișierul este
complet separat de orice fișier preconectat pe altă imagine.
Machine Translated by Google

350 Fortran modern explicat

Figura 19.7 oprire și oprire de eroare într-un model climatic.


program climate_model
utilizare, intrinsec :: iso_fortran_env, numai: stat_stopped_image întreg, alocabil :: ocean_team(:),
atmosfera_team(:) întreg :: i, sync_stat

:
! Formați două echipe

echipă_oceană = [ (i,i=1,num_imagini()/2) ] echipă_atmosferă =


[ (i,i=1+num_imagini()/2,num_imagini()) ]
:

! Efectuați calcule independente


dacă (this_image() > num_images()/2) atunci apelați
atmosfera(atmosphere_team)
altfel

call ocean(ocean_team) end if

! Așteptați ca ambele echipe să termine

sincronizați tot (stat=sync_stat) dacă


(sync_stat == stat_stopped_image) atunci
: ! Păstrați datele în fișier

stop
end if

apelează exchange_data ! Schimb de date între echipe


:
con ine

subrutină atmosferă (echipă) întreg :: echipă(:)

: ! Efectuați calculul atmosferei. daca atunci ! Ceva a mers


ușor greșit
: ! Păstrați datele în fișier

stop
end if
:

dacă (...) eroare stop ! Ceva a mers foarte rău


:

sincronizați imagini (echipă, stat=sync_stat)) dacă (sync_stat


== stat_stopped_image) atunci
: ! Imaginile de atmosferă rămase păstrează datele într-o oprire a fișierului

sfâr itul dacă

sfâr itul atmosferei subrutinei


Machine Translated by Google

Coarrays 351

Declarația open conectează un fișier la o unitate numai pe imaginea de execuție. Dacă un fișier cu un nume
dat este același fișier pentru toate imaginile sau variază de la o imagine la alta, depinde de procesor.

Deși nu este permis ca un fișier să fie conectat la mai mult de o imagine în Fortran 2008,
este de așteptat ca un viitor Raport tehnic să definească o astfel de facilitate.

19.16 Proceduri intrinseci


Se adaugă următoarele proceduri intrinseci. Niciuna nu este permisă într-o expresie constantă.
Din nou, folosim paranteze pătrate italice [ ] pentru a indica argumente opționale.

19.16.1 Funcții de interogare

image_index (coarray, sub) returnează un scalar întreg implicit.

Dacă sub deține o secvență validă de cosubscripti pentru coarray, rezultatul este indexul de imagine
corespunzător. În caz contrar, rezultatul este zero.

coarray este un coarray de orice tip. sub

este o matrice întregă de rangul unu de dimensiune egală cu corangul coarray.

lcobound (coarray [ , dim ] [ , în același mod în kind ] ) returnează colimitele inferioare ale unui coarray
care lbound returnează limitele inferioare ale unei matrice.

ucobound (coarray [ , dim ] [ , în același mod în kind ] ) returnează colimitele superioare ale unui coarray
care ubound returnează limitele superioare ale unei matrice.

19.16.2 Funcții de transformare

num_images () returnează numărul de imagini ca un scalar întreg implicit.

this_image () returnează indexul imaginii invocate ca un scalar întreg implicit.

this_image (coarray [ , dim ] ) returnează setul de co-subscripte ale coarray care denotă date de pe imaginea
invocată. coarray este un coarray de orice tip. dim este un număr întreg scalar a cărui valoare este în

intervalul 1 dim n unde n este corrank

de coarray.

Dacă dim este absent, rezultatul este o matrice de numere întregi implicite de rang unu și dimensiune
egală cu corangul coarray; deține setul de cosubscripte ale coarray pentru datele de pe imaginea
invocată. Dacă dim este prezent, rezultatul este un scalar întreg implicit care deține cosubscript dim of
coaarray pentru datele din imaginea invocată.
Machine Translated by Google

352 Fortran modern explicat

Exerciții

1. Scrieți un program în care imaginea 1 citește o valoare reală dintr-un fișier și o copiază în celelalte imagini, apoi toate
imaginile își imprimă valorile. Este necesară o instrucțiune de sincronizare a tuturor înainte de imprimare?

2. Scrieți un program în care există un coarray de matrice alocabil care este alocat de dimensiunea 3, date valori pentru
toate imaginile de imaginea 1 și apoi imprimat de toate imaginile. Este necesară o instrucțiune de sincronizare a
tuturor după alocare?

3. Scrieți o subrutină care are un argument scalar coarray și îl înlocuiește cu suma tuturor valorilor din imagini cu doar
referințe τ la imaginile de la distanță, presupunând că numărul de imagini este 2τ.
Sugestie: Tratați imaginile ca într-un cerc și aranjați astfel încât, la începutul buclei a I-a, fiecare imagine să dețină
suma valorii sale originale și următoarele 2i -1 valori originale.

4. Să presupunem că avem o grilă dreptunghiulară de dimensiune nrow by ncol cu o valoare reală în fiecare punct și
ncol==num_images(). Primul și ultimul rând sunt considerate vecine, iar prima și ultima coloană sunt considerate
vecine. Dacă valorile sunt distribuite în coarray u(1:nrow)[*], scrieți o subrutină cu argumentele nrow, ncol și u care
înlocuiește fiecare valoare cu suma valorilor de la cei patru vecini minus de patru ori propria sa valoare.

5. Să presupunem că avem coarasele a(1:nx,1:ny)[*] și b(1:ny,1:nz)[*]. Presupunând că max(nx,ny,nz) num_images(),


scrieți cod pentru a copia datele din b în a cu redistribuire astfel încât a(i,j)[k] == b(j,k)[i] pentru toate valorile valide ale
indicilor.

Codul dvs. are blocaje în care aceeași imagine este cerută de date de mai multe imagini?
Dacă da, modificați-l pentru a evita acest lucru.

6. Adaptează-ți subrutina din exercițiul 3 pentru a se aplica unei echipe de imagini, adăugând un argument matrice care
conține indicii echipei și un argument scalar care menține poziția imaginii care execută în echipă, presupunând că
dimensiunea echipei este o putere de 2. Într-un program principal, configurați două echipe și valori într-un coarray,
apoi apelați subrutina simultan pentru cele două echipe.
Machine Translated by Google

20. Alte îmbunătățiri Fortran 2008

Repere ale celorlalte îmbunătățiri Fortran 2008 sunt un număr mare de funcții intrinseci noi, mai ales
funcții speciale matematice și manipulare de biți, precum și caracteristici care vizează piața de înaltă
performanță. Caracteristicile rămase sunt toate minore, în mare parte menite să faciliteze scrierea
programelor fără a oferi funcționalități noi semnificative.

20.1 Facilități sintactice banale

20.1.1 Matrice cu formă implicită

Când definiți o constantă numită care este o matrice, nu mai este necesar să declarați forma în avans:
forma poate fi luată din valoare. Aceasta se numește o matrice cu formă implicită și este specificată printr-
un asterisc ca limită superioară. De exemplu,

caracter, parametru :: vocale(*) = [ 'a', 'e', 'i', 'o', 'u']

În cazul unei constante de matrice numite de dimensiune mai mare, asteriscul trebuie specificat
pentru fiecare limită superioară, de exemplu

întreg, parametru :: puteri (0:*,*) = &


remodelare( [ 0, 1, 2, 3, 0, 1, 4, 9, 0, 1, 8, 27 ], [ 4,3])

declară că puterile au limite (0:3, 1:3).

20.1.2 Bucle-do implicite în instrucțiunile de date

În standardele Fortran până la Fortran 2003, expresiile utilizate pentru subscripte și limitele do implicite
într-o buclă implicită într-o declarație de date au fost limitate la combinații de constante, variabile
implicite și operații intrinseci. Acest lucru a fost mult mai strict decât cerințele pentru expresii constante
în altă parte; de exemplu, referirile la funcții intrinseci nu erau permise. Aceste restricții au fost acum
relaxate, astfel încât cerințele pentru aceste expresii sunt acum identice cu cele pentru alte expresii
constante.
De exemplu,

real :: a(10,7,3) date


((a(i,i,j),i=1,min(size(a,1),size(a,2))),j=1,size (a,3))/21*1,0/

este valabil Fortran 2008; în standardele anterioare acela i efect putea fi ob inut prin
Machine Translated by Google

354 Fortran modern explicat

real :: a(10,7,3)
întreg,parametru :: diagonal_size = min(size(a,1), size(a,2)) integer,parameter :: dim3_size
= size(a, 3) data (( a(i,i,j),i=1,diagonal_size),j=1,dim3_size)/21*1.0/

20.1.3 Proceduri legate de tip

O instrucțiune de declarație de procedură legată de tip are acum o listă de legături de procedură, astfel
încât mai multe proceduri legate de tip pot fi declarate într-o singură instrucțiune. De exemplu, în loc de

tip mycomplex
:
con ine
procedura :: procedura
i_plus_myc :: procedura
myc_plus_i :: procedura myc_plus_myc => procedura
myc_plus :: procedura myc_plus_r :: r_plus_myc

:
tipul de capăt

se poate scrie

tip mycomplex
:
con ine
procedura :: i_plus_myc, myc_plus_i, myc_plus_myc => myc_plus, &
myc_plus_r, r_plus_myc
:
tipul de capăt

Aceasta poate fi o îmbunătățire semnificativă atunci când un tip are multe proceduri legate de tip.

20.1.4 Constructori de structură

Un constructor de structură poate omite valoarea pentru o componentă alocabilă; aceasta este echivalentă cu
specificarea null() pentru acea valoare a componentei. De exemplu, având în vedere definiția tipului

tip element
caracter(:), alocabil :: nume întreg tip final
:: n_in_stoc = 0

constructorul de structură item() este permis și este echivalent cu item(null()). Omiterea acestei
caracteristici din Fortran 2003 a fost într-adevăr doar o neglijență.
Machine Translated by Google

Alte îmbunătățiri Fortran 2008 355

20.1.5 Punct virgulă

O linie de continuare în program este acum permisă să înceapă cu punct și virgulă. De exemplu,

a = 1; b = 2; c = 3& ; d = 4; e = 5

Acest lucru a fost invalid conform standardelor Fortran 95 și 2003, dar restricția a fost larg acceptată ca fiind
o greșeală și puțini compilatori au aplicat-o. Acum este considerat acceptabil de către Fortran 2008.

20.1.6 Instrucțiunea stop

Instrucțiunea stop acceptă acum orice expresie constantă scalară cu caracter întreg implicit sau caracter implicit
ca cod de oprire, în loc de doar literale simple. De exemplu,

caracter(*), parametru :: pu_name = 'load_data_type_1'


:
stop pu_name//': valoare în afara intervalului'

În plus, valoarea unui cod de oprire întreg nu este limitată la intervalul 0 99999, deci declarația „stop -2**20”
este validă.
În cele din urmă, standardul recomandă ca codul de oprire să fie scris în fișierul de eroare (unit error_unit al
modulului intrinsec iso_fortran_env) și, dacă este un număr întreg, să fie folosit ca stare de ieșire a procesului
dacă sistemul de operare are un astfel de concept ( și să fie furnizată o stare de ieșire de zero dacă codul de
oprire este de tip caracter sau programul este terminat printr-o instrucțiune de final de program). Totuși, acestea
sunt doar recomandări și, în orice caz, sistemele de operare au adesea doar un interval limitat pentru starea de
ieșire a procesului, astfel încât valorile din afara intervalului 0 127 ar trebui evitate în acest scop.

Aceleași recomandări se aplică instrucțiunii de stop de eroare, adică dacă furnizează un cod de oprire întreg,
să fie utilizat pentru starea de ieșire și, în caz contrar, starea de ieșire ar trebui să fie zero. Acest lucru este
oarecum în dezacord cu convențiile tipice ale sistemului de operare, în care codurile de ieșire diferite de zero
indică în mod convențional terminarea erorilor, mai ales că în alte situații de terminare a erorilor, cum ar fi o
eroare de intrare/ieșire netratată sau eșec de alocare, standardul Fortran este tăcut asupra ieșirii. starea ar
trebui să fie. Din nou, se pare că această facilitate este dificil de utilizat într-un mod portabil.

Din aceste motive, recomandăm utilizarea unui mesaj informativ mai degrabă decât a unui număr întreg, atât
pentru instrucțiunile stop cât și pentru eroare stop.

20.1.7 Ieșire din aproape orice construcție

Instrucțiunea de ieșire poate fi acum utilizată pentru a transfera controlul la sfârșitul unui construct asociat, bloc,
if, select case sau select type. Pentru a face acest lucru, constructul trebuie să fie numit și acel nume folosit în
instrucțiunea de ieșire. Un exemplu în acest sens este prezentat în Figura 20.1.

Rețineți că o instrucțiune de ieșire fără un nume de construcție iese în continuare din construcția do cea mai din interior.

Deoarece diferitele comportamente pot confunda cu ușurință, vă recomandăm ca dacă această nouă ieșire (de la
Machine Translated by Google

356 Fortran modern explicat

Figura 20.1 Ieșire din construcția if.


adding_to_set: if (add_x_to_set) atunci find_position: do
i=1, size(set)
if (x==set(i)) exit adding_to_set if (x>set(i)) exit
find_position end do find_position set = [ set(:i-1),
x, set(i:) ] end if adding_to_set

un construct non-do) este folosit în apropierea unei ieșiri dintr-un construct do, ambele instrucțiuni de ieșire
au etichete de construcție.
Construcțiile pentru care noua ieșire nu poate fi folosită sunt cele critice și fac constructii concurente (vezi
Secțiunile 19.13.4 și 20.4.1). De asemenea, este interzisă ieșirea dintr-o construcție exterioară dintr-o
construcție critică sau executarea concomitentă.

20.2 Modificări ale limitărilor

20.2.1 Suport pentru numere întregi pe 64 de biți

Dimensiunea maximă a întregului este acum necesară pentru a avea un interval de cel puțin 18 cifre zecimale;
adică declara ia

întreg(selected_int_kind(18)) :: big_int

va fi neapărat acceptată, iar pe o mașină binară (toate computerele moderne) aceasta va fi o variabilă întreagă
de 64 de biți.

20.2.2 Rangul maxim al matricei

În conformitate cu apariția mașinilor pe 64 de biți și cu dimensiunile mult mai mari de memorie care sunt
acum disponibile, rangul maxim al unei matrice a fost crescut de la 7 la 15. De exemplu,

întreg, parametru :: ieee_single = ieee_selected_real_kind(6) real(ieee_single) :: x(10, 10, 10,


10, 10, 10, 10, 10, 10, 10)

declară o matrice care are 1010 elemente; întrucât dimensiunea unei virgule mobile IEEE cu precizie unică
este de patru octeți, aceasta necesită 40 GB de memorie.
Dacă o matrice este, de asemenea, un coarray, limita se aplică sumei rangului și corancului.

20.3 Expresivitatea datelor

20.3.1 Componente alocabile de tip recursiv

O componentă alocabilă este acum permisă să fie de orice tip derivat, inclusiv tipul care este definit sau un tip
definit mai târziu în unitatea de program. Aceasta poate fi folosită pentru a defini
Machine Translated by Google

Alte îmbunătățiri Fortran 2008 357

structuri dinamice fără a implica pointeri, câștigând astfel beneficiile obișnuite ale variabilelor alocabile: fără
aliasing (cu excepția cazului în care este utilizat atributul țintă), contiguitate și dealocare automată. Dealocarea
automată înseamnă că dealocarea variabilei părinte (sau revenirea din procedura în care este definită) va
dezaloca complet întreaga structură dinamică.

Figura 20.2 Exemplu de listă alocabilă. tastați


my_real_list valoarea reală

tip(my_real_list), allocabil :: următorul tip final tip(my_real_list),


allocatable, target :: list type(my_real_list), pointer :: last real ::
x

:
ultimul => null()
do
citește (unitate, *, iostat=ios) x if (ios/=0)
exit if (.not.associated(last)) apoi alocă
(listă, sursă=my_real_list(x)) last => list

altfel
alocă (last%next, source=my_real_list(x)) last => last%next

sfâr itul dacă

sfâr itul face

! lista conține acum toate valorile de intrare, în ordinea citirii.


:
dealocare (lista) ! dealocarea fiecărui element din listă.

Figura 20.2 arată cum aceasta poate fi folosită pentru a construi o listă. La construirea listei din
acel exemplu, a fost convenabil să folosiți un indicator către sfârșitul listei. Dacă, pe de altă parte,
dorim să inserăm o nouă valoare în altă parte (cum ar fi la începutul listei), se recomandă utilizarea
cu grijă a intrinsecului move_alloc pentru a evita realizarea unor copii temporare ale întregii liste.
Ilustram acest lucru cu împingerea subrutinei pentru adăugarea unui element în partea de sus a
unei stive din Figura 20.3. Comentarii similare se aplică ștergerii elementelor, ilustrate prin pop
subrutine în Figura 20.3.
S-ar putea imagina că compilatorul va produce cod similar (adică, evitarea codului
copii adânci) pentru enun urile mult mai simple

list = my_real_list(newvalue, list)

și

list = list%next
Machine Translated by Google

358 Fortran modern explicat

Figura 20.3 Proceduri de stivă alocabile. push


subrutine (listă, valoare nouă)
tip(my_real_list), allocabil :: list, temp real, intent(in) call
:: valoare nouă
move_alloc(list, temp) allocate (list, source=my_real_list(x))
apel move_alloc(temp, list%next) final subrutine

subrutine pop(lista)
tip(my_real_list), alocabil :: listă, apel temporar
move_alloc(list%next, temp) apel move_alloc(temp, listă)

sfâr itul subrutinei

ca părțile executabile ale push și respectiv pop, dar de fapt modelul pentru alocare alocabilă din
standard specifică dealocarea automată numai atunci când o formă de matrice, un parametru de
tip lungime sau un tip dinamic diferă; nu este cazul în aceste exemple, așa că se așteaptă ca
compilatorul să efectueze o copiere profundă. (Un program conform standardului poate face
diferența doar atunci când tipul are subrutine finale sau lista are atributul țintă; deci, dacă variabilele
implicate nu sunt polimorfe și nu sunt ținte, un compilator ar putea produce un cod mai optim.)

20.3.2 Asocierea inițială a pointerului

Starea inițială de asociere a unui pointer poate fi acum definită pentru a fi asociată cu o țintă, atâta
timp cât acea țintă are atributul de salvare și nu are atributul alocabil. De exemplu,

real, țintă :: x(10,10) = 0 real, pointer ::


p(:,:) => x

Mai mult, un pointer poate fi asociat cu o parte a unei astfel de ținte, inclusiv cu o secțiune de
matrice (dar nu cu un indice vectorial). Orice poziție de indice sau subșir din specificația țintă trebuie
să fie o expresie constantă. De exemplu,

real, pointer :: column_one(:) => x(:,1)

Acest lucru se aplică și inițializării implicite a componentelor structurii. De exemplu, în

tip tpc(ipos, jpos) întreg,


fel :: ipos, jpos real, pointer :: pc =>
x(ipos, jpos) tip final tip(tpc(2, 8)) :: ps28 tip(tpc(3,
5) )) :: tip ps35(tpc(7, 9)) :: ps79 = tpc(x(1, 1))
Machine Translated by Google

Alte îmbunătățiri Fortran 2008 359

componenta pointer ps28%pc este asociată cu x(2,8), ps35%pc este asociată cu x(3,5) și ps79%pc este asociată
cu x(1,1). Cu toate acestea, astfel de zbârcituri pot fi puțin confuze, așa că vă recomandăm ca această
caracteristică să fie utilizată cu moderație și, poate, numai pentru indicatorii cu nume.

20.4 Caracteristici orientate spre performanță

20.4.1 Construc ia do concurrent

O nouă formă a construcției do, constructul do concurrent, este furnizată pentru a ajuta la îmbunătățirea
performanței, permițând execuția paralelă a iterațiilor buclei. Ideea de bază este că, folosind acest construct,
programatorul afirmă că nu există interdependențe între iterațiile buclei. Efectul este similar cu cel al diferitelor
directive specifice compilatorului, cum ar fi '!dec$ivdep'; astfel de directive sunt disponibile de mult timp, dar au
adesea semnificații ușor diferite pe diferite compilatoare.

Utilizarea do concurrent are o listă lungă de cerințe care pot fi grupate în „limitări” asupra a ceea ce poate
apărea în construcție și „garantează” de către programator că calculul are anumite proprietăți (în esență, fără
dependențe) care permit paralelizarea. Rețineți că, în acest context, paralelizarea nu necesită neapărat mai multe
procesoare sau chiar dacă sunt disponibile mai multe procesoare, acestea vor fi utilizate: alte optimizări care
îmbunătățesc performanța cu un singur thread sunt, de asemenea, activate de aceste proprietăți, inclusiv
vectorizarea, pipeliningul și alte posibilități. pentru suprapunerea execuției instrucțiunilor de la mai mult de o
iterație pe un singur procesor.

Forma instrucțiunii do concurrent este similară cu cea a constructului forall

declarație, inclusiv noile îmbunătățiri (vezi Secțiunea 20.5.6), și anume

do [ , ] concurent ( [ tip-spec :: ] index-spec-list [ , scalar-mask-expr ] )

unde tip-spec (dacă este prezent) specifică tipul și tipul variabilelor de index, iar index spec-list este o listă de
specificații de index de forma

index-variable-name = initial-value : final-value [ : step-value ]

ca în

faceți concomitent (i=1:n, j=1:m)

Fiecare nume-variabilă-index este locală buclei, deci nu are niciun efect asupra vreunei variabile cu același
nume care ar putea exista în afara buclei; totuși, dacă tip-spec este omis, are tipul și tipul pe care le-ar avea dacă
ar fi o astfel de variabilă. În ambele cazuri, trebuie să fie scalar și să aibă tip întreg. Fiecare valoare inițială,
valoare finală și valoare de pas este o expresie întreagă scalară.
Opționalul scalar-mask-expr este de tip logic; dacă apare, doar acele iterații care
satisface condiția sunt executate. Acesta este,

face concurente (i=1:n, j=1:m, i/=j)


:
:
sfâr itul face
Machine Translated by Google

360 Fortran modern explicat

are exact acelasi sens ca

faceți concomitent (i=1:n, j=1:m)


dacă (i/=j) atunci
:
sfâr itul dacă

sfâr itul face

Un exemplu simplu de construcție concomitentă este

face concurente (i=1:n) a(i, j) =


a(i, j) + alfa*b(i, j)
sfâr itul face

Următoarele elemente sunt toate interzise într-o construcție concurentă do (și compilatorul
este necesar pentru a detecta acestea):

• o declarație de returnare;

• o instrucțiune de control al imaginii (vezi capitolul 19); •

o ramură (de exemplu, go to sau err=) cu o etichetă care se află în afara constructului; • o referire la

o procedură care nu este pură; • o referire la una dintre procedurile ieee_get_flag,

ieee_set_halting_mode sau ieee_get_halting_mode din modulul intrinsec ieee_exceptions; • o instruc iune


exit care ar ie i din constructul do concurrent; i • o instruc iune de ciclu care denume te un
construct exterior do.

Folosind do concurrent, programatorul garantează:

• orice variabilă referită este fie definită anterior în aceeași iterație, fie valoarea acesteia este
nu este afectat de nicio altă iterație;

• orice pointer care este referit este fie un pointer asociat anterior în aceeași iterație, fie nu are asocierea
pointerului său schimbată de nicio altă iterație; • orice obiect alocabil care este alocat sau dealocat într-o

singură iterație nu este


referit sau definit prin orice altă iterație;

• orice obiect alocabil care este alocat de mai multe iterații este ulterior dealocat de aceleași iterații;

• orice obiect alocabil care este alocat sau dealocat de mai mult de o iterație nu este referit, definit sau
dealocat de nicio iterație care nu îl alocă mai întâi; și • înregistrările (sau pozițiile) dintr-un fișier nu sunt

atât scrise printr-o iterație, cât și citite înapoi


altă iterație.

Dacă înregistrările sunt scrise într-un fișier secven ial prin mai mult de o iterație a buclei, ordonarea între
înregistrările scrise prin diferite iterații este nedeterminată. Adică, înregistrările scrise de o iterație pot apărea
înaintea înregistrărilor scrise de cealaltă, după înregistrările scrise de cealaltă sau pot fi intercalate.

În plus, când execuția constructului sa încheiat, • orice variabilă a cărei

valoare este afectată de mai mult de o iterație devine nedefinită la terminarea buclei; și
Machine Translated by Google

Alte îmbunătățiri Fortran 2008 361

• orice pointer a cărui stare de asociere este schimbată de mai multe iterații are un
statutul de asociere de nedefinit.

Rețineți că orice buclă do obișnuită care satisface limitările și care are în mod evident proprietățile necesare
poate fi paralelizată, deci utilizarea concurenței do nu este necesară pentru execuția paralelă. De fapt, un
compilator care paralelizează do concurrent este probabil să îl trateze ca pe o solicitare ca să paralelizeze acea
buclă; dacă numărul de iterații al buclei este foarte mic, acest lucru ar putea duce la o performanță mai slabă
decât o buclă do obișnuită din cauza supraîncărcării de inițiere a firelor de execuție paralele. Astfel, chiar și
atunci când garanțiile oferite de programator sunt derivate trivial din corpul buclei în sine, do concurrent este
încă util pentru

• indicarea compilatorului că este probabil să aibă un număr de iterații suficient de mare


faceți paralelizarea să merite; •
documentarea paralelizabilității pentru citirea și întreținerea codului; și • ca o cârjă pentru
compilatorii ale căror capacități de analiză sunt limitate.

20.4.2 Atributul contiguu

Atributul contiguu este un nou atribut pentru pointerii de matrice și matricele simulate de formă presupusă.
Specifică faptul că matricea va fi întotdeauna asociată cu o țintă învecinată sau cu un argument real și nu va fi
niciodată asociată cu una necontiguă. Ideea de bază este că o matrice contiguă este una în care elementele nu
sunt separate de alte obiecte de date.
O matrice necontiguă arhetipală este o secțiune de matrice cu mai mult de un element în care elementele
adiacente din secțiune nu sunt adiacente în matricea originală (de bază); de exemplu,

vector(::2) ! toate elementele impare

dtarray%re ! părțile reale ale unui tablou complex

În prima linie, elementele adiacente ale secțiunii sunt separate de unul dintre elementele pare ale vectorului
original. În a doua linie (care folosește sintaxa din Secțiunea 20.5.1), elementele adiacente ale secțiunii sunt
separate de componentele imaginare ale originalului
matrice.

Știind că o matrice este învecinată în acest sens simplifică parcurgerea matricei și calculele de adrese ale
elementelor de matrice, îmbunătățind potențial performanța. Dacă această îmbunătățire este semnificativă
depinde de fracțiunea de timp petrecută efectuând operațiuni de parcurgere și calcul de adrese; în unele
programe acest timp este substanțial, dar în multe cazuri este nesemnificativ în primul rând.

În mod tradițional, standardul Fortran s-a ferit de a specifica dacă matricele sunt învecinate, în sensul că
ocupă locații de memorie secvențiale fără spații neocupate. În trecut, această tradiție a permis implementări de
înaltă performanță ale limbajului multiprocesor, dar atributul contigu este o mișcare către limitări hardware mai
specifice. Deși matricele învecinate sunt descrise doar în termeni de restricții de limbă și nu în termeni de
hardware de memorie, interacțiunea dintre acestea și interoperabilitatea cu limbajul C înseamnă că aceste
matrice vor fi aproape sigur stocate în locații de memorie adiacente.

Oricare dintre următoarele matrice este considerată a fi învecinată conform standardului:

• o matrice cu atributul contiguu;


Machine Translated by Google

362 Fortran modern explicat

• o matrice întreagă (denumită matrice sau componentă matrice fără alte calificări) adică
nu un indicator sau o formă presupusă;

• o matrice de formă presupusă care este argument asociat cu o matrice care este contiguă; • un tablou

alocat printr-o instruc iune de alocare; • un pointer asociat cu o țintă adiacentă; sau • o sec iune de

matrice de dimensiuni diferite de zero cu condi ia ca

– obiectul său de bază este contiguu;

– nu are un indice vectorial; – elementele

secțiunii, în ordinea elementelor matrice, sunt elemente ale obiectului de bază


care sunt consecutive în ordinea elementelor de matrice;

– dacă tabloul este de tip caracter și apare un selector de subșir, selectorul


specifică toate caracterele șirului de caractere;

– nu este o componentă a unui tablou; și – nu

este partea reală sau imaginară a unei matrice de tip complex.

Un subobiect (al unui tablou) cu siguranță nu este contiguu dacă se aplică toate aceste condiții:

• it (subobiectul) are două sau mai multe elemente; •

elementele sale în ordinea elementelor matrice nu sunt consecutive în elementele originalului


matrice;

• nu este o matrice de caractere de lungime zero; și •

nu este de tip derivat fără componente finale, altele decât matrice de dimensiune zero și
șiruri de caractere de lungime zero.

Dacă o matrice care nu se află în nicio listă este contiguă sau nu este specific compilatorului.
Atributul contiguous poate fi specificat cu cuvântul cheie contiguous într-o declarație de tip, de exemplu

subrutină s(x) real,


contiguu :: x(:,:) real, pointer, contiguu ::
coloană(:)

Poate fi specificat și prin instrucțiunea contiguă, care are forma

contiguu [::] listă-nume-obiect

Contiguitatea poate fi testată cu funcția de interogare

is_contiguous (a) unde a este o matrice de orice tip. Aceasta returnează un scalar logic implicit cu valoarea .true.
dacă a este învecinat i .fals. in caz contrar. Dacă a este un pointer, acesta trebuie să fie asociat cu o țintă.

Matricele în C sunt întotdeauna contigue, deci c_loc nu a fost permis în Fortran 2003 pentru un pointer de
matrice sau o matrice de formă presupusă. În Fortran 2008, c_loc este permis pentru orice țintă care este învecinată
(la momentul execuției). Exemplul din Figura 20.4 folosește is_contiguous pentru a verifica dacă i se cere să
proceseze un obiect contiguu și produce un mesaj de eroare dacă nu este. De asemenea, folosește noua funcție
c_sizeof pentru a calcula dimensiunea lui x în octeți (vezi Secțiunea 20.13.1).
Machine Translated by Google

Alte îmbunătățiri Fortran 2008 363

Figura 20.4 Utilizarea is_contiguous înainte de a utiliza c_loc. proces de


subrutină (x)
real(c_float), target :: x(:) subrutine de interfață
c_routine(a, nbytes) folosește iso_c_binding

type(c_ptr), value integer(c_size_t), value :: nbytes


end subroutine
:: A

interfață finală
:

dacă (este_contiguu(x)) atunci


apelați c_routine(c_loc(x), c_sizeof(x))
altfel

oprire „x trebuie să fie contiguu”


sfâr itul dacă

sfâr itul subrutinei

Există și conceptul de pur și simplu contiguu; adică nu numai că obiectul este contiguu, dar se poate
vedea că este în mod evident la momentul compilării. Spre deosebire de „a fi învecinat”, acest lucru este
complet standardizat. Acest lucru este discutat în continuare în secțiunea următoare.
Când aveți de-a face cu tablouri și pointeri de matrice contigue, este important să aveți în vedere diferitele cerințe și
restricții de rulare. Pentru matricele de formă presupusă, atributul contiguu nu face cerințe suplimentare programului:
dacă argumentul real nu este contiguu, se face o copie locală la intrarea în procedură și orice modificări ale valorii sale
sunt copiate înapoi în argumentul actual pe Ieșire. În funcție de cantitatea și modalitatea referințelor la matrice din
procedură, costul copierii poate fi mai mare decât orice economie presupusă de performanță dată de atributul contiguu.
De exemplu, în

funcție complexă f(v1, v2, v3) reală, contiguă,


intenție(în) :: v1(:), v2(:), v3(:) f = cmplx(sum(v1*v2*v3))**( -size(v1)) funcția finală

deoarece tablourile sunt accesate o singură dată, dacă orice argument real este necontiguu, aceasta va funcționa cu mult
mai rău decât dacă atributul contiguu nu ar fi prezent.
Pentru pointerii matrice, atributul contiguu are o cerință de rulare ca să fie asociat doar cu o țintă adiacentă (prin
atribuirea pointerului). Cu toate acestea, este responsabilitatea programatorului să verifice acest lucru sau să „știe” că
pointerul nu va deveni niciodată asociat cu o secțiune necontiguă. (Asemenea cunoștințe sunt predispuse să devină false
în cursul întreținerii programului, deci se recomandă verificarea fiecărei atribuiri de pointer.) Comentarii similare se aplică
la utilizarea funcției c_loc pe o matrice care ar putea să nu fie contiguă. Dacă aceste cerințe sunt încălcate, programul va
produce aproape sigur răspunsuri incorecte fără nicio indicație a eșecului.
Machine Translated by Google

364 Fortran modern explicat

20.4.3 Designatori de matrice pur și simplu contigui

Un desemnator de matrice pur și simplu contiguu este, în principiu, un desemnator care nu numai că
descrie o matrice (sau o secțiune de matrice) care este învecinată, dar care poate fi văzut cu ușurință în
momentul compilării ca fiind contiguă. Dacă un desemnator este pur și simplu contigu nu depinde de
valoarea vreunei variabile.
O matrice pur și simplu contiguă poate fi utilizată în următoarele moduri:

• ca țintă a unei atribuiri de pointer de remapare a rangului (adică asocierea unui pointer cu o țintă de
un rang diferit, vezi Secțiunea 15.6) – aceasta permitea anterior numai tablouri de rang unu;

• ca un argument real corespunzător unui argument inactiv care nu este o matrice de formă presupusă
sau care este o matrice de formă presupusă cu atributul contiguu, când ambele au fie atributul
asincron sau volatil;
• ca argument real corespunzând unui pointer inactiv cu atributul contiguu (aceasta necesită, de
asemenea, ca argumentul actual să aibă pointerul sau atributul țintă).

Exemplul din Figura 20.5 „aplatizează” matricea a într-un vector simplu și apoi îl folosește
pentru a asocia un alt pointer cu diagonala matricei.

Figura 20.5 Diagonala matricei contigue. real,


target :: a(n, m) real, pointer :: a_flattened(:),
a_diagonal(:) a_flattened(1:n*m) => a a_diagonal

=> a_platit(::n+1)

În Figura 20.6, copierea în copiere trebuie evitată în apelul start_bufferin deoarece va începe o operație
de intrare asincronă pentru a citi valorile în matrice și citirea va continua după revenire. Deoarece ambele x
și y sunt pur și simplu învecinate, copierea în copiere este evitată.

Figura 20.6 Buffer contigu pentru intrare/ieșire asincronă.


subrutină
interfață start_bufferin(a, n) întreg, intent(in)
real, intent(out), asincron :: a(n) :: n

sfâr itul subrutinei


interfață finală
reală, asincronă :: x(n), y(n)
:
apelează start_bufferin(x)
:
apelează start_bufferin(y)
Machine Translated by Google

Alte îmbunătățiri Fortran 2008 365

Un alt exemplu de utilizare a pur și simplu contiguu pentru a impune contiguitatea unui real
argumentul este explicat în Secțiunea 20.14.2.
De asemenea, pentru asocierea argumentelor cu un coarray inactiv (vezi Secțiunea 19.6) care este o matrice
cu atributul contiguu sau o matrice care nu are formă asumată, argumentul real trebuie să fie pur și simplu
contiguu pentru a evita orice posibilitate de copiere. în copiere care are loc. Din păcate, standardul Fortran nu
necesită detectarea încălcării acestei reguli, ceea ce înseamnă că un program care o încalcă se poate bloca sau
produce răspunsuri greșite fără niciun avertisment.

De asemenea, atunci când o matrice pur și simplu învecinată cu atributul țintă și nu atributul valoare este
utilizată ca argument real corespunzător unui argument inactiv care are atributul țintă și este o matrice de
formă presupusă cu atributul contiguu sau este o formă explicită
matrice,

• un pointer asociat cu argumentul real devine asociat cu manechinul


argumentare privind invocarea procedurii; și
• când execuția procedurii se încheie, pointerii din alte domenii care au fost asociate cu argumentul inactiv
sunt asociați cu argumentul actual.

Cu toate acestea, nu recomandăm folosirea acestui fapt complicat, deoarece este greu de înțeles și întreținerea
programului este destul de probabil să rupă una dintre condițiile esențiale pentru aplicabilitatea acestuia.

Un designator de matrice este pur și simplu contiguu dacă și numai


dacă este • un întreg tablou care are atributul contiguu; • un întreg
tablou care nu este o matrice de formă presupusă sau un pointer de matrice; sau • o
sec iune a unui tablou pur i simplu contigu care – nu este partea reală sau
imaginară a unui tablou complex (vezi Sec iunea 20.5.1); – nu are selector de subșiruri; –
nu este o componentă a unui tablou; și – fie nu are o listă de secțiuni-subscript, fie are o
listă de secțiuni-subscript care specifică o secțiune pur și simplu adiacentă.

O listă-secțiune-subindice specifică o secțiune pur și simplu contigua dacă și numai dacă •


nu are un indice vectorial; • toate, cu excepția ultimului indice-triplet, sunt două puncte;
• ultimul indice-triplet nu are pas; și • niciun indice-triplet nu este precedat de un indice-
secțiune care este un indice.

O variabilă matrice este pur și simplu contiguă dacă și numai dacă este un designator de matrice pur și
simplu contiguă sau o referință la o funcție care returnează un pointer cu atributul contiguu.

20.5 Expresivitatea computațională

20.5.1 Accesarea unor părți ale variabilelor complexe

În Fortran 2003 și mai devreme, părțile reale și imaginare ale unei variabile complexe erau accesibile doar prin
funcțiile intrinseci real și aimag. Acest lucru a fost incomod pentru actualizarea unui
Machine Translated by Google

366 Fortran modern explicat

variabilă complexă, astfel încât acestea pot fi acum accesate ca pseudo-componente re și im pentru părțile reale
și, respectiv, imaginare. De exemplu,

complex :: impedan ă
impedan ă%re = 1,0

Selectorii re și im pot fi aplicați și la matrice complexe, unde produc o matrice


secțiune care cuprinde partea reală sau imaginară a fiecărui element al matricei. De exemplu,

complex :: x(n), y(n) x%im =


2,0*y%im

20.5.2 Funcții pointer care denotă variabile

Când o funcție pointer returnează un pointer asociat, acel pointer este întotdeauna asociat cu o variabilă care
are atributul țintă, fie prin alocare de pointer, fie prin alocare. Fortran 2008 permite ca o astfel de referință la o
funcție de indicator să fie utilizată în contexte care până acum necesitau o variabilă, în special

• ca un argument real pentru o intenție inout sau out dummy argument; • în partea
stângă a unei declarații de atribuire.

În acest sens, o referință de funcție pointer poate fi folosită exact ca și cum ar fi variabila
aceasta este ținta rezultatului indicatorului.
Acestea sunt uneori cunoscute ca funcții accesorii; prin abstracția locației variabilei, ele permit obiectelor cu
caracteristici speciale, cum ar fi stocare rară, accese instrumentate și așa mai departe, să fie utilizate ca și cum
ar fi matrice normale. Ele permit, de asemenea, modificarea mecanismelor de implementare subiacente fără a
fi nevoie să se schimbe codul folosind obiectele. Un exemplu al acestei caracteristici este prezentat în Figura
20.7.

20.5.3 Construcția bloc

Construcția bloc este un nou tip de unitate de definire a domeniului care este un construct executabil, oferind
capacitatea de a declara entități din partea executabilă a unui subprogram care au domeniul de aplicare al
constructului. Astfel de entități pot fi variabile, tipuri, constante sau chiar proceduri externe.
Orice entitate a unității de delimitare gazdă cu același nume este ascunsă de declarație.
De exemplu, în

do i=1, m bloc

alfa reală, temp(n) întreg j

:
temp(j) = alfa*a(j, i) + b(j)
:
bloc final
sfâr itul face
Machine Translated by Google

Alte îmbunătățiri Fortran 2008 367

Figura 20.7 Exemplu de funcții accesorii. modulul


indexed_store
real, privat, pointer întreg, privat, :: valori(:) => null()
pointer :: chei(:) => null() întreg, privat conține funcția stocare(cheie) întreg,
intent(in) :: cheie real, pointer :: număr întreg::de
maxvals = ::
stocare 0 loc dacă
(.nu.asociat(valori)) atunci

alocare (valori(100), chei(100)) chei(1) = stocare chei =>


valori(1) maxvals = 1

altfel

loc = findloc(keys(:maxvals), key) if (loc>0) then storage


=> values(loc)

altfel

: (Cod pentru a stoca elementul nou eliminat.) end if

sfâr itul dacă

funcția finală
modul final
:

stocare(13) = 100 print *,


stocare(13)

variabilele alfa, temp și j sunt locale blocului și nu au niciun efect asupra variabilelor din afara blocului care ar putea avea
același nume. Folosit judicios, acest lucru poate face codul mai ușor de înțeles (nu este nevoie să căutați întregul
subprogram pentru accesări ulterioare la alpha, de exemplu) și, deoarece compilatorul știe, de asemenea, că acestea sunt
locale pentru fiecare iterație, acest lucru poate ajuta la optimizare.

Un alt exemplu este

bloc

utilizați modulul_convoluție
norma intrinseca2
:

x = convolut(y)*norma2(z)
:
bloc final
Machine Translated by Google

368 Fortran modern explicat

Aici, entitățile aduse de instrucțiunea use sunt vizibile doar în interiorul blocului, iar declarația intrinsecă
a norm2 evită ciocnirea cu orice norm2 care ar putea exista în afara blocului. Aceste tehnici pot fi utile în
subprograme mari sau în timpul întreținerii codului atunci când se dorește accesarea unui modul sau
procedură fără a risca perturbarea restului subprogramului.

Nu toate declarațiile sunt permise într-un construct bloc. Declarațiile de intenție, opționale și de
valoare nu sunt disponibile (deoarece un bloc nu are argumente fictive), iar instrucțiunea implicită este
interzisă deoarece ar fi confuz să se schimbe regulile implicite de tastare în mijlocul unui subprogram.
Definițiile funcției de instrucțiuni, declarațiile comune, de echivalență și lista de nume sunt toate interzise
din cauza potențialelor ambiguități sau confuzii.
În cele din urmă, este permisă o instrucțiune de salvare care specifică entitățile din bloc, dar o salvare
globală este interzisă, din nou, deoarece ar fi ambiguă în ceea ce privește exact ce ar fi salvat.
Ca și altor constructe, constructului bloc i se poate da un nume de construcție, iar acel nume de
construcție poate fi folosit în instrucțiunile de ieșire pentru a ieși din construct (vezi Secțiunea 20.1.7). În
mod similar, constructele bloc pot fi imbricate în același mod în care sunt imbricate alte constructe. Un
exemplu în acest sens este prezentat în Figura 20.8.

Figura 20.8 Construcții de blocuri de imbricare.


find_solution: bloc
real :: munca(n)
:
buclă: do i=1, n
bloc
real :: rezidual
:
if (residual<epsilon(x)) exit find_solution
bloc final
sfârșitul buclei
:
bloc final find_solution

Construcția bloc are o utilizare limitată în programarea normală, dar este cu adevărat utilă atunci când
sunt utilizate tehnici de generare a programelor, cum ar fi macrocomenzi, pentru a evita conflictele cu
entitățile din altă parte dintr-un subprogram. (Macro-urile nu fac parte din Fortran 2008, dar diferite
procesoare macro sunt utilizate pe scară largă cu Fortran.)

20.5.4 Proceduri elementare impure

Procedurile elementare, așa cum sunt definite în Fortran 95 și 2003, trebuie să fie pure, o condiție care
ajută la evaluarea paralelă. Deși acest lucru este avantajos pentru performanță, împiedică alte posibilități
în care se dorește să efectueze procesare impură în mod elementar pe matrice de rang arbitrar. În astfel
de cazuri, cineva a fost forțat să furnizeze o funcție separată pentru fiecare permutare a rangurilor
conforme; pentru o procedură cu două argumente, adică 22 de proceduri separate (8 cazuri în care
ambele argumente au avut același rang, 7 în care primul a fost scalar și
Machine Translated by Google

Alte îmbunătățiri Fortran 2008 369

al doilea era o matrice, iar 7 unde primul era o matrice și al doilea era scalar). Odată cu creșterea rangului
maxim la 15, acesta crește la 16+15+15 = 46 de proceduri separate.
Prefixul impur de pe antetul procedurii permite definirea unei proceduri elementare impure, care procesează
elementele argumentului matricei unul câte unul în ordinea elementelor matricei. Un exemplu este prezentat
în Figura 20.9. Acest exemplu este impur în trei moduri: numără numărul de depășiri din variabila globală
overflow_count, înregistrează fiecare depășire pe unitatea externă error_unit și încheie programul cu stop când
au fost întâlnite prea multe erori.

Figura 20.9 O funcție elementară impură.


modul safe_arithmetic
întreg :: max_overflows = 1000 întreg ::
overflow_count = 0 conține

funcția întreg elementar impur square(n) folosește iso_fortran_env,


only:error_unit integer, intent(in) :: n precizie dublă,
parametru :: sqrt_huge = & sqrt(real(huge(n), kind(0d0)))

dacă (abs(n)>sqrt_huge) atunci


scrieți (error_unit,*) „Depășire în pătrat (', n, ')' overflow_count = overflow_count
+ 1 if (overflow_count>max_overflows) stop '?Too many overflows' square =
huge(n) else

pătrat = n**2
sfâr itul dacă

funcția finală
modul final

Numai cerințele referitoare la „puritate” (lipsa efectelor secundare) sunt eliminate: elemental
rămân cerințe, adică:

• toate argumentele dummy ale unei proceduri elementare trebuie să fie scalare non-coarray dummy
obiecte de date și nu trebuie să aibă pointerul sau atributul alocabil; • toate
argumentele fictive ale unei proceduri elementare trebuie să aibă o intenție specificată; •
variabila rezultat a unei functii elementare trebuie sa fie scalara, nu trebuie sa aiba pointerul sau atributul
alocabil si nu trebuie sa aiba un parametru de tip care este definit printr-o expresie care nu este o
expresie constanta;
• într-o referire la o procedură elementară, toate argumentele reale trebuie să fie conformabile;
și

• într-o referire la o procedură elementară, argumente reale corespunzătoare inten iei


iar argumentele dummy inout trebuie fie să fie toate matrice, fie toate să fie scalare.
Machine Translated by Google

370 Fortran modern explicat

20.5.5 Procedurile interne ca argumente reale

O procedură internă poate fi folosită ca argument real sau ca țintă a unui pointer de procedură. Când
este invocat prin argumentul simulat corespunzător sau prin indicatorul procedurii, are acces la variabilele
procedurii gazdă ca și cum ar fi fost invocat acolo. De exemplu, în Figura 20.10, invocările funcției fun de
la integrare vor folosi valorile pentru variabilele frecvență și alfa din procedura gazdă.

Figura 20.10 Quadratura folosind proceduri interne.


subrutina s (frecvență, alfa, inferior, superior, ...)
real(wp), intent(in):: frecvență, alfa, inferior, superior
:
z = integra (distracție, inferior, superior)
:
conține
funcția real(wp) f(x)
real(wp), intent(in) :: xf =
x*sin(frecv*x)/sqrt(1-alpha*x**2)
funcția finală
sfâr itul subrutinei

În afară de comoditate, acest cod poate face parte în siguranță dintr-un program cu mai multe fire
deoarece datele pentru evaluarea funcției nu sunt transmise de variabile globale.
În cazul unui pointer de procedură asociat unei proceduri interne, atunci când procedura gazdă revine,
pointerul de procedură va deveni nedefinit – deoarece mediul necesar pentru evaluarea procedurii
interne va fi dispărut. De exemplu, în Figura 20.11, la întoarcerea din sub, variabila n nu mai există la care
f să se refere.

20.5.6 Specificarea tipului unei variabile de index forall

Într-o instrucțiune sau un construct forall, toate variabilele de index sunt locale pentru construct; de
exemplu, în

idx = 3
forall (idx=100:200) a(idx, idx) = idx**2 print *, idx

valoarea „3” este tipărită deoarece idx-ul din forall nu este același cu idx-ul din afara forall-ului. Totuși, idx-
ul din forall are același tip și fel ca și cel din afară dacă ar exista; iar dacă există, trebuie să fie o variabilă
întregă scalară.
Acest lucru este puțin incomod și, implicit, declararea variabilei index forall a necesitat crearea unei
variabile în afara forallului.
Astfel, Fortran 2008 permite ca tipul și tipul indexului forall să fie specificat în declarația forall în sine;
de exemplu, în
Machine Translated by Google

Alte îmbunătățiri Fortran 2008 371

Figura 20.11 Indicator nesigur către procedura internă.


modul nesigur

procedure(real), pointer :: funptr contine subrutina sub(n)


funptr => f ! Asociază funptr cu funcția internă f. proces de

apel! funptr va rămâne asociat cu f în timpul ! executarea


subrutinei „proces”.

întoarcere ! Revenirea din sub face ca funptr să devină nedefinit.


con ine

funcția reală f(x) reală,


intenție(in) :: x
f = x**n
funcția finală
sfâr itul subrutinei
modul final

complex :: i(100)
:

forall (integer(int64) :: i=1:2_int64**32) a(i) = i*2.0**(-32)

variabila exterioară i este o matrice complexă, dar este complet ascunsă de declarația din instrucțiunea forall.

20.5.7 Rezoluție generică

Setul de proceduri specifice care sunt identificate prin același identificator generic trebuie să îndeplinească cerințe stricte
pentru a se asigura că toate referințele posibile la identificatorul generic pot fi rezolvate fără ambiguitate la o procedură
specifică. Pentru fiecare pereche de proceduri specifice, acest lucru necesită de obicei să existe un argument inactiv într-
una care să fie distins de argumentul inactiv corespunzător în cealaltă (vezi Secțiunea 5.18). Există două extensii ale
caracteristicilor care fac deosebirea unui argument fals:

1) o procedură inactivă este considerată distinsă de o variabilă inactivă; și 2) un argument dummy cu atributul

alocabil este considerat distins de un argument dummy cu atributul pointer atunci când pointerul nu are intenție.

Blocul de interfață din Figura 20.12 ilustrează prima extensie: deoarece compilatorul știe întotdeauna dacă un argument
real este o procedură, nicio referință la g1 nu ar putea fi vreodată ambiguă.

Blocul de interfață din Figura 20.13 ilustrează a doua extensie: în acest caz, scopul interfeței este de a permite comutarea
între utilizarea alocabil și pointer, fără a fi nevoie să se schimbe numele procedurii de dealocare.
Machine Translated by Google

372 Fortran modern explicat

Figura 20.12 Dezambiguizare generică bazată pe procedura.


interfață g1
subrutină s1(a)
real a
subrutină finală

subrutină s2(a) real,


extern :: o subrutină finală

interfață finală

Figura 20.13 Dezambiguizare generică bazată pe pointer vs. alocabil.


interfață log_deallocate subrutine
log_deallocate_real_pointer_2(a) real, pointer, intent(inout) :: a(:, :)

final subrutine subrutine

log_deallocate_real_allocatable_2(a) real, allocabil, intent(inout) :: a(:, :)

sfâr itul subrutinei


interfață finală

Motivul pentru care alocabil și pointer sunt considerate a fi distinse reciproc numai atunci când pointerul nu are
intenție este că există o interacțiune cu caracteristica de direcționare automată (vezi Secțiunea 20.14.2) care ar fi făcut
posibilă scrierea unui ambiguu referin ă.

20.6 Utilizarea și calculul datelor


20.6.1 Îmbunătățiri ale declarației de alocare

În Fortran 2003, pentru a „clona” o matrice utilizând instrucțiunea alocare cu specificatorul sursă=, limitele trebuiau
specificate în alocare; de exemplu

real, alocabil :: a(:), b(:)


:

alocă (b(lbound(a, 1):ubound(a, 2)), sursă=a)

În Fortran 2008, limitele pot fi omise, caz în care vor fi preluate din specificatorul sursă=, permițând o variantă mult mai
simplă.

aloca (b, sursa=a)

Fortran 2008 adaugă, de asemenea, facilitatea de a aloca o variabilă la forma, tipul și parametrii
de tip ai unei expresii fără a copia valoarea acesteia. Acest lucru se face cu specificatorul mold=,
de exemplu
aloca (b, matriță=a)
Machine Translated by Google

Alte îmbunătățiri Fortran 2008 373

După alocare, orice inițializare implicită relevantă va fi aplicată la b.


În cele din urmă, restricția din Fortran 2003 care limitează specificatorul sursă= la acționarea asupra
unei singure alocări a fost ridicată: atât mold= cât și sursa= pot fi utilizate atunci când se alocă mai multe
obiecte, de exemplu,

alocă (a(10), b(20), sursă=173)

20.6.2 Realocare automată

Atribuirea intrinsecă unei variabile polimorfe alocabile este acum permisă, iar aceasta extinde
caracteristica de realocare automată introdusă de Fortran 2003 pentru forma matricei și parametrii
de tip amânat pentru a gestiona tipuri.
Dacă variabila este alocată și tipul ei dinamic diferă de cel al expresiei, variabila este dealocată (la fel ca și
cum ar fi o matrice cu formă diferită sau ar avea valori diferite ale parametrilor de tip amânat). Dacă variabila a
fost nealocată sau este dealocată de pasul anterior, aceasta este alocată pentru a avea tipul dinamic al expresiei
(și limitele matricei sau valorile parametrilor de tip, dacă este cazul). În cele din urmă, valoarea este copiată la fel
ca în alocarea normală (cu copiere superficială pentru orice componente pointer și copiere profundă pentru
orice componente alocabile).
Un exemplu este

clasa(*), alocabil :: x
:
x=3

Efectul realocării automate este similar cu cel al

if (alocat(variabilă)) dealocare (variabilă) alocare (variabilă,


sursă=expresie)

cu excepția faptului că, în cazul atribuirii intrinseci,

• variabila poate apărea în expresie, iar orice realocare are loc după evaluarea expresiei și înainte de
copierea valorii; și
• dacă variabila este deja alocată cu tipul corect (și cu valorile parametrilor de formă și tip amânat, dacă este
cazul), nu se realocă; în afară de performanță, acest lucru contează doar atunci când variabila are și
atributul target și există un pointer asociat cu acesta: în loc ca pointerul să devină nedefinit, acesta va
rămâne asociat și va vedea noua valoare.

20.6.3 Restricții elementare ale subprogramelor

În Fortran 2003, un argument inactiv al unui subprogram elementar nu a fost permis să fie utilizat într-o expresie
de specificație pentru o variabilă locală. Scopul acestei restricții a fost de a facilita optimizarea unor astfel de
proceduri, asigurându-se că spațiul necesar variabilelor locale va fi fix pentru întregul tablou. Cu toate acestea,
restricția a fost ușor depășită prin utilizarea variabilelor alocabile sau a procedurilor interne și s-a dovedit a nu fi
deosebit de utilă în practică (este trivial pentru compilator să detecteze dacă o astfel de restricție a avut loc și,
prin urmare,
Machine Translated by Google

374 Fortran modern explicat

dacă orice optimizări relevante ar putea fi aplicate oricum). Prin urmare, această restricție a fost eliminată
în Fortran 2008.
Iată un exemplu parțial:
funcție reală elementară f(a, b, ordine) real, intenție
(în) :: a, b întreg, intenție (în) :: ordine
(ordine)
real :: temp

În această funcție elementară, variabila locală temp este o matrice a cărei dimensiune depinde de
argumentul de ordine.

20.7 Intrare/ieșire

20.7.1 Intrare/ieșire recursive

O instrucțiune recursivă de intrare/ieșire este o instrucțiune de intrare/ieșire care este executată ca rezultat
al unei referințe de funcție dintr-o listă de I/O. În Fortran 2003, intrarea/ieșirea recursive a fost permisă,
dar numai pentru citirea și scrierea din fișierele interne. În Fortran 2008, intrarea/ieșirea recursive este
permisă și pentru fișierele externe, cu condiția ca aceeași unitate să nu fie implicată în ambele acțiuni de
intrare/ieșire. Acest lucru este util în special în timpul depanării și, de asemenea, pentru înregistrarea în
jurnal și raportarea erorilor. De exemplu,
print *, invert_matrix(x)
:
con ine
funcția invert_matrix(a)
:
dacă (singular) atunci
scrieți (unitatea_eroare,*) &
„Nu se poate inversa matricea singulară – continuă!”
întoarcere
sfâr itul dacă

20.7.2 Specificatorul newunit=

Un inconvenient de lungă durată în programele Fortran a fost nevoia de a gestiona manual numerele de
unități de intrare/ieșire. Acest inconvenient devine o problemă reală atunci când se utilizează biblioteci mai
vechi de la terți care efectuează intrare/ieșire și pentru care codul sursă nu este disponibil; la deschiderea
unui fișier, nu este dificil să găsești un număr de unitate care nu este utilizat în prezent, dar poate fi același
cu unul care este folosit ulterior de alt cod.
Aceste inconveniente sunt rezolvate prin specificatorul newunit= din declarația deschisă. Aceasta
returnează un număr unic de unitate negativ la o deschidere reușită. Fiind negativ, nu se poate ciocni cu
niciun număr de unitate specificat de utilizator (acestea fiind necesar să fie nenegative), iar procesorul va
alege o valoare care nu se ciocnește cu nimic din ceea ce folosește intern.
Machine Translated by Google

Alte îmbunătățiri Fortran 2008 375

Un exemplu este:

întreg :: în deschis
(fișier='input.dat', status='old', form='unformatted', newunit=in)
:
citiți (în) date

Pentru a evita orice confuzie în rezultatul numărului = specificatorul declarației de întrebare,


unde 1 indică un fișier care nu este conectat, newunit= nu va returna niciodată 1.

20.7.3 Scrierea valorilor separate prin virgulă

Două extensii au fost adăugate la procesarea formatului pentru a ușura scrierea fișierelor CSV (valori separate
prin virgulă).
Prima extensie este descriptorul de editare g0; aceasta transferă datele utilizatorului după cum urmează:

• datele întregi sunt transferate ca și cum i0 ar fi fost specificat; •


datele reale și complexe sunt transferate ca și cum ar fi fost specificat esw.dee, unde compilatorul alege
valorile lui w, d și e în funcție de valoarea reală care urmează să fie scoasă;
• datele logice sunt transferate ca și cum l1 ar fi fost specificat; •
datele de caractere sunt transferate ca și cum ar fi fost specificat.

De exemplu,

tipăriți „(1x, 5(g0, ";"))', 17, 2.71828, .false., „Bună ziua”

va imprima ceva de genul

17;2.7183e+00;F;Bună ziua;

(în funcție de valorile pentru w, d și e alese de compilator pentru data în virgulă mobilă).
Descriptorul de editare g0.d este similar cu descriptorul de editare g0, dar specifică valoarea care trebuie
utilizată ca d pentru datele în virgulă mobilă; după cum se vede în exemplul de mai sus, acest lucru poate fi
necesar dacă valoarea aleasă de compilator este nepotrivită. Din păcate, standardul Fortran 2008 interzice
utilizarea g0.d pentru orice altceva decât date în virgulă mobilă, chiar dacă d este ignorat pentru gw.d pentru
acele tipuri de date, eliminând o mare parte din comoditatea acestuia dintr-o singură lovitură.
Sub cea de-a doua extensie, repetarea nelimitată a formatului poate fi utilizată pentru a repeta o specificație
de format fără nicio limită prestabilită, atâta timp cât mai sunt date de transferat în sau din lista I/O. Acest lucru
este specificat de *(element-format) și este permis doar ca ultimul element dintr-o specificație de format. Se
comportă în mod similar cu N(elementele-format) pentru un număr întreg foarte mare N, de exemplu

tipăriți „(4x, „Lista: „,*(g0,:,","))”, 10, 20, 30

va imprima

Lista: 10,20,30

Cu toate acestea, din cauza unui defect de redactare în standardul publicat, comportamentul poate diferi
dacă nu există două puncte între ultimul descriptor de editare a datelor și paranteza din dreapta de închidere a
controlului de format nelimitat. Se așteaptă ca această defecțiune să fie corectată în timp util, dar recomandăm
evitarea situației – adică ar trebui să existe două puncte între ultimul descriptor de editare a datelor și paranteza
de închidere.
Machine Translated by Google

376 Fortran modern explicat

20.8 Proceduri intrinseci

Există o serie de extensii la unele proceduri intrinseci existente și au fost adăugate un număr mare de
proceduri intrinseci noi. Acest lucru evidențiază necesitatea ca programatorii să folosească interfețe
explicite (și, acolo unde este posibil, proceduri ale modulelor) pentru a evita modificările accidentale ale
semanticii programelor lor după o actualizare la un nou nivel de limbaj în compilatorul lor.
În noile proceduri intrinseci descrise în acest capitol, toate argumentele sunt intenționate, dacă nu se
specifică altfel. Orice argument numit fel trebuie (dacă este prezent) să fie o expresie de inițializare a
unui întreg scalar.

20.9 Funcții matematice intrinseci

20.9.1 Modificări ale funcțiilor trigonometrice

Funcțiile intrinseci acos, asin, atan, cosh, sinh, tan și tanh acceptă acum argumente de tip complex. În
cazul cosh, sinh, tan și tanh, acestea erau disponibile anterior prin utilizarea identităților simple

cosh x = cos ix sinh x


= i sin ix
tan x = (sin x)/(cos x)
tanh x = i tan ix

sau, folosind funcții de instrucțiuni Fortran (vezi Anexa C.1.5), complex ::


cosh, sinh, tan, tanh, x intrinsec :: cos, sin cosh(x) = cos((0,1)*x) sinh( x)
= (0,-1)*sin((0,1)*x) tan(x) = sin(x)/cos(x) tanh(x) = (0,-1)*sin((0) ,1)*x)/
cos((0,1)*x)

20.9.2 Noi funcții trigonometice hiperbolice

S-au adăugat funcții intrinseci elementare pentru funcțiile trigonometrice hiperbolice inverse:

acosh (x) returnează cosinusul hiperbolic invers al lui x, adică y astfel încât cosh(y) ar
să fie aproximativ egală cu x.

asinh (x) returnează sinusul hiperbolic invers al lui x.

atanh (x) returnează tangenta hiperbolică inversă a lui x.

În fiecare caz, x trebuie să fie de tip real sau complex, iar rezultatul are același tip și fel.
Rețineți că pentru numerele complexe, aceste funcții sunt legate de trigonometricul normal
funcții prin identități simple:
Machine Translated by Google

Alte îmbunătățiri Fortran 2008 377

acosh x = i acos x asinh x =


i asin ix
atanh x = atan ix

20.9.3 Noi funcții matematice speciale

Au fost adăugate noi funcții intrinseci elementare pentru calcularea funcțiilor Bessel:

bessel_j0 (x) primul fel și ordinea zero;

bessel_j1 (x) primul fel și comanda unul;

bessel_jn (n, x) primul fel și ordine n;

bessel_y0 (x) al doilea fel și ordinul zero;

bessel_y1 (x) al doilea fel și comanda unul;

bessel_yn (n, x) al doilea fel și ordinul n.

În fiecare caz, x trebuie să fie de tip real, iar n trebuie să fie de tip întreg cu o valoare nenegativă.
Două noi funcții transformaționale returnează vectori ai mai multor valori ale funcției Bessel:

bessel_jn (n1, n2, x) primul fel și ordinele de la n1 la n2;

bessel_yn (n1, n2, x) al doilea fel și ordinele de la n1 la n2.

În acest caz, n1 și n2 trebuie să fie de tip întreg cu valori nenegative și toate cele trei argumente trebuie să fie
scalare. Dacă n2<n1, rezultatul are dimensiunea zero.
Este potențial mai eficient să se calculeze valorile succesive ale funcției Bessel împreună, mai degrabă decât
separat, deci dacă acestea sunt necesare, formele transformaționale ar trebui folosite în loc de apeluri multiple
către cele elementare.
Au fost adăugate trei noi funcții elementare pentru calcularea funcției de eroare.

2 x 2
erf (x) returnează valoarea funcției de eroare a lui x, 0 e t dt.
π

erfc (x) returnează complementul funcției de eroare, 1 erf(x). Aceasta are


2
2 forma matematică X e t dt.
π

erfc_scaled (x) returnează funcția de eroare scalată exponențial, exp(x**2)*erfc(x).

În fiecare caz, x trebuie să fie de tip real. Rețineți că pentru valori mici ale lui x (aproximativ 9,0 pentru precizia
unică IEEE), erf(x) este egal cu unu și erfc(x) depășește zero; atunci când lucrați în afara acestui interval,
erfc_scaled este mai util.
Două funcții elementare noi au fost adăugate pentru funcția gamma:

gamma (x) returnează valoarea funcției gamma la x.

log_gamma (x) returnează logaritmul natural al valorii absolute a funcției gamma, log(abs(gamma(x))).

În fiecare caz, x trebuie să fie de tip real, cu o valoare care nu este zero sau un număr întreg negativ.
Machine Translated by Google

378 Fortran modern explicat

20.9.4 Norme euclidiene

Au fost adăugate două funcții noi pentru calcularea normelor euclidiene (sau distanței). Prima functie
este elementara:

hypot (x, y) returnează distanța euclidiană, adică x2 +y2, calculată fără depășire sau depășire excesivă.
Argumentele x și y trebuie să fie de tip real cu același tip de parametru, iar rezultatul este și real
de acest fel. Această adăugare la lista de funcții intrinseci face un total de trei funcții intrinseci
care calculează distanțe euclidiene, ceea ce pare un fleac inutil pentru un lucru atât de simplu.
(Celelalte două funcții sunt abs(cmplx(x, y)) și norm2([x, y]) – prima fiind disponibilă în acest scop
încă din Fortran 90.)

A doua funcție este transformațională:

norm2 (x [ , de dim ] ) returnează norma L2 a unui tablou real x; rezultatul este un scalar real de
același fel ca x.

Norma L2 este rădăcina pătrată a sumei pătratelor elementelor; pentru un vector real, acesta
este egal din punct de vedere matematic (dar nu neapărat egal din punct de vedere computațional)
cu sqrt(dot_product(x, x)).

Dacă argumentul dim este prezent, acesta trebuie să fie un întreg scalar care să satisfacă
1 dim n, unde n este rangul lui x; nu este permis să fie un argument fals opțional. În acest caz,
în loc să se calculeze norma L2 a întregului tablou, matricea este redusă exact ca pentru sum(x,
dim) cu excepția faptului că, în loc de însumare, valorile sunt calculate prin aplicarea normei2
vectorilor care se reduc.

De exemplu,

norm2(reforma([1.0, 3.0, 2.0, 4.0], [2, 2]), dim=2)

este aproximativ egal cu [2,236, 5,0].

Standardul recomandă, dar nu impune, ca norma2 să fie calculată fără preaplin sau depășire
excesivă.

20.10 Manipularea biților

O gamă largă de funcții intrinseci noi oferă o funcționalitate suplimentară de manipulare a biților.

20.10.1 Comparație biți (fără semnă).

Au fost furnizate patru funcții elementare noi pentru efectuarea comparațiilor pe biți, returnând un
rezultat logic implicit. Comparațiile pe biți tratează valorile întregi ca numere întregi fără semn; adică,
bitul cel mai semnificativ nu este tratat ca un bit cu semn, ci ca având valoarea 2b 1, unde b este
numărul de biți din întreg.
Machine Translated by Google

Alte îmbunătățiri Fortran 2008 379

bge (i, j) returnează valoarea adevărată dacă i este pe bit mai mare sau egal cu j, iar valoarea
fals altfel.

bgt (i, j) returnează valoarea adevărată dacă i este mai mare pe bit decât j, iar valoarea falsă
in caz contrar.

ble (i, j) returnează valoarea adevărată dacă i este pe bit mai mic sau egal cu j, iar valoarea falsă
in caz contrar.

blt (i, j) returnează valoarea adevărată dacă i este mai mică pe biți decât j, iar valoarea falsă în caz contrar.

Argumentele i și j trebuie fie să fie de tip întreg, fie să fie constante literale binare, octale sau hexazecimale ('boz');
dacă sunt de tip întreg, nu trebuie să aibă același parametru de tip tip.
De exemplu, pe un procesor cu complement în doi, -1_int8 are modelul de biți z'ff', iar acesta are valoarea 255
atunci când este tratat ca fără semn, deci bge(-1_int8, 255) este adevărat și blt(-1_int8, 255) este fals.

20.10.2 Schimbarea cu lățime dublă

Două funcții elementare neobișnuite asigură schimbarea cu lățime dublă. Aceste funcții concatenează i și j și schimbă
valoarea combinată la stânga sau la dreapta prin deplasare; rezultatul este jumătatea cea mai semnificativă pentru o
schimbare la stânga și jumătatea cea mai puțin semnificativă pentru o schimbare la dreapta.

dshiftl (i, j, shift) returnează jumătatea cea mai semnificativă a unei deplasări la stânga cu lățime dublă.

dshiftr (i, j, shift) returnează jumătatea cea mai mică semnificativă a deplasării la dreapta cu lățime dublă.

Argumentele i și j trebuie să fie fie constante literale „boz”, fie de tipul întreg; dacă sunt de tip întreg trebuie
să aibă același fel, dacă una este o constantă „boz” va fi convertită în tipul celeilalte ca și cum ar fi prin funcția int.
Ele nu pot fi ambele constante „boz”. Argumentul shift trebuie să fie un număr întreg, dar poate fi de orice fel.
Tipul de rezultat este un întreg cu același fel ca i sau j. Un exemplu este dshiftl(21_int8, 64_int8, 2), care are
valoarea 85_int8.

În general, aceste funcții sunt mai greu de înțeles și vor funcționa mai rău decât utilizarea simplă a deplasărilor
obișnuite pe numere întregi cu lățimea dublă, așa că ar trebui să fie utilizate numai dacă funcționalitatea exactă
este într-adevăr ceea ce este necesar.

20.10.3 Reduceri pe biți

Trei noi funcții transformaționale reduc o matrice (cu un rang, sau complet la un scalar, în analogie exactă cu
suma și produsul), dar folosind operații pe biți în loc de adunare sau înmulțire (vezi Secțiunea 8.11).

iall (array [, mask ] ) reduce matricea la o valoare scalară folosind funcția iand.

iall (matrice, dim [, mask ] ) reduce dimensiunea dim a matricei folosind iand
func ie.
Machine Translated by Google

380 Fortran modern explicat

iany (matrice [, mask ] ) reduce matricea la o valoare scalară folosind funcția ior.

iany (matrice, dim [, mask ] ) reduce dimensiunea dim a matricei folosind ior
func ie.

iparitatea (array [, mask ] ) reduce matricea la o valoare scalară folosind funcția ieor.

iparity (matrice, dim [, mask ] ) reduce dimensiunea dim a matricei folosind ieor
func ie.

Argumentul matricei trebuie să fie un tablou întreg. Dacă este prezent, argumentul masca trebuie să fie
o matrice logică cu aceeași formă ca și matrice și numai acele elemente pentru care masca este adevărată
contribuie la valoarea finală. Argumentul dim trebuie să fie un scalar întreg în intervalul 1 dim
rank(array); nu trebuie să fie un argument fals opțional. Rezultatul fiecărei funcții este de tip întreg cu același
tip ca și matrice și este fie scalar, fie, dacă este specificat dim, are forma unui tablou cu acea dimensiune
eliminată.
Dacă niciun element nu contribuie la rezultat sau la un element al rezultatului, valoarea este zero pentru
iany și iparity, și nu(0_k) pentru iall, unde k = fel (matrice).
De exemplu, valoarea lui iall([14, 13, 11]) este egală cu 8, iar valoarea lui iall([14, 13, 11],
mask=[.true., .false., .true]) este egal cu 10.

20.10.4 Numărarea biților

Sunt furnizate mai multe funcții elementare noi pentru numărarea biților dintr-un întreg.

leadz (i) returnează numărul de biți zero de început (cei mai semnificativi) în i.

popcnt (i) returnează numărul de biți diferit de zero din i.

poppar (i) returnează paritatea numărului de biți a unui număr întreg, adică poppar(i) este identic
la iand(popcnt(i), 1).

trailz (i) returnează numărul de biți zero de la urmă (cel mai puțin semnificativi) în i.

Argumentul i poate fi orice fel de număr întreg, iar rezultatul este un număr întreg implicit.
Rețineți că valorile popcnt, poppar și trailz depind doar de valoarea argumentului, în timp ce valoarea
leadz depinde și de tipul argumentului. De exemplu, leadz(64_int8) are valoarea 2, în timp ce leadz(64_int16)
are valoarea 10; valorile popcnt(64_k), poppar(64_k) și trailz(64_k) sunt 1, 1 și, respectiv, 5, indiferent de ce
valoare este k.

20.10.5 Producerea măștilor de biți

Noile funcții elementare facilitează producerea de măști de biți simple:

maskl (i [, kind ] ) returnează un număr întreg cu biții i din stânga setați și restul zero.
Machine Translated by Google

Alte îmbunătățiri Fortran 2008 381

maskr (i [, fel ] ) returnează un număr întreg cu biții i cei mai din dreapta setați și restul zero.
Aceasta are aceeași valoare ca ishft(1, i) - 1 pe mașinile de complement a două (toate computerele
moderne).

Tipul rezultat este un întreg cu tipul specificat (sau un întreg implicit dacă nu este specificat niciun fel).
Argumentul i trebuie să fie de tipul întreg de orice fel (tipul de i nu are efect asupra rezultatului) și cu valoare în
intervalul 0 i b, unde b este dimensiunea biților a rezultatului.
De exemplu, maskl(3,int8 este egal cu int(b'11100000',int8) și maskr(3,int8)
este egal cu 7_int8.

20.10.6 Îmbinarea biților

O nouă funcție elementară îmbină biții din numere întregi separate.

merge_bits (i, j, mask) returnează biții i și j îmbinați sub controlul mascai. Argumentele i, j și masca trebuie să fie
numere întregi de același fel sau să fie constante „boz”. Cel puțin unul dintre i și j trebuie să fie un întreg,
iar o constantă „boz” este convertită la acel tip ca și cum ar fi prin intrinsecul int; rezultatul este de tipul
întreg cu același fel.

Această funcție este modelată pe combinația intrinsecă, tratând 1 și 0 biți ca fiind adevărati și, respectiv,
fals. Valoarea rezultatului este determinată luând pozițiile biților unde masca este 1 din i și pozițiile biților
unde masca este 0 din j; acesta este egal cu ior(iand(i, mask), iand(j, not(mask))).

20.10.7 Operații suplimentare în schimburi

Există trei noi funcții intrinseci pentru deplasarea biților:

shifta (i, shift) returnează biții lui i deplasați la dreapta cu biți de deplasare, dar în loc să se schimbe cu zero biți
de la stânga, „bitul de semn” este replicat. Pe o mașină de complement a doi, aceasta o face o schimbare
aritmetică (deci numele de shifta), adică împărțirea cu o putere a doi; în cazul improbabil al întâlnirii unei
mașini de complement sau semn de magnitudine, interpretarea valorii rezultatului este oarecum diferită.

shiftl (i, shift) returnează biții din i deplasați la stânga, echivalent cu ishft(i, shift);

shiftr (i, shift) returnează biții din i deplasați la dreapta, echivalent cu ishft(i,
-schimb).

În fiecare caz, i și shift sunt ambele de tip întreg (de orice fel), shift trebuie să fie în
interval 0 shift bit_size(i), iar rezultatul este de tip întreg cu același tip ca i.
Singurele avantaje ale shiftl și shiftr față de ishft sunt: • direcția de

schimbare este implicată de nume, așa că nu trebuie să ne amintim că o valoare de schimbare pozitivă
înseamnă „deplasare la stânga” și o valoare de schimbare negativă înseamnă „deplasare la dreapta”; •
dacă valoarea deplasării este variabilă, codul generat pentru schimbare este teoretic mai eficient (în practică,
dacă nu se fac multe alte lucruri cu valorile, performanța va fi oricum limitată de lățimea de bandă a
memoriei principale, nu de func ie de schimbare).
Machine Translated by Google

382 Fortran modern explicat

20.11 Diverse proceduri intrinseci

20.11.1 Proceduri care suportă coarrays

Subrutinele intrinseci atomic_define și atomic_ref și funcțiile intrinseci image_index, lcobound, num_images,


this_image și ucobound au fost adăugate pentru a sprijini programarea cu coarrays. Acestea sunt descrise
în capitolul 19.

20.11.2 Executarea unui alt program

Capacitatea de a executa un alt program din cadrul unui program Fortran este oferită de noua subrutină
intrinsecă execute_command_line; după cum sugerează și numele, acesta transmite o „linie de comandă”
procesorului care o va interpreta într-un mod total dependent de sistem. De exemplu,

apelează execute_command_line('ls -l')

este probabil să producă o listă de directoare pe Unix și un mesaj de eroare pe Windows. Sintaxa completă
este următoarea.

apelați execute_command_line (comandă [, așteptați] [, exitstat] [, cmdstat]


[, cmdmsg]) unde argumentele sunt după cum urmează:

comanda are intenția și este un șir de caractere implicit scalar care conține linia de comandă care
urmează să fie interpretată de procesor.

wait are intentia in si este o logica implicita scalara care indica daca comanda trebuie executata
asincron (wait=.false.), sau daca procedura ar trebui sa astepte sa se termine inainte de a
reveni la programul Fortran (implicit).

exitstat are intentia inout si este o variabila intreaga implicita scalara careia, cu exceptia cazului in
care asteptarea este falsa, i se va atribui "starea de iesire a procesului" din comanda (sensul
acesteia este si dependent de sistem).

cmdstat are intent out și este o variabilă integrală scalară implicită căreia îi este atribuită zero dacă
execute_command_line în sine este executată fără eroare, -1 dacă procesorul nu acceptă
executarea comenzii, -2 dacă wait=.true. a fost specificat, dar procesorul nu acceptă execuția
asincronă a comenzii și o valoare pozitivă dacă a apărut orice altă eroare.

cmdmsg are intent inout și este un șir de caractere implicit scalar căruia, dacă cmdstat i se atribuie
o valoare pozitivă, i se atribuie un mesaj explicativ.

Dacă apare vreo eroare (astfel încât o valoare diferită de zero i-ar fi atribuită cmdstat) și cmdstat nu
este prezent, programul este terminat de eroare.
Rețineți că, chiar dacă procesorul acceptă execuția asincronă a comenzii, nu există niciun mecanism
prevăzut pentru a afla mai târziu dacă comanda care este executată asincron s-a terminat sau care a fost
starea ei de ieșire.
Machine Translated by Google

Alte îmbunătățiri Fortran 2008 383

20.11.3 Compararea caracterelor

În cazul puțin probabil ca compilatorul să accepte un tip de caractere ASCII, dar acesta nu este tipul de
caractere implicit, funcțiile intrinseci pentru compararea caracterelor utilizând secvența de colating ASCII
lge, lgt, lle și llt vor accepta, de asemenea, argumente de tip ASCII. Ambele argumente trebuie să aibă
același fel.
Este puțin probabil ca acest lucru să fie util, deoarece comparația obișnuită folosind operatorii
relaționali < etc. are exact aceleași rezultate cu tipul ASCII.

20.11.4 Căutare matrice

Funcțiile intrinseci transformaționale maxloc și minloc, care caută într-o matrice valoarea maximă sau,
respectiv, minimă, au adăugat un argument înapoi opțional pentru a indica dacă se dorește prima sau
ultima apariție. Argumentul din spate este argumentul final din listă și trebuie să fie logic scalar. De
exemplu, maxloc([1,4,4,1]) este egal cu 2, în timp ce maxloc([1,4,4,1],back=.true.) este egal cu 3.

Noua funcție intrinsecă de transformare findloc reduce o matrice exact în același mod ca maxloc sau
minloc, dar returnând poziția unui element cu valoarea specificată în loc de maximă sau minimă – sau
zero dacă nu a fost găsit niciun element. Forma sa este după cum urmează:

findloc (array, value [, mask ] [, kind ] [, back ] ) caută în întregul tablou, eventual
mascat de mască, valoare, și returnează vectorul pozițiilor indicelui care identifică acel element.
Argumentul matricei trebuie să fie de tip intrinsec, iar valoarea trebuie să fie un scalar de tip și tip
comparabil (nu neapărat de același tip). Dacă este prezentă, masca trebuie să fie de tip logic, cu
aceeași formă ca și matricea, tipul trebuie să fie o expresie constantă cu număr întreg scalar, iar
înapoi trebuie să fie o expresie logică scalară. Dacă back este prezent și adevărat, funcția găsește
ultima valoare potrivită în matrice, în caz contrar, găsește prima astfel de valoare.

findloc (matrice, valoare, dim [, mask ] [, kind ] [, back ] )


reduce dimensiunea dim a matricei, rezultatul fiind poziția în fiecare vector de-a lungul dimensiunii
dim unde a fost găsit elementul. Argumentele sunt aceleași ca înainte, cu excepția dim care
trebuie să fie un întreg scalar.

Tipul rezultat este un întreg cu tipul specificat (sau un întreg implicit dacă nu este specificat niciun fel).
De exemplu, findloc([(i, i = 10, 1000, 10)], 470) are valoarea 47.
Rețineți că, deoarece căutăm egalitate, poate fi folosit orice tip intrinsec (în timp ce maxloc și minloc
nu permit complex sau logic); pentru tipul logic, .eqv. operația este utilizată pentru comparație.

20.11.5 Paritate logică

Noua paritate a funcției intrinseci transformaționale reduce masca matricei, care trebuie să fie de tip
logic, la fel ca toate sau oricare, dar cu .neqv. func ionarea în loc de . i. sau sau. Operațiune. Are forma

paritatea (mască) reduce masca la un scalar și


Machine Translated by Google

384 Fortran modern explicat

paritatea (mască, dim) reduce dimensiunea dim a măștii.

Valoarea sa este identică cu cea a lui iand(count(mask [, dim]),1)==1, adică testează


dacă numărul de valori adevărate este impar, dar este posibil mai eficient și mai clar.
Rețineți că, ca și în count, argumentul real corespunzător lui dim nu trebuie să fie el însuși un
argument fals opțional.

20.11.6 Suport aritmetic zecimal

Pentru a facilita suportul aritmeticii nebinare, în special a aritmeticii zecimale specificate de standardul
IEEE 754 în virgulă mobilă din 2008, a fost adăugat un argument radix opțional la funcția de interogare
selected_real_kind la sfârșitul listei sale de argumente. Trebuie să fie un număr întreg scalar și limitează
valorile returnate la tipurile în virgulă mobilă cu acea rădăcină; de exemplu,

tipul_real_selectat(p=6, radix=10)

va returna parametrul tip al unui tip zecimal în virgulă mobilă cu cel puțin șase cifre de precizie, dacă este
disponibilă una. O valoare returnată de 5 indică faptul că procesorul nu are niciun fel real cu acea bază.

Argumentul radix a fost adăugat și la funcția ieee_selected_real_kind


din modulul ieee_arithmetic, cu semantică similară.

20.11.7 Dimensiunea unui obiect din memorie

storage_size (a [, kind ] ) returnează dimensiunea, în biți, care ar fi luată în memorie


printr-un element de matrice cu tipul dinamic a.

Argumentul a poate fi de orice tip sau rang (inclusiv un scalar). Este permis să fie un pointer nedefinit
dacă nu este polimorf și este permis să fie un pointer disociat sau alocat nealocat, dacă nu are un
parametru de tip amânat sau este polimorf nelimitat.
Tipul returnat este un întreg cu tipul specificat sau tipul implicit dacă felul nu este prezent.
Rețineți că standardul nu necesită aceeași dimensiune pentru variabilele numite, elementele de matrice
și componentele de structură de același tip; într-adevăr, frecvent acestea vor avea o umplutură diferită
pentru a îmbunătăți alinierea adreselor de memorie și, prin urmare, performanța.
În plus, dacă a este de tip derivat cu componente alocabile sau componente a căror dimensiune
depinde de valoarea unui parametru de tip lungime, compilatorului i se permite să stocheze acele
componente separat de restul variabilei, cu un descriptor în variabilă indicând spre stocarea suplimentară.
Nu este clar dacă storage_size va include spațiul ocupat de astfel de componente, în special în cazul
parametrului de tip lungime. Prin urmare, utilizarea acestei funcții ar trebui evitată în astfel de cazuri
problematice.
Machine Translated by Google

Alte îmbunătățiri Fortran 2008 385

20.12 Adăugiri la modulul iso_fortran_env

20.12.1 Informații de compilare

Două funcții de interogare au fost adăugate acestui modul pentru a returna informații despre compilator (așa-
numita fază de traducere a programului).

compiler_version () returnează un șir care descrie numele și versiunea compilatorului


folosit.

compiler_options () returnează un șir care descrie opțiunile utilizate în timpul compilării.

În fiecare caz, șirul este un caracter scalar implicit.


Aceste funcții pot fi utilizate în expresii de inițializare, de exemplu

modul my_module
utilizați iso_fortran_env, numai: opțiuni_compilator, versiunea_compilator opțiunile_compilator
private, caracterul versiunii_compilatorului(*), parametrul :: compilat_by =
versiunea_compilatorului() caracter(*), parametrul :: compilat_with = opțiunile_compilatorului()

:
modul final

Nu există cerințe reale privind lungimea acestor șiruri sau conținutul lor, dar este de așteptat ca acestea să
conțină ceva util și informativ. De exemplu, compiler_version() ar putea returna șirul „NAG Fortran 6.0(1273)”, iar
compiler_options() ar putea returna șirul „-C=array -O3”.

20.12.2 Nume pentru tipuri comune

Au fost adăugate constante denumite pentru unele valori de tip frecvent dorite pentru tipurile întregi și reale,
acestea sunt:

int8 întreg pe 8 biți int16


întreg pe 16 biți int32 întreg
pe 32 de biți int64 întreg pe
64 de biți
real32 pe 32 de biți real64
pe 64 de biți real128 pe
128 de biți real

De exemplu, în

proces de subrutină (matrice) folosește


iso_fortran_env matrice reală (:, :)
întreg (int64) i, j do j=1, ubound
(matrice, 2, int64)
Machine Translated by Google

386 Fortran modern explicat

face i=1, ubound(matrice, 1, int64)


: ! face ceva cu array(i, j) end do

sfâr itul face

sfâr itul subrutinei

utilizarea int64 permite acestei subrutine să proceseze matrice foarte mari.


Dacă compilatorul acceptă mai multe tipuri cu o anumită dimensiune, standardul nu specifică care dintre
ele va fi ales pentru constantă. Dacă compilatorul nu acceptă un fel cu o anumită dimensiune, acea constantă
va avea valoarea -2 dacă acceptă un fel cu o dimensiune mai mare și -1 dacă nu acceptă nicio dimensiune
mai mare.
Aceasta poate fi folosită împreună cu îmbinare pentru a specifica o dimensiune dorită cu o alternativă la cealaltă
dimensiuni predeterminate dacă nu este disponibilă, așa cum se arată în Figura 20.14.

Figura 20.14 Selecție tip cu constante standard denumite.


subrutine process_bytes(bytes)
utilizați iso_fortran_env
integer(merge(int8, merge(int16, int32, int16>=0), int8>=0)) octeți dacă (kind(bytes)==int8) atunci!
procesează octeți de 8 biți altfel if (kind(bytes)==int16) atunci ! procesează octeți de 8 biți în perechi
:

:
altfel
: ! proces cvadruple de octeți de 8 biți se termină dacă

sfâr itul subrutinei

20.12.3 Matrice tip

Au fost adăugate constante de matrice denumite care conțin toate valorile parametrilor tip tip pentru tipurile
intrinseci care sunt acceptate de procesor. Constantele numite character_kinds, integer_kinds, logical_kinds
și real_kinds conțin tipurile suportate de tip caracter, întreg, logic și, respectiv, real. Aceste matrice sunt de
tipul implicit întreg și au o limită inferioară de 1. Ordinea valorilor din fiecare matrice este dependentă de
procesor.

20.12.4 Facilități de sprijin Coarray

Modulul conține, de asemenea, tipul derivat lock_type și constantele numite atomic_int_kind,


atomic_logical_kind, stat_locked_other_image, stat_stopped-_image și stat_unlocked. Toate acestea sunt
descrise în capitolul 19.
Machine Translated by Google

Alte îmbunătățiri Fortran 2008 387

20.13 Modificări la alte module intrinseci standard


20.13.1 Modulul iso_c_binding

Funcția de interogare c_sizeof a fost adăugată la modulul intrinsec iso_c_binding. Oferă o funcționalitate similară
cu cea a operatorului dimensiunea din C.

c_sizeof (x) Dacă x este scalar, aceasta returnează valoarea pe care procesorul însoțitor o returnează pentru
operatorul C sizeof aplicat unui obiect de tip care interoperează cu parametrii de tip și tip ai lui x. Dacă x
este o matrice, rezultatul este valoarea returnată pentru un element al matricei înmulțită cu numărul
său de elemente. x trebuie să fie interoperabil (vezi Capitolul 12) și nu este permis să fie o matrice de
dimensiune presupusă (vezi Secțiunea B.3).

De exemplu,

utilizați iso_c_binding
integer(c_int64_t) x print *,
c_sizeof(x)

va tipări valoarea 8 și dacă n este egal cu 10,

subrutina s(y, n)
utilizați iso_c_binding
integer(c_int64_t) y(n) print *,
c_sizeof(y) end subrutine

va imprima valoarea 80.


Este necesară prudență atunci când faceți programare în limbi mixte atât în C, cât și în Fortran, deoarece
acest lucru nu este exact ceea ce face operatorul de dimensiune C; în multe contexte (cum ar fi un argument
fals) o matrice C „decade” la un pointer și apoi sizeof va returna dimensiunea pointerului (nu întregul tablou) în
octeți.

20.13.2 Modulul ieee_arithmetic

Pentru funcția de interogare ieee_selected_real_kind, a fost adăugată o bază opțională a argumentelor la sfârșitul
listei de argumente. Trebuie să fie un număr întreg scalar și limitează valorile returnate la tipurile în virgulă
mobilă cu acea rădăcină; de exemplu,

ieee_selected_real_kind (p=6, radix=10)

va returna parametrul tip al unui tip de virgulă mobilă zecimală IEEE cu cel puțin șase cifre de precizie, dacă una
este disponibilă. O valoare de returnare de 5 indică faptul că procesorul nu are un tip real IEEE cu acea bază.

Argumentul radix a fost adăugat și la funcția intrinsecă selected_real_kind,


cu semantică similară (vezi Secțiunea 20.11.6).
Machine Translated by Google

388 Fortran modern explicat

20.14 Programe și proceduri

20.14.1 Entități modul salvate

Variabilele și pointerii de procedură declarați în partea de specificații a unui modul au acum implicit atributul
save; acest lucru poate fi confirmat printr-o specificație explicită. Adică în

modul salvat2008
real :: x
real, salvați :: y final
module

variabilele x și y au ambele atributul de salvare. Aceasta înseamnă că își vor păstra valorile chiar și atunci când
nicio procedură nu face referire la modul; în Fortran 2003, variabila x ar fi devenit nedefinită într-un asemenea
moment.
În plus, compilatorilor li s-a permis să dezaoce variabilele alocabile nesalvate într-un modul atunci când
modulul nu era referit. Deoarece nu existau compilatori Fortran care să profite de această licență pentru a
dealoca memoria unor astfel de variabile (astfel încât, în practică, toate variabilele modulului au fost oricum
salvate efectiv), permiterea utilizatorului să se bazeze pe variabilele salvate este o simplificare utilă a limba.

20.14.2 Direc ionarea automată a pointerului

Un argument real cu atributul țintă este acum permis să corespundă unui pointer fictiv cu atributul intenție.
Acest lucru este ilustrat de Figura 20.15; în acest caz,

Figura 20.15 Direc ionare automată convenabilă.


modulul m
real, pointer, protejat :: lista_parametrii(:) conține

subrutina set_params(listă) real, pointer,


intent(in) :: list(:) parameter_list => sfârșitul listei
subrutine

:
modul final
:
rezolvarea subrutinei (problemă, argumente)
folosește m

real, target :: args(:)


:
apelați set_params(args)
:
sfâr itul subrutinei
Machine Translated by Google

Alte îmbunătățiri Fortran 2008 389

țintirea automată este folosită doar pentru comoditate, pur și simplu salvând necazul creării unui
pointer local, îndreptându-l către args și transmiterea pointerului local către set_params.
Cu toate acestea, direcționarea automată poate fi utilizată și pentru a impune cerințele de contiguitate;
dacă un indicator fals are atributul contiguu, argumentul actual trebuie să fie pur și simplu contiguu
(vezi Secțiunea 20.4.3). Acest lucru înseamnă că utilizatorul poate fi sigur că nu are loc nicio copiere
neintenționată, printr-un mecanism de transmitere a argumentelor de copiere în copiere. Acest lucru
este ilustrat de Figura 20.16, care necesită o matrice contiguă care să fie utilizată pentru operațiunile de
tamponare. Un apel la set_buffer cu un argument care nu este pur și simplu contigu ar produce o eroare
la momentul compilării.

Figura 20.16 Direc ionarea automată a matricei învecinate.


modul buffer_control caracter(:),
contiguu, pointer, protejat :: buffer(:) conține

subrutina set_buffer(charbuf)
caracter(*), pointer, intent(in), contiguu :: charbuf(:) buffer => charbuf

sfâr itul subrutinei


modul final
:

caracter, alocabil, țintă :: mybuf(:)


:

alocare (mybuf(n)) apel


set_buffer(mybuf)

20.14.3 Indicarea argumentelor absente

Un pointer nul sau un alocat nealocat poate fi folosit pentru a desemna un argument opțional absent non-alocabil non-
pointer. De exemplu, în

interfata

subrutina s(x)
real, opțional :: x end subrutine

interfață finală
:

apelați s(null())

referința null() este tratată ca și cum nu ar fi prezentă.


Acest lucru este util în situația ușor născocită în care există o procedură cu multe argumente opționale, împreună cu
pointeri sau alocabile care urmează să fie transmise ca argumente reale numai dacă sunt asociate sau alocate. În absența
acestei facilități, este nevoie de un set cu 2 n căi de constructe if imbricate, unde n este numărul de variabile locale în cauză.
Figura 20.17 oferă o schiță a modului în care funcționează acest proces. În acest exemplu, noua caracteristică permite
apelul către
Machine Translated by Google

390 Fortran modern explicat

process_work să fie o singură instrucțiune; fără caracteristica, apelul ar trebui să fie constructele
imbricate if necitit de complicate prezentate în Figura 20.18.

Figura 20.17 Denotație opțională absentă.


subrutina top(x, a, b) real :: x

real, opțional, țintă :: a(:), b(:) real, alocabil ::


worka(:), workb1(:), workb2(:) real, pointer :: pivotptr : (Cod pentru a
aloca condiționat worka etc. elid.) apelați proces_lucrul(x, worka,
workb1, workb2, pivot) end subrutine

subrutină process_work(x, wa, wb1, wb2, pivot)


real :: x
real, opțional :: wa(:), wb1(:) , wb2(:), pivot

Figura 20.18 Imbricat imens de necitit dacă.


dacă (alocat(lucră)) atunci
dacă (alocat(lucrub1)) atunci
dacă (alocat(lucrub2)) atunci
if (asociat(pivot)) atunci apelați
process_work(x, worka, workb1, workb2, pivot) altfel

apelați process_work(x, worka, workb1, workb2)


sfâr itul dacă

else if (asociat(pivot)) atunci


apelați process_work(x, worka, workb1, pivotptr=pivot)
altfel
apelați process_work(x, worka, workb1) end if

: (Rămânul de imbricat imens dacă construcția este eliminată.)

Este adevărat că, în acest exemplu, a face variabilele fictive din process_work diverse alocabile
sau pointer ar atinge aceleași scopuri, dar alți apelanți ai process_work ar putea avea amestecuri
diferite de alocabil și pointer, sau chiar doresc să treacă variabile simple.

Exerciții

1. Folosiți funcțiile pointer pentru a implementa un vector care numără de câte ori este accesat ca un
vector întreg și de câte ori este accesat un singur element din acesta.
Machine Translated by Google

Alte îmbunătățiri Fortran 2008 391

2. Scrieți un modul care implementează interfața standard random_number, pentru numere reale cu
precizie simplă și dublă, prin generatorul „bun, standard minim” de la Random Number Generators:
Good Ones Are Hard to Find (SK Park și KW Miller, CACM octombrie 1988). , Volumul 31 Numărul
10, pp 1192 1201). Acesta este un algoritm congruențial liniar multiplicativ parametric

xnew = mod(16807xold,231 1).


Machine Translated by Google

Această pagină a fost lăsată goală în mod intenționat


Machine Translated by Google

A. Proceduri intrinseci

În această anexă, listăm toate procedurile intrinseci, dând numele argumentelor lor și o scurtă descriere. În
cazul în care o procedură sau un argument de procedură a fost adăugat în Fortran 2003, plasăm un
superscript 3 după numele său. În cazul în care o procedură sau un argument de procedură a fost adăugat
în Fortran 2008, plasăm un superscript 8 după numele său.
Numele tuturor procedurilor intrinseci sunt incluse în Index, care, prin urmare, poate fi folosit pentru a
găsi descrierile complete.

Nume Descriere
abs (a) Valoare absolută.
achar (i [,kind3]) Caracter în pozi ia i a ASCII colating se
quence.
acos (x) Funcția arc cosinus (cosinus invers).
acosh8 (x) Funcția cosinus hiperbolic invers.
adjustl (șir) Ajustați la stânga, eliminând spațiile de început și inserând
spațiile finale.
ajustator (șir) Ajustați la dreapta, eliminând spațiile finale și inserând
spațiile principale.
aimag (z) Parte imaginară a numărului complex.
(un [,un fel]) Trunchiați la un număr întreg. nu
adevărate. toate (mască [,dim]) Adevărat dacă toate elementele sunt

alocată. alocat (matrice) sau alocat3 (scalar) Adevărat dacă matricea este
Adevărat dacă scalarul este alocat.
întreg. anint (un [,tip]) Cel mai apropiat număr
adevărat. orice (mască [,dim]) Adevărat dacă orice element este
(x) asinh8 (x) Funcția arcsinus (sinus invers). asin
Funcția sinus hiperbolic invers. asociat
(pointer [,țintă]) Adevărat dacă pointerul este asociat cu ținta.
(x) atan8 (y, x) Funcția arctangentă (tangentă inversă). atan
Argumentul numărului complex (x, y). atanh8
(x) Funcția tangentă hiperbolică inversă.
(y, x) apel atomic_define8 (atom, Definiți atomic cu
Argumentul
valoarea valoare.
numărului
valoare)
complex
apel (x,
atomic_ref8
y). atan2
(valoare, atom)

Definiți valoarea atomică cu valoarea atomului.


Machine Translated by Google

394 Fortran modern explicat

bessel_j08 (x) Funcția Bessel de primul fel și ordinul zero.

bessel_j18 (x) Funcția Bessel de primul fel și una de ordin.

bessel_jn8 (n, x) bessel_jn8 Funcția Bessel de primul fel și ordinul n.

(n1, n2, x) bessel_y08 (x) bessel_y18 Funcții Bessel de primul fel, ordinele de la n1 la n2.
(x) bessel_yn8 (n, x) bessel_yn8 Funcția Bessel de al doilea fel și ordinul zero.

(n1, n2, x) Funcția Bessel de al doilea fel și una de ordin.


Funcția Bessel de al doilea fel și ordinul n.
Funcții Bessel de al doilea fel, ordinele de la n1 la n2.

bge8 (i, j) bgt8 Adevărat dacă i este mai mare sau egal cu j.
(i, j) ble8 (i, j) blt8 Adevărat dacă i este mai mare decât j.
(i, j) Adevărat dacă i este mai mic sau egal cu j.
Adevărat dacă i este mai mic decât j.
bit_size (i) Numărul maxim de biți care pot fi păstrați într-un număr
întreg.
btest (i, pos) plafon Adevărat dacă bit pos al întregului i are valoarea 1.
(a [, kind]) char (i [,kind]) Cel mai mic număr întreg mai mare sau egal cu argumentul său.
Caracter în pozi ia i a secven ei de cola ionare a
procesorului.
cmplx (x [,y] [,kind]) Convertiți în tip complex.
command_argument_count3 () conjg (z) Numărul de argumente ale comenzii.
cos (x) cosh (x) count (mask [,dim] Conjugatul unui număr complex.
[,kind3]) apel cpu_time (time) cshift Funcția cosinus.
(matrice, shift [ ,dim]) apel data_și_ora Funcția cosinus hiperbolic.
([data] Numărul de elemente adevărate.
Timpul procesorului.
Efectuați o schimbare circulară.

Ora și data citirii ceasului în timp real.


[,time] [,zonă] [,valori]) dble (a)
Convertiți în reală dublă precizie. cifre (x)
Numărul de cifre semnificative din model pentru x. dim (x,
y) max(xy, 0). dot_product (vector_a, vector_b) Produs punctual.

(x, y). Produs real de precizie dublă a doi scalari dprod reali impliciti

dshiftl8 (i, j, shift) dshiftr8 (i, j, Cea mai semnificativă jumătate a unei deplasări la stânga cu lățime dublă.

shift) eoshift (matrice, shift Cea mai puțin semnificativă jumătate dintr-o schimbare la dreapta cu lățime dublă.

[,boundary] [,dim]) epsilon (x) Efectuați tura de sfârșit.

Număr care este aproape neglijabil în comparație cu unul din


modelul pentru numere precum x.
erf8 (x) Funcția de eroare.
erfc8 (x) Funcție de eroare complementară.
erfc_scaled8 (x) Funcție de eroare complementară la scară.
Machine Translated by Google

Proceduri intrinseci 395

apel execute_command_line8 (comandă Executați linia de comandă.


[, așteptați] [,exitstat] [,cmdstat]
[,cmdmsg]) exp (x) exponent (x)
extends_type_of3 (a, matriță)
findloc8 (matrice, valoare [,mask] [,kind] Functie exponentiala.
[, înapoi]) sau findloc8 (matrice, valoare, Parte exponentă a modelului pentru x.
dim Întrebare extensie de tip.
Găsiți locația în matrice a unui element cu valoare.

[,mask] [,kind] [,back]) floor (a [,kind])


fracție (x) gamma8 (x) apel get_command3 Cel mai mare număr întreg mai mic sau egal cu argumentul său.
([comandă] [,lungime] [,status]) apel Parte fracționară a modelului pentru x.
get_command_argument3 (număr [, valoare] Funcția Gamma.
[,lungime] [,stare]) apel Obțineți linia de comandă.

get_environment_variable3 (nume [,
valoare] [,lungime] [,stare] [,nume_trim]) Obține un singur argument de comandă.
imens (x) hypot8 (x, y) iachar (c
[,kind3]) iall8 (matrice, dim [,mask])
sau iall8 (matrice [,mask]) iand (i, j) iany8 Obțineți variabila de mediu.
(matrice, dim [,mask]) sau iany8 (array
[,mask]) ibclr (i, pos) ibits (i, pos, len)
ibset (i, pos) ichar (c [,kind3]) ieor (i, j) Cel mai mare număr din model pentru numere precum x.
image_index8 (coarray, sub) Funcția distanță euclidiană x2 +y2.
Poziția în secvența de cola ionare ASCII.
Efectuați biți și operații.

Logic și pe biți.
Efectuați operațiuni pe biți sau.

Ștergeți poziția bitului la zero.

Extrageți o secvență de biți.


Setați poziția bitului la unu.

Poziția în secvența de colazionare a procesorului.


Exclusiv sau pe biți.

Indexul imaginii dat de cosubscriptii sub pentru coarray.

index (șir, subșir [,back] [,kind3]) int Poziția de pornire a subșirului în șir.
(a [,kind]) ior (i, j) iparity8
(matrice, dim [,mask]) sau Efectuați Convertiți în tipul întreg.
operații exclusive sau pe biți. iparity8 Inclusiv sau pe biți.

(matrice [,mask]) is_contiguous8 (matrice)

Adevărat dacă matricea este învecinată.


Machine Translated by Google

396 Fortran modern explicat

ishft (i, shift) ishftc (i, Schimbarea logică a biților.


shift [,size]) is_isostat_end3 (i) Deplasare circulară logică pe un set de biți din dreapta.
is_isostat_eor3 (i) Valoarea de testare pentru starea de sfârșit al fișierului.

Valoarea de testare pentru starea de sfârșit de

înregistrare. fel (x) Tipul de valoare a parametrului.


lbound (matrice [,dim] [,kind3]) Limite inferioare ale matricei. lcobound8
(coarray [,dim] Coarray inferioare colimitate.
[,kind]) leadz8 (i) len (șir [,kind3]) len_trim (șir [,kind3]) lge (șir_a, șir_b)
lgt (șir_a, șir_b) lle (șir_a, șir_b) llt (șir_a, șir_b) logNumărul
(x) log_gamma8 (x) log10
de biți zero de început în i.
(x) logic (l, [,kind]) maskl8 (i [,kind]) maskr8 (i [,kind]) matmul
Lungimea (matricea_a,
caracterelor.
matricea_b) max (a1, a2 [,a3) ,...]) maxexponent Lungimea
(x) maxloc șirului
(matrice [,mask]
fără spații libere.
[,kind3] [,back8]) sau maxloc (matrice, dim [,mask] [,kind3]
ASCII [,back8])
mai mare sau egal.
ASCII mai mare decât.
ASCII mai mic sau egal.
ASCII mai puțin decât.

Funcția de logaritm natural (baza e).


Logaritmul valorii absolute a funcției gamma.
Funcție logaritmică comună (bază 10).
Convertiți între tipuri de logici.
Număr întreg cu i biții din stânga 1 și restul 0.
Număr întreg cu biții i din dreapta 1 și restul 0.
Înmulțirea matricei.
Valoare maximă.
Exponent maxim în model pentru reali ca x.
Locația elementului matrice maximă.

maxval (array [,mask]) sau maxval Valoarea elementului de matrice maximă.


(array, dim [,mask]) merge (tsource,
fsource, mask) tsource atunci când mask este adevărat; fsource altfel. merge_bits8 (i, j, masca)
Îmbinați biții i și j sub controlul măștii.
min (a1, a2 [,a3,...]) minexponent Valoarea minima.
(x) minloc (matrice [,mask] Exponent minim în model pentru reali ca x.
[,kind3] [,back8]) sau minloc Locația elementului de matrice minimă.
(matrice, dim [,mask] [,kind3]
[, back8]) minval (matrice [,mask]) sau
minval (matrice, dim [,mask])
mod (a, p) modulo (a, p) apel Valoarea elementului de matrice minimă.
move_alloc3 (de la, la) apel mvbits (de
la, frompos, Restul modulo p, adică a-int(a/p)*p. un modulo p.

Mutați alocarea.
Biți de copiere.
len, to, topos)
Machine Translated by Google

Proceduri intrinseci 397

cel mai apropiat (x, s) Cel mai apropiat număr diferit de mașină în direcția dată de

semnul s.
new_line3 (a) nint Caracter Newline.
(a [,kind]) norm28 (x[,dim]) Cel mai apropiat număr întreg.

not (i) null([mold]) Norma vectorială euclidiană.


num_images8 () pack Complement logic al biților.
(matrice, mască [,vector]) Indicator disociat.
Numărul de imagini.
Împachetați elementele corespunzătoare elementelor adevărate ale
măștii într-un rezultat de rangul unu.

parity8 (mask [,dim]) popcnt8 (i) Adevărat dacă numărul de valori adevărate este impar.

poppar8 (i) precizie (x) prezent (a) Numărul de biți unu în i. 1 dacă

produs (matrice [,mask]) sau popcnt(i) este impar sau 0 în caz contrar.
produs (matrice, dim [,mask]) Precizie zecimală în model pentru x.
radix (x) call random_number Adevărat dacă este prezent un argument opțional.
( recolta) apelează semințe_aleatoare Produsul elementelor matricei.
([dimensiune]
Baza modelului pentru numere precum x.

Numere aleatoare în intervalul 0 x < 1.


Inițializați sau reporniți generatorul de numere aleatoare.
[put] [get]) range
(x) real (a [,kind]) repetare Interval de exponent zecimal în model pentru x.
(șir, ncopii) remodelare Convertiți la tipul real.
(sursă, formă [,pad] [,order]) rrspacing Concatenează ncopii de șir.
(x) Reforma sursa pentru a modela forma.

Reciprocă a distanței relative a numerelor de model lângă x.

same_type_as3 (a, b) Comparați tipurile dinamice.


scale (x, i) scan x×bi unde
, b=radix(x).
(șir, set [,back] [,kind3]) selected_char_kind3 Indexul celui mai stâng (cel mai din dreapta dacă înapoi este
(nume) selected_int_kind (r) adevărat) caracter al șirului care este în set; zero dacă nici unul.
Un fel de set de caractere numit nume.

Tip de parametru de tip pentru intervalul de exponent specificat.

select_real_kind ([p] [,r] [,radix8]) Tip de parametru de tip pentru precizia specificată și intervalul
set_exponent (x, i) de exponent.
Număr de model al cărui semn și parte fracțională sunt cele ale
lui x și a cărui parte exponent este i.
form (sursa [,kind3]) shifta8 (i, shift) Forma matrice (sau scalară).
Schimbați biții din i dreapta cu schimbare, umplând cu bitul din
stânga.

shiftl8 (i, shift) shiftr8 (i, Schimbați biții din i la stânga cu shift, completând cu 0.
shift) Schimbați biții din i la dreapta cu shift, completând cu 0.
Machine Translated by Google

398 Fortran modern explicat

semn (a, b) sin Valoarea absolută a unui semn de timp al lui b.


(x) sinh (x) Funcția sinusoidală.

dimensiune Funcția sinus hiperbolic.


(matrice [,dim] [,kind3]) spațiere (x) Dimensiunea matricei.

răspândire (sursă, dim, ncopii) Spațierea absolută a numerelor de model lângă x.


ncopie copii ale sursei formând o matrice de rang unu mai
mare.
sqrt (x) Funcția rădăcină pătrată.
storage_size8 (a, [kind]) sum (matrice Dimensiunea stocării în biți.
[,mask]) sau sum (array, dim [,mask]) Suma elementelor matricei.
apel system_clock ([număr]
Date întregi din ceasul în timp real.
[,count_rate] [,count_max]) tan (x) tanh
(x) Funcția tangentă.
Funcția tangentă hiperbolică.
this_image8 () sau Indexul imaginii invocatoare.
this_image8 (coarray [,dim]) Cosubscripte ale coarasei care denotă date despre imaginea
invocată.
mic (x) Cel mai mic număr pozitiv din model pentru numere
precum x.
trailz8 (i) Numărul de biți de zero în urmă în i.
transfer (sursă, matriță [,dimensiune]) Aceeași reprezentare fizică ca și sursă, dar tip de mucegai.

matricei. transpune (matrice) Transpunerea


(șir) ubound (matrice [,dim] [,kind3]) Limite superioare
Îndepărtați
ale matricei.
spațiile
ucobound8
de sfârșit (coarray
dintr-un singur
[,dim] șir. trim

Coarray superioare colimitate.


[,drăguț])
despachetați (vector, mască, câmp) Despachetați elementele vectorului corespunzătoare elementelor
adevărate ale măștii.

verifica (șir, set [,back] [,kind3]) Zero dacă toate caracterele șirului de caractere aparțin setului sau
indexului celui mai din stânga (cel mai din dreapta dacă este adevărat),
asta nu.
Machine Translated by Google

B. Caracteristici depreciate

B.1 Introducere

Acest apendice descrie caracteristici care sunt redundante în Fortran 95 și a căror utilizare o depreciem. Ele ar
putea deveni învechite într-o revizuire viitoare, dar aceasta este o decizie care poate fi luată numai în cadrul
procesului de standardizare. Remarcăm că această decizie de a grupa anumite caracteristici într-o anexă și de
a le deprecia utilizarea este numai a noastră și nu are aprobarea reală sau implicită a WG5 sau J3.

Fiecare descriere menționează modul în care caracteristica în cauză poate fi înlocuită efectiv cu o
caracteristică mai nouă.

B.2 Asociere de depozitare

B.2.1 Unități de depozitare

Unitățile de stocare sunt unitățile fixe de stocare fizică alocate anumitor date. Există o unitate de stocare
numită numerică pentru orice scalar non-pointer al tipurilor implicite real, întreg implicit și logic implicit și o
unitate de stocare numită caracter pentru orice scalar non-pointer de tip caracter implicit și lungime de
caractere 1. Non-pointer scalarii de tip implicit complex sau dublă precizie real (Anexa B.6) ocupă două unități
de stocare numerice contigue. Scalarii non-pointer de tip caracter implicit și lungime len ocupă len unități de
stocare a caracterelor contigue.
Pe lângă unitățile de stocare numerice și de caractere, există un număr mare de unități de stocare
nespecificate. Un obiect scalar non-pointer de tip întreg non-implicit, real, altul decât precizia implicită sau
dublă, logic non-implicit, complex non-implicit sau caracter non-implicit de orice lungime particulară ocupă o
singură unitate de stocare nespecificată care este diferită pentru fiecare caz. Un obiect de date cu atributul
pointer are o unitate de stocare nespecificată, diferită de cea a oricărui obiect non-pointer și diferită pentru
fiecare combinație de tip, parametri de tip și rang.
Standardul nu face nicio declarație cu privire la dimensiunile relative ale tuturor acestor unități de depozitare
și permite asocierea de depozitare să aibă loc numai între obiecte cu aceeași categorie de unități de depozitare.
O matrice non-pointer ocupă o secvență de secvențe de stocare contigue, câte una pentru fiecare element,
în ordinea elementelor de matrice.
Obiectele de tip derivat nu au asociere de stocare, fiecare ocupând o unitate de stocare nespecificată care
este diferită în fiecare caz, cu excepția cazului în care un anumit tip conține o instrucțiune de secvență care îl
face un tip de secvență:
Machine Translated by Google

400 Fortran modern explicat

tip depozitare
secvență
întreg i real ! Prima unitate de stocare numerică; !
a(0:999) tip final 1000 de unită i numerice de stocare ulterioare.
de stocare

Dacă într-o astfel de definiție apar și alte tipuri derivate, trebuie să fie și ele tipuri de secvență.
În Fortran 2003, tipul este permis să aibă parametri de tip (Secțiunea 13.4), dar nu sunt permise proceduri
legate de tip (Secțiunea 14.6).
Un tip de secvență este un tip de secvență numerică dacă nu are parametri de tip, nicio componentă nu
este un pointer sau alocabil și fiecare componentă este de tipul implicit întreg, implicit real, dublă precizie
real, implicit complex sau implicit logic. O componentă poate fi, de asemenea, de tipul secvenței numerice
definite anterior. Aceasta implică faptul că componentele finale ocupă unități de stocare numerice și tipul
în sine are asociere de stocare numerică. În mod similar, un tip de secvență este un tip de secvență de
caractere dacă nu are parametri de tip, nicio componentă nu este un pointer sau alocabil și fiecare
componentă este de tip caracter implicit sau un tip de secvență de caractere definită anterior. Un astfel de
tip are asociere de stocare a caracterelor.
Un scalar de tip secvență numerică sau de caractere ocupă o secvență de stocare care constă în
concatenarea secvențelor de stocare ale componentelor sale. Un scalar de orice alt tip de secvență ocupă
o singură unitate de stocare nespecificată care este unică pentru fiecare combinație de parametri de tip și
tip.
O declarație privată poate fi adăugată la o definiție a tipului de secvență, făcând componentele sale
private. Declarațiile private și secvența pot fi interschimbate, dar trebuie să fie a doua și a treia instrucțiuni
ale definiției tipului.
Două definiții de tip în unități de acoperire diferite definesc același tip de date dacă au același nume1,
ambele au atributul secvență și au componente care nu sunt private și sunt de acord în ordine, nume și
atribute. Cu toate acestea, o astfel de practică este predispusă la erori și nu oferă niciun avantaj față de a
avea o singură definiție într-un modul care este accesat prin asociere de utilizare.

Un tip de secvență este permis să aibă o componentă alocabilă, care permite declarații independente
de același tip în domenii diferite, dar un astfel de tip, ca un pointer, are o unitate de stocare nespecificată.

B.2.2 Declarația de echivalență

Declarația de echivalență specifică că o anumită zonă de stocare poate fi partajată de două sau mai multe
obiecte. De exemplu,

real aa, unghi, alfa, a(3) echivalență


(aa, unghi), (alfa, a(1))

permite aa și angle să fie folosite interschimbabil în textul programului, deoarece ambele nume se referă
acum la aceeași locație de stocare. În mod similar, alfa și a(1) pot fi utilizate interschimbabil.
Este posibil să echivalăm tablouri împreună. În

1Dacă unul sau ambele tipuri au fost accesate prin asociere de utilizare și redenumite, numele originale trebuie să fie de acord.
Machine Translated by Google

Funcții depreciate 401

real a(3,3), b(3,3), col1(3), col2(3), col3(3) echivalență (col1, a,


b), (col2, a(1,2)), ( col3, a(1,3))

cele două tablouri a și b sunt echivalente, iar coloanele lui a (și, prin urmare, ale lui b) sunt echivalente
cu tablourile col1, etc. Observăm în acest exemplu că mai mult de două entități pot fi echivalente
împreună, chiar și într-o singură declarație.
Este posibil să se echivaleze variabile de același tip intrinsec și parametru de tip tip sau de același
tip derivat având atributul secvență. De asemenea, este posibilă echivalarea variabilelor de diferite
tipuri dacă ambele au asociere de stocare numerică sau ambele au asociere de stocare a caracterelor
(a se vedea apendicele B.2.1). Variabilele de caractere implicite nu trebuie să aibă aceeași lungime,
ca în

caracter(len=4) un
caracter(len=3) b(2)
echivalență (a, b(1)(3:))

unde variabila caracter a este echivalată cu ultimele patru caractere ale celor șase caractere ale
matricei de caractere b. Nu este permisă lungimea de caractere zero. Un exemplu pentru diferite
tipuri este

întreg i(100) real


x(100) echivalență
(i, x)

unde tablourile i și x sunt echivalente. Acesta poate fi folosit, de exemplu, pentru a economisi spațiu de
stocare dacă i este utilizat într-o parte a unității de program și x separat într-o altă parte. Aceasta este o
practică extrem de periculoasă, deoarece poate apărea o confuzie considerabilă atunci când o zonă de stocare
conține variabile de două sau mai multe tipuri de date, iar modificările programului pot fi foarte dificile dacă
cele două utilizări ale aceleiași zone trebuie să fie păstrate distincte.
Sunt permise tipurile cu inițializare implicită, cu condiția ca fiecare componentă inițializată să aibă
același tip, parametri de tip și valoare în orice pereche de obiecte echivalente.
Au fost descrise toate combinațiile diferite de tipuri care pot fi echivalente. Nu este permisă alta.
De asemenea, în afară de tipurile reale de precizie dublă și de tipurile numerice implicite, nu este
permisă echivalarea obiectelor care au parametri de tip diferit. Forma generală a enun ului este

echivalență (obiect, listă de obiecte) [, (obiect, listă de obiecte)]...

unde fiecare obiect este un nume de variabilă, un element de matrice sau un subșir. Un obiect
trebuie să fie o variabilă și nu trebuie să fie un argument fals, un rezultat al funcției, un pointer, un
obiect cu o componentă pointer la orice nivel de selecție a componentei, un obiect alocabil, un
obiect automat, o funcție, o componentă de structură, o structură cu o componentă finală alocabilă
sau un subobiect al unui astfel de obiect. Fiecare subscript de matrice și interval de subșir de
caractere trebuie să fie o expresie constantă. Interpretarea numelui unui tablou este identică cu cea
a primului său element. Un obiect de echivalență nu trebuie să aibă atributul target.
Se spune că obiectele dintr-o mulțime de echivalență sunt asociate cu stocarea. Cele de lungime
diferită de zero împart aceeași primă unitate de stocare. Cele de lungime zero sunt asociate între
ele și cu prima unitate de stocare a celor de lungime diferită de zero. O declarație de echivalență poate
Machine Translated by Google

402 Fortran modern explicat

face ca alte părți ale obiectelor să fie asociate, dar nu astfel încât subobiecte diferite ale aceluiași
obiect să împartă spațiu de stocare. De exemplu,

real a(2), b
echivalență (a(1), b), (a(2), b) ! Interzis

nu este permis. De asemenea, obiectele declarate în diferite unități de acoperire nu trebuie să fie echivalate.
De exemplu,

folosește my_module, numai: xx real


bb
echivalență (xx, bb) ! Interzis

nu este permis.
Diferitele utilizări cărora le-a fost pusă echivalența sunt înlocuite cu matrice automate, matrice alocabile,
pointeri (reutilizarea stocării, Secțiunile 6.4 și 6.5), pointerii ca alias (maparea stocării, Secțiunea 6.15) și funcția
de transfer (matarea unei date). tastați pe altul, Secțiunea 8.9).

B.2.3 Blocul comun

Am văzut în Capitolul 5 cum două unități de program sunt capabile să comunice trecând variabile sau valori
ale expresiilor între ele prin liste de argumente sau folosind module.
De asemenea, este posibil să se definească zone de depozitare cunoscute sub numele de blocuri comune.
Fiecare are o secvență de stocare și poate fi fie denumită, fie nenumită, așa cum se arată în sintaxa
simplificată a declarației de specificație comună:

comun [ / [ cname ] /] vlist

în care cname este un nume opțional, iar vlist este o listă de nume de variabile, fiecare urmată
opțional de o specificație a limitelor matricei. Un bloc comun fără nume este cunoscut ca un bloc
comun gol. Exemple pentru fiecare sunt

comune /mâini/ nshuff, nplay, nhand, cards(52)

și

comun // buffer(10000)

în care mâinile de bloc comune numite definesc o zonă de date care conține cantitățile care ar putea fi cerute
de subrutinele unui program de joc de cărți, iar comuna goală definește o zonă mare de date care ar putea fi
utilizată de diferite rutine ca zonă tampon.
Numele unui bloc comun are un domeniu de aplicare global și trebuie să difere de cel al oricărei alte
entități globale (procedură externă, unitate de program sau bloc comun). Cu toate acestea, poate fi același
cu cel al unei entități locale, alta decât o constantă numită sau o procedură intrinsecă.
Niciun obiect dintr-un bloc comun nu poate avea atributul de parametru sau poate fi un argument fals, un
obiect automat, un obiect alocabil, o structură cu o componentă alocabilă finală, un pointer polimorf sau o
funcție. Un tablou poate avea limitele declarate fie în instrucțiunea comună, fie într-o declarație de tip sau o
instrucțiune de dimensiune. Dacă este un non-pointer
Machine Translated by Google

Funcții învechite 403

matrice, limitele trebuie declarate explicit și cu expresii constante. Dacă este o matrice de pointeri, totuși,
limitele pot să nu fie declarate în declarația comună în sine. Dacă un obiect este de tip derivat, tipul
trebuie să aibă atributul secvență sau bind și nu trebuie să aibă inițializare implicită.

Pentru ca o subrutină să acceseze variabilele din zona de date, este suficient să se insereze definiția
comună în fiecare unitate de scoping care necesită acces la una sau mai multe dintre entitățile din listă.
În acest mod, variabilele nshuff, nplay, nhand și cards sunt puse la dispoziția acelor unități de acoperire.
Nicio variabilă nu poate apărea de mai multe ori în toate blocurile comune dintr-o unitate de acoperire.

De obicei, un bloc comun conține nume de variabile identice în toate aparițiile sale, dar acest lucru nu
este necesar. De fapt, zona de date partajată poate fi împărțită în moduri destul de diferite în diferite
rutine, folosind diferite nume de variabile. Se spune că sunt asociate cu stocarea. Este astfel posibil ca o
subrutină să con ină o declara ie

comune /coords/ x, y, z, i(10)

iar altul să con ină o declara ie

comune /coords/ i, j, a(11)

Aceasta înseamnă că o referire la i(1) în prima rutină este echivalentă cu o referire la a(2) în a doua. Prin
mai multe referințe prin utilizare sau asociere gazdă, acest lucru se poate întâmpla chiar și într-o singură
rutină. Acest mod de codificare este atât neîngrijit, cât și periculos și ar trebui depuse toate eforturile
pentru a se asigura că toate declarațiile unei anumite declarații comune sunt identice din toate punctele
de vedere. În special, prezența sau absența atributului țintă este necesară să fie consecventă, deoarece
altfel un compilator ar trebui să presupună că tot ce este în comun are atributul țintă, în cazul în care îl
are într-o altă unitate de program.
O altă practică care este permisă, dar pe care nu o recomandăm, este amestecarea diferitelor unități
de depozitare în același bloc comun. Când se face acest lucru, fiecare poziție din secvența de stocare
trebuie să fie întotdeauna ocupată de o unitate de depozitare din aceeași categorie.
Numărul total de unități de stocare trebuie să fie același în fiecare apariție a unui bloc comun numit,
dar comunul gol poate varia în dimensiune și cea mai lungă definiție se va aplica pentru programul
complet.
O altă practică care trebuie evitată este utilizarea întregii sintaxe a declarației comune:

comun [[cname]/]vlist [[,]/[cname]/vlist]...

care permite definirea mai multor blocuri comune într-o singură instrucțiune și declararea unui singur
bloc comun în părți. Un exemplu combinat este

comun /pts/x,y,z /matrice/a(10,10),b(5,5) /pts/i,j,k

care este echivalent cu

comun /pts/ x, y, z, i, j, k comun /matrice/


a(10,10), b(5,5)

care este cu siguranță o declarație mai înțeleasă a două zone de date partajate. Singura nevoie pentru
declararea pe bucăți a unui bloc este atunci când limita de 39 de linii de continuare este altfel prea mică.
Machine Translated by Google

404 Fortran modern explicat

Declarația comună poate fi combinată cu declarația de echivalență, ca în exemplu

real a(10), b
echivalență (a,b) comună /
schimbare/ b

În acest caz, a este considerat parte a blocului comun, iar lungimea sa este extinsă în mod corespunzător.
O astfel de echivalență nu trebuie să determine ca datele din două blocuri comune diferite să devină asociate
stocării, nu trebuie să provoace o extensie a blocului comun decât la coada acestuia și două obiecte sau
subobiecte diferite din același bloc comun nu trebuie să devină asociate cu stocarea.
Nu trebuie să determine ca un obiect să devină asociat cu un obiect dintr-un bloc comun dacă are o
proprietate care l-ar împiedica să fie un obiect dintr-un bloc comun.
Un bloc comun poate fi declarat într-un modul, iar variabilele sale pot fi accesate prin asociere de utilizare.
Numele de variabile dintr-un bloc comun dintr-un modul pot fi declarate ca având atributul privat, dar acest
lucru nu împiedică ca variabilele asociate să fie declarate în altă parte prin alte
declarații.

O variabilă individuală dintr-un bloc comun poate să nu primească atributul de salvare, dar întregul bloc
poate. Dacă un bloc comun are atributul de salvare în orice unitate de acoperire, alta decât programul
principal, trebuie să aibă atributul de salvare în toate aceste unități de acoperire. Forma generală a
instrucțiunii de salvare este

salvați [[::] listă-entitate-salvată]

unde entitate-salvată este nume-variabilă sau nume-bloc-comun. Un exemplu simplu este

salvează schimbarea/

Un comun gol are întotdeauna atributul de salvare.


Datele dintr-un bloc comun fără atributul de salvare devin nedefinite la întoarcerea dintr-un subprogram,
cu excepția cazului în care blocul este declarat și în programul principal sau într-un alt subprogram care este
în execuție.
Utilizarea modulelor (Secțiunea 5.5) înlătură necesitatea blocurilor comune.

B.2.4 Unitatea de program de date bloc

Variabilele non-pointer din blocurile comune numite pot fi inițializate în instrucțiuni de date, dar astfel de
instrucțiuni trebuie colectate într-un tip special de unitate de program, cunoscută sub numele de unitate de
program de date bloc. Trebuie să aibă forma

bloc date [bloc-date-name] [specification-


stmt]... final [block data [bloc-
date-nume]]

unde fiecare specificație-stmt este o declarație implicită, utilizare, tip (inclusiv precizie dublă), intrinsecă,
pointer, țintă, comună, dimensiune, date, echivalență, parametru sau declarație de salvare sau definiție de
tip derivat. O declarație de tip nu trebuie să specifice atributele alocabile, externe, de intenție, opționale,
private sau publice.
Un exemplu este
Machine Translated by Google

Funcții depreciate 405

blocarea datelor

/axe comune/ i,j,k date i,j,k /


1,2,3/ date bloc final

în care variabilele din axele comune ale blocului sunt definite pentru a fi utilizate în orice altă unitate de scoping care le
accesează.

Este posibil să colectați multe blocuri comune și instrucțiunile lor de date corespunzătoare împreună într-o unitate
de program de date bloc. Cu toate acestea, poate fi o practică mai bună să aveți mai multe unități de program de date
bloc diferite, fiecare conținând blocuri comune care au o anumită asociere logică între ele. Pentru a permite acest lucru,
unitățile de program de date bloc pot fi denumite pentru a le putea distinge. Un program complet poate conține orice
număr de unități de program de date bloc, dar numai una dintre ele poate fi nedenumită. Un bloc comun nu trebuie să
apară în mai mult de o unitate de program de date bloc. Nu este posibilă inițializarea comună goală.

Numele unei unități de program de date bloc poate apărea într-o declarație externă. Când un procesor încarcă
unități de program dintr-o bibliotecă, poate avea nevoie de o astfel de instrucțiune pentru a încărca unitatea de
program de date bloc.
Utilizarea modulelor (Secțiunea 5.5) înlătură nevoia de date bloc.

B.2.5 Coarrays și asociere de stocare

Coarray-urile nu sunt permise în declarațiile comune și de echivalență.

B.3 Dezacord între formă și lungimea caracterului

În Fortran 77, a fost adesea convenabil, când trecea o matrice, să nu fie nevoie să specificați dimensiunea matricei
inactiv. Pentru acest caz, este disponibilă declarația matricei de dimensiune presupusă, unde ultimele limite din lista
de limite sunt

[limita inferioară:] *

iar celelalte limite (dacă există) trebuie declarate explicit. O astfel de matrice nu trebuie să fie un rezultat al funcției.

Deoarece o matrice de dimensiune presupusă nu are limite în ultima sa dimensiune, nu are o formă și, prin urmare,
nu trebuie utilizată ca un întreg tablou într-o instrucțiune executabilă, decât ca argument pentru o procedură care nu
necesită forma sa. . Cu toate acestea, dacă o secțiune de matrice este formată cu o limită superioară explicită în ultima
dimensiune, aceasta are o formă și poate fi utilizată ca un întreg tablou.

O matrice de dimensiune presupusă nu are voie să aibă intentie dacă este polimorfă, de tip derivat cu inițializare
implicită sau o componentă alocabilă finală sau de tip finalizabil.
Acest lucru se datorează faptului că aceasta ar necesita o acțiune pentru fiecare element dintr-o matrice de formă necunoscută.
Un obiect de o dimensiune sau un rang poate fi trecut la o matrice de argumente inactiv de formă explicită sau de
dimensiune presupusă care este de altă dimensiune sau rang. Dacă un element de matrice este transmis unei matrice,
argumentul real este privit ca o matrice cu elemente care sunt formate din matricea părinte de la elementul de matrice
dat în sus, în ordinea elementelor de matrice. Figura B.1 ilustrează acest lucru.
Machine Translated by Google

406 Fortran modern explicat

Aici, numai ultimele 49 de elemente ale lui a sunt disponibile pentru sub, deoarece primul element de matrice
a lui a care este transmis la sub este a(52). În cadrul sub, acest element este referit ca b(1).

Figura B.1 Transmiterea unui element de matrice la o matrice.


real a(100)
:
apel sub (a(52), 49)
:
subrutină sub(b,n)
:
b(n) real

În același exemplu, ar fi perfect legitim ca declarația lui b să fie scrisă ca b real (7, 7) și ca ultimele 49 de
elemente ale lui a să fie adresate ca și cum ar fi ordonate ca o matrice 7×7. Este adevărat și invers. O
matrice dimensionată 10×10 într-un subprogram apelant poate fi dimensionată ca o matrice dimensionată
unic de dimensiunea 100 în subprogramul apelat. În cadrul sub, este ilegal să se adreseze b(50) în orice
fel, deoarece aceasta ar depăși lungimea declarată a lui a în rutina de apelare. În toate cazurile, asocierea
se face prin secvență de stocare, în ordinea elementelor de matrice.

În cazul tipului de caracter implicit, acordul privind lungimea caracterului nu este necesar. Pentru un
argument inactiv scalar cu lungimea caracterului len, argumentul real poate avea o lungime mai mare a
caracterului, iar caracterele len cele mai din stânga sunt asociate cu argumentul inactiv. De exemplu, dacă
chasub are un singur argument fals cu lungimea caracterului 1,

chemați chasub (cuvânt (3:4))

este o declarație de apel validă. Pentru un argument inactiv al matricei, restricția se referă la numărul
total de caractere din matrice. Un element de matrice sau un subșir de element de matrice este privit ca o
secvență de caractere de la primul său caracter până la ultimul caracter al matricei. Pentru o matrice de
dimensiune presupusă, dimensiunea este numărul de caractere din secvență împărțit la lungimea
caracterului argumentului inactiv.
Dezacordul privind forma sau lungimea caracterului nu poate apărea atunci când un argument fals
este asumat formă (prin definiție, forma este asumată din argumentul real). Poate apărea pentru matrice
de formă explicită și dimensiune presupusă. Implementările primesc de obicei matrice de formă explicită
și dimensiune presupusă în stocare contiguă, dar permit orice spațiere uniformă a elementelor unei
matrice de formă presupusă. Ei vor trebui să facă o copie a oricărui argument de matrice care nu este
stocat contiguu (de exemplu, secțiunea a(1:10:2)), cu excepția cazului în care argumentul inactiv este
presupus. Pentru a evita copiile de acest fel, un argument real scalar este permis să fie asociat cu o matrice
numai dacă argumentul real este un element al unui tablou care nu este polimorf, o matrice în formă
presupusă, un pointer de matrice sau este un subobiect de un astfel de element.
În Fortran 2003, aceste reguli privind dezacordul privind lungimea caracterelor au fost extinse pentru
a include caracterul (kind=c_char) (care va fi adesea același cu caracterul implicit) și pentru a trata orice alt
argument scalar real de tip caracter sau caracter implicit (kind=c_char) ) ca și cum ar fi o matrice de
dimensiunea unu. Aceasta include cazul în care argumentul este un element al
Machine Translated by Google

Funcții depreciate 407

o matrice de formă presupusă sau un indicator de matrice, sau un subobiect al acestuia; rețineți că doar acel
element sau subobiect este transmis, nu restul matricei.
Când o procedură este invocată printr-un nume generic, ca o operație definită sau ca o atribuire definită, este
necesar acordul de rang între argumentele actuale și cele simulate. Rețineți, de asemenea, că doar un argument
inactiv scalar poate fi asociat cu un argument real scalar.
Matricele de formă asumată (Secțiunea 6.3) înlocuiesc această caracteristică.

B.4 Linia include


Uneori este util să puteți include text sursă din altă parte în sursă

flux prezentat compilatorului. Această facilitate este posibilă folosind o linie include:

include char-literal-constant

unde char-literal-constant nu trebuie să aibă un parametru de tip care este o constantă numită. Această linie nu
este o instrucțiune Fortran și trebuie să apară ca o singură linie sursă în care poate apărea o declarație. Acesta va
fi înlocuit cu material într-un mod dependent de procesor determinat de șirul de caractere char-literal-constant.
Textul inclus poate conține în sine linii de includere, care sunt înlocuite în mod similar. O linie de includere nu
trebuie să facă referire la sine, direct sau indirect.
Când o linie de includere este rezolvată, prima linie inclusă nu trebuie să fie o linie de continuare, iar ultima linie nu
trebuie continuată. O linie include poate avea un comentariu final, dar nu poate fi etichetată și nici, atunci când
este extinsă, poate conține instrucțiuni incomplete.
Linia include a fost disponibilă ca o extensie pentru multe sisteme Fortran 77 și a fost adesea folosită pentru a
se asigura că fiecare apariție a datelor globale într-un bloc comun este identică. În Fortran modern, același efect
este mai bine obținut prin plasarea datelor globale într-un modul (Secțiunea 5.5). Acest lucru nu poate duce la
declarații accidentale ale variabilelor locale în fiecare procedură.
Această caracteristică este utilă atunci când sunt necesare instrucțiuni executabile identice pentru mai multe

tipuri, de exemplu într-un set de proceduri pentru sortarea valorilor datelor de diferite tipuri. Instrucțiunile
executabile pot fi menținute într-un fișier include care este referit în fiecare instanță a procedurii de sortare.

B.5 Alte forme de control al buclei


B.5.1 Construcția do etichetată

O altă formă a constructului do (Secțiunea 4.4) folosește o etichetă de instrucțiune pentru a identifica sfârșitul
constructului. În acest caz, instrucțiunea de încheiere poate fi fie o instrucțiune etichetată end do, fie o instrucțiune
etichetată continue („nu face nimic”) .2 Eticheta este, în fiecare caz, aceeași cu cea de pe instrucțiunea do în sine.
Eticheta de pe instrucțiunea do poate fi urmată de o virgulă. Exemple simple sunt

face 10 i = 1, n
:
10 sfâr itul face

2Instrucțiunea continue nu se limitează la a fi ultima instrucțiune a unui construct do; poate apărea oriunde
dintre instrucțiunile executabile.
Machine Translated by Google

408 Fortran modern explicat

și

face 20 i = 1, j face
10, k = 1, l
:
10 continua 20
continua

După cum se arată în al doilea exemplu, fiecare buclă trebuie să aibă o etichetă separată. Sintaxa do
suplimentară, dar și redundantă, este descrisă în Anexa C.1.8.

B.5.2 Do while

În Secțiunea 4.4, a fost descrisă o formă a constructului do care poate fi scrisă ca

do
if (scalar-logic-expr) exit
:
sfâr itul face

O formă alternativă, dar redundantă, a acesteia este reprezentarea sa folosind o instrucțiune do while:

do [label] [,] while (.not.scalar-logical-expr)

Preferăm forma care utilizează instrucțiunea exit, deoarece aceasta poate fi plasată oriunde în buclă, în timp
ce instrucțiunea do while își realizează întotdeauna testul la începutul buclei. Dacă expresia logică scalară
devine falsă în mijlocul buclei, restul buclei este încă executat.
Penalizările potențiale de optimizare pe care le implică utilizarea do while sunt descrise complet în Capitolul
10 din Optimizing Supercompilers for Supercomputers, M. Wolfe (Pitman, 1989).

B.6 Precizie dublă reală


Un alt tip care poate fi utilizat într-o declarație de tip, funcție, implicită sau declarație de componentă este
precizia dublă care specifică precizia dublă reală. Precizia este mai mare decât cea implicită reală.

Constantele literale scrise cu litera d (sau D) sunt implicit de tip dublă precizie real; nu poate fi specificat
niciun parametru de tip dacă se folosește această literă de exponent. Astfel, 1d0 este de tip dublă precizie
reală. Dacă dp este un număr întreg numit constantă cu valoarea kind(1d0), precizia dublă este sinonimă cu
real(kind=dp).
Există un descriptor de editare a anunțului (sau D) care a fost inițial destinat cantităților de dublă
precizie, dar, acum, este identic cu descriptorul de editare e, cu excepția faptului că forma de ieșire
poate avea un D în loc de un E ca literă de exponent. O constantă literală reală cu precizie dublă, cu
litera d exponent, este acceptabilă la intrare ori de câte ori este acceptabilă orice altă constantă literală reală.
Există două funcții intrinseci elementare care nu au fost descrise în Capitolul 8 deoarece au un rezultat de
tip dublă precizie reală.

dble (a) pentru un de tip întreg, real sau complex returnează valoarea reală dublă precizie real(a, kind(0d0)).
Machine Translated by Google

Funcții învechite 409

dprod (x, y) returnează produsul x*y pentru x și y de tipul implicit real ca dublu
precizie rezultat real.

Tipul de date reale cu precizie dublă a fost înlocuit cu tipul real de tip tip (0.d0).

B.7 Declarațiile de dimensiune, codimensiune și parametri

Pentru a declara entitățile, folosim de obicei specificații de tip. Cu toate acestea, dacă toate entitățile implicate
sunt matrice, acestea pot fi declarate fără specificații de tip într-o declarație de dimensiune:

dimensiunea i(10), b(50,50), c(n,m) ! n și m sunt întregi fictive


! argumente sau constante numite

Forma generală este

dimensiunea [::] nume-matrice (spec-matrice) [,nume-matrice (spec-matrice)]...

Aici, tipul poate fi specificat fie într-o declarație de tip, cum ar fi


întreg i

care nu specifică informațiile despre dimensiune sau poate fi declarat implicit. Părerea noastră este că niciuna
dintre acestea nu este o practică corectă; instrucțiunea de declarație de tip arată ca o declarație a unui scalar și
am explicat în Secțiunea 7.2 că considerăm scrierea implicită ca fiind periculoasă.
Prin urmare, utilizarea declarației de dimensiune nu este recomandată.
În Fortran 2008, există instrucțiunea de codimensionare pentru declararea co-arrays (Capitolul 19) cu sintaxa
codimension [::] coarray-decl-list unde fiecare coarray-decl este coarray-name [(array-spec)][coarray-spec]

O modalitate alternativă de a specifica o constantă numită este prin instrucțiunea parametrului. Are parametrul
de formă generală ( named-constant-definition-list ) unde fiecare definiție-denumită-constant-este

constant-name = constant-expr

Fiecare constantă numită trebuie fie să fi fost introdusă într-o declarație anterioară de tip declarație în unitatea
de definire a domeniului, fie să ia tipul acesteia din prima literă a numelui, conform regulii implicite de tastare a
unității de acoperire. În cazul tastării implicite, apariția constantei numite într-o declarație de tip ulterioară în
unitatea de scoping trebuie să confirme parametrii de tip și tip și nu trebuie să existe o declarație implicită pentru
scrisoarea ulterior în unitatea de scoping. În mod similar, forma trebuie să fi fost specificată anterior sau să fie
scalară.
Fiecare constantă numită din listă este definită cu valoarea expresiei corespunzătoare conform regulilor de
atribuire intrinsecă.
Un exemplu folosind tastarea implicită și o expresie constantă care include o constantă numită
care este definit în aceeași declarație este

parametru întreg implicit (a, p)


(măr = 3, pară = măr**2)

Din aceleași motive ca și pentru dimensiune, vă recomandăm să evitați declarația parametrilor.


Machine Translated by Google

410 Fortran modern explicat

B.8 Denumiri specifice ale procedurilor intrinseci

În timp ce toate procedurile intrinseci sunt generice, unele dintre funcțiile intrinseci au, de asemenea, nume
specifice versiuni specifice, care sunt enumerate în tabelele B.1 și B.2. În tabele, „Caractere” reprezintă
caracterul implicit, „Întreg” înseamnă întreg implicit, „Real” înseamnă real implicit, „Dublu” înseamnă dublă
precizie reală și „Complex” înseamnă complex implicit. Acele funcții din Tabelul B.2 pot fi transmise ca
argumente reale unui subprogram, cu condiția să fie specificate într-o declarație intrinsecă (Secțiunea 8.1.3).

Tabelul B.1. Funcții intrinseci specifice nu sunt disponibile ca argumente reale.


Descriere Generic Specific Argument Funcție Nume Tip Tip
Formă

Conversie int(a) int Real Întreg


la un întreg repar Real Întreg
idint Double Întreg

Conversie real(a) real Integer Real


la real float Integer Real

dublu Sngl Real

max(a1,a2,...) max(a1,a2,...) max0 Întreg Întreg


amax1 Real Real
dmax1 Dublu Dubla

amax0 Număr întreg Real


max1 Real Întreg

min(a1,a2,...) min(a1,a2,...) min0 Întreg Întreg


amin1 Real Real
dmin1 Dublu Dubla

amin0 Număr întreg Real


min1 Real Întreg

lge(șir_a, șir_b) lge(șir_a, șir_b) lge Caracter logic

lgt(șir_a, șir_b) lgt(șir_a, șir_b) lgt Caracter logic

lle(șir_a,șir_b) lle(șir_a,șir_b) lle Caracter logic

llt(șir_a, șir_b) llt(șir_a, șir_b) llt Caracter logic


Machine Translated by Google

Funcții depreciate 411

Tabelul B.2. Funcții intrinseci specifice disponibile ca argumente reale.


Descriere Nume specific Generic Funcția de argumentare
Formă Tip Tip

Valoarea absolută a semnului (a,b) este semnul a Întreg Întreg


timpului semnului b semn Real Real
design Dublu dublu
max(xy,0) dim(x,y) idim Întreg Întreg
dim Real Real
ddim Dublu dublu

x y dprod(x,y) Real aint Dubla


Trunchiere nu (a) Real Real
dint Dublu dublu
Cel mai apropiat întreg anint(a) anint Real Real
număr dnint Dublu dublu

Cel mai apropiat număr întreg nint(a) nint Real Întreg


idnint Număr întreg dublu
Valoare absolută abs(a) iabs Întreg Întreg
abs Real Real
tamponări Dublu dublu
taxiuri Complex Real
Rest mod(a,p) mod Întreg Întreg
modulo p amod Real Real
dmod Dublu dublu

Rădăcină pătrată sqrt(x) sqrt Real Real


dsqrt Dublu dublu
csqrt Complex Complex
Exponen ial exp(x) exp Real Real
dexp Dublu dublu
cep Complex Complex
Logaritmul natural log(x) alog Real Real
dlog Dublu dublu
clog Complex Complex
Logaritm comun log10(x) alog10 Real Real
dlog10 Dublu dublu
Sinus sin(x) păcat Real Real
dsin Dublu dublu
csin Complex Complex
Cosinus cos(x) cos Real Real
dcos Dublu dublu
ccos Complex Complex
Machine Translated by Google

412 Fortran modern explicat

Tangentă tan(x) bronzat Real Real


dtan Dublu dublu
Arcsin asin(x) ca în Real Real
dasin Dublu dublu
Arccozină acos(x) acos Real Real
dacos Dublu dublu
Arctangent atan(x) un bronz Real Real
datan Dublu dublu
atan2(y,x) atan2 Real Real
datan2 Dublu dublu
Sinus hiperbolic sinh(x) sinh Real Real
dsinh Dublu dublu
Cosinus hiperbolic(x) cosh Real Real
dcosh Dublu dublu
Tangenta hiperbolica tanh(x) tanh Real Real
dtanh Dublu dublu
Partea imaginară aimag(z) aimag Complex Real
Conjugat complex conjg(z) conjg Complex Complex
Lentile de lungime a caracterelor len Caracter întreg
Indicele poziției de pornire (s,t). Caracter întreg

B.9 Mapare non-implicit pentru tastarea implicită

Valoarea implicită pentru tastarea implicită (Secțiunea 7.2) este aceea că entitățile ale căror nume încep
cu una dintre literele i,dej, tipul
..., n sunt de real.
implicit tipul Dacă
implicit
se întreg,
doreșteiar variabilele
tastarea care încep
implicită cu literele
cu o regulă a, b,într-o
diferită z sunt
..., h sau o, p, ..., unitate de acoperire dată, poate fi folosită instrucțiunea implicită. Acest lucru modifică
maparea dintre litere și tipurile cu instrucțiuni precum

întreg implicit (ah) implicit


real(selected_real_kind(10)) (r,s) implicit tip(intrare) (u,xz)

Literele sunt specificate ca o listă în care un set de litere adiacente din alfabet poate fi prescurtat, ca în
ah. Nicio literă nu poate apărea de două ori în instrucțiunile implicite ale unei unități de acoperire și, dacă
există o declarație implicită none, nu trebuie să existe o altă declarație implicită în unitatea de acoperire.
Pentru o scrisoare care nu este inclusă în declarațiile implicite, maparea dintre literă și un tip este
maparea implicită.
În cazul unei unități de delimitare, alta decât o unitate de program sau un bloc de interfață, de
exemplu un subprogram de modul, maparea implicită pentru fiecare literă dintr-o unitate de acoperire
interioară este maparea pentru scrisoarea din gazda imediată. Dacă gazda conține o instrucțiune implicită
none, maparea implicită este nulă și efectul poate fi că tastarea implicită este disponibilă pentru unele
litere, din cauza unei declarații implicite suplimentare în domeniul intern, dar nu pentru toate. Maparea
poate fi la un tip derivat chiar și atunci când acel tip nu este altfel
Machine Translated by Google

Funcții învechite 413

accesibil în unitatea interioară de scoping din cauza unei declarații acolo de alt tip cu
acelasi nume.

Tastarea implicită nu se aplică unei entități accesate prin utilizare sau asociere gazdă, deoarece tipul
acesteia este același ca în modul sau gazdă. Figura B.2 oferă o ilustrare cuprinzătoare a regulilor de
tastare implicită.
Forma generală a enun ului implicit este

implicit nici unul

sau

tip implicit (listă-specificație-litere) [,tip (listă-spec-scrisoare)]...

unde tip specifică tipul și parametrii de tip (Secțiunea 7.13) și fiecare literă-spec este

litera [- litera ].

Declarația implicită poate fi utilizată pentru un tip derivat. De exemplu, având acces la tip

tip posn
real :: x, y întreg :: z
end tip posn

și dată declarația

tip implicit (posn) (a,b), întreg (cz)

variabilele care încep cu literele a și b sunt implicit tastate posn iar variabilele care încep cu literele c,d,...,z
sunt implicit tastate întregi.
O instrucțiune implicită none poate fi precedată într-o unitate de definire numai de instrucțiuni de
utilizare (și format), iar alte instrucțiuni implicite pot fi precedate numai de instrucțiuni de utilizare,
parametru și format. Vă recomandăm ca fiecare declarație implicită none să fie la începutul specificațiilor,
imediat după orice instrucțiuni de utilizare.

B.10 Caracteristici depreciate Fortran 2008

B.10.1 Instrucțiunea memoriei de sincronizare și subrutinele atomice

Execuția unei instrucțiuni de memorie de sincronizare definește o graniță pe o imagine între două
segmente, fiecare dintre acestea putând fi ordonat într-un mod definit de utilizator în raport cu
segmentele din alte imagini. Spre deosebire de celelalte instrucțiuni de control al imaginii, nu are niciun
efect de sincronizare integrat. În cazul în care există o ordine definită de utilizator între imagini,
compilatorul va evita probabil optimizările care implică mutarea instrucțiunilor în instrucțiunea memoriei
de sincronizare și se va asigura că orice date modificate pe care imaginea le păstrează în memoria
temporară, cum ar fi cache sau registre sau chiar pachete în tranzit între imagini, sunt făcute vizibile
pentru alte imagini. De asemenea, orice date din alte imagini care sunt păstrate în memoria temporară
vor fi tratate ca nedefinite până când sunt reîncărcate din imaginea gazdă.
Machine Translated by Google

414 Fortran modern explicat

Figura B.2
module example_mod implicit
nici unul
:
interfata

funcția distracție (i) ! i este implicit întreg :: fun !


număr întreg declarat. sfârșitul funcției distracție

interfață finală
con ine

funcția jfun(j) întreg :: ! Toate entitățile de date trebuie


jfun, j să ! să fie declarat în mod explicit.
:

end function jfun end


module example_mod subrutine sub

complex implicit (c) c = (3.0,2.0)


! c este implicit declarat complex
:
con ine
subrutina sub1

întreg implicit (a,c) c = (0,0,0,0) ! c


este asociat gazdei si de tip complex z = 1,0 ! z este implicit declarat real. ! a este implicit
declarat întreg. ! cc este implicit declarat întreg.
a=2
cc = 1,0
:
sfâr itul subrutinei sub1
subrutinei sub2
z = 2,0 ! z este implicit declarat real și este ! diferită de variabila z din
sub1.
:
sfâr itul subrutinei sub2
subrutina sub3

folosește ! Accesați funcția întreg distractiv. ! q este implicit


example_mod q = fun(k) declarat real și ! k este implicit declarat întreg.

:
sfâr itul subrutinei sub3
end subrutine sub
Machine Translated by Google

Funcții depreciate 415

Considerăm că construirea unui cod fiabil și portabil în acest fel este foarte dificilă – totul este
prea ușor să introduci bug-uri subtile care se manifestă doar ocazional.
O modalitate de a efectua ordonarea definită de utilizator între imagini este prin utilizarea subrutinelor
atomice, o nouă clasă de subrutine intrinseci. O subrutină atomică acționează asupra unui atom variabil
scalar de tip integer(atomic_int_kind) sau logic(atomic_logical_kind), a cărui valoare tip este definită în
modulul intrinsec iso_fortran_env. Atomul variabil trebuie să fie un coarray sau un obiect coindexat. Efectul
executării unei subrutine atomice este ca și cum acțiunea asupra argumentului atom are loc instantaneu
și, prin urmare, nu se suprapune cu alte acțiuni atomice care ar putea avea loc asincron. Pentru a evita
pierderea performanței, ordonarea acțiunilor intercalate pe diferite variabile atomice în imagini diferite nu
este definită de Standard.

call atomic_define (atom, valoare) definește atomic cu valoarea


valoare.

atom are intent out și este un coarray scalar sau un obiect coindexat de tip integer(atomic_int_kind)
sau logic(atomic_logical_kind). Dacă tipul său este același cu cel al valorii sau tipul său este
logic, i se acordă valoarea valorii.
În caz contrar, i se dă valoarea int(value, atomic_int_kind).
value are intentia in si este un scalar de acelasi tip ca atom.

call atomic_ref (valoare, atom) definește valoarea atomic cu valoarea lui


atom.

value are intent out și este un scalar de același tip ca atom. Dacă tipul său este același cu cel al
atomului sau tipul său este logic, i se acordă valoarea atomului. În caz contrar, i se dă valoarea
int(atom, kind(value)).
atom are intentia in si este un coarray scalar sau obiect coindexat. Are tip
întreg(atomic_int_kind) sau logic(atomic_logical_kind).

De exemplu, luați în considerare codul din Figura B.3, care este executat pe imaginile p și q. Bucla do este
cunoscută sub numele de buclă spin-wait. Odată ce imaginea q începe să o execute, aceasta va continua până
când va găsi valoarea .false. pentru val. Apelul atomic_ref asigură că valoarea este reîmprospătată la fiecare
execuție a buclei. Efectul este că segmentul de pe imaginea p înaintea primei instrucțiuni de memorie de
sincronizare precede segmentul de pe imaginea q care urmează celei de-a doua instrucțiuni de memorie de sincronizare.
Textul normativ al Standardului nu specifică modul în care resursele ar trebui distribuite între imagini, dar
o notă se așteaptă ca partajarea să fie echitabilă. Prin urmare, este posibil ca o implementare conformă să-
și dea toate resursele buclei de rotație fără a face nimic pe imaginea p, provocând blocarea programului.

Rețineți că segmentul în care este modificat blocat[q] este neordonat în raport cu segmentul în care
este referit. Acest lucru este permis de regulile din penultimul paragraf al Secțiunii 19.13.1.

Având în vedere subrutinele atomice și declarația de memorie de sincronizare, sincronizările


personalizate pot fi programate în Fortran ca proceduri, dar poate fi dificil pentru programator să se
asigure că vor funcționa corect pe toate implementările.
Toate declarațiile de control al imaginii, cu excepția criticilor, criticilor de final, blocării și deblocării
include efectul executării unei instrucțiuni de memorie de sincronizare.
Machine Translated by Google

416 Fortran modern explicat

Figura B.3 Utilizarea buclei


Spin-wait, intrinsecă :: iso_fortran_env
logical(atomic_logical_kind) :: locked[*] = .true. logic :: val întreg :: iam,
p, q

:
iam = this_image() if (iam
== p) atunci
apel de
memorie de sincronizare atomic_define(blocat[q], .false.)
! Are efectul blocat[q]=.false.
else if (iam == q) atunci val = .true.

! Rotiți până când val este fals do


while (val) apel atomic_ref(val,
blocat)
! Are efectul val=blocat
sfâr itul face

sincronizare se termină

memoria dacă

B.10.2 Componente de tip c_ptr sau c_funptr

Un coarray este permis să aibă o componentă de tip c_ptr sau c_funptr, dar un obiect coindexat nu este
permis să fie de niciunul dintre aceste tipuri, deoarece este aproape sigur că implică o referință la
distanță. În plus, atribuirea intrinsecă pentru oricare dintre aceste tipuri face ca variabila să devină
nedefinită, cu excepția cazului în care variabila și expresia sunt pe aceeași imagine. Este foarte greu să
vezi utilizări bune pentru această caracteristică.

B.10.3 Declarații de tip

Cuvântul cheie type poate fi utilizat cu o specificație de tip intrinsec în loc de o specificație de tip derivat
și aceasta declară entitățile ca fiind de acel tip intrinsec. De exemplu,

tip(complex(tip(0d0))) :: a, b, c

declară a, b și c ca fiind de tip complex intrinsec cu parametru de tip tip egal cu fel (0d0), adică complex
de dublă precizie. Această sintaxă este complet redundantă și exemplul este echivalent cu

complex(tip(0d0)) :: a, b, c

Această caracteristică a fost adăugată pentru coerență cu instrucțiunea type is din constructul de tip
select: în acea instrucțiune, un tip intrinsec este specificat prin cuvântul său cheie, dar un tip derivat
este specificat pur și simplu prin numele său tip, fără cuvântul cheie type (sau parantezele concomitente) .
Machine Translated by Google

Funcții depreciate 417

Considerăm că această caracteristică nu adaugă nimic limbajului; în plus, ar putea deruta cititorul să creadă
că un tip intrinsec este într-adevăr un tip derivat, așa că nu recomandăm utilizarea acestuia.

B.10.4 Instrucțiunea conține redundante

Declarația conține într-un modul, o unitate de program non-modul sau o definiție de tip nu mai este necesar să
fie urmată de o definiție de procedură de modul, de o definiție de procedură internă sau de o declarație de
procedură legată de tip. De exemplu,

modul trivial
logic :: ok = .adevărat. con ine

! modul nimic
final

este permis. În fiecare caz, apariția unei declarații conține fără nicio definiție sau declarație următoare nu are
efect.
Utilizarea pretinsă a acestei caracteristici este pentru generarea automată a unităților de program
Fortran în care generatorul automat nu este suficient de inteligent pentru a omite declarația conține în
cazul în care nu există nimic cu care să-l urmărească. Considerăm că această caracteristică este confuză
și nu recomandăm utilizarea ei.

B.10.5 Declarația finală

Cuvintele cheie ale funcției și subrutinei sunt acum opționale pe instrucțiunea finală a unui modul sau
subprogram intern. De exemplu,

modulul m25
con ine
orbital subrutinei
...
Sfâr it

modul final

Aceasta înseamnă că dacă aveți un subprogram vechi Fortran 77 îl puteți transforma într-un subprogram
intern sau modul prin simpla includere într-un fișier sursă care are aceeași formă sursă (adică formularul sursă
fix învechit).
De asemenea, înseamnă că a vedea un capăt gol nu mai înseamnă neapărat că vedeți sfârșitul unei unități
de program (sau corp de interfață). Este de părere că această caracteristică nu adaugă nimic la limbaj și că este
mai bine ca toate instrucțiunile de final să specifice ce se termină, de exemplu modulul final.
Machine Translated by Google

418 Fortran modern explicat

B.10.6 Referirea la atan2 prin numele atan

Rupând cu 50 de ani de tradiție și aproape orice alt limbaj de programare existent, forma cu două
argumente a arctangentului poate fi acum referită prin numele atan în loc de atan2.

Numele atan2 va rămâne pentru totdeauna utilizabil în acest scop (pentru compatibilitate
inversă), așa că există puțin de câștigat din asta, în afară de confuzia utilizatorilor („de ce este
apelat atan cu două argumente?”) și compilator și limbaj bloat.
Machine Translated by Google

C. Caracteristici învechite

C.1 Învechit în Fortran 95

Caracteristicile acestei secțiuni sunt descrise de standardul Fortran 95 ca fiind învechite. Înlocuirile lor sunt
descrise în subsecțiunile relevante.

C.1.1 Formă sursă fixă

În vechea formă de sursă fixă, fiecare instrucțiune constă dintr-una sau mai multe rânduri lungi de exact 72 de
caractere1 și fiecare rând este împărțit în trei câmpuri. Primul câmp este format din pozițiile de la 1 la 5 și poate
conține o etichetă de declarație. O declarație Fortran poate fi scrisă în al doilea câmp de până la 20 de rânduri
consecutive. Prima linie a unei instrucțiuni cu mai multe linii este cunoscută ca linia inițială, iar liniile ulterioare ca

linii de continuare.
O linie de non-comentare este o linie inițială sau o linie de continuare, în funcție de faptul că există un caracter,
altul decât zero sau gol, în poziția 6 a rândului, care este al doilea câmp. Primul câmp al unei linii de continuare
trebuie să fie necompletat. Ampersand nu este folosit pentru continuare.
Al treilea câmp, de la pozițiile 7 la 72, este rezervat declarațiilor Fortran în sine.
Rețineți că, dacă o construcție este numită, numele trebuie plasat aici și nu în câmpul de etichetă.
Cu excepția unui context de caracter, spațiile sunt nesemnificative.
Prezența unui asterisc (*) sau a unui caracter c în poziția 1 a unei linii indică faptul că întreaga linie este
comentariu. Un semn de exclamare indică începutul comentariului, cu excepția poziției 6, unde indică continuarea.

Pe o singură linie pot apărea mai multe instrucțiuni separate prin punct și virgulă (;). În acest caz, punctul și
virgulă nu pot fi în coloana 6, unde ar indica continuarea. Numai prima dintre afirmațiile de pe o linie poate fi
etichetată. Un punct și virgulă care este ultimul caracter neblank al unui rând sau ultimul caracter neblank înaintea
comentariului este ignorat.
O instrucțiune de final de unitate de program nu trebuie continuată și orice altă instrucțiune cu un
linia inițială care pare a fi o instrucțiune de final de unitate de program nu trebuie continuată.
Un procesor poate restricționa aspectul caracterelor sale de control definite, dacă există, în această formă
sursă.

În aplicațiile în care un grad ridicat de compatibilitate între vechea și noua sursă


este necesară, respectarea următoarelor reguli poate fi de mare ajutor:

• limitați etichetele declarațiilor la pozițiile 1 la 5 și declarațiile la pozițiile 7 la 72;

1Această limită depinde de procesor dacă linia conține alte caractere decât cele de tip implicit.
Machine Translated by Google

420 Fortran modern explicat

• tratați spațiile libere ca fiind semnificative;

• utiliza i numai ! pentru a indica un comentariu (dar nu in pozitia 6);

• pentru instrucțiunile continuate, plasați un ampersand în ambele poziții 73 a unei linii continuate
i pozi ia 6 a unei linii continue.

Formularul sursă fixă a fost înlocuit cu formularul sursă liberă (Secțiunea 2.4).

C.1.2 Calculat merge la

O formă de instrucțiune de ramură este go to calculată, care permite selectarea unei căi dintre multe, în
funcție de valoarea unei expresii întregi scalare. Forma generală este

merge la (sl1, sl2, sl3,...) [,] intexpr

unde sl1, sl2, sl3 etc. sunt etichete ale instrucțiunilor din aceeași unitate de acoperire, iar intexpr este
orice expresie întregă scalară. Aceeași etichetă de declarație poate apărea de mai multe ori. Un exemplu
este

mergi la (6,10,20) i(k)**2+j

care face referire la trei etichete de declarații. Când instrucțiunea este executată, dacă valoarea expresiei
întregi este 1, se va lua prima ramură, iar controlul este transferat instrucțiunii etichetate 6. Dacă valoarea este
2, se va lua a doua ramură și așa mai departe. Dacă valoarea este mai mică de 1 sau mai mare de 3, nu va fi
luată nicio ramură, iar următoarea instrucțiune după go to va fi executată.

Această afirmație este înlocuită cu constructul case (Secțiunea 4.3).

C.1.3 Caracter de specificare a lungimii caracterelor*

Alternative pentru caracterele implicite la

caracter([len=] valoare-len)

ca tip într-o declarație de tip, o declarație de definiție a funcției, implicite sau a componentelor

caracter*(len-valoare)[,]
și
caracter*len[,]

unde len este o constantă literală întreagă fără o valoare specifică de tip și virgula opțională este permisă
numai într-o declarație de tip și numai atunci când :: este absent:

caracter*20 cuvânt, litera*1

C.1.4 Instrucțiuni de date între executabile

Declarația de date poate fi plasată printre instrucțiunile executabile, dar o astfel de plasare este rar folosită și
nu este recomandată, deoarece inițializarea datelor aparține în mod corespunzător instrucțiunilor de
specificație.
Machine Translated by Google

Caracteristici învechite 421

C.1.5 Funcții de enunț

Este posibil ca într-o singură unitate de program să existe apariții repetate ale unui calcul care poate fi
reprezentat ca o singură instrucțiune. De exemplu, pentru a calcula funcția parabolică reprezentată de

y = a+bx+cx2

pentru valori diferite ale lui x, dar cu aceiași coeficienți, pot exista referiri la

y1 = 1. + x1*(2. + 3.*x1)
:
y2 = 1. + x2*(2. + 3.*x2)
:

etc. În Fortran 77, era mai convenabil să se invoce o așa-numită funcție de instrucțiuni (acum mai bine
codificată ca subrutină internă, Secțiunea 5.6), care trebuie să apară după orice instrucțiuni implicite și alte
specificații relevante și înaintea instrucțiunilor executabile. Exemplul de mai sus ar deveni

parab(x) = 1. + x*(2. + 3.*x)


:
y1 = parab(x1)
:
y2 = parab(x2)

Aici, x este un argument inactiv, care este utilizat în definiția funcției de instrucțiune. Variabilele x1 și x2 sunt
argumente reale ale funcției.
Forma generală este

nume-funcție([lista-argumente-dummy] ) = scalar-expr

unde numele-funcție și fiecare argument-fictiv trebuie specificate, explicit sau implicit, pentru a fi obiecte de
date scalare. Pentru a clarifica faptul că aceasta este o funcție de instrucțiune și nu o atribuire unui element
de matrice gazdă, vă recomandăm să declarați tipul plasând numele funcției într-o declarație de tip; acest
lucru este necesar ori de câte ori o entitate gazdă are același nume. Expr-ul scalar trebuie să fie compus din
constante, referințe la variabile scalare, referințe la funcții și operații intrinseci. Dacă există o referire la o
funcție, funcția nu trebuie să fie un intrinsec transformațional și nici să necesite o interfață explicită, rezultatul
trebuie să fie scalar și orice argument al matricei trebuie să fie un tablou numit. O referire la o funcție non-
intrinsecă nu trebuie să necesite o interfață explicită. O constantă numită la care se face referire sau un tablou
al cărui element este referit trebuie să fie declarată mai devreme în unitatea de acoperire sau să fie accesată
prin utilizare sau asociere gazdă. O variabilă scalară la care se face referire poate fi un argument inactiv al
funcției de instrucțiune sau o variabilă care este accesibilă în unitatea de acoperire. Un argument fals al
procedurii gazdă nu trebuie să fie referit decât dacă este un argument fals al intrării principale sau al unei
intrări care precede funcția de instrucțiune. Dacă orice entitate este introdusă implicit, o declarație de tip
ulterioară trebuie să confirme parametrii de tip și tip. Argumentele fictive sunt scalare și au doar un scop al
instrucțiunii de funcție de instrucțiune.

O funcție de instrucțiune are întotdeauna o interfață implicită și nu poate fi furnizată ca argument de


procedură. Poate apărea într-o procedură internă și poate face referire la altele
Machine Translated by Google

422 Fortran modern explicat

funcțiile de instrucțiune care apar înaintea ei în aceeași unitate de definire a domeniului, dar nu ea însăși și
nici oricare care apare după. O referință de funcție din expresie nu trebuie să redefinească un argument fals.
O funcție de declarație este pură (Secțiunea 6.10) dacă se referă doar la funcții pure.
O instrucțiune de funcție de instrucțiune nu este permisă într-un bloc de interfață.
Rețineți că funcțiile de declarație sunt neregulate prin aceea că utilizarea și asocierea gazdei nu sunt disponibile.

C.1.6 Lungimea caracterelor presupuse a rezultatelor funcției

O funcție externă nerecursivă al cărei rezultat este scalar, caracter și non-pointer poate să fi presupus
lungimea caracterului, ca în Figura C.1. O astfel de funcție nu este permisă pentru a specifica o operație
definită. Într-o unitate de scoping care invocă o astfel de funcție, interfața trebuie să fie implicită și trebuie să
existe o declarație a lungimii, ca în Figura C.2, sau o astfel de declarație trebuie să fie accesibilă prin utilizare
sau asociere gazdă.

Figura C.1 O funcție al cărei rezultat are lungimea presupusă a caracterelor.


function copy(word)
character(len=*) copy, word copy = word
end function copy

Figura C.2 Apelarea unei funcții al cărei rezultat are lungimea presupusă a caracterelor.
program principal extern copie caracter(len=10) copiere scriere (*, *) copy('Acest
! Blocul de interfață nu este permis.
mesaj va fi trunchiat') final program principal

Această facilitate este inclusă numai pentru compatibilitatea cu Fortran 77 și este complet în contradicție
cu filozofia Fortran 90/95 conform căreia atributele unui rezultat al funcției depind doar de argumentele reale
ale invocării și de orice date accesibile de funcție prin gazdă sau asociere de utilizare.

Această facilitate poate fi înlocuită prin utilizarea unei subrutine ale cărei argumente corespund cu
rezultatul funcției și argumentele funcției.

C.1.7 Aritmetica if

Aritmetica if oferă un mecanism de ramificare în trei căi, în funcție de faptul dacă o expresie aritmetică are o
valoare care este mai mică, egală cu sau mai mare decât zero. Este înlocuit cu instrucțiunea și constructul if
(Secțiunea 4.2). Forma sa generală este

dacă (expr) sl1, sl2, sl3


Machine Translated by Google

Caracteristici învechite 423

unde expr este orice expresie scalară de tipul întreg sau real, iar sl1, sl2 și sl3 sunt etichetele instrucțiunilor
din aceeași unitate de acoperire. Dacă rezultatul obținut prin evaluarea expr este negativ atunci se ia
ramura la sl1, dacă rezultatul este zero se ia ramura către sl2, iar dacă rezultatul este mai mare decât
zero se ia ramura către sl3. Un exemplu este

dacă (pq) 1,2,3 1 p


= 0. mergi la 4 2 p = 1. q
= 1. mergi la 4 3 q =
0. 4

...

în care o ramură la 1, 2 sau 3 este luată în funcție de valoarea pq. Aritmetica if poate fi folosită ca ramură
bidirecțională atunci când două dintre etichete sunt identice:

dacă (xy) 1,2,1

C.1.8 Terminare do-loop partajată

O buclă do poate fi terminată pe o instrucțiune etichetată, alta decât un end do sau continue.
O astfel de instrucțiune trebuie să fie o instrucțiune executabilă, alta decât go to, o instrucțiune return
sau final a unui subprogram, o instrucțiune stop sau final a unui program principal, exit, cycle, aritmetică
if, sau atribuită instrucțiune go to. Buclele imbricate pot împărtăși aceeași instrucțiune terminală
etichetată, caz în care toate regulile obișnuite pentru blocurile imbricate sunt valabile, dar o ramură a
etichetei trebuie să fie din bucla cea mai interioară. Astfel, putem scrie o înmulțire matriceală
la fel de

a(1:n, 1:n) = 0. do 1 i =
1, n do 1 j = 1, n do 1 l
= 1, na(i, j) = a(i, j)
+ b(i , l)*c(l, j)
1

Executarea unei instrucțiuni de ciclu repornește bucla fără executarea instrucțiunii terminale.
Această formă de do-loop nu oferă nicio funcționalitate suplimentară, ci un spațiu considerabil pentru
greșeli neașteptate.

C.1.9 Retur alternativ

La apelarea anumitor tipuri de subrutine, este posibil să apară condiții excepționale specifice, care ar
trebui să provoace o întrerupere a fluxului normal de control. Este posibil să se anticipeze astfel de
condiții și să se codifice diferite căi de flux în urma unui apel de subrutină, în funcție de faptul dacă
subrutina apelată s-a încheiat în mod normal sau a detectat o condiție excepțională sau anormală. Acest
lucru se realizează folosind facilitatea de returnare alternativă care utilizează lista de argumente în felul
următor. Să presupunem că o afacere subrutină primește într-un
Machine Translated by Google

424 Fortran modern explicat

Argumentul listează numărul de cărți dintr-un pachet amestecat, numărul de jucători și numărul de cărți
care trebuie împărțite pentru fiecare mână. În interesul generalității, ar fi o precauție rezonabilă ca prima
declarație executabilă de acord să fie o verificare a faptului că există cel puțin un jucător și că există, de
fapt, suficiente cărți pentru a satisface cerințele fiecărui jucător. Dacă nu există jucători sau cărți
insuficiente, acesta poate semnala acest lucru programului principal care ar trebui să ia măsurile
corespunzătoare. Acest lucru poate fi scris în schiță ca

call deal(nshuff, nplay, nhand, cards, *2, *3) call play

:
2 ........ ! Gestionați cazul fără jucător
:
3 ........ ! Manevrați cazul cu carduri insuficiente
:

Dacă cărțile pot fi împărțite, controlul normal este returnat și apelul la joc este executat. Dacă apare o
excepție, controlul este transmis instrucțiunii etichetate 2 sau 3, moment în care trebuie luată o acțiune
- pentru a opri jocul sau amestecați mai multe cărți. Eticheta declarației relevante este definită prin
plasarea etichetei declarației precedate de un asterisc ca argument real în lista de argumente. Trebuie
să fie o etichetă a unei instrucțiuni executabile a aceleiași unități de acoperire. Poate fi specificat orice
număr de astfel de returnări alternative și pot apărea în orice poziție din lista de argumente. Deoarece,
totuși, sunt utilizate în mod normal pentru a gestiona excepții, ele sunt cel mai bine plasate la sfârșitul
listei.
În subrutina apelată, argumentele dummy corespunzătoare sunt asteriscuri, iar returnarea alternativă
este luată prin executarea unei instrucțiuni de forma

returnează intexpr

unde intexpr este orice expresie întreagă scalară. Valoarea acestei expresii la momentul execuției
definește un index la întoarcerea alternativă care trebuie luată, în funcție de poziția sa în lista de
argumente. Dacă intexpr evaluează la 2, va fi luată a doua retur alternativă. Dacă intexpr evaluează la o
valoare care este mai mică decât 1 sau mai mare decât numărul de returnări alternative din lista de
argumente, va fi luată o întoarcere normală. Astfel, în deal, putem scrie simplu

ofertă subrutină (nshuff, nplay, nhand, cards, *, *)


:
if (nplay.le.0) return 1 if (nshuff .lt.
nplay*nhand) return 2

Această caracteristică este disponibilă și pentru subrutinele definite de instrucțiuni de intrare. Nu


este disponibil pentru funcții sau subrutine elementare.
Această caracteristică este înlocuită cu utilizarea unui argument întreg care conține un cod de returnare utilizat
într-un construct de caz următor.

C.2 Caracteristică învechită în Fortran 2008: Declarație de intrare

Un subprogram definește de obicei o singură procedură, iar prima instrucțiune care trebuie executată
este prima instrucțiune executabilă după instrucțiunea antet. În unele cazuri este util să poți
Machine Translated by Google

Caracteristici învechite 425

definiți mai multe proceduri într-un singur subprogram, în special atunci când doriți să partajați accesul la
unele variabile locale salvate sau la o secțiune de cod. Acest lucru este posibil pentru subprogramele externe
și module (dar nu pentru subprogramele interne) prin intermediul instrucțiunii de intrare. Aceasta este o
afirmație care are forma

intrare nume-intrare [[([ lista-argumente-inactiv ] ) [rezultat(nume-rezultat)]]

și poate apărea oriunde între linia antetului și conține (sau se termină dacă nu are nicio instrucțiune conține)
a unui subprogram, cu excepția unui construct. Instrucțiunea de intrare furnizează o procedură cu o listă de
argumente fictive asociată, exact ca instrucțiunea de subrutină sau funcție, iar aceste argumente pot fi
diferite de cele date în instrucțiunea de subrutină sau funcție. Execuția începe cu prima instrucțiune
executabilă după instrucțiunea de intrare.

În cazul unei funcții, fiecare intrare definește o altă funcție, ale cărei caracteristici (adică, formă, tip,
parametri de tip și dacă un pointer) sunt date de specificațiile pentru numele rezultat (sau numele intrării
dacă nu există niciun rezultat). clauză). Dacă caracteristicile sunt aceleași ca pentru intrarea principală, se
utilizează o singură variabilă pentru ambele rezultate; în caz contrar, ele nu trebuie să fie alocabile, nu
trebuie să fie pointeri, trebuie să fie scalari și ambele trebuie să fie unul dintre tipurile implicite întregi, real
implicit, real cu precizie dublă (Anexa B.6) sau complexe implicite și sunt tratate ca echivalat. Clauza rezultat
joacă exact același rol ca și pentru intrarea principală.
Fiecare intrare este considerată ca definind o altă procedură, cu propriul nume. Numele tuturor acestor
proceduri și variabilele lor de rezultat (dacă există) trebuie să fie distincte. Numele unei intrări are același
domeniu ca și numele subprogramului. Nu trebuie să fie numele unui argument fals al vreuneia dintre
procedurile definite de subprogram. O instrucțiune de intrare nu este permisă într-un bloc de interfață;
trebuie să existe un alt corp pentru fiecare intrare a cărei interfață este dorită, folosind o instrucțiune de
subrutină sau funcție, mai degrabă decât o instrucțiune de intrare.
O intrare este apelată exact în același mod ca o subprogram sau o funcție, în funcție de faptul că apare
într-un subprogram de subprogram sau într-un subprogram de funcție. Un exemplu este dat în Figura C.3
care prezintă o funcție de căutare cu două puncte de intrare. Remarcăm că looku și looks sunt sinonime în
cadrul funcției, astfel încât nu are importanță ce valoare este setată înainte de returnare.

Niciuna dintre procedurile definite de un subprogram nu este permisă să facă referire la sine, cu excepția
cazului în care cuvântul cheie recursiv este prezent în subrutină sau în instrucțiunea funcției. Pentru o
funcție, o astfel de referință trebuie să fie indirectă, cu excepția cazului în care există o clauză rezultat pe
funcție sau instrucțiunea de intrare. Dacă o procedură poate fi referită direct în subprogramul care o
definește, interfața este explicită în subprogram.
Numele unui argument inactiv de intrare care apare într-o instrucțiune executabilă care precede
instrucțiunea de intrare în subprogram trebuie să apară și într-o funcție, subrutină sau instrucțiune de
intrare care precedă instrucțiunea executabilă. De asemenea, dacă un argument inactiv este utilizat pentru
a defini dimensiunea matricei sau lungimea caracterelor unui obiect, obiectul nu trebuie să fie referit decât
dacă argumentul este prezent în referința de procedură care este activă.
În timpul executării uneia dintre procedurile definite de un subprogram, o referire la a
argument dummy este permis numai dacă este un argument dummy al procedurii la care se face referire.
Declarația de intrare este făcută inutilă prin utilizarea modulelor (Secțiunea 5.5), fiecare procedură definită
de o intrare devenind o procedură de modul.
Machine Translated by Google

426 Fortran modern explicat

Figura C.3 O funcție de căutare cu două puncte de intrare.


funcția looku(listă, membru) întreg looku,
list(:), membru, arată

Pentru a localiza un membru într-o listă de matrice.


Dacă lista nu este sortată, se utilizează intrarea looku; dacă
!!!!
lista este sortată, se utilizează aspectul de intrare.
!
! Lista este nesortată.
face looku = 1, size(list) if (list(looku)
== member) return
sfâr itul face

!
! Nu a fost gasit.
uite = 0
întoarcere
!
Punct de intrare pentru lista sortată.
!!

intrare looks(listă, membru) face


looks = 1, size(list) if (listă(looks) ==
membru) return if (listă(looks) > member) exit end
do

!
! Nu a fost gasit.
aspect = 0
!
funcția finală

C.3 Caracteristică ștearsă în Fortran 2003: Controlul transportului

Declarațiile de ieșire formatate ale Fortran au fost concepute inițial pentru imprimante cu linii, cu conceptul lor
de linii și pagini de ieșire. Pe un astfel de dispozitiv, primul caracter al fiecărei înregistrări de ieșire trebuie să
fie de tip implicit. Nu este tipărit, ci interpretat ca un caracter de control al căruciorului.
Dacă este un spațiu liber, nu se întreprinde nicio acțiune și este o bună practică să inserați un spațiu liber ca
''
prim caracter al fiecărei înregistrări, fie în mod explicit,
Secțiunea fie 9.12.4),
folosindpentru
descriptorul
a evitade
generarea
editare t2
accidentală
(descris înde
caractere false de control al căruciorului. Acest lucru se poate întâmpla atunci când primul caracter dintr-o
înregistrare de ieșire nu este gol și poate apărea, de exemplu, la tipărirea valorilor întregi cu formatul „(i5)”.
Aici, toate valorile de ieșire între 999 și 9999 vor avea un gol în prima poziție, dar toate celelalte vor genera
acolo un caracter care poate fi folosit în mod greșit pentru controlul căruciorului.

Caracterele de control al căruciorului definite de standard sunt:


Machine Translated by Google

Caracteristici învechite 427

b pentru a începe o nouă linie

+ pentru a rămâne pe aceeași linie (supraprintare) 0


pentru a sări peste o linie 1 pentru a avansa la
începutul paginii următoare

Ca măsură de precauție, primul caracter al fiecărei înregistrări produs de ieșirea listă-direcționată și namelist este un gol,
cu excepția cazului în care este continuarea unei constante de caractere delimitate.
În acest context, observăm că execuția unei instrucțiuni de tipărire nu implică că tipărirea va avea loc efectiv și nici
executarea unei instrucțiuni de scriere nu implică faptul că tipărirea nu va avea loc.
apar.

C.4 Caracteristici șterse în Fortran 95

Caracteristicile enumerate în această secțiune au fost șterse în întregime din limba Fortran 95.
Deși se poate aștepta ca compilatorii să continue să accepte aceste caracteristici pentru o anumită perioadă, utilizarea lor
ar trebui evitată complet pentru a asigura portabilitatea pe termen foarte lung și pentru a evita mesajele de avertizare
inutile ale compilatorului. Ele sunt descrise pe deplin în edițiile anterioare ale acestei cărți.

Indicii do non-întregi Variabila do și expresiile care specifică limitele și pasul unui construct do sau unui-do implicit într-o
instrucțiune I/O pot fi de tipul implicit real sau dublă precizie real.

Formate go to atribuite și formate atribuite O altă formă de instrucțiune de ramificație este de fapt scrisă în două părți, o
instrucțiune de atribuire și o instrucțiune de tip atribuit. O utilizare a instrucțiunii de atribuire este înlocuită cu
expresii de caractere pentru a defini specificatorii de format (Secțiunea 9.4).

Ramificare la o instrucțiune end if Era permisă ramificarea la o instrucțiune end if din afara construcției pe care o termină.
O ramură a următoarei declarații este un înlocuitor pentru această practică.

Instrucțiunea de pauză În anumite momente ale execuției unui program a fost posibilă întreruperea, pentru a permite
efectuarea unei eventuale intervenții externe în condițiile de rulare.

Descriptorul de editare H Descriptorul de editare H (sau h) a oferit o formă timpurie a șirului de caractere
editați descriptorul.
Machine Translated by Google

Această pagină a fost lăsată goală în mod intenționat


Machine Translated by Google

D. Evitarea cascadelor de compilare

La compilarea unui modul, majoritatea compilatorilor produc două fișiere: un fișier obiect și un fișier separat
care conține informațiile pentru utilizarea modulului (vom numi pe acesta din urmă fișierul .mod, deoarece
acesta este cel mai frecvent sufix utilizat pentru acesta) .
O unitate de program care utilizează un modul depinde de fișierul .mod, nu de fișierul obiect sau de sursă
fișier, dar o cascadă de recompilare apare din orice modificare a modulului deoarece

i) fișierul .mod depinde de fișierul sursă; și

ii) compilatorul actualizează fișierul .mod la compilarea fișierului sursă chiar dacă nu există
modificări la acesta.

Pentru a evita această cascadă, trebuie să întrerupeți conexiunea dintre fișierul sursă și fișierul

.mod și evitați actualizarea fișierului .mod atunci când nu există modificări ale acestuia.
Vom descrie cum să faceți acest lucru folosind instrumentul make. În primul rând, Makefile are nevoie de o
descriere a modului de generare a fișierului .mod atunci când acesta nu există. Acest lucru ar trebui făcut
printr-o invocare recursivă make. În al doilea rând, compilatorul trebuie să fie împiedicat să actualizeze
fișierul .mod atunci când nu există modificări. Acest lucru se poate face folosind un script shell pentru a încheia
invocarea compilatorului. Vom arăta cum să faceți acestea cu compilatorul NAGWare f95.
În cel mai simplu caz avem un program, example.exe cu două fișiere sursă, unul.f90 care conține un modul
care este folosit de celălalt, two.f90. Makefile este prezentat în Figura D.1, iar scriptul shell este prezentat în
Figura D.2 (f95 este numele prin care este invocat compilatorul).

Scriptul shell folosește opțiunile -M și -nomod care fac ca compilatorul să producă numai
fișierele .mod sau să nu producă fișiere .mod, respectiv.
În cazul puțin mai complicat în care se folosește un alt modul zero și nu face totul de la zero privat, atunci
zero.mod trebuie să apară ca o dependență a one.mod (precum și a one.o), adică

one.mod: zero.mod

Această tehnică poate fi utilizată pentru majoritatea compilatoarelor care stochează informațiile modulului
într-un fișier separat; pentru unele dintre ele, timpul de compilare este scris în fișierul .mod, așa că trebuie
folosit un instrument mai inteligent pentru a compara fișierele .mod noi și vechi - aceste informații pot fi
obținute de obicei de la furnizorul compilatorului sau de la World- Wide Web.
Machine Translated by Google

430 Fortran modern explicat

Figura D.1
# Makefile de exemplu.exe

# Opțiuni de compilare: #
(opțiunea -nocheck_modtime suprimă verificarea compilatorului pentru ca fișierul # .mod să
fie învechit - deoarece folosim make, nu avem nevoie de # această verificare.)

F90FLAGS = -O -nocheck_modtime

# Opțiuni de conectare:
F90LINKFLAGS =

# Executabilul depinde de toate fișierele obiect: example.exe:


one.o two.o
f95 $F90LINKFLAGS -o exemplu.exe unu.o doi.o

# Utilizați mf95 pentru a evita actualizările


fișierelor .mod one.o: one.f90
mf95 unul unu.f90

# Nu există module în two.f90, așa că puteți folosi f95 direct two.o: two.f90
one.mod f95 two.f90

# Dacă nu există nici un fișier .mod, „make one.f90” se va asigura că avem # un


fișier sursă actualizat, apoi îl compilați cerând ca fișierul # .mod să fie produs (acest
lucru este foarte rapid). one.mod:

face unul.f90
f95 -M unu.f90
Machine Translated by Google

Evitarea cascadelor de compilare 431

Figura D.2

#!/bin/sh if
[ -f $1.mod ]; apoi # Produceți
doar fișierul .mod... mkdir $1.tmp f95 -M
$F90FLAGS -mdir $1.tmp $2 cmp -s
$1.tmp/$1.mod $1.mod dacă [ $? != 0 ];
apoi Conținut diferit de fișier .mod, așa că
actualizați-l mv $1.tmp/$1.mod $1.mod
#

fi
rm -r $1.tmp #
Acum produceți fișierul obiect și nu produceți un fișier .mod f95 $F90FLAGS
-nomod $2 altfel # fișierul .mod nu există, doar compilați f95 $F90FLAGS $2

fi
Machine Translated by Google

Această pagină a fost lăsată goală în mod intenționat


Machine Translated by Google

E. Exemplu de listă orientată pe obiecte

O problemă recurentă în calcul este necesitatea de a manipula o structură dinamică de date. Aceasta ar
putea fi o simplă listă omogenă legată ca cea întâlnită în Secțiunea 2.13, dar adesea este necesară o
structură mai complexă.
Exemplul din această anexă constă dintr-un modul care oferă două tipuri – un tip listă anylist și un
element de tip anyitem – pentru construirea de liste liniare eterogene, dublu legate, plus o funcție simplă
de constructor de articole newitem. Operațiunile pe listă sau pe articole sunt furnizate prin proceduri cu
caracter obligatoriu. Fiecare element din listă are o valoare scalară care poate fi de orice tip; la crearea
unui nou articol de listă, valoarea necesară este copiată în articol. Un articol din listă poate fi în cel mult o
listă odată.
Operațiunile cu listă includ inserarea unui nou articol la începutul sau la sfârșitul listei, returnarea
articolului la începutul sau la sfârșitul listei și numărarea, imprimarea sau ștergerea întregii liste.
Operațiunile asupra unui articol includ eliminarea acestuia dintr-o listă, returnarea articolului următor
sau anterior din listă, modificarea valorii articolului și imprimarea sau ștergerea articolului. Când
parcurgeți lista înapoi (prin intermediul funcției prev), lista este circulară; adică ultimul element din listă
este anterior primului. Când parcurgeți lista înainte (prin următoarea funcție), un pointer nul este returnat
după ultimul element.
Intern, modulul folosește componente private pointer (firstptr, nextptr, prevptr și upptr) pentru a
menține structura listelor.
Operația de imprimare a articolului poate fi suprascrisă într-o extensie a oricărui articol pentru a oferi
capacitate de imprimare pentru tipurile definite de utilizator; acest lucru este demonstrat de tipul myitem.
Toate celelalte proceduri nu pot fi suprascrise, astfel încât extinderea tipului de listă nu poate rupe
structura listei.
Codul sursă este disponibil la ftp://ftp.numerical.rl.ac.uk/pub/MRandC/oo.f90

modul anylist_m
!
! Modul pentru un tip de listă care poate conține elemente cu orice valoare scalară.
! Valorile sunt copiate în elementele din listă. !

! Un articol din listă poate fi în cel mult o listă odată.


!
implicit nici unul
privat public ::
anylist, anyitem, newitem !

! type(anylist) este tipul antetului listei.


Machine Translated by Google

434 Fortran modern explicat

!
tastați anylist
class(anyitem), pointer, private :: firstptr => null() conține

procedure, non_overridable :: procedură de


adăugare, non_overridable :: procedură de
numărare_liste, non_overridable :: procedură de
ștergere_list, non_overridable :: prima procedură,
non_overridable :: ultima procedură, non_overridable ::
procedură de anteprimare, non_overridable :: tip de
final de lista_printare

!
! type(anyitem) este tipul de element din listă.
! Acestea sunt alocate prin element nou. !

tip anyitem
class(*), class :: valoare
alocatable(anyitem), pointer, private :: nextptr => null(), prevptr => null() class(anylist), pointer,
private :: upptr => null() conține

procedure, non_overridable :: procedura de


schimbare, non_overridable :: procedura de
ștergere, non_overridable :: procedura listă,
non_overridable :: următoarea procedură,
non_overridable :: procedura anterioară ::
procedura de tipărire, non_overridable
eliminarea
::
tipului final

con ine
!
! Creați un nou element de listă (orfan). !

function newitem(ceva) class(*),


intent(in) :: ceva class(orice element), pointer ::
newitem alocare (newitem) alocare
(newitem%value, source=something)
newitem%prevptr => newitem end function

!
! Adăugați un articol la o listă. !

subrutină anexează (listă, articol)


clasa(anylist), intent(inout), target :: list class(anyitem), target
class(anyitem), pointer if (asociat(item%upptr)) apel :: articol
remove(item) :: ultimul
Machine Translated by Google

Exemplu de listă orientată pe obiecte 435

item%upptr => listă dacă


(asociat(list%firstptr)) apoi ultima => list%firstptr%prevptr
last%nextptr => element item%prevptr => ultima
listă%firstptr%prevptr => element alt

list%firstptr => item item%prevptr


=> item
sfâr itul dacă

sfâr itul subrutinei


!

! Numărați câte articole sunt într-o listă. !

funcția întreg count_list(listă)


class(anylist), intent(in) :: list class(anyitem), pointer :: p
count_list = 0 p => list%firstptr do

if (.not.associated(p)) exit count_list = count_list


+ 1 p => p%nextptr

sfâr itul face

funcția finală
!
! Ștergeți conținutul unei liste.
!

subrutină delete_list(listă)
class(anylist), intent(inout) :: list do

if (.not.associated(list%firstptr)) ieșiți apel ștergere(list%firstptr)

sfâr itul face

sfâr itul subrutinei


!
! Returnează primul element al unei liste.
!

funcția first(list) class(anylist),

intent(în) :: list class(anyitem), pointer :: first first =>


list%firstptr

funcția finală
!
! Returnează ultimul element al unei liste
!

ultima functie (lista)

class(anylist), intent(in) :: list class(anyitem), pointer :: last


Machine Translated by Google

436 Fortran modern explicat

last => list%firstptr if


(asociat(ultimul)) last => last%prevptr funcția finală

!
! Introduceți un element la începutul unei liste. !

subrutine prepend(listă, articol)


class(anylist), intent(inout), target :: list class(anyitem), target ::
item if (asociat(item%upptr)) call remove(item) item%upptr
list if =>
(asociat(list%firstptr) )) atunci

item%prevptr => list%firstptr%prevptr item%nextptr


=> list%firstptr list%firstptr%prevptr => articol

altfel
item%prevptr => articol se
termină dacă
list%firstptr => subrutină de
sfârșit a articolului
!
! Imprimați articolele dintr-o listă.
!
subrutina print_list(lista, show_item_numbers, show_empty_list)
class(anylist), intent(in) :: listă logică, intent(in),
opțional :: show_item_numbers, show_empty_list class(anyitem), pointer :: p integer i logic ::
show_numbers if (prezent(show_item_numbers)) apoi show_numbers = show_item_numbers

altfel
show_numbers = .true.
sfâr itul dacă

p => list%firstptr if
(.not.associated(p)) apoi if
(present(show_empty_list)) atunci dacă
(show_empty_list) print *, 'Lista este goală.' altfel

print *, „Lista este goală”.


sfâr itul dacă

altfel
face i=1, uriaș(i)-1
if (show_numbers) write (*, 1, advance='nu') i format(1x, 'Item
1 ', i0, ':') call p%print p => p%nextptr if (.not.associated(p) ) Ieșire

sfâr itul face


Machine Translated by Google

Exemplu de listă orientată pe obiecte 437

sfâr itul dacă

sfâr itul subrutinei


!

! Schimbați valoarea unui articol. !

modificarea subrutinei (articol, valoare nouă)


class(orice element), intent (inout) :: item class(*), intent (in)
:: valoare nouă
dealocare (item%value) alocare (item%value, source=newvalue)

sfâr itul subrutinei


!
! Ștergeți un articol: îl elimină din listă și îl dealoca.
!

ștergerea subrutinei (articol)

clasă(orice element), țintă :: element clasă(orice


element), pointer :: temp temp => apel articol
remove(element) dealocare (temp) sfârșit subrutine

! Întoarce lista din care face parte un articol. Nul dacă este orfan. !

listă de funcții (articol) clasă

(orice element), intenție (în) :: clasă de articole (o listă),


pointer :: listă listă => item%upptr

funcția finală
!
! Întoarceți următorul articol din listă.

function next(item)

class(anyitem), intent(in) :: item class(anyitem), pointer ::


next next => item%nextptr end function

! Returnează articolul anterior din listă, ! sau ultimul articol dacă


acesta este primul.
!

function prev(item)
class(anyitem), intent(in) :: item class(anyitem), pointer ::
prev prev => item%prevptr end function

!
! Imprimați un articol. Acest lucru este anulabil.
!
Machine Translated by Google

438 Fortran modern explicat

imprimare subrutine (aceasta)


class(anyitem) :: acest tip de
selectare a lungimii întregi
(v=>această %valoare) tip este
(caracter(*)) lungime = len(v) dacă
(lungime>40) atunci tipăriți 1,
lungime, v(:36) format(1x,
'caracter(len=', i0, ') = "', a, '"...')
1 altfel

'"'
print *, 'caracter = "', v,
sfâr itul dacă

tipul este (complex)


print *, 'complex', v tipul este
(complex(kind(0d0))) print 2, kind(v), v
format(1x, 'complex(kind=', i0, ') =
2 ( ', es22.16, ', ', es22.16, ')') tipul este (real(kind(0d0))) print 3, kind(v), v format(1x, 'real(kind=',
i0 , ') = ', es22.16)

3
tipul este (întreg) print
*, 'integer = ', v tipul este (real)
print *, 'real = ', v tipul este (logic)
print *, 'logic = ', v

implicit de clasă

print *, „tip de articol nerecunoscut - nu poate afișa valoarea”


final select
sfâr itul subrutinei
!
! Eliminați un articol dintr-o listă (dar păstrați-l și valoarea acestuia). !

eliminare subrutină (articol)


class(anyitem), intent(inout), target :: item class(anylist),
pointer :: list list => item%upptr if (asociat(lista)) atunci

dacă (asociat(item%prevptr, item)) atunci


! Un singur articol din listă.
nullify(lista%firstptr)
altfel dacă (.not.associated(item%nextptr)) atunci ! Ultimul
articol din listă.
list%firstptr%prevptr => item%prevptr
nullify(item%prevptr%nextptr) item%prevptr =>
item else if (asociat(list%firstptr, item)) atunci

! Primul articol din listă.


list%firstptr => item%nextptr ! primul = următorul.
Machine Translated by Google

Exemplu de listă orientată pe obiecte 439

item%prevptr%prevptr => item%nextptr ! last%prev = item%next. item%nextptr%prevptr =>


item%prevptr ! next%prev = ultimul. sfâr itul dacă

sfâr itul dacă

nullify(item%upptr) sfâr itul


subrutinei
modul final
!

! Modul pentru a demonstra extinderea oricărui element pentru a gestiona un tip definit de utilizator. !

modul myitem_list_m folosește


anylist_m implicit niciun tip,
extins(anyitem) :: myitem
conține

procedura :: print => tipul meu tip final tipul


intreg rational :: numarator = 0 intreg :: numitor = 1
tip final contine

! Versiune de imprimare care se va ocupa de tipul rațional. !

subrutine amprenta mea(aceasta)


class(myitem), intent(in):: acest tip de selectare
(v=>this%value) clasa este (rațională)

print *, 'rational =', v%numerator, '/', v%numitor


implicit de clasă

numiți asta%anyitem%print
final select
sfâr itul subrutinei

funcția new_myitem(orice)
class(*), intent(in) :: orice clasă(myitem), pointer ::
new_myitem alocare (new_myitem) alocare
(new_myitem%value, source=orice)

funcția finală
modul final
!

! Program demonstrativ. !

demonstrația programului folosește


myitem_list_m implicit niciun
tip(anylist) :: list class(anyitem),
pointer :: p
Machine Translated by Google

440 Fortran modern explicat

! Mai întâi demonstrează cele mai de bază funcționalități ale unei liste. print *, „Lista
inițială are”, list%count_list(), „articole”. call list%append(newitem(17)) print *, 'Lista are acum',
list%count_list(), 'items.' call list%append(newitem('world')) print *, 'Lista are acum', list%count_list(),
'items.' lista de apeluri%prepend(newitem('bună ziua')) print *, 'Lista are acum', list%count_list(),
'articole.' call list%append(newitem(2.25)) print *, 'Lista are acum', list%count_list(), 'items.' scrie (*,
'(1x, a)', advance='nu') 'Primul element este: ' p => list%first() call p%print write (*, '(1x, a)', advance
='nu') 'Ultimul element este: ' p => list%last() call p%print print *, 'După ștergerea ultimului element,
conținutul listei este:' call p%delete call list%print_list !

! Acum ștergeți vechea listă și faceți una nouă, ! cu unele valori din
myitem_list_m. !

lista de apeluri%delete_list lista de


apeluri%append(new_myitem('Următoarea valoare este o treime.')) lista de
apeluri%append(new_myitem(rational(1,3))) lista de apeluri%append(new_myitem('De unde a venit
acest număr) din?')) call list%append(new_myitem(rational(173,13))) print *, 'Conținutul noii noastre liste
sunt:' call list%print_list

! Acum testați unele dintre celelalte proceduri, doar pentru a dovedi că funcționează. !

p => list%first() p => p%prev()


apel p%elimină lista de ! Test prev(), acesta va fi ultimul element.
apeluri%prepend(p) ! Pune- ! Eliminați ultimul element.

l înapoi, la începutul listei. p => p%next()


! Test next(), acesta va fi al doilea element, ! cel cu șirul „...al treilea.”.

apelați p%change((0,1))! Înlocuiește-l cu un număr complex. print *, „Conținutul listei revizuite:”


apel list%print_list program final
Machine Translated by Google

F. Termenii Fortran

Mai jos este o listă a principalilor termeni tehnici utilizați în această carte și definițiile acestora.
Pentru a facilita referirea la standard, ne-am păstrat îndeaproape semnificațiile folosite acolo. Nu facem
nicio referire la caracteristicile depreciate, învechite sau șterse (Anexele B și C) în această anexă.

interfață abstractă set de caracteristici de procedură cu nume de argumente false

entitate argument real care apare într-o referință de procedură

alocabil având atributul alocabil

set de matrice de date scalare, toate de același tip și parametri de tip, ale căror elemente individuale
sunt aranjate într-un model dreptunghiular

element de matrice element scalar individual al unei matrice

de matrice matrice de pointeri cu atributul pointer matrice

secțiune subobiect matrice care este el însuși o matrice de

formă presupusă matrice nealocabilă matrice de argumente inactiv non-pointer care își ia forma
din argumentul său efectiv

assumed-size array dummy argument array a cărui dimensiune este presupusă din cea a acestuia
argument eficient

matrice de formă amânată matrice alocabilă sau indicator de

matrice matrice de matrice cu formă explicită declarată cu valori explicite pentru limitele din
fiecare dimensiune a matricei

Caracter ASCII a cărui metodă de reprezentare corespunde ISO/IEC 646:1991

nume asociat numele entității de construcție asociată cu un selector al unui construct de tip asociat sau
select

entitate asociată (într-o asociere stabilită dinamic) entitatea care nu a existat


anterior constituirii asocia iei

asociație asociere de moștenire, asociere de nume, asociere pointer sau asociere de stocare
ciation.
Machine Translated by Google

442 Fortran modern explicat

asociere asociere argument între un argument eficient și un manechin


argument
asociere de asociere de construcție între un selector și un nume asociat într-un construct de tip asociat
sau select

Asocierea numelui de asociere gazdă , alta decât asocierea argumentelor, între entitățile dintr-un
submodul sau o unitate de acoperire conținută și entitățile din asocierea de asociere de moștenire

gazdă între componentele moștenite ale unui tip extins și componentele componentei sale părinte

asociere de legături între o variabilă cu atributul bind și o variabilă globală C

asociere de nume asociere argument, asociere de construct, asociere gazdă, asociere de vârstă a
legăturii sau asociere de utilizare

asociere de asociere de pointer între un pointer și o entitate cu ținta


atribut

utilizați asocierea de asociere între entitățile dintr-un modul și entitățile dintr-o unitate de definire a domeniului sau
construcția care face referire la acel modul, așa cum este specificat de o instrucțiune de utilizare

proprietatea de atribut a unei entități care îi determină utilizările

obiect de date automat (de asemenea, obiect automat) obiect de date non-fictiv cu un parametru de tip sau o
matrice legată care depinde de valoarea unei expresii de specificație care nu este o expresie constantă

obiect de bază (al unui subobiect) obiect desemnat de numele părții din stânga

procedura legată de tip obligatoriu sau subrutină finală

nume obligatoriu nume dat unei proceduri specifice sau generice legate de tip în tip
definiție

etichetă obligatorie caracter implicit valoare care specifică numele prin care o entitate globală cu
Atributul BIND este cunoscut procesorului însoțitor

secvență de bloc de constructe executabile într-un construct executabil care este delimitat de
instrucțiuni ale constructului executabil

bound (de asemenea, array bound) limita unei dimensiuni a unui tablou

Declarație de instrucțiune țintă de ramură a cărei etichetă de instrucțiune apare ca o etichetă într-o instrucțiune
Go to, calculată Go to, end= specifier, eor= specifier sau err= specifier

Valoarea adresei C care identifică locația unui obiect de date sau a unei proceduri, fie definită de procesorul
însoțitor, fie care ar putea fi accesibilă procesorului însoțitor (acesta este conceptul pe care standardul C
îl numește adresa)

context de caractere într-o constantă literală de caractere sau într-un șir de caractere editat
descriptor
Machine Translated by Google

Termenii Fortran 443

caracteristicile (ale unui argument dummy) fiind un obiect de date dummy sau o procedură dummy

entitate de date coarray care are corrank diferit de zero

cobound legat (limita) unei codimensioni

dimensiunea codimensională a modelului format dintr-un set de coarase corespunzătoare

obiect de date coindexat al cărui designator include un selector de imagine

asamblarea secvenței de mapare unu-la-unu dintr-un set de caractere în numere întregi nenegative

parte componentă a unui tip derivat sau a unui obiect de tip derivat, definit într-o declarație de tip

componentă directă una dintre componente sau una dintre componentele directe ale unei componente
nealocabile non-pointer

componentă componentă părinte a unui tip extins al cărei tip este cel al tipului părinte și ale cărei
componente sunt moștenire asociată cu componentele moștenite ale tipului părinte

subcomponentă (a unei structuri) componentă directă care este un subobiect al structurii componentă

finală o componentă de tip intrinsec, un pointer sau alocabilă; sau o componentă finală a unei componente
nealocabile non-pointer de tip derivat

ordonarea în ordinea componentelor componentelor neparentale ale unui tip derivat care este utilizat pentru
constructorii de intrare/ieșire formatați intrinsec și structura (unde nu sunt utilizate cuvintele cheie ale
componentelor)

conformabil (din două entități de date) având aceeași formă, sau una fiind o matrice și cealaltă fiind scalară

relație conectată între o unitate și un fișier: fiecare este conectat dacă și numai dacă unitatea se referă la fișier

obiect de date constante care are o valoare și care nu poate fi definit, redefinit sau deveni nedefinit în timpul
execuției unui program

constantă literală care nu are un nume

constantă numită obiect de date numit cu atributul parametru

entitate de construcție entitate al cărei identificator are sfera de aplicare a unui construct

expresie constantă care satisface cerințele specificate în secțiunea 7.4, asigurându-se astfel că valoarea sa este
constantă

contiguu (matrice) având elemente de matrice în ordine care nu sunt separate de alte date
obiecte, așa cum este specificat în secțiunea 20.4.2
Machine Translated by Google

444 Fortran modern explicat

contiguu (obiect de date cu mai multe părți) că părțile în ordine nu sunt separate de alte date
obiecte

numărul corank de codimensioni ale unui coarray (zero pentru obiectele care nu sunt coarrays)

expresie întreg scalar cosubscript într-un selector de imagine

obiect de date entitate de date, rezultat al evaluării unei expresii sau rezultat al execuției unei referințe de funcție

obiect de date (de asemenea, obiect) constantă, variabilă sau subobiect al unei constante

caracter simbol zecimal care separă părțile întregi și fracționale în reprezentarea zecimală a unui număr real dintr-
un fișier

specificarea de declarare a atributelor pentru diferite entități de program (adesea aceasta implică specificarea
tipului unui obiect de date numit sau specificarea formei unui obiect matrice numit)

mecanism de inițializare implicit pentru inițializarea automată a componentelor pointerului pentru a avea o stare
definită de asociere a pointerului, iar componentele non-pointer să aibă o anumită valoare

inițializat implicit (subcomponentă) supus unei inițializări implicite specificate în tip


definiție pentru acea componentă

definibil capabil de definire și permis să devină definit

definit (obiect de date) are o valoare validă

definit (pointer) are un statut de asociere de pointer asociat sau disociat

atribuire de atribuire definită definită printr-o procedură

intrare/ieșire definită intrare/ieșire definită printr-o procedură și accesată printr-o editare dt


descriptor

operațiune de operare definită definită printr-o procedură

proces de definire (a unui obiect de date) prin care obiectul de date devine definit

definiție (a unui tip derivat, enumerare sau procedură) specificație a tipului, enumerației sau procedurii

descendent (modul sau submodul) submodul care extinde acel modul sau submodul sau care extinde un alt
descendent al acestuia

numele desemnatorului urmat de zero sau mai multe selectoare de componente, selectoare de piese complexe,
selectoare de secțiune de matrice, selectoare de elemente de matrice, selectoare de imagine și selectoare de subșiruri

designator de parte complexă desemnare care desemnează partea reală sau imaginară a unui obiect de
date complex, independent de cealaltă parte
Machine Translated by Google

Termenii Fortran 445

desemnator de obiect (de asemenea desemnator de obiect de date) desemnator pentru un obiect de date (un
numele obiectului este un caz special al unui desemnator de obiect)

desemnator de procedură desemnare pentru o procedură

disociat (asociere pointer) statut de asociere pointer de a nu fi asociat cu nicio țintă și de a nu fi nedefinit

disociat (pointer) are statutul de asociere de pointer de disociat

entitate cu argument fals al cărei identificator apare într-o listă de argumente false într-o funcție sau subrutină sau al
cărei nume poate fi folosit ca cuvânt cheie argument într-o referire la o procedură intrinsecă sau la o procedură
dintr-un modul intrinsec

dummy data object dummy argument care este un obiect de date dummy

function dummy procedure care este o functie

entitate argument eficientă care este argument asociat cu un argument inactiv

obiect scalar efectiv care este asociat cu un descriptor de editare ca rezultat al regulilor pentru o listă de intrare/ieșire

aplicarea scalară independentă elementară a unei acțiuni sau operații la elementele unei matrice sau elementele
corespunzătoare ale unui set de matrice conformabile și scalari sau care posedă capacitatea de operare
elementară (combinația de operanzi sau argumente scalari și matrice combină operanzii scalari cu fiecare
element al operanzilor matricei)

atribuire elementară care operează operație elementar elementară operație

care operează elementar operator operator elementar într-o operație

elementară procedură elementară procedură intrinsecă elementară sau

procedură definită de un subprogram elementar

referință elementară la o procedură elementară cu cel puțin o matrice actuală


argument

subprogram elementar subprogram cu prefixul elementar

inițializarea explicită inițializarea unui obiect de date printr-o declarație de specificație

interfață explicită a unei proceduri care include toate caracteristicile


procedura și nume pentru argumentele sale fictive

extinderea numărului de elemente dintr-o singură dimensiune a unui tablou

fișier extern care există pe un mediu extern programului

unitate externă (de asemenea, unitate externă de intrare/ieșire) entitate care poate fi conectată la o unitate externă
fi ier

unitate de stocare a fișierelor unitate de stocare într-un fișier flux sau un fișier de înregistrare neformatat
Machine Translated by Google

446 Fortran modern explicat

subrutină finală subrutină al cărei nume apare într-o declarație finală într-o definiție de tip și care poate fi invocată
automat de procesor atunci când un obiect de acel tip este finalizat

finalizabil (tip) are o subrutină finală sau o componentă nealocabilă non-pointer a


tip finalizabil

finalizabil (entitate de date non-pointer) de tip finalizabil

finalizarea procesului de apelare a subrutinelor finale atunci când variabilele sunt dealocate sau în alt mod încetează
să existe

procedură a funcției care este invocată de o expresie

identificator generic indicativ lexical care identifică un set generic de proceduri, operații intrinseci și/sau atribuiri
intrinseci

instanță gazdă (a unei proceduri interne, sau procedură inactivă sau indicator de procedură asociat unei proceduri
interne) instanță a procedurii gazdă care furnizează mediul gazdă al procedurii interne

unitatea de acoperire gazdă ( găzduiește, de asemenea) unitatea de acoperire care înconjoară imediat un alt scop
unitatea sau unitatea de acoperire extinsă printr-un submodul

Valoare infinită în virgulă mobilă infinită IEEE conformă cu standardul IEEE

IEEE NaN Date în virgulă mobilă conformă cu standardul IEEE care nu reprezintă un număr

instanță de imagine a unui program Fortran

indice de imagine valoare întreagă care identifică o imagine

instrucțiunea de control al imaginii care afectează ordonarea execuției între imagini

interfață implicită de interfață a unei proceduri care include doar dacă este o funcție și dacă
deci, tipul și parametrii de tip ai rezultatului său

unitate de acoperire non-bloc cu scop inclusiv plus fiecare unitate de acoperire a blocului a cărei gazdă este acea unitate
de acoperire sau care este imbricată într-o astfel de unitate de acoperire a blocului (adică, domeniul de aplicare
inclusiv este domeniul ca și cum constructele bloc nu ar fi unități de acoperire)

moșteniți (pentru tipul extins) achiziționați entități (componente, proceduri de limite de tip și tip
parametri) prin extensie de tip de la tipul părinte

funcția de interogare funcție intrinsecă sau funcție într-un modul intrinsec, al cărei rezultat depinde de proprietățile
unuia sau mai multor argumente în loc de valorile acestora

numele interfeței (ale unei proceduri), caracteristicile procedurii, nume de argumente inactiv, etichetă de legare și
identificatori generici

interfață generică set de interfețe de procedură identificate printr-un identificator generic


Machine Translated by Google

Termenii Fortran 447

interfață specifică identificată printr-un bloc de interfață cu nume non-

generic bloc de interfață abstract, bloc de interfață generic sau interfață specifică
bloc

bloc de interfață abstract bloc de interfață cu cuvântul cheie abstract; colecție de


corpuri de interfață care specifică interfețe abstracte numite

bloc de interfață generic bloc de interfață cu o specificație generică; colecție de corpuri de interfață și
instrucțiuni de procedură cărora li se va atribui acel identificator generic

bloc de interfață specific bloc de interfață fără niciun cuvânt cheie cu specificații generice sau abstract;
colecție de corpuri de interfață care specifică interfețele procedurilor

interoperabil (entitate Fortran) echivalent cu o entitate definită sau definibilă de procesorul însoțitor

tip intrinsec , procedură, modul, atribuire, operator sau operație de intrare/ieșire definită în standard și accesibilă
fără definiții sau specificații suplimentare, sau o procedură sau modul furnizat de un procesor, dar nedefinit
în standard

intrinsec standard (procedură sau modul) definit în standard intrinsec nestandard

(procedură sau modul) furnizat de un procesor, dar nedefinit în standard

variabilă de caractere de fișier intern care este conectată la o unitate internă

unitate internă de intrare/ieșire care este conectată la un fișier intern

Caracter ISO 10646 a cărui metodă de reprezentare corespunde cu UCS-4 in


ISO/IEC 10646

cuvânt cheie cuvânt cheie, cuvânt cheie argument, cuvânt cheie parametru tip sau componentă
cuvânt cheie

cuvânt cheie argument care identifică argumentul inactiv corespunzător într-o listă de argumente
componentă cuvânt cheie care identifică o componentă într-o structură instrucțiunea constructor

cuvânt cheie care face parte din sintaxa unui parametru de tip declarație cuvânt cheie care identifică un

parametru de tip într-un parametru de tip

listă

cuvânt cheie indicativ lexical , nume, constantă literală, alta decât o constantă literală complexă, operator,
etichetă, delimitator, virgulă, =, =>, :, ::, ;, sau %

secvență de linii de zero sau mai multe caractere

unitatea de program principală a programului care nu este un subprogram, modul, submodul sau bloc de date
unitatea de program

Instrucțiunea de alocare a matricei mascate într-o instrucțiune where sau where construct
Machine Translated by Google

448 Fortran modern explicat

unitate de program modul care conține (sau accesează din alte module) definiții care urmează să fie făcute accesibile
altor unități de program

identificatorul de nume al unui component de program, format conform regulilor date în Secțiunea 2.7

valoarea datelor operand care face obiectul unui operator

operator operator intrinsec, operator unar definit sau operator binar definit

argument dummy -object transmis argument dummy al unei proceduri legate de tip sau al unei componente
pointer de procedură care devine asociată cu obiectul prin care procedura este invocată

pointer pointer de date sau pointer de procedură

pointer de date entitate de date cu procedura de atribut pointer

procedura pointer cu atributele externe și pointer

asocierea de atribuire de pointer a unui pointer cu o țintă, prin executarea unei atribuiri de pointer sau a unei
instrucțiuni de atribuire intrinsecă pentru un obiect de tip derivat care are pointerul ca subobiect

polimorf (entitate de date) capabil să aibă diferite tipuri de dinamică în timpul execuției programului

preconectat (fișier sau unitate) conectat la începutul execuției programului

entitate de procedură care încapsulează o secvență arbitrară de acțiuni care pot fi invocate direct
în timpul executării programului

procedură de procedură inactivă care este o procedură de procedură

externă a argumentului fals definit de un subprogram extern sau prin alte mijloace
decât Fortran

procedură de procedură internă definită de un subprogram intern procedura de

procedură de modul care este definită de un subprogram de modul procedură de

procedură pură declarată sau definită a fi pură conform regulilor din


Secțiunea 6.10

procedură de procedură legată de tip care este legată de un tip derivat și referită printr-un obiect de acel tip

combinație de procesoare a unui sistem de calcul și mecanism prin care sunt programele
transformat pentru a fi utilizat pe acel sistem de calcul

dependent de procesor nespecificat complet în Standard, având metode și semantică determinate de procesor

set de programe de unități de program Fortran și, probabil, entități definite prin alte mijloace decât
Fortran, care include exact un program principal
Machine Translated by Google

Termenii Fortran 449

unitatea de program program principal, subprogram extern, modul, submodul sau date bloc
unitatea de program

rang numărul de dimensiuni ale matricei unei entități de date (zero pentru o entitate scalară)

înregistrarea secvenței de valori sau caractere într-un fișier

fi ier fi ier de înregistrare compus dintr-o secven ă de înregistrări

referință referință la obiect de date, referință la procedură sau referință la modul

Apariția referinței la obiect de date a unui desemnator de obiect de date într-un context care necesită valoarea
acestuia în acel moment în timpul execuției

apariția referinței la funcție a indicatorului de procedură pentru o funcție sau simbolul operatorului într-un context
care necesită execuția funcției în timpul evaluării expresiei

apariția referinței la modul a unui nume de modul într-o instrucțiune de utilizare

apariția de referință de procedură a unui desemnator de procedură, simbol de operator sau simbol de atribuire într-
un context care necesită executarea procedurii în acel moment în timpul executării; sau apariția unei intrări/
ieșiri definite sau a unei finalizări de tip derivat

variabilă rezultat care returnează valoarea unei funcții

salvate având atributul de salvare

entitate de date scalare care poate fi reprezentată printr-o singură valoare de tip și care nu este o matrice

construcție bloc de unități de acoperire , definiție de tip derivat, corp de interfață, unitate de program sau subprogram,
excluzând toate unitățile de acoperire imbricate din acesta

unitate de acoperire a blocului unitate de acoperire a unui construct bloc

secvență set de elemente ordonate printr-o corespondență unu-la-unu cu numerele 1,2,...n

dimensionalitatea matricei de formă a unei entități de date, reprezentată ca o matrice de rang unu a cărei dimensiune este
rangul entității de date și ale cărei elemente sunt extinderile entității de date (astfel forma unei entități de date scalare
este o matrice cu rangul unu și dimensiune zero)

pur și simplu contigu (desemnator de matrice sau variabilă) care îndeplinește condițiile specificate în Secțiunea 20.4.3 (aceste
condiții sunt simple, care arată clar că desemnatorul sau variabila desemnează o matrice contiguă)

dimensiune (matrice) numărul total de elemente din matrice

expresie de specificație expresie care îndeplinește cerințele specificate în secțiunea 7.14,


fiind astfel potrivit pentru utilizare în specifica ii

nume specific nume care nu este un nume generic


Machine Translated by Google

450 Fortran modern explicat

program de program conform standardului care utilizează numai acele forme și relații descrise în și are o
interpretare în conformitate cu standardul

secvență de instrucțiuni a uneia sau mai multor linii complete sau parțiale care îndeplinesc regulile de
Secțiunea 2.4

instrucțiune de instrucțiune executabilă care efectuează sau controlează una sau mai multe acțiuni,
excluzând cele din partea de specificație a unui construct bloc

instrucțiune de instrucțiune nonexecutable care nu este o instrucțiune executabilă

entitate de declarație entitate al cărei identificator are sfera de aplicare a unei declarații sau a unei părți a unei declarații

etichetă declarație (de asemenea, etichetă) număr pozitiv nesemnat de până la cinci cifre care se referă la un
declarație individuală

fișierul de fișiere în flux compus dintr-o secvență de unități de stocare a fișierelor

structură obiect de date scalare de tip derivat

structura componentă componentă a unei structuri

sintaxa constructorului de structură care specifică o valoare de structură sau creează o astfel de valoare

unitate de program submodul care extinde un modul sau alt submodul

porțiune subobiect a obiectului de date care poate fi referită și, dacă este o variabilă definită, independent de
orice altă porțiune

subprogram functie subprogram sau subprogram subprogram

subprogram extern subprogram care nu este conținut într-un program principal, modul, submodul sau
alt subprogram

subprogram intern subprogram care este conținut într-un program principal sau altul
subprogram

modul subprogram subprogram care este conținut într-un modul sau submodul, dar este
nu un subprogram intern

procedură de subrutină invocată de o instrucțiune de apel, de atribuire definită sau de


unele opera ii asupra entită ilor de tip derivat

subrutină atomică subrutină intrinsecă care efectuează o acțiune pe argumentul său atom
atomic

entitate țintă care este pointer asociat cu un pointer, entitate din partea dreaptă a unui pointer
instrucțiunea de atribuire sau entitate cu atributul țintă

funcție de transformare funcție intrinsecă, sau funcție într-un modul intrinsec, care nu este nici elementară, nici
o funcție de cercetare
Machine Translated by Google

Termenii Fortran 451

tip (de asemenea , tip de date) categorie de date denumită caracterizată printr-un set de valori, o sintaxă
pentru denotarea acestor valori și un set de operații care interpretează și manipulează valorile

tip de tip abstract cu atributul abstract tipul de tip

declarat pe care o entitate de date este declarată că îl are, fie tip derivat explicit, fie implicit , definit

printr-o definiție de tip sau printr-un tip de tip dinamic al modulului intrinsec al unei entități de date la

un anumit punct în timpul execuției un tip de tip extins de program cu atributul extins tip de tip

extensibil care nu are atributul bind și care, prin urmare, poate fi extins folosind clauza extends

tipul de extensie (de un tip față de altul) este același tip sau este un tip extins al cărui tip părinte este
un tip de extensie al celuilalt tip tip de tip intrinsec definit de Standard care este întotdeauna

accesibil tip numeric unul dintre tipurile întreg, tipul de tip părinte real și complex (de tip extins) numit

în tipul de clauză extins compatibilitatea tipului unei entități în raport cu alta, în scopuri precum

asocierea argumentelor, asocierea pointerului și alocarea

valoarea parametrului de tip folosit pentru a parametriza un

parametru de tip asumat tip lungime parametru de tip care presupune valoarea parametrului
de tip de la o altă entitate (cealaltă entitate este
• selectorul pentru un nume de asociat; •

constant-expr pentru o constantă numită de tip caracter; sau • argumentul


efectiv pentru un argument fals).
parametru de tip amânat parametru de tip lungime a cărui valoare se poate modifica în timpul
execuției unui program și a cărui valoare a parametrului de tip este un parametru de tip
de tip două puncte a cărui valoare trebuie să fie implicită sau dată de o expresie constantă tip
de lungime parametru de tip parametru a cărui valoare este permisă a fi presupus, amânat
sau dat de o specificație expresie tip parametru sintaxa de interogare care este utilizată pentru
a întreba valoarea unui tip

parametrul unui obiect de date


ordinea parametrilor de tip ordonarea parametrilor de tip ai unui tip utilizat pentru specificatorii
de tip derivat

entitate non-fictivă cu argumentul final cu care este asociat un argument inactiv printr-un lanț de asocieri de
argumente

undefined (obiect de date) nu are o valoare validă

undefined (pointer) nu are un statut de asociere de pointer de asociat sau dezasociat

unitate (de asemenea, unitate de intrare/ieșire) înseamnă pentru referire la un fișier


Machine Translated by Google

452 Fortran modern explicat

polimorf nelimitat capabil să aibă orice tip dinamic în timpul execuției programului

nesalvat neavând atributul de salvare

entitate de date variabile care poate fi definită și redefinită în timpul execuției unui program

variabilă locală într-o unitate de acoperire care nu este un argument inactiv sau o parte a acestuia, nu
este o entitate globală sau o parte a acesteia și nu este accesibilă în afara acelei unități de
acoperire

lock variable variabilă scalară de tip lock_type care este definită în intrinsec
modul iso_fortran_env

indice vectorial secțiune indice care este o matrice

componenta matricei întregi sau numele matricei fără alte calificări


Machine Translated by Google

G. Solu ii la exerci ii

Notă: Câteva exerciții au fost lăsate cititorului.

capitolul 2
1.
b este mai mic decât m Adevărat

8 este mai mic decat 2 fals

* este mai mare decât T $ nedeterminat

este mai mic decât / gol este nedeterminat

mai mare decât A fals


golul este mai mic de 6 Adevărat

2.
x=y 3 a corect
= b+c ! adăuga corect, cu comentariu corect
cuvânt = „șir” a = 1,0; b
= 2,0 a = 15. ! inițializați corect

a; b = 22. ! și b incorect (comentariul încorporat) corect, linia

inițială corectă, continuarea incorectă,


cântec = „Viața este doar& finalul și lipsa incorecte, înainte și lipsesc
și un castron cu cireșe" incorecte (etichetă nevalidă pentru
chide = „Nu risipi, declarație; formă nevalidă a constantei
nu vreau!' caracterului)
0 c(3:4) = „în sus”

3.
-43 întreg 'cuvânt' caracter
4.39 real 1,9-4 nu caracter
0,0001e+20 real „lucruri și prostii” juridic
49 caracter (0.,1.) caracter
(1.e3,2) complex 'Nu pot' complex

'(4.3e9, 6.2)' nu juridic .adevărat._1 caracter


e5 nu întreg „nu ar trebui” logic1 nu
1_2 legal1 nu "BINE" legal
z10 legal z'10' hexazecimal

1 Legal, cu condiția ca tipul să fie disponibil.


Machine Translated by Google

454 Fortran modern explicat

4.
Nume legal nume32 legal
coeficient legal 123 nu legal
a182c3 legal impas nu legal
Stop! nu legal a arde_ legal
impas legal nume__lung legal
5.
real, dimensiune(11) :: A a(1), a(10), a(11), a(11)
real, dimensiune(0:11) real, :: b b(0), b(9), b(10), b(11) c(-11),
dimensiune(-11:0) :: c real, c(-2), c(-1), c(0) d(1,1), d( 10,1),
dimensiune(10,10) :: d real, dimensiune(5,9) d(1,2), d(10,10) e(1,1), e(5,2), e(1,3), e(5,9)
real, dimensiune(5,0:1,4 ):: f :: e f(1,0 ,1), f(5,1,1), f(1,0,2), f(5,1,4)

Constructor de matrice: (/ (i, i = 1,11) /)


6.
c(2,3) legal c(4:3)(2,1) c(5,3) nu legal
c(6,2) nu legal (9:9) legal
c(0,3) legal c(2,1)(4:8) legal
c(4,3)(:) c(5) legal c(3,2)(0:9) nu legal
(2:3) c(5,3) nu legal c(5:6) c(,) nu legal
(9) nu legal nu legal
7.
i) tip vehicul_înmatriculare
caracter(len=3) :: litere
întreg :: cifre tip final
vehicul_înmatriculare

ii) tip cerc real


:: raza
real, dimensiunea(2) :: cerc de tip sfârșit
central

iii) tip book


character(len=20) :: titlu
character(len=20), dimension(2) :: autor întreg sfârșit tip
carte :: nr_de_pagini

Constante de tip derivat:

vehicul_înregistrare('PQR', 123) cerc(15.1, (/


0., 0. /)) carte(„Progresul pelerinului”, (/
„Ioan”, „Bunyan” /), 250 )

8.
t matrice t(4)%vertex(1) t(5:6) scalar
t(10) scalară matrice

t(1)%vertex matrice t(5:5) matrice (dimensiunea 1)


Machine Translated by Google

Soluții la exercițiile 455

9.

a) întreg, parametru :: twenty = selected_int_kind(20)


integer(kind=douăzeci) :: counter

b) întreg, parametru :: mare = selectat_tip_real(12.100) real(tip = mare) :: mare

c) caracter(fel=2) :: semn

capitolul 3

1.
a+b valabil -c valabil
a+-c invalid d+(-f) valabil

(a+c)**(p+q) - valabil (a+c)(p+q) 4. invalid


(x+y)**i valabil ((ad)-(a+4.*x)+1) invalid

2.
c+(4.*f)
((4.*g)-a)+(d/2.)
a**(e**(c**d)) ((a*e)-
((c** d)/a))+e (i .și. j) .or. k
((.nu. l) .sau. ((.nu. i) .și.
m)) .neqv. n ((b(3).și.b(1)).sau.b(6)).sau.(.nu.b(2))

3.
3+4/2 = 5 6/4/2 = 0
3.*4**2 = 48. 3.**3/2 = 13,5
-1.***2 = -1. (-1.)**3 = -1.

4.
ABCDEFGH
ABCD0123
ABCDEFGu u = neschimbat
ABCDbbuu b = gol

5.
.nu.b(1).și.b(2) valabil .sau.b(1) invalid
b(1).sau..nu.b(4) valabil b(2)(.și.b(3).sau.b(4)) invalid

6.
d .le. c valabil p .lt. t > 0 x+y < invalid
x-1 /= y valabil 3 .sau. > 4. q.eq.r . i. s>t invalid
d.lt.c.and.3.0 invalid valabil

7.

a) 4*l

b) b*h/2. c)

4./3.*pi*r**3 (presupunând că pi are valoarea π).


Machine Translated by Google

456 Fortran modern explicat

8.
întreg :: n, unu, cinci, zece, douăzeci_cinci douăzeci_cinci = (100-
n)/25 = (100-n-25*douăzeci_cinci)/10 = (100-
zece n-25*douăzeci_cinci-10*zece)/5 = 100-
cinci n-25*douăzeci_cinci-10*zece-5*cinci
unu

9.
a=b+c valabil
c = b + 1,0 valabil
d=b+1 invalid
r=b+c valabil
a=r+2 valabil

10.
a=b valabil c = a(:,2) + b(5,:5) c = a(2,:) + valabil
a = c+1,0 invalid b(:,5) b(2:,3)=c+ b(:5,3) invalid

a(:,3) = c valabil invalid

capitolul 4

1.
întreg :: i, j, k, temp
întreg, dimensiune(100) :: inversă do i = 1.100
inversă (i) = i

sfâr itul face

citește *, i, j do k=
i, i+(ji-1)/2 temp = reverse(k)
reverse(k) = reverse(j-k+i)
reverse(j-k+i) = temp end do

Sfâr it

Notă: O metodă mai simplă pentru efectuarea acestei operații va deveni evidentă în Secțiunea 6.13.

2.
întreg :: limită, f1, f2, f3 citire *, limită

f1 = 1
dacă (limit.ge.1) print *, f1 f2 = 1

dacă (limit.ge.2) print *, f2 do i = 3, limit


f3 = f1+f2

print *, f3 f1 = f2

f2 = f3
sfâr itul face

Sfâr it
Machine Translated by Google

Soluții la exerciții 457

6.
x real
do

citește *, x
dacă (x /= -1.) ieșire tipărire
*, 'valoare de intrare -1. invalid'
sfâr itul face

imprimare *, x/(1.+x)
Sfâr it

7.
tip (intrare), pointer :: primul, curent, curent anterior => primul

dacă (index% curent == 10) atunci


primul => primul%următorul
altfel
do

precedent => curent curent =>


curent%next

if (current%index == 10) exit end do

previous%next => curent%next end if

capitolul 5

1.
calcularea subrutinei (x, n, medie, varianță, ok)
întreg, intenție (în) reală, :: n

dimensiune (n), intenție (în) :: x real, intenție (out) :: medie,


varianță logică, intenție (în) :: ok întreg :: vreau să spun
0. =

varianță = 0.
ok = n > 1

dacă (ok) atunci do


i = 1, n medie =
medie + x(i) sfâr itul do

medie = medie/n
do i = 1, n varianță
= varianță + (x(i) - medie)**2 end do varianță = varianță/(n-1)

sfâr itul dacă

sfârșitul subrutinei calculează

Notă: O metodă mai simplă va deveni evidentă în Capitolul 8.


Machine Translated by Google

458 Fortran modern explicat

2.

subrutine matrix_mult(a, b, c, i, j, k) :: i, j, k
întreg, intenție(în) reală,
dimensiune(i,j), intenție(în) :: a real, dimensiune(j,k), intent(în) ::
b real, dimensiune(i,k), intent(out) ) :: c întreg :: l, m, nc(1:i, 1:k)
= 0. do n = 1, k do l = 1, j do m = 1, i

c(m, n) = c(m, n) + a(m, l)*b(l, n)


sfâr itul face

sfâr itul face

sfâr itul face

sfâr itul subrutinei matrix_mult

3.

amestecarea subrutinei (carti)


intreg, dimensiune(52), intent(in) :: carduri intreg real
:: stânga, alegere, i, temp
:: r

cards = (/ (i, i=1,52) /) do left = 52,1,-1 apel ! Inițializați pachetul.

random_number(r) choice = r*left + 1 ! Buclă peste numărul de carduri rămase.


! Trage o carte
! din posibilitățile rămase și schimbați cu ultima
temp = cards(stânga) ! rămasă.
cards(left) = cards(alegere)! cards(choice) =
temp end do

terminați amestecul subrutinei

4.

funcția caracterului cel mai devreme (șir)


caracter(len=*), intent(in) :: șir întreg :: j, lungime lungime =
len(șir) dacă (lungime <= 0) atunci cel mai devreme =

''

altfel

cel mai devreme = șir(1:1) do j = 2,


lungime dacă (șir(j:j) < cel mai
devreme) cel mai devreme = șir(j:j)
sfâr itul face

sfâr itul dacă

terminați funcția cel mai devreme


Machine Translated by Google

Soluții la exerciții 459

5.

eșantion de subrutină
real :: r, l, v, pi pi = acos(-1.)

:
r = 3.
l = 4.
v = volum(r, l)
:
con ine
volumul funcției (rază, lungime)
real, intent(in) :: raza, lungime real :: volum

volum = pi*raza**2*lungimea sfârșitului


funcției volum
sfâr itul e antionului subrutinei

7.

modul șir_tip tip șir


întreg :: lungime
caracter(len=80) ::
șir_date tip șir final alocare interfață (=)

procedura modulului c_to_s_assign, s_to_c_assign


end interface (=)
interface len
procedura modulului string_len end
interface
operator de interfață(//)
procedura de modul șir_concat interfața
finală (//) conține

subrutină c_to_s_assign(e, c)
tip (șir), intent (out) :: s caracter(len=*),
intent(in) :: cs%string_data = cs%length = len(c)
dacă (s%length > 80) s%length = 80

sfârșit subrutinei c_to_s_assign subrutine


s_to_c_assign(c, s) tip (șir), intenție(în)
caracter(len=*), intent(out) :: cc = :: s
s%string_data(1:s%lungime)

end subroutine s_to_c_assign function


string_len(s) integer :: string_len
type(string) :: s string_len
s%lungime
=
Machine Translated by Google

460 Fortran modern explicat

end function string_len function


string_concat(s1, s2) tip (șir), intent(in) ::
s1, s2 tip (șir) :: string_concat
string_concat%string_data = s1%string_data(1:s1%lungime) //
& s2% string_data(1:s2%lungime) &

string_concat%lungime = s1%lungime + s2%lungime dacă


(string_concat%lungime > 80) string_concat%lungime = 80 &

funcția de terminare șir_concat


modul de terminare tip_șir

Notă: Funcția len intrinsecă, utilizată în subrutina c_to_s_assign, este descrisă mai întâi în Secțiunea 8.6.

Capitolul 6

1.

i) a(1, :)

ii) a(:, 20)

iii) a(2:50:2, 2:20:2)

iv) a(50:2:-2, 20:2:-2)

v) a(1:0, 1)

2.

unde (z.gt.0) z = 2*z

3.

întreg, dimensiune(16) :: j

4.
w indicator de
a, b formă asumată
d explicit

5.

real, pointer :: x(:, :, :) x => tar(2:10:2,


2:20:2, 2:30:2)%du(3)

6.
ll = ll + ll
ll = mm + nn + n(j:k+1, j:k+1)
Machine Translated by Google

Soluții la exercițiile 461

7.
program invers întreg ::
i, j întreg, dimensiune(100) :: invers invers
= (/ (i, i=1, 100) /) citește *, i, j invers (i:j) = invers
(j:i :-1) terminați programul înapoi

10.
tip(stiva) a alocă
(a%content(4)) a%index = 1

a%conținut = (/ 1, 2, 3, 4 /) a = stivă(2,
(/a%conținut, 5, 6 /))

11.
type(emfield) a, temp alocare
(a%strength(4, 6)) a%strength = 1.0
temp = a dealocate (a%strength)
alocare (a%strength(0:5, 0:8)) ! alocarea automată a temp%conținut
a%strength (1:4, 1:6) =
temperatură%tărie a%tărie(0:5:5, :) = 0
a%tărie(1:4, 0) = 0 a%tărie(1:4, 7:8) ) = 0

12.
type(emfield) a alocă
(a%strength(4, 6)) a%strength = 1.0
a = emfield(reshape( (/
(a%strength(:,i),0.,0.,i=1,6) ), &

(0.,0.,0.,0.,0.,0.,0.,0.,i=7,9) /), & (/ 6,9/) ))

Capitolul 7

1.
i) întreg, dimensiune(100) :: bin

ii) real(select_real_kind(6, 4)), dimensiune(0:20, 0:20) :: &


temperatura_iron

iii) logic, dimensiune(20) :: comutatoare

iv) caracter(len=70), dimensiune(44) :: pag

2.
Valoarea primului i este 3,1, dar poate fi modificată;
valoarea celui de-al doilea i este 3,1, dar nu poate fi modificată.
Machine Translated by Google

462 Fortran modern explicat

3.
i) număr întreg, dimensiune(100) :: i = (/ (0, k=1, 100) /) ii) număr întreg, dimensiune(100) ::

i = (/ (0, 1, k=1, 50) /)

iii) real, dimensiune(10, 10) :: x = remodelare((/ (1,0, k=1, 100) /), &
(/10, 10/) )

iv) caracter(len=10) :: șir = '0123456789'

Notă: funcția de remodelare va fi îndeplinită în Secțiunea 8.13.3.


4.
Unitate de acoperire
Scrisoare mod exterior interior distrac ie

a,b caracter (10,2) — ——


ce real — ——

f real — — real

real — ——

g,h în întreg
— ——

Au real — ——

X real — — real

y real — ——

z real - complex -

5.
i) tip(persoană) șef = persoană(„Smith”, 48.7, 22)

ii) a) Acest lucru este imposibil deoarece o componentă pointer nu poate fi o


constantă. b) tip (intrare) curent
date current%value, current%index /1.0, 1/

6.
Toate sunt expresii constante, cu excepția:

iv) din cauza exponentului real; și


viii) din cauza componentei pointer.

Capitolul 8

1.
program qroots ! Rezolvarea ecuației pătratice.
!
real :: a, b, c, d, x1, x2
!
citiți (*, *) a, b, c
scrieți (*, *) ' dacă a = ', a, 'b = ', b, 'c = ', c
(a == 0.) atunci
dacă (b /= 0.) atunci
scrie (*, *) ' Linear: x = ', -c/b altfel

scrie (*, *) ' endif Fără rădăcini!


Machine Translated by Google

Soluții la exerciții 463

altfel
d = b**2 - 4.*a*c
dacă (d < 0.) atunci
scrieți (*, *) 'Complex', -b/(2.*a), '+-', sqrt(-d)/(2.*a) &

altfel
x1 = -(b + semn(sqrt(d), b))/(2.*a) x2 = c/(x1*a)
scrieți (*, *) 'Rădăcini reale', x1, x2 endif

endif
termina programul qroot

Notă istorică: O problemă similară a fost pusă într-una dintre primele cărți despre programarea Fortran – A
FORTRAN Primer, E. Organick (Addison-Wesley, 1963). Este interesant de comparat soluția lui Organick, scrisă în
FORTRAN II, la p. 122 din acea carte, cu cea de mai sus. (Este reprodus în Encyclopedia of Physical Science &
Technology (Academic Press, 1987), vol. 5, p. 538.)

2.
subrutina calculate(x, mean, variance, ok) real, intent(in)
:: X(:)logica, intent(out) ::
real, intent(out) :: medie, varianza
ok ok = dimensiune(x) > 1 dacasumă(x)/dimensiune(x)
(ok) atunci medie =
varianță = sumă((x-medie)**2)/(dimensiune(x)-1) sfârșit
dacă

sfârșitul subrutinei calculează

3.
F p1 și p2 sunt asociate cu aceleași elemente de matrice, dar în ordine inversă p1 și
T p2(4:1:-1) sunt asociate exact cu aceleași elemente de matrice, a(3), a(5), a(7), a(9).

4.
51a are limite 5:10 și a(:) are limite 1:6.
5 1 p1 are limite 5:10 și p2 are limite 1:6.
11x și y ambele au limite 1:6.

Capitolul 9

1.
i) printează '(a/ (t1, 10f6.1))', 'grid', grid ii) printează '(a, " ",
25i5)', 'list', (list(i), i = 1, 49, 2)
sau

tipăriți „(a, „ „, 25i5)”, „listă”, listă(1:49:2)


iii) tipăriți '(a/ (" ", 2a12))', 'titluri', titluri
Machine Translated by Google

464 Fortran modern explicat

iv) tipăriți '(a/ (t1, 5en15.6))', 'putere', putere v) imprimați '(a, 10l2)',
'steaguri', steaguri vi) imprimați '(a, 5(" (" , 2f6.1, ")"))', 'plan', plan

2.
caracter, dimensiune(3,3) :: tic_tac_toe integer :: unit

:
scrie (unitatea, '(t1, 3a2)') tic_tac_toe

4.

i) citiți (*, *) grila


1,0 2,0 3,0 4,0 5,0 6,0 7,0 8,0 9,0 10,0

ii) citiți (*, *) lista (1:49:2) 25*1

iii) citește (*, *) titluri transfer


de date

iv) citiți (*, *) putere


1.0 1.e-03

v) citiți (*, *) steaguri


tftftfftft

vi) citit (*, *) avion


(0,0, 1,0),(2,3, 4)

5.
subrutina get_char(unitate, c, sfâr itul_fi ierului) ::
întreg, intenție(în) caracter, unit
intent(out) :: c logic, intent(out) ::
end_of_file întreg :: ios end_of_file = .false. do

citește (unitate, '(a1)', advance='nu', iostat=ios, end=10) c if (ios == 0) return end


do

10 c= ''

sfâr itul_fi ierului = .true.


sfâr itul subrutinei get_char

Capitolul 12

1.
interfață erf

funcția erff(x) bind(c) folosește


iso_c_binding real(c_float),
value :: x real(c_float) :: erff end
function
Machine Translated by Google

Soluții la exercițiile 465

funcția erf(x) bind(c) folosește

iso_c_binding real(c_double),
value :: x
funcția finală :: erf
real(c_double).
interfață finală

2.

! float dot_productf(float a[], float b[], size_t n); !

funcția dot_productf(a, b, n) bind(c) folosește iso_c_binding


integer(c_size_t), value :: n real(c_float), intent(in) :: a(n), b(n)

dot_productf = dot_product(a, b)
funcția finală

! produs_dublu punct(double a[], double b[], size_t n); !

funcția dot_productd(a, b, n) bind(c, name='dot_product') folosește iso_c_binding integer(c_size_t),


value :: n real(c_double), intent(in) :: a(n), b(n) dot_productd = dot_product(a, b)

funcția finală

Capitolul 13

1.

tip cmplx (tip) întreg :: fel =


fel (0,0) real, privat :: r, tip theta final cmplx

2.

function cat(a, b)
type(char_with_max_length(*)), intent(in) :: a, b type(char_with_max_length(a%len+b%len))
cat%value = a%value(1:a%len )//b%valoare(1:b%len) :: pisică

cat%len = len(cat%value) funcția finală


cat

funcția întreg index(șir, subșir, înapoi)


tip (char_with_max_length(*)), intent(in) :: șir, subșir logic, opțional index = index(șir%value(1:șir%len) și
subșir%value(1:subșir%len), back) end funcția index :: înapoi
Machine Translated by Google

466 Fortran modern explicat

Capitolul 15

1.

b = remodelare( [ (0,i=1,size(b,1)+2), (0,b(:,i),0,i=1,size(b,2)), & (0, i=1,dimensiune(b,1)+2)], [ mărime(b,1)+2,


mărime(b,2)+2] )

Capitolul 16

1.
!

! Funcție pentru a formata o valoare reală de peste 15 cifre ca și cum ar fi euro.


! În special, ne așteptăm la o virgulă zecimală și niciun zero negativ. !

! Funcția returnează un șir cu lungime amânată alocabil. !

funcția real_to_estring(valoare) rezultat(r)


real(kind=selected_real_kind(15)), intent(in) :: valoare
caracter(:), alocabil :: r caracter(15) :: format
caracter(precizie(valoare)+2) :: temp !

! Setați dimensiunea câmpului de format astfel încât să fie completat

! cu asteriscuri dacă mărimea valorii este astfel încât ! câmpul „cent” depășește precizia zecimală. !

scrie (format, '("(ss, dc, f", i0, ".2)")') precizie(valoare) + 1 scrie (temp, format) abs(valoare) if (valoare<0)
atunci r = '-'//trim(reglare(temp))

altfel

r = trim(adjustl(temp)) end if

funcția finală

2.

program command_sum
folosește iso_fortran_env
real(selected_real_kind(15)) :: număr, suma caracter întreg (1024)
:: arglen, i, ios, numere
:: arg, eroare ::
intrinsec command_argument_count, get_command_argument
suma = 0
numere = 0

do i=1, command_argument_count() call


get_command_argument(i, arg, arglen) if (arglen>len(arg)) apoi
scrieți (error_unit, *) „Ignorând numărul argumentului extrem
de lung”, i ciclul
Machine Translated by Google

Soluții la exerciții 467

else if (arglen==0) apoi scrieți


(error_unit, *) „Ignorând numărul argumentului de lungime zero”, mă termin dacă

if (scan(arg(:arglen), '0123456789.eEdD+-')==0) atunci scrie i (error_unit,


*) „Caractere nevalid în: „',arg(:arglen), '"' sfâr itul ciclului dacă

citește (arg(:arglen), *, iostat=ios, iomsg=eroare) număr dacă (ios/=0) atunci

scrie (error_unit, *) 'Eroare pentru "', arg(:arglen), '": ', trim(eroare) else

numere = numere + 1
sumă = sumă + număr
sfâr itul dacă

sfâr itul face

dacă (numerele==0) atunci


print *, „Nu s-au găsit numere”
altfel
print *, numere, 'numere găsite, suma este:', suma final dacă

termina programul

Capitolul 17

1.
program check_file_format implicit
niciun caracter :: ch, file*1024,
lastch = 'x'
întreg :: crlf_found = 0, lf_found = 0, apel ios
get_command_argument(1, file) open (10, file=file,
form='unformatted', access='stream', action='read')
do
citește (10, iostat=ios) ch if
(is_iostat_end(ios)) exit if (ios/=0) stop
'I/O error' if (ch==achar(10)) then if
(lastch==achar(13) )) atunci

crlf_found = crlf_found + 1 else

lf_found = lf_found + 1 sfâr it


dacă
sfâr itul dacă

lastch = ch
sfâr itul face

if (lf_found>0) print *, lf_found, 'Unix record terminators' if (crlf_found>0) print *,


crlf_found, 'DOS/Windows record terminators' if (lf_found+crlf_found==0) print *, 'Fără terminator
record găsit' programul final
Machine Translated by Google

468 Fortran modern explicat

2.
program show_sign_effects scrie (*,
10) scrie (*, 10, semn='suprimă') „implicit”, 1., 2., 3., 4.
'suprima', 1., 2., 3., 4. scrie (*, 10, semn='plus') 'plus' , 1., 2., 3., 4. 10 format (1x,
a10, ': ', f6.2, ss, f6.2, sp, f6.2, s, f6.2) program final

implicit: 1,00 2,00 +3,00 4,00

suprima: 1,00 2,00 +3,00 4,00 plus: +1,00 2,00


+3,00 4,00

Capitolul 19

1.
program principal
implicit nici unul
real :: z[*] întreg ::
imagine if
(this_image()==1) then
open (10, file='ex1.data') read (10, *)
z do imagine = 2, num_images()
z[image] = z end do

sfâr itul dacă

sincronizați
'
toate scrierile (*, '(a,f6.3,a,i2)') 'z=', z, termina pe imagine', this_image()
programul

Fără o instrucțiune sync all, o imagine ar putea încerca să-și scrie valoarea înainte ca imaginea 1 să o fi setat.

2.

program principal
implicit nici unul
real, alocabil :: z(:)[:] întreg :: imagine
alocare (z(3)[*]) if (this_image()==1)
atunci z(:) = [1.2, 1.3, 1.4] do imagine =
2, num_images() z(:)[image] = z(:) +
imagine - 1

sfâr itul face

sfâr itul dacă

sincronizați
'
toate scrierile (*, '(a,3f4.1,a,i2)') 'z=', z, finalul pe imagine', this_image()
programului principal

Sincronizarea este încorporată în instrucțiunea alocare pentru un coarray, deci nu este necesară o instrucțiune
sync all.
Machine Translated by Google

Soluții la exerciții 469

3.
subrutine colectiv_add(a) real :: a[*] real,
save :: b[*] integerne
me = this_image()
= num_images()

:: eu, eu, nu, tu

i=1
do
sync all if
(i>ne) exit
tu = eu + i dacă
(tu>nu) tu = tu - ne b=a+ a[tu] sync all a=b

i = i*2
sfâr itul face

sfâr itul subrutinei

4.
subrutină laplace (nrow, ncol, u)
intreg, intent(in) :: nrow, ncol real, intent(inout) ::
u(nrow)[*] real
:: new_u(nrow) eu, eu,
întreg eu :: stânga, dreapta
= this_image() stânga =
merge(ncol, me-1, me==1) right = merge(1,
me+1, me==ncol) new_u(1) = u(nrow) + u(2)

new_u(nrow) = u(1) + u(nrow-1) new_u(2:nrow-1)


= u(1:nrow-2) + u(3:nrow) new_u(1:nrow) = new_u(1 :nrow) +
u(1:nrow)[stânga] + u(1:nrow)[dreapta] sincronizați tot u(1:nrow) = new_u(1:nrow) - 4.0*u(1:nrow)

sfâr itul subrutinei laplace

5.

Cod cu blocaje Cod fără blocaje

k = this_image() if (k<=nz) k = this_image() if (k<=nz)


atunci face i = 1, nx a(i, then
1:ny) = b(1:ny, k)[i] do ii = 1, nx i=1+
mod(k+ii, nx) a(i, 1:ny) = b(1:ny,
sfâr itul face k)[i] end do
sfâr itul dacă

sfâr itul dacă


Machine Translated by Google

470 Fortran modern explicat

6.

echipe de module
con ine
subrutine team_add(echipă, poziția_echipă, a)
întreg real :: team(:), team_position :: a[*] real,
save :: b[*] integer me =
team_position ne = size(team) i=1
:: eu, eu, nu, tu

do
sincronizați imagini
(echipă) dacă (i>=ne) ieșiți
tu = eu + eu dacă
(tu>nu) tu = tu - ne b=a+ a[echipă(tu)]
sincronizați imagini (echipă) a=b

i = i*2
sfâr itul face

sfâr itul subrutinei


modul final

echipele de utilizare

principală a programului

implicit niciun
întreg, alocabil :: team(:) :: a[*]
real
întreg eu :: i, me, ne, team_position
= this_image() ne =
num_images() allocate
( team(ne/2) ) if (me<=ne/2)
then team = [ (i,i=1,ne) ]
team_position = eu else

team = [ (i,i=ne/2+1,ne) ]
team_position = me - ne/2 end if

a = this_image() sync
all call
team_add(echipă,echipă_poziție, a)
write(*,'(a,f6.1,a,i2)') 'a=',a,' on image', this_image()
termina programul principal
Machine Translated by Google

Soluții la exerciții 471

Capitolul 20

1.

! Modul care conține un tip de vector care numără numărul de accesări, ! atât ca vector întreg cât și pe element.

modul de numărare_vector
utilizați iso_fortran_env, numai: int64 tip privat,
public :: realvec_t

real privat,
pointer :: valoare(:) întreg logic(int64) ::
:: alocat = .false.
ecount = 0, vcount = 0 conține

procedura :: element, get_usage, nou, vector final :: tipul de final zap


con ine

element de funcție (vec, sub)


tip(realvec_t), intent(inout) :: vec integer, intent(in) :: sub real,
pointer vec%ecount = vec%ecount + 1
:: element

element => vec%value(sub)


funcția finală

subrutină new(this, n) type(realvec_t),


intent(inout) :: acest număr întreg, intent(in) if (this%allocated)
:: n
dealocate (this%value) allocate (this%value(n)) this%allocate
= .Adevărat.

sfâr itul subrutinei


vector de funcție (vec)

tip(realvec_t), intent(inout) :: vec real, pointer :: vector(:)


vec%vcount = vec%vcount + 1

vector => vec%valoare


funcția finală

subrutina get_usage(this, ecount, vcount)


type(realvec_t), intent(in) integer(int64), :: acest

intent(out), opțional :: ecount, vcount if (prezent(ecount)) ecount = this%ecount if


(prezent(vcount)) vcount = this%vcount

sfâr itul subrutinei


subrutină elementară zap(this) type(realvec_t),
intent(inout) :: this if (this%allocat) dealocate (this%value)
this%allocat = .false.

sfâr itul subrutinei


Machine Translated by Google

472 Fortran modern explicat

modul final
:

tip(realvec_t) :: un întreg(int64) ::
i, nerefs
:

apelați a%new(n) do
i=1, na%element(i) =
end do ...

print *,a%vector()
:

apelați un%get_usage(ecount=nerefs)

2.

! Un modul generator de numere pseudo-aleatoare cu același ! interfață ca cea intrinsecă


standard.

modulul prng
folosește iso_fortran_env, numai: int32 private
integer(int32), parameter :: a = 16807_int32
integer(int32), parameter :: m = 2147483647_int32 integer(int32),
parameter :: q = m/a integer(int32), parameter :: r = mod(m, a) integer :: seed
integer :: init_count = 0 public :: random_number, random_seed interface
random_number

procedura modulului :: random_number_r, random_number_d


interfață finală

interfață random_seed
procedura modulului :: random_seed_specific
interfață finală
con ine

subrutine init_random_number intreg :: values(8)


call data_and_time(valori=valori) seed =
init_count + sum(values) if (seed<=0 .or. seed>m) seed
= 25058 ! Trebuie să fie în intervalul 1-m. init_count =
init_count + 1

sfâr itul subrutinei


subrutine advance_generator
integer(int32) :: salut, lo, testează dacă
(init_count==0) apelează init_random_number !

! Calculați sămânță = mod(a*sămânță, m), ! fără preaplin


sau aritmetică de precizie mai mare. !

hi = seed/q lo =
mod(seed, q)
Machine Translated by Google

Soluții la exerciții 473

test = a*lo - r*hi

seed = merge(test, test+m, test>0) final subrutine

subrutină elementară impură random_number_r(recoltă)


real, intent (out) :: harvest call
advance_generator !

! Înmulțiți cu reciproca lui m, ! pentru a pune rezultatul


în intervalul (0.0,1.0). ! recoltare = sămânță*(1,0/m) subrutină finală

subrutină elementară impură random_number_d(recolta)


dublă precizie, intenție (out) :: harvest call advance_generator

! Înmulțiți cu reciproca lui m, ! pentru a pune rezultatul


în intervalul (0.0,1.0). !

recoltare = sămânță*(1,0d0/m)
subrutină finală

subrutine random_seed_specific(size, put, get) integer, intent(out),


optional :: size integer, intent(in), optional :: put(:) integer, intent(out),
optional :: get(:) if ( count([prezent(mărime), prezent(pun),
prezent(obține)])>1) și

opriți „?Prea multe argumente pentru RANDOM_SEED” dacă


(prezent(size)) atunci
dimensiune = 1

else if (prezent (pune)) atunci


if (ubound(put)<1) stop '?RANDOM_NUMBER: PUT is too small' seed = sum(put) if (seed<=0 .or.
seed>m) apel init_random

else if (prezenta(obține)) atunci


if (ubound(get)<1) stop '?RANDOM_NUMBER: GET este prea mic' get(1) = seed get(2:) = 0 else

apelează init_random
end if
sfâr itul subrutinei
modul final
Machine Translated by Google

Această pagină a fost lăsată goală în mod intenționat


Machine Translated by Google

Index

un descriptor de editare, 203, caractere alfanumerice, 9 retur


204 abs, 163 rezumat alternativ, 423 ampersand, 12,
18 ani, 163
interfață, 259
bloc, 259 tip, ANSI, 2, 3
280 cuvânt cheie orice, 175
abstract, 280 access= argument, 67, 73, 78–83, 388, 389, 424 intentie,
specificator, 216, 220, 326 achar, 166, 162 listă, 83, 92, 402, 424
309 acos, 165, 376 acosh, 376 action=
specificator, 217, 221 actual instrucțiune aritmetică if, matrice
422, 23–29, 99–127, 138, 409 alocare,
102 argument, 73, 100 atribuire,
48, 111 limite, 23, 28, 100
argument, 74–92, 100, 405, 406, 421, 424,
constantă, 136 constructor, 25,
425
26, 81, 81, 8 , 135, 137, 297–298
procedura, 82
element, 119 ordine, 24
adjustl, 167 adjustr,
expresie, 46–48 funcție, 110 secțiune, 25, 27,
167 advance= specifier,
120 subobiect , 120–123 indice, 26,
199, 209, 212 aimag, 163 aint, 163 alias,
247 funcție cu valori matrice, 105, 210
124 all, 175 allocatable coarray, 340
component, 106, 354, 352 of coarray, 352
of coarray atribut alocabil, 102, 123, 144,
151

Standard ASCII, 19, 166, 309 asin,

declarație alocabilă, 144 declarație 165, 376 asinh, 376 asamblator, 2

de alocare, 28, 102–109, 270, 291, 372 alocat, 176, limbaj de asamblare, 81 atribuit
293 alocare (din sursă), 292 stare de merge la, 427 atribuire, 46
alocare, 102, 176
Machine Translated by Google

476 Index

declarație de atribuire, 33, 37–42, 121, 135 legare, 279


etichetă, 249,
atribuire la matrice alocabilă, 294 252 atribut de legătură, 246, 249, 250,
atribuire(=), 91, 92 construct asociat, 271 252 declarație de legătură, 250 de biți,
nume asociat, 271 asociat, 37, 163 126, 171 procedura de manipulare, 171
asociere (de pointer), 37, 77, 79, 103, 104, dimensiune_biți, 172 caractere
145, 358 lungime presupusă de caractere, goale, 10, 11, 18, 1919, comun 403
93, 422 parametru de tip derivat asumat, 258 câmp, 217 necompletat= specificator,
matrice de formă presupusă, 100 205, 217, 221, 326 ble, 378 bloc, 55
matrice de dimensiune presupusă, 405, 406 bloc de date, 404, 405 bloc
atribut asincron, 322 coaarray, 343 instrucțiune construct, 366 blt, 378 mld
asincronă, 322 intrare/ieșire asincronă, 320–320 descriptor de editare, 205, 217 bound,
asincron, 320–3222 asincrone , 165, 376 atan2, 212, 328, 23, 3 , 136, 176 branch, 63 target
165, 314 atanh, 376 subrutine atomice, 347, 415 statement, 63 btest, 172 byte, 15–17, 19,
atomic_define, 415 atomic_ref, 415 126 bz edit descriptor, 205, 217
atribute, 145, 219 automat

Matrice C, 247
matrice, 100 Funcția C, 249, 251
obiect de date, 100 C pointer, 245
direcționare, 388 Limbajul de programare C, 243
Prototip C, 251
b edita descriptor, 202 Tip structura C, 246
instrucțiune backspace, 214 C Tip, 244, 245
mod batch, 191 bessel_j0, 377 C_Alert, 244
bessel_j1, 377 bessel_jn, 377 C_ASSOCIAT 245 ,
bessel_y0, 377 bessel_y1, 377 C_BACKSPACE, 244
bessel_yn, 377 bge, 378 bgt, C_BOOL, 244
operare, 378 bgt, constantă, 3 C_CARRIAGE_RETURN, 244
4 128 39, 43 C_CHAR, 244 C_DOUBLE, 244
C_DOUBLE_COMPLEX, 244
C_F_POINTER, 245, 253
C_F_PROCPOINTER, 244
C_FOLATE, 244
C_FLOATE_COCPOLEX, 244
C_FED, 244
Machine Translated by Google

Index 477

c_funloc, 245 coarray, 333–352


c_funptr, 245 alocabil, 340
c_horizontal_tab, 244 c_loc, componentă alocabilă, 342
245, 253 c_long_double, componentă, 342 argument
244 c_long_double_complex, inactiv, 338 intrare/ieșire, 349
244 c_new_line, 244 c_null_char, componentă pointer, 342
244 c_null_char, 244 c_long_double, componentă procedură, 342
244 c_ptr, 244 c_ptr, 244 c_ptr, 244 cu componentă alocabilă, 341
c_ptr, 244 c_ptr7 (de caractere), 9 cu componentă pointer, 341
case implicite, 58 plafon, 164
caractere, 166 caractere, 9, 18, 19, cobound, 335
26, 419 atribuire, 41, 196 constantă, codimensionare,
195, 196 context, 12 expresie, 41, 335 constrângere,
186, 189 funcție, 166 constantă 35 coextent, 335
literală, 17, 18 set, 9, 29, 33, 309 obiect coindexat, 336
asociere de stocare, 400 unitate, secvență de colating, 18, 19
399 șir, 205 subșir, 26 variabilă, editare două puncte, 208
26, 186, 194, 204 lungime variabilă, argument comandă, 308
5, 101 instrucțiune de caractere, linie, 308
21, 153 , 420 character_kinds,
386 child data transfer command_argument_count, 308
statement, 318 class attribute, linie de comentariu, 12 comentariu,
268 class default guard, 273 12, bloc comun, 42591 284, 402–
class default statement, 272 405, 407 declarație comună, 402 cascadă
class is guard, 273 class is de compilare, 332, 429 compilator, 2, 50,
statement, 272 class keyword, 133 compiler_options, 385 compiler_version,
267 clone, 293 close 385 constant complex, 195, 196, 314
statement, 215, 218, 219 exponentiation, 37 părți, operand
cmplx, 164 constantă, 37 părți, constantă literală 365
de valori, 203 variabile, 20 instrucțiuni
complexe, 21, 152 selector de
componente, 22 calculate merge la,
420 concatenare, 41, 168 compilare
condiționată, 6 conformitate

(de matrice),
46 (la standardul Fortran), 3, 7
Machine Translated by Google

478 Index

conjg, 164 punct, 311


conexiune (de fișier), 213 zecimal= specificator, 311, 326 tip
constantă, 13 expresie, 134, declarat, 268 copiere profundă,
135, 137, 163, 166, 168, 298 subobiect al 108, 358, 373 constantă de
unui, 137 constrângere, 8 nume caractere implicite, 19, 205
construct, 61 conține declarația, 69–73, 190 inițializare, 141 constantă reală,
contiguitate, 76 atribut contiguu, 361 continuare 16 variabilă reală, 20 tip valoare
parametru, 258 tip amânat, 280
parametru, 255, 258, 291
procedură legată de tip, 281
cuvânt cheie amânat, 280 atribuire
definită, 45, 78, 82, 91, 110, 147
linie, 12, 312, 403, 419 operație, 91 operator, 42, 44, 47,
mark, 12, 18 continua 78, 82, 110 variabilă, , 37 definiție,
declarație, 407 copy-in copy-out, 37 definiție (a indicatorului), 79
77 corrank, 335 cos, 165, 376 caracteristici șterse, 4, 427 delim=
cosh, 165, 376 count, 175, 313 specificator, 217, 221, 326 delimitator, 17,
18, 196, 217 număr denormalizat, 224
caracteristici depreciate, 399 tip derivat, 49
derivat 21–24, 28, 42, 188, 204, 208, 246,
CPU, 2, 181, 213 255, 399 componentă, 106, 289 definiție, 149,
cpu_time, 181 257 intrare/ieșire, 317–320 constantă literală, 22
secțiune critică, 347 interogare parametri, 2596 descriptor, descriptor,
cshift, 178 înregistrare 257 , 27, 198 dialect, 2 cifre, 169 argument dim,
curentă, 199 declarație 175 procedura dim, 164 atribut dimensiune, 23,
de ciclu, 60, 423 100 declarație dimensiune, 402, 409 dimensiuni
ale matricei, 23 recursivitate directă, 88
d editare descriptor, 408
date
abstractizare, 13
baze, 209
structură, 21
transfer, 211, 318
tip, 13, 14, 29
declarație de date, 15, 138, 312, 404, 420
declarație de date, 353 data, 180
date_and_time, 180 dble edit, descriptor 408
311 cod mort, 63 instrucțiune de dealocare,
102, 104 virgulă zecimală, 311 modul de
editare, 311
Machine Translated by Google

Index 479

fișier cu acces direct, 209, 215–217, spații libere încorporate, 205,


220 direct= specificator, 220 disociere, 217 ro de editare descriptor,
37, 104, 179 unitate de disc, 190 distins, 203, 206 codificare=
270, 371 divide_by_zero, 225 face specificator, 310 instrucțiune finală,
concurrent construct, 359 concurrent 63, 68, 73, 190, 419 instrucțiune end
do statement, 359 concurrent do do, 59–407 instrucțiune final forall,
statement, 359 –62, 125, 135, 408 do 116 instrucțiune finală funcție, 70
construct index, 61 do while, 408 dot instrucțiune end if , 427 instrucțiune
product, 237 dot_product, 174 double end interface, 81 instrucțiune final
precision statement, 16, 408 dp edit program, 68 instrucțiune end select,
descriptor, 311 dprod, 409 dshiftl, 379 57, 58 instrucțiune end, 417
dshifttr, edit descriptor, 379 dshiftr173 instrucțiune subrutine final,
argument fals, 74–78, 92, 93, 100, 144, instrucțiune tip final 70, 22, 149
163, 405, 406, 421, 424 alocabil, 105 instrucțiune end where, 112, 113
procedura, 82 operator diadic, 33 tip end= specificator, 193, 212, 215
dinamic, 268 înregistrare endfile, 193, 209, 215
instrucțiune endfile, 215, 217
instrucțiune de intrare, instrucțiune
enum 424–425, enumerare 253, 253
variabilă de mediu, 307 eor=
specificator, 199, 212 eoshift, 178
echivalare, instrucțiune 178 epsilon–
echival 402, 404 erf, 377 erfc, 377
erfc_scaled, 377 err= specifier, 193,
194, 212, 214–220 errmsg= specifier,
314, 348

e edita descriptor, 202, 206


Standard EBCDIC, 309
descriptor de editare, 185, 186, 192, 194,
200– 207, 426 alocare elementară,
funcție 110 caractere, funcție 166, 163, 164, eroare

234 funcție matematică, 165 funcție mesaj, 314


numerică, operație 1103, 1103 118– recuperare,
119, 162, 373 subrutină, 230 clauză 194 es descriptor de editare,
elementară, 118 instrucțiune else, 56 203, 206 excepție, 193
instrucțiune altfel, 112 clauză else if, steaguri, 227 manipulare,
56 instrucțiune else if, 56 223 instrucțiune
executabilă, 63, 421, 424
execute_command_line, 382 exist=
specificator, 220 existență (de
instrucțiuni, 2203 exit), 2203 60, 355
exp, 165 interfață explicită, 80–82,
85, 90, 100, 110
Machine Translated by Google

480 Index

matrice de formă explicită, fracție, funcție


153 exponent, 16 litere, 170, 78, 410, 421 nume,
16, 202, 203, 408 funcție 70, 73, 425
de exponent, 170 exponențiere, instrucțiune de funcție, 95, 425
35 expresie, 24, 33, 38 extinde
atribut, 265 extins_type_of, 286 g edita descriptor, 201, 204, 206, 375
extent, reprezentare externă, gamma, 377 legare generică, 317
123, 24 mediu, reprezentare identificator, 142 interfață, 90, 371
externă 208 subprogram, 67, nume, 82, 87, 90–92 procedură
69, 70, 72 atribut extern, 151 legată de tip, 277–280 instrucțiune
declarație externă, 81–82, generică, 273 get_command, 273
151 get_command_argument, 308
get_environment_variable, 307
global data, 70 name, 88 go to statement,
63 gradual underflow, 229 graphics
display, 192, 194
f edita descriptor, 202, 206
câmp, 185, 419 fișier, 185
declarații de poziționare,
214 fișier= specificator, 216,
219 instrucțiune finală, 281, 284
subrutină finală, 281, 284 finalizare,
281–282 findloc, (IEEE38)3 , 225
floor, 164 flush statement, 324
fmt= specifier, 193, 194, 212 forall h Editare descriptor,
construct, 115, 116, 370 forall 427 oprire, 228 linie
statement, 114, 116, 370 form= antet, 424 stocare
specifier, 216, 220 format heap, 102 constantă
specification, 18 , 18, 18 , 370 193, hexazecimală, 15, 312
194, 200, 205, 375 declarație, 190, Fortran de înaltă performanță,
201, 209 formatate 4 limbaje de nivel înalt, 2 gazdă,
67, 73 asociere, 86, 149, 413
imens, 169 ipoten, 378
funcție ipotenuză, 238

I/O, 185, 208 Editez descriptorul, 201


ieșire, 194 I/O
citire, 193 listă, 186
formatat= specificator, 220 declarație, 188
Fortran 66, 2 declarație de stare,
Fortran 77, 3–7, 405, 407, 421, 422 213 unitate, 190 iachar,
Fortran 90, 3 166, 309, 313 iall, 379
Fortran 95, 4
Machine Translated by Google

Index 481

iand, 172 ieee_selected_real_kind, 236, 387


iany, 379 ieee_set_flag, 231 ieee_set_halting_mode,
ibclr, 172 231 ieee_set_rounding_mode, 236
ibits, 172 ieee_set_status, 231
IBM, 2 ieee_set_underflow_mode, 236
ibset, 172 ieee_sqrt, 226 ieee_status_type, 230
ichar, 166, 313 ieee_support_datatype, 233
id= specificator, 321 ieee_support_denormal, 233
ieee_support_divide, 233
Diviziune IEEE, ieee_support_flag, 230
224 de intrare/ieșire de valori ieee_support_halting, 230
excepționale, 323 de steaguri, 225 ieee_support_inf, 233 ieee_support_io,
rădăcină pătrată, 224 standard, 233 ieee_support_nan, 233
169, 223, 224 ieee_arithmetic, 232, ieee_support_rounding, 233
234–236, 387 ieee_class, 234 ieee_class, ieee_support_sqrt, 233
234 ieee2_classe2_eeee_2_copy_eee_type,234 , ieee_support_standard, 233
226 ieee_exceptions, 229–231 ieee_support_underflow_control, 233
ieee_features_type, 225 ieee_flag_type, ieee_underflow_flag, 226 ieee_unordered,
229 ieee_get_flag, 230 235 ieee_value, 235 ieor, 172 if
ieee_get_halting_mode, 230 construct, 55–57, 61 if statement, 56, 80
ieee_get_rounding_mode, 235 image, 333 image control statement, 337,
ieee_get_status, 231 348 image index , 334 image_index, 351
ieee_get_underflow_mode, 236 implicit interface, 81 typing, 133, 412
ieee_halting, 226 ieee_inexact_flag, 226 implicit none statement, 134 implicit
ieee_inf, 226 ieee_invalid_flag, 226 statement, 137, 409, 413, 421 implicite-do
ieee_is_finite, 234 ieee_is_nan, 234 list, 188 implicite-do bucla, 88, 126, 138,
ieee_is_negative, 234 ieee_is_normal , 138, 134 193 matrice de formă implicită,
234 ieee_logb, 234 ieee_nan, 226 353 instrucțiune de import, 304–305
ieee_next_after, 235 ieee_rem, 235 procedură impură, 368 include linie, 407
ieee_rint, 235 ieee_round_type, 232 index, 167, 313 recursivitate indirectă, 89
ieee_rounding, 226 iee, 235sb inexact, 225
Machine Translated by Google

482 Index

infinit (semnat), 224 cuvânt cheie intrinsec, 306


moștenire, 265, 267, 279 instrucțiune intrinsecă, 162
linie inițială, 419 puncte, invalid, 225 iolength=
214, 217 valoare, 137, specifier, 221 iomsg=
358 inițializarea specifier, 325 ior, 173 iostat=
componentelor, 141, specifier, 193–194, 212,
358 instrucțiune de întrebare, 217, 219– 214– 216, 218, 3036_parity, 218, 218, 218,
222, 3106, funcție de interogare 3, 310 , 218, 30362_parity, 2036_ior , 326
168–170, 176, 230, 232 is_iostat_eor, 326 ishft, 173 ishftc, 173
instrucțiune, 2, 133 int, 164 int16, 385 int32,
385 int64, 385 int8, 385 diviziune
întreagă, 35 expresie, 24, 420 expresie, 24,
420 variabilă 18, constantă 204, instrucțiune
18, constantă 204 , 21, 152 integer_kinds,
386 atribut intent, 77–78, 144, 151, 301 ISO/IEC 10646, 309, 310
intent statement, 144 interfață, 80, 330 iso_c_binding, 387
block, 43–45, 69, 75, 81–85 , 90, 254 body, iso_fortran_env, 306, 385
4 , 82, 85, 92 declarație de interfață, 81, 90 iso_c_binding, 243 iterații, 60
fișier intern, 191–195, 200, 205, 208
reprezentare, 185, 200, 208
subprogram, 67, 73 internaționalizare, J3, 3, 4, 399
308 interoperabilitate cu C, 243–254
pentru cooperare, 243–254 intrinsec Kanji, 19, 21
argument
cheie, 83, 85, 92, 289 apel,
161 pentru tip derivat, 257
specificator, 193 fel

valoarea parametrului, 14, 16, 19, 20,


37, 41, 93 parametru de tip, 13–
17, 21, 36, 40, 135, 163–168, 173, 209,
325 funcția tip, 15–17, 19, 20 ,
163 fel= specificator, 152

l editați descriptor, 203


etichetă, 13, 63, 85
domeniul de
atribuire, 46, 110 aplicare, 85 lbound, 176,
tipuri de date, 13 313 lcobound, 351 semn
funcții, 410 modul, de început, 205 leadz,
306 procedură, 161 380 limită de filă stânga,
atribut intrinsec, 151 206 len, 168, 313
Machine Translated by Google

Index 483

len= specifier, 152 maxval, 175, 313


len_trim, 167, 313 scurgeri de memorie, 79, 104, 109, 140, 294
comparație lexicală, 167 merge, 177 merge_bits, 381 method, 274
token, 10 lge, 167,
383 lgt, 167, 383
line, 11 linked list, 28, 123 MIL-STD 1753, 171 min,
list-directed 164, 313 minexponent,
170 descriptor de editare
a lățimii minime a câmpului, 202 minloc, 179,
313, 383 minval, 175, 313 expresie în mod mixt,
I/O, 189, 214, 217 35 nume mnemonic, 168 număr de mod, 116
intrare, 195 ieșire, 20 mod. , 169, 224 modul, 43, 45, 67, 70–72, 78–
190, 195, 427 constantă 82, 142– 145, 329–332, 388, 407 nume, 71, 86
literală, 13, 14 lle, 167, 383 llt, procedura, 90, 330 subprogram, 67 modul
167, 383 entitate locală, 85 instrucțiune, instrucțiune, 90, 91, 314
blocare, 345–347 log, 145 log,
161 , 165 log_gamma, 377
matrice logică, 126, 175
atribuire, 39 expresie, 39
constantă literală, 19 variabilă,
19, 20 funcție logică, 168
instrucțiune logică, 21, 152
logical_kinds, 386 instrucțiune de modul,
parametru de buclă, 59, 71 modulo, 165 operator
23 inferior , limită 149 monadic, 33 move_alloc,
295
MPI, 303
multiplicare, 10
funcții, 174
mvbits, 173

nume, 20, 312


domeniul de
program principal, 67, 68, 403, 424 aplicare, 85 nume=
make tool, 429 many-one section, 121 specificator, 220

mask, 112, 126 mask argument, 176 constantă numită, 134–137,


maskl, 380 maskr, 380 function 143 obiect, 27 named=
matematica, 165 matmul, 174 max, specificator, 220 comentarii liste
164, maxexponent, 313 maxloc, 179, de nume, 198 date, 197 grup, 143,
313, 383 155, 197

I/O, 197, 327


declarație de listă de nume, 155
NaN, 224
Machine Translated by Google

484 Index

NCITS, 3 sistem de operare, 217


cel mai operator, 33, 42–47
apropiat, 170 imbricat, redenumire, 297
55, 62, 67, 423 new_line, token, 42 operator,
324 newunit= specifier, 91, 92, 297 atribut
374 nextrec= specifier, opțional, 83, 84, 85, 144, 289 instrucțiune
221 nint, 164 nml= opțională, 144 atribut opțional, 389 ordine
specifier, 197, 212 non- de evaluare, 80 ordinea instrucțiunilor, 68,
advancing I/O, 20 , 919 subrutină 70, 71, 73, 134, 190, 420, 425 listă de ieșiri,
non-elementală, 231, 235 tipuri non- 186, 192 depășire, 225 supraîncărcare, 90
numerice, 14 cuvinte cheie non_intrinsic, anulare, 279
306 norm2, 378 nu, 173 nul, 141, 179
valoare nulă, 196, 197 declarație nullify,
51 num_images, 351 număr

p editați descriptor, 206,


pachet de 324, 177 pad=
specificator, 218, 221, 326
conversie, procesare paralelă, 114, 333
reprezentare 185, atribut parametru, 134, 137, 151
număr 185 = specificator, instrucțiune de parametru, 409 tip
220 numeric derivat parametrizat, 256, instrucțiune
atribuire, 38 de transfer de date parental 182
expresie, 34 paranteze, 34 paritate, 383 atribut de
funcție, 163, 168 trecere, 262, 263 argument dummy
operator intrinsec, 34 pentru obiectul trecut, 263 instrucțiune
asociere de stocare, de pauză, 427 în așteptare= specificator,
400 unități, 399 tip, 321 la sută, 22 pointer, 27, 37, 49–50, 60,
14 75–79, 3 –104, 123, 124, 140, 149, 187,
208, 269 alocare, 28, 103 argument, 75,
388 atribuire, 42, 48, 50, 79, 115, 270, 296
o editare descriptor, declarație , 1249
202 obiect, 27
coindexat, 336
cod obiect, 133 exemplu
orientat pe obiect, 433
programare
orientată pe obiect, 265–282 caracteristici
învechite, 4, 419, 422 constantă octală, 15, asociat, 37, 103
312 numai opțiune, 147 declarație deschisă, asociere, 37, 75, 125, 301
190, 209, 210, 213, 216– 222, 310 declarație componentă a coarray, 341–
deschisă, 374 342 disociat, 37, 104, 140
expresie, 48
Machine Translated by Google

Index 485

funcție, 79, 104, 366 random_seed, 181


inițializare, 140 intenție, range, 14–16, 61
301 nedefinit, 37, 140 range, 15–17, 170
atribut pointer, 27, 49, rank, 23–25, 28, 110, 356
75, 78, 100, 123, 144, 149, 151, 399 read statement, 186, 193, 192–194, 196–199,
209, 21, 21, 21 , 217, 326 read=
instrucțiune pointer, 144 specificator, 221 readwrite=
entitate polimorfă, 267–269, 292 specificator, 221 real
popcnt, 380 poppar, 380 pos=
specificator, 323 poziție= specificator,
217, 221 argument pozițional, 83, 92 constantă literală, 15,
precedența operatorilor, 43 precizie, 16 operand, 36 variabilă,
16 funcție de precizie, 117 , 170 20, 186 funcție reală,
preconectare (de fișiere), 213, 307 164 declarație reală, 21, 152
prezent, 84, 163 declarație de ceas în timp real, 180 real128,
tipărire, 193, 194, 215, 217 atribut 385 real32, 385 real64, 385
privat, 142, 143, 289, 315, 404 real_kinds, 386 realocare,
108, 723 rec. = specificator,
209, 212 recl= specificator,
217, 220, 222, 306 înregistrare,
185, 193, 195, 198–209, 213–
declarație privată, 142, 150 215 lungime, 217, 220
procedură, 67 argument, 82, recursivitate, 88–89, 45 input5,
370 pointer, 261 recursiv/output 45 , 324, 374 registru, 2
componentă, 261, 262 expresii relaționale, 39 repetare, 168
variabilă, 261 numărare repetare, 138, 186, 196, 200, 201,
instrucțiune de 375 cuvinte rezervate, 20 remodelare, 177
procedură, 259–261, 276, 314 dependență clauză rezultat, 88, 425 declarație returnare,
de procesor, 7 produs, 175 program, 10, 67 77, 4224241 inversare, instrucțiune de
nume, terminare, 348–349 unitate, 10, 67, derulare înapoi, 214 rotunjire= specificator,
70, 74, 216, 402, 403, 405, 421 instrucțiune 325 rotunjire, 228 moduri de rotunjire, 224
de program, 68 atribut protejat, 296, rrspacing, 171
297 prototip, 249 atribut public, 142,
142, declarație publică , 150 procedură
pură, 117–118, 153

radix, 170 s edit descriptor, 206


fișier cu acces aleatoriu, safety, 3, 4
209 număr_aleatoriu, 181 same_type_as, 287
Machine Translated by Google

486 Index

salvare atribut, 137, 145, 404 sursă

salvare instrucțiune, 145 cod, 2, 133, 429


salvare atribut, 388 scară, 171 formular, 3, 11, 18,
factor de scară, 206 scanare, 419 sursă= clauză, 291,
167, 313 scope, 85, 145 scope 293 descriptor de editare
unit, 85, 186, 190 segment, sp, 206 spații, 207 spațiere,
343–344 neordonat, 344 case 171 nume specific, 82, 90,
statement, 57 select type 410 procedură specifică
construct, 268, 270, 272, 274 legată de tip, 274, 279 expresie specificație,
selected_char_kind, 309 153, 153, 298 funcție, 153 instrucțiune,
selected_int_kind, 14, 171, 68, 133, 421
356 selected_real_kind, 16, 171, 384
selector, 58 semantics, 7
separator19, attribute 196, 102, attribute , spread, 178
401, 403 instrucțiune de secvență, 399 tip sqrt, 166, 314
de secvență, 399 fișier secven ial, 209, paranteze pătrate, 55, 298,
210, 213, 217, 220 secven ial= specificator, 333 ss descriptor de editare,
220 set_exponent , 171 copiere superficială, 206 stivă, 101 stat= specificator,
108, 210, 213, 217, 4, 1, 4 , 3, 4, 1 134, 176, 103, 104, 348 instrucțiune, 10–
405 funcție de formă, 176, 313 script shell, 12, 33, 312, 412, 419 funcție de
429 shifta, 381 shiftl, 381 shiftr, 381 efecte etichetă, 419 , 186, 190,
secundare, 79, 117 semn, 165, 169 semn= 193, 407, 424 separator, 12 status=
specificator, 31 16 pur și simplu de specificator, 216, 218 instrucțiune
semnificație, 31 1 de semnificație continuu , stop, 69, 355 cod stop, 69, 349, 355
364 sin, 166, 376 sinh, 166, 376 size, 176, stocare, 104 alocare, 2, 103, 104
313 size (of array), 23, 176 size= specifier, asociere, 399 system, 213 unit, 401
199, 212, 326 slash edit descriptor, 207, 209 storage_size, 384 stream access input/
output, 323 stream= specifier, 326
stride, 122 string-handling function,
167 interogation function, 168
transformational function, 168
strong typing, 133 27 , 213 componentă,
123 constructor, 22, 42, 138, 289, 354
de pointeri, 123 submodul, 329–332
Machine Translated by Google

Index 487

entitate, conversie, 163


331 din submodul, declarație declarație, 20–22, 150
331 procedura, 331 extensibil, 267 extensie, 265–267, 284
instrucțiune submodul, 330, 331 nume, 69 parametru, 21, 150, 255, 256,
subobiect, 27, 120–123 subprogram, 291 interogare, 256, 259 specificație,
67 subrutină, 67, 145, 423 nume, 152
70, 73 instrucțiune subrutină, 70,
23–5, subscript. 26, 29, 120,
125, 271 subșir, 8, 26, 120, 123 sumă, caietul de sarcini,
175 sync all statement, 337 sincronizare 152 declarație, 150
imagini, 344–345 sincronizare memorie, instrucțiunea de tip (a se vedea și tipul derivat),
347, 413–415 sincronizare, 302, 8 syntax 22, 69, 152 tipul este instrucțiunea,
–333 , 9, 20, 33 reguli, 7 system_clock, 272 tipul este paza, 273 procedura legată de
180, 313 tip, 274–280, 354

ubound, 176, 313


ucobound, 351
ultimate component, 187
unar
operare, 46
t editați descriptor, 206
operator, 33, 39, 43
tabulație, 206 tan, 166,
variabilă nedefinită, 37, 145
376 tanh, 166, 376 țintă,
underflow, 225, 229 underscore,
28, 48, 50, 103, 104
9, 19, 20 I/O neformatat, 208,
atribut țintă, 50, 76, 144, 151,
219, 221 neformatat= specificator,
403, 406 țintă atribut, 406 atribut țintă, 388
220
terminal point, 214, 217 termination,
Unicode, 309
348–349 this_image, 351 time, 180 tiny, 170 tl
unit, 190
edit descriptor, 206 token, 10, 12, 13 tr edit
number, 190, 193, 213, 215, 216, 219, 307
descriptor, 206 trailz, 380 transfer, 1742 transfer
unit= specifier, 193, 194, 212, 214–
of allocation, 17495 funcție, 162, 236
216, 218, 219 unix, 308 pointer unlimited, 308
transpunere, 179 trim, 168 tip, 133 alocare, 291,
pointer unlimited, polimorfic 2,
292
nelimitat 269 deblocare, 345–347 despachetare,
177 unitate de stocare nespecificată, 399 limita
superioară, 23, 99, 149 declarație de
utilizare, 72, 86, 146, 305 asociere de
utilizare, 86, 149, 413

Format UTF-8, 310

atribut de valoare, 248


Machine Translated by Google

488 Index

variabilă, 13, 20, 27, 186


(definit), 37, 153
(nedefinit), 37, 145, 153 indice
vectorial, 25, 121, 121 verificare,
168, 313 atribut volatil, 301–304
coarray, instrucțiune volatilă, 3343
301

Declarație de așteptare, 321, 322


WG5, 3–5, 399
where construct, 111, 112, 115
where statement, 111, 126 while,
408 whole coarray, 335 write
statement, 194, 197–200, 211, 215,
217, 326 write=21 specifier

x edita descriptor, 206, 207


X3J3, 3, 4, 399

z edita descriptor, 202


zero (semnat), 224 șir de
lungime zero, 18, 26, 39 matrice
de dimensiune zero, 99, 126

S-ar putea să vă placă și