A Fortran programozási nyelv

Változók, típusok, deklaráció, kifejezések

FORTRAN-ban a változók nevei maximum 6 karakter hosszúak lehetnek, és az angol ábécé 26 (nagy)betűjéből, illetve számjegyekből állhatnak. A Fortran90 31-re, a Fortran95 256-ra emelte ezt a számot. Az első karakter nem lehet számjegy. Tulajdonképpen ezek alkotják a karakterkészletet is, kiegészítve néhány speciális karakterrel. Az azonosítókra is vonatkozik az a szabály, hogy bárhova bármennyi szóköz tehető, például a ’KUTYA’ és a ’KU TYA’ azonosítók ugyanannak számítanak. Vannak ún. speciális (védett) szavak, amelyeket nem használhatunk változó névként: az utasítások kódjai tartoznak ide. A nyelvben számokkal, logikai értékekkel és sztringliterálokkal dolgozhatunk. Utóbbiak csak I/O műveletekben fordulhatnak elő, megadásuk: 'FORTRAN' vagy 7HFORTRAN. A skalárok és logikai típusok a következők lehetnek:

Típus Példa
INTEGER -3, 0, 7
REAL 3.14, .7, 0.3141E1
DOUBLE PRECISION 314.D-2
COMPLEX (3.5,-1E-5)
LOGICAL .TRUE. és .FALSE.

A komplex szám két valósból áll. A C-hez hasonlóan az eredeti nyelvben nem volt meghatározva, hogy az egyes típusokat hány biten reprezentálja a fordító. Az értékadás formája a következő : változó = kifejezés.

A skalárokkal végezhető műveletek: a szokásos aritmetikai műveletek: + - * / ** (hatványozás).

A logikai műveletek (relációk):

.EQ. = egyenlő

.NE. = nemegyenlő

.GT. = nagyobb

.GE. = nagyobbegyenlő

.LT. = kisebb

.LE. = kisebbegyenlő

.NOT. = negáció

.AND. = és

.OR. = vagy

A megszokott műveleteken kívül vannak ún. standard belső és külső függvények is, mint nyelvi elemek. Ilyenek pl. az ABS, EXP, SIN, COS, SQRT, stb. Ha az értékadás jobb oldalán skalár kifejezés szerepel, akkor nem kell a baloldal típusával megegyeznie a kifejezés típusának, mert létezik automatikus típuskonverzió. Ennek szabályait a következő táblázatok ismertetik:

= INTEGER REAL DBLE COMPLEX
INTEGER INTEGER IFIX SNGL+IFIX
REAL REAL REAL SNGL
DBLE REAL+DBLE DBLE DBLE
COMPLEX COMPLEX

+, -,*, / INTEGER REAL DBLE COMPLEX
INTEGER INTEGER REAL DBLE COMPLEX
REAL REAL REAL DBLE COMPLEX
DBLE DBLE DBLE DBLE
COMPLEX COMPLEX COMPLEX COMPLEX

** INTEGER REAL DBLE COMPLEX
INTEGER INTEGER REAL DBLE
REAL REAL REAL DBLE
DBLE DBLE DBLE DBLE
COMPLEX COMPLEX

Az azonos precedencia osztályba eső műveletek esetén a nyelv nem írja elő a kiértékelés sorrendjét, csak azt, hogy az eredmény feleljen meg a matematikai szabályoknak. Természetesen zárójelezéssel szabályozhatjuk a kiértékelési sorrendet.

A bináris műveletek mellett léteznek unáris operátorok is: +, -. A + helybenhagyja a számot, a - negáció. A műveletek kiértékelése teljesen megfelel a matematikai szabályoknak. A kiértékelés balról jobbra történik. Erre egyetlen kivétel a ** (hatványozás) művelet:

A ** B ** C kifejezés kiértékelése ekvivalens: A ** (B ** C) kifejezéssel.

A műveletek precedenciatáblázata:

Műveletek Precedencia
** 1.
*/ 2.
+- 3.

További érdekesség a műveletekkel kapcsolatosan a típus kérdése. Veszélyes, hogy INTEGER típusú számok között végzett műveletek INTEGER típust adnak vissza. Így pl.:

2 / 3 + 3 / 4 = 0

hiszen ilyenkor / művelet az INTEGER-ek közötti egészosztás.

A logikai műveletek precedenciatáblázata:

Műveletek Precedencia
.NOT. 1.
.AND. 2.
.OR. 3.
.NEQV., .XOR., .EQV. 4.

Az azonos precedenciájú logikai műveletek kiértékelődése balról jobbra történik.

A különböző típusú műveletek precedenciatáblázata:

Műveletek Precedencia
aritmetikai 1.
karakter 2.
relációs 3.
logikai 4.

Karakter kifejezések

A FORTRAN-t nem kifejezetten szövegek kezelésére találták ki, ezért egyetlen karakterművelet található meg: // - szövegek összefűzése. A művelet argumentuma lehet konstans sztring, sztring típusú változó, sztring tömbelem, sztring visszatérési értékű függvény és részsztring. Sztringek értékadásakor, ha a baloldal rövidebb, mint a jobboldal, akkor csonkítás történik, ha a baloldal hosszabb, mint a jobboldal, akkor kiegészítés üres karakterekkel. A sztringek ugyan nem tömbök (betehetők egy 7 dimenziós tömbbe), de szeletelhetők, és a szeletere való hivatkozás a cserét is lehetővé teszi. Üres sztring nincs.

Relációk

A nyelvben a relációk precedenciája azonos, de alacsonyabb az aritmetikai és karakter műveletekéhez képest. A reláció két operandusa előbb kiértékelődik, majd összehasonlítódik, ez alapján tér vissza igaz/hamis értékkel. Sztringek között a .GT., .GE., .LT., .LE. azt adja meg, hogy a lexikografikus rendezés szerint melyik van előbb (az lesz a kisebb) az ASCII kódtábla alapján. A rövidebb sztring kiegészül üres karakterekkel.

Konstans kifejezések

A konstans kifejezések konstansokból, paraméterekből és FORTRAN műveletekből állnak. Egy operandus lehet maga is egy konstans kifejezés, konstans szimbólikus neve, valamely konstans paraméterrel meghívott beépített függvény.

parameter (t=2.0*(3.0**2.5));

Kimenet:

"", line ..: Warning: parameter t set to a nonconstant

Amint látható, automatikusan változót csinál a konstans kifejezésből.

A (a Fortran 90 előtti) nyelvben egyáltalán nincsenek mutatók. A nyelv sajátossága, hogy nem kötelező minden változót deklarációs utasításban deklarálni. Ha a végrehajtható utasításokban olyan változónév fordul elő, amit nem deklaráltunk, akkor a fordítóprogram utólag felveszi ezt is a változók közé. Ezt nevezzük implicit vagy automatikus deklarációnak. Az implicit módon deklarált változó típusa a név első betűjéből állapítható meg: az I,J,K,L,M,N betűkkel kezdődő azonosító egész típusú, a többi pedig valós típusú lesz. Azonban nem árt megjegyezni, hogy az implicit deklaráció szándékos használata drasztikusan megnöveli az esetleges programhibák elofordulásának esélyeit. Érdemes tehát a változóinkat következetesen explicit módon deklarálni. Az implicit deklaráció az IMPLICIT NONE kifejezéssel tiltható le.

Amennyiben ettől el szeretnénk térni, a szegmens deklarációs részében típusnév változónév szintaxissal deklarálhatunk változókat (pl. INTEGER X).

Tömbök

A tudományos számításoknál gyakran előfordulnak vektorok és mátrixok. A LAPACK és a BLAS könyvtárak tartalmaznak lineáris algebrai rutinokat az egy és két dimenziós tömbökre.Ezen adattípusokat a FORTRAN tömbökként kezeli. Az egydimenziós tömb a vektorral ekvivalens, míg a kétdimenziós tömb a mátrixok megfelelője. A tömbök FORTRAN-os kezeléséhez azonban nemcsak a szintaxis használatátáról, hanem az objektumok memóriában való elhelyezkedéséről is szót kell ejteni. A nyelvben megkülönböztetünk egy- és többdimenziós tömböket.

Egydimenziós tömbök

A legegyszerűbb tömbtípus, az egydimenziós, elemek egymásutáni sorozatából áll, melyek folyamatosan tárolódnak a memóriában. Például, a

real a(20)

deklarációval, megneveztünk egy a 20 hosszúságú valós egydimenziós tömböt, ami 20 valós számból áll, melyek folyamatosan tárolódnak a gép memóriájában. A konvenció alapján a FORTRAN-ban az elemek indexelését 1-től kezdjük felfelé. Tehát az első elemét a tömbünknek az a(1)jelöli, míg az utolsót az a(20). Ettől függetlenül mi is definiálhatunk egyéni indexelést saját tömbünkre, az alábbi módon:

real b(0:19), weird(-162:237)

Itt a b teljesen megegyezik az előző példabeli a-val, kivéve hogy itt az index 0-tól fut 19-ig. A weird tömb hossza ebben a példában 237-(-162)+1 = 400.

A tömb elemének típusa bármilyen alaptípus lehet, de természetesen a tömb elemeinek azonos típusúaknak kell lenniük. Például:

integer i(10) logical aa(0:1) double precision x(100)

Minden egyes elem felfogható akár egy-egy különálló változóként is. Az i-edik elemre az a(i)-vel hivatkozhatunk. A következő kis programszegmens az első 10 négyzetszámot tárolja egy sq tömbben:

integer i, sq(10) do 100 i = 1, 10 sq(i) = i**2 100 continue

Gyakori hiba, hogy a program olyan tömbelemre is hivatkozik, ami nem definiált vagy kívül esik a definiált tömbön. Azért különösen fontos a programozó figyelmessége, mert ezen hibatípust a Fortran-fordítók nem ismerik fel, tehát nem adnak rá fordítási hibát!

Kétdimenziós tömbök

A lineáris algebrában alapvető a mátrixok használata. A mátrixokat általában kétdimenziós tömbök formájában jelenítjük meg. Például a

real A(3,5)

deklaráció, definiál nekünk egy 2 dimenziós tömböt, mely 3*5=15 valós számból áll. Hasznos tudni, hogy az első index a sorindex, a második pedig az oszlop index. Így az alábbi grafikus képet kapjuk:

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

Kétdimenziós tömböknél is definiálhatunk egyéni indexelést. Az általános formalizmus az alábbi:

név (low_index1 : hi_index1, low_index2 : hi_index2)

A tömb teljes mérete tehát :

méret = (hi_index1-low_index1+1)*(hi_index2-low_index2+1)

(low=kezdőindex, hi=legmagasabb index)

A Fortranban gyakran szoktak nagyobb tömböt deklarálni, mint amekkora mátrixra a számításhoz és tároláshoz szükség lenne. Ennek oka, hogy a Fortran 77 nem rendelkezik rugalmas és dinamikus tárolási funkcióval. Ez teljesen elfogadott eljárás. Például:

real A(3,5) integer i,j c c Mi csak a felso 3 * 3 -ast részét hasznoljuk a tombnek c do 20 j = 1, 3 do 10 i = 1, 3 a(i,j) = real(i)/real(j) 10 continue 20 continue

A kétdimenziós tömbök tárolási formája

A Fortran a magasabb dimenziójú tömböket is elemek folyamatos sorozataként tárolja. Fontos tudni, hogy a kétdimenziós tömböket oszlop szerint tárolja. Tehát a fenti példát nézve, a tömb (2,1)-vel jelzett elemét (3,1) elem követi. Majd ezután végighalad a kettes, majd a többi oszlopon is.

Térjünk kicsit vissza ahhoz a példához, mikor csak a felső 3*3 részmátrixot használtuk, a 3*5-ös A(3,5)tömbből. A kilenc számunkra fontos elem a memória első 9 helyén fog tárolódni, míg a következő hat helyet nem használjuk fel. Ez kicsit talán fura, hiszen a fődimenziója mindkét mátrixnak (az eredeteinek és a tároltnak) ugyanaz. Azonban gyakran a fődimenziója a használt tömbnek nagyobb lesz, mint a definiáltnak. Tehát a mátrix nem folyamatosan fog tárolódni, habár maga a tömb folytonos. Például a fenti példánkban, ahol A(5,3) volt, lesz két "kihasználatlan" cellánk (az első oszlop alja és a második eleje között).

Probléma esetén, nem árt megnézni, az egyes elemekre való memória-hivatkozást (azaz memória címét). Minden egyes tömbhöz hozzárendelődik egy, a tömb első eleme (1,1) alapján kiosztott memória cím. Az (i,j)-edik elem memóriacíme:

cím[A(i,j)] = cím[A(1,1)] + (j-1)*lda + (i-1)

ahol lda a fő (többnyire sor) dimenziója A-nak. Fontos, hogy az lda nem azonos az aktuális mátrix dimenziójával. Ezek keveréséből sok programhiba származhat!

Három- vagy még több dimenziós tömbök

A Fortran 77-ben maximum hétdimenziós tömböt használhatunk. A kezelés szintaxisa és formalizmusa teljesen analóg a kétdimenzióséval, ezért erre nem térünk ki külön.

A dimension utasítás:

Más úton is definiálhatunk egy tömböt a Fortran 77-ben:

real A, x dimension x(50) dimension A(10,20)

ami ekvivalens ezzel:

real A(10,20), x(50)

A dimension utasítás használata manapság már egyre kevésbé jellemző.

A d alakja: [ d1 : ] d2 , ahol d1 az alsó index, d2 a felső index. A tömbök indexelése 1-től kezdődik (alapbeállítás), ha nem adunk meg alsó indexet, az automatikusan 1. Nem legális index használatra nem ad fordítási hibát, így lehet pl. egy tömböt -3-mal indexelni. A tömböket oszlopfolytonosan tárolja a FORTRAN program. A tömb elemei azonos típusúak kell, hogy legyenek. A nyelvben lehetőség van többdimenziós tömb deklaráslására, a dimenziók maximális száma 7 lehet.

Az EQUIVALENCE utasítással előírhatjuk, hogy különböző változók ugyanazon a memóriaterületen legyenek ábrázolva. Például:

INTEGER A(4) INTEGER B EQUIVALENCE B, A(3)

Innentől az A 4. elemére A(4)-el és B-vel is hivatkozhatunk. Fordítási hibát okoz, ha pl. a B változót két különböző változóval tesszük ekvivalenssé, vagy ha tömbök különböző elemeire akarunk ekvivalenciát alkalmazni, illetőleg ha a két változó típusa nem egyezik meg. Így például hibásak a következők:

INTEGER A, B, C EQUIVALENCE B, A (B nem foglalhatja le ugyanazt a területet mint EQUIVALENCE B, C A és mint C, kivéve ha a lenti példát alkalmazzuk) INTEGER A(4), B(4) EQUIVALENCE B(1), A(1) (Az A(1), A(2) szekvenciálisan követik egymást, EQUIVALENCE B(3), A(2) nincs hely a B(2)-nek) REAL A(4) INTEGER B EQUIVALENCE B, A(1)

Helyes viszont a következő:

INTEGER A, B, C EQUIVALENCE B, A (A, B, C ekvivalens egymással) EQUIVALENCE C, B

A DATA utasítással kezdőértéket adhatunk változóknak.

Igaz, hogy nincsen char vagy string típusunk, szövegkonstansok tárolására mégis van mód. Erre a célra megteszi bármilyen eddig tárgyalt típusú változó. Attól függően, hogy a változó ábrázolására szolgáló memóriarekesz 24, 36 vagy 48 bitből áll, 4, 6 vagy 8 karaktert tartalmazhat. A karaktereket balról jobbra számozzuk. A változó karakter típusú értéket a DATA utasítás segítségével vehet fel. Pl.:

DATA A(3)/14HPAPRIKA JANCSI/

Ekkor A(3) felveszi a ’PAPRIKA_’ szöveget, a ‘JANCSI__’ pedig az A(4) tömbelembe csúszik át, ahol a _ a szóközt jelöli.

Változó hosszúságú tömbök

Nézzünk példának egy vektoroperátort:

y := alpha*x + y

ahol alpha skalár, de x és y vektor. Nézzünk egy egyszerűbb subroutine-t, felhasználva a fentit:

subroutine saxpy (n, alpha, x, y) integer n real alpha, x(*), y(*) c c Saxpy: Szamold ki y := alpha*x + y, c ahol x és y n hosszusagu vektorok c c lokalis valtozok integer i c do 10 i = 1, n y(i) = alpha*x(i) + y(i) 10 continue c return end

Az x(*) és y(*) jelöléssel mondhatjuk meg, hogy az x és y vektorok hossza bármekkora lehet. Arra azonban figyeljünk, hogy a tomb hosszát tudnunk kell valahonnan, hogy ne hivatkozzunk a tomb egy nem létező elmére. A változó hosszúságú tömbök nagy előnye, hogy ugyanazt a segédprogramot használhatjuk különböző hosszúságú tömbökre.

Néhány régebbi Fortran 77 programban ilyen deklarációt is láthatunk:

real x(1), y(1)

Megtévesztő lehet, de ez a jelölés akkor is helyes, ha a tömb mérete nagyobb, mint 1. Lehetőleg kerüljük e jelölés használatát.

Absztrakt adatszerkezetek

Mivel a FORTRAN-ban nincsenek globális változók, a programegységek egy speciális tárterületet használhatnak: az ún. közös adatmezőt. Az egyes programegységek deklarálják a saját neveiket, egymással paraméterátadással, állományokon keresztül és a közös adatmezőkön keresztül kommunikálnak. A közös adatmező tehát a kommunikáció egy eszköze, egy speciális közös tárterület, amelyet minden programegység írhat, és olvashat. Ennek deklarációja és szerkezete a következőképpen néz ki:

COMMON [/n11/] a11[(d11)] [,a12[(d12)]]...

és ez a szerkezet tetszőlegesen ismételhető. Tetszőlegesen sok deklarációs utasítás lehet egy programegységen belül. Az egyes elemek jelentése a következő:

Ugyan a nyelvben a tömböket kivéve nincsenek típuskonstrukciós műveletek, de az alábbi példa (verem adattípus implementálása) azt mutatja, hogy a közös adatmezők használata lehetőséget ad adatabsztrakció megvalósítására.

SUBROUTINE PUSH(X) COMMON /VEREM/ V(100), N N = N + 1 V(N) = X RETURN END SUBROUTINE POP(X) COMMON /VEREM/ V(100), N X = V(N) N = N - 1 RETURN END BLOCK DATA COMMON /VEREM/ V(100), N DATA N /0/ END

Tömbök inicializálása

A Fortran tömbök inicializálása sok esetben körülményes és fárasztó lehet, különösen ha külön-külön egyesével akarnánk megadni az egyes tömbelemeket. Szerencsére a Fortran 90-től kezdődően lehetőség van a nyelvben tömböket egyszerűbben is inicializálni. Erre az egyik lehetőség a tömb konstruktor. A tömb konstruktor egy, a megfelelő típusú, elemkből álló lista és a (/ és /) zárójelpárokból áll ( Megjegyzés: Fortran 2003-tól kezdve a (/ és /) zárójelek helyett használható a [ és ] zárójelpár is ). Például:

INTEGER, DIMENSION(5) :: ARR = (/ 1, 2, 3, 4, 5 /)

Amennyiben a tömb elemei között valamilyen szabályosság van, úgy használhatjuk implicit DO ciklust is:

INTEGER, DIMENSION(5) :: ARR = (/ (I,I=2,8,2), 0 /)

A tömb konstruktorban az elemek száma pontosan meg kell hogy egyezzen a tömb elemeinek a számával. Több dimenziós tömbök esetén is csupán egy dimenziós tömbként adhatjuk meg az elemeket, majd ezeket konvertálhatjuk a RESHAPE() és SHAPE() intrinsic-ek segítéségével:

INTEGER, DIMENSION(3, 3) :: array array = reshape((/ 1, 2, 3, 4, 5, 6, 7, 8, 9 /), shape(array))

Ilyenkor azonban ügyelnünk kell arra, hogy a Fortran a tárolást oszlopfolytonosan végzi, így a 3x3-as mátrixunk az alábbi módon fog kinézni:

1 4 7 2 5 8 3 6 9

Ha a megszokott módon soronként szeretnénk az elemeket megadni, célszerű még a TRANSPOSE() intrinsic-et is használni.

Tömb szeletek

Fortran 90-től kezdődően lehetőség van a tömbökből csupán szeleteket kiválasztani. Megadása úgy történik hogy kettősponttal elválasztjuk az alsó és felső indexhatárt ami a szeletet kijelöli. Így a arr(7:9) egy 3 elemű szelete az arr tömbnek. Lehetséges továbbá hogy átfedő tömb szeletek közötti értékadás is, ekkor a fordító vigyáz arra, hogy olyan kódot generáljon, hogy ne írjuk felül akaratlanul a tömbelemeket:

a(2:10) = a(1:9) ! shift up one element b(1:9) = b(3:11) ! shift down two elements

Tömbszeletek megadásakor ritka résztömböket is megadhatunk, a tömb hármas jelöléssel. A lépésköz (a harmadik elem a hármasban) ekkor nyilván nem lehet nulla:

b(1:10:2) ! selects five elements: 1, 3, 5, 7, 9 b(90:80:-3) ! selects four elements 90, 87, 84, 81 in that order

A tömb szeletek mellett használhatunk vektor indexeket is használhatunk:

INTEGER :: myidx(4) REAL :: vector(100) mysub = (/ 32, 16, 17, 18 /) WRITE(*,*) vector(mysub)