A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
de 10 siffrorna
0 1 2 3 4 5 6 7 8 9
samt de 10 specialtecknen
= + - * / ( ) , . ¤
där den sista är en myntsymbol, som kan variera från land till land, till exempel
£ i Storbritannien, $ i USA eller ¥ i Japan. Övriga specialtecken har en naturlig användning i Fortran.
Dessutom ingår naturligtvis blank i teckenuppsättningen. Det låga antalet tecken
(totalt 47) beror på att dåtidens utrustning (hålkortsstans) hade en mycket begränsad
teckenrepertoar.
Notera att de små bokstäverna inte fanns med. De kom "nästan" i Fortran 90,
där standarden föreskriver att om implementeringen tillåter små bokstäver
så skall dessa vara ekvivalenta med motsvarande stora bokstäver, utom i text-sammanhang.
De flesta Fortran 77 implementationer har samma tolkning av små bokstäver.
I Fortran 77 tillkom följande båda specialtecken
' :
I Fortran 90 tillkom ytterligare nio specialtecken
_ ! " % & ; < > ?
De båda symbolerna $ och ? har ingen specificerad användning i Fortran 90, utan är
tänkta främst för utmatning. Symbolen $ har dock haft en viss specificerad betydelse
i vissa utvidgningar av Fortran 77, den har dessutom en viktig betydelse i
UNIX. Mer om detta senare.
Naturligtvis kommer jag även att förklara de övriga nytillkomna specialtecknen
efter hand.
De nationella bokstäverna (av typ å ä ö é è ë ü ñ) kan oftast användas i text-sammanhang, om stöd för dem finns i implementeringen. De kan nästan aldrig användas i variabelnamn, och bör aldrig användas i variabelnamn om något system skulle råka tillåta det!
A ADAM H2SO4 ILL HEJSAN H2J3A4 O
J JAMEN SLASK TMP ULI LIU MAI
I Fortran 90 har största tillåtna längden på ett variabelnamn ökats
från 6 till 31, och understrykningstecknet _ får ingå inuti ett variabelnamn.
Tillåtna variabelnamn, förutom de ovanstående, är
T_1 AVSTAAND_TILL_MAANEN DISTANCE_TO_THE_SUN_
HEJSANSVEJSAN O123456789ABCDEFHIJKLMNOPQRSTUV
De nya reglerna innebär dock inte att man bör välja krångliga namn. Avsikten
med understrykningstecknet är att användas när det är lämpligt med
namn bestående av mer än ett ord. Blanka är ju inte tillåtna inuti namn.
Man bör vara försiktig med tecken som kan misstolkas, ofta blir det fel mellan bokstaven
O och siffran 0, dessa är väldigt olika i vissa typsnitt, men ganska lika i typsnittet
Courier, nämligen O respektive 0. Tyvärr finns det ingen bestämd regel mellan
olika typsnitt om att just bokstaven skall vara "bredare" än siffran.
A2 GUSTAVUS ADOLFUS GUSTAV_ADOLF
2C 2_CESAR ÅKE $KE
C-B DOLLAR$ OOOOOO DO
K**2 HEJ_DU_GLADE _STOCKHOLM_ GOETEBORG
EIIR Bettan ABCDEFGHIJKLMNOPQRSTUVWXYZ
REAL :: A, B, C
INTEGER :: I, J, K, L, M, N
LOGICAL :: BO
CHARACTER (LEN=10) :: TEXT1
CHARACTER (10) :: TEXT2
CHARACTER*10 :: TEXT3
Dessa deklarationer talar om att variablerna A, B och C är flyttal, variablerna I, J, K,
L, M och N är heltal, variabeln BO är logisk (boolsk), och variablerna TEXT1, TEXT2
och TEXT3 är textsträngar som rymmer 10 tecken.
De båda första deklarationerna är strängt taget onödiga, eftersom
Fortran har den regeln att odeklarerade variabler som börjar på någon av bokstäverna
I, J, K, L, M eller N automatiskt blir heltal, och de som börjar på någon annan
bokstav blir flyttal. Denna regel har gett upphov till mycket elände, eftersom kompilatorn
då inte upptäcker felstavade variabler, liksom att felaktig användning av Fortran-kommandon
i stället kan ge upphov till nya variabler. Det har därför införts ett
helt nytt kommando i Fortran 90, nämligen IMPLICIT NONE. Detta kan placeras först i
varje programenhet, och slår av regeln om initialbokstäverna. Jag kommer att försöka
att i fortsättningen genomgående använda IMPLICIT NONE i denna bok, det är
ett mycket bra verktyg för att få korrekta program.
I Fortran 77 användes inte beteckningen med dubbelkolon ::, att den behövs i Fortran 90 beror på att man infört attribut som tilläggsspecifikationer vid deklarationer.
IMPLICIT NONE
INTEGER :: I
REAL :: AREA, R
LOGICAL :: KLAR, OKLAR
R = 2.0
AREA = 3.141592654*R**2
I = 0
I = I + 1
KLAR = .FALSE.
OKLAR = .TRUE.
END
I detta enkla program sättes cirkelns radie R till 2 enheter och dess yta AREA beräknas
med den välkända formeln a = pi·r2. Därefter nollställes heltalet I, varefter
det stegas upp med 1. De båda sista tilldelningssatserna sätter de båda logiska
variablerna KLAR och OKLAR till värdena falskt respektive sant, vilka värden skrives
på detta underliga sätt, där punkterna på båda sidor ingår
i konstanterna. Det bör noteras att variabler i Fortran ej är automatiskt nollställda
från början.
I Appendix 5, avsnitt 2, finns något som kallas numeriska funktioner, vilka innefattar sådant som absolutbelopp, realdel, heltalsdel och tecken. Där finns även funktioner för omvandling mellan olika precisioner, vilket vi återkommer till. De numeriska funktionerna är således av en mer maskinnära natur än de matematiska.
I Fortran 90 tillkom ett stort antal nya inbyggda funktioner, och även ett par inbyggda subrutiner, varför kommittén fann det lämpligt att gruppera dem som i Appendix 5. Bara funktioner i avsnitten 2, 3, 4 och 5 fanns med i Fortran 77 (och ej ens alla dom).
IMPLICIT NONE
INTEGER :: I, J, K
REAL :: AREA, R, X, Y
AREA = 1.0
R = SQRT(AREA/3.141592654)
I = INT(-3.141592654)
J = FLOOR(-3.141592654)
K = CEILING(-3.141592654)
X = REAL(I*J*K)
Y = SIN(LOG10(ABS(X)))
Y = ACOS(Y)
END
I ovanstående lilla program beräknas först radien på
en cirkel med ytan
en enhet, varefter tre olika heltalsdelar av -pi beräknas, först den
vanliga med INT
som avrundar (trunkerar) mot noll och gav -3, sedan golvfunktionen FLOOR
som avrundar nedåt
mot - oändligheten och gav -4, och slutligen takfunktionen CEILING
som avrundar uppåt mot + oändligheten och gav -3.
Därefter beräknas X som flyttalet svarande mot produkten av dessa tre heltal (-36) och man bildar Y som sin(10log |x|), varefter Y blir arcus cosinus av sig själv.
För arcustangenten finns två funktioner, dels den vanliga ATAN(X) som svarar mot arctan x, dels den ovanliga ATAN2(Y,X). Den senare har den uppgiften att den skall kunna klara av även de punkter där tangenten blir oändlig, nämligen udda multipler av pi/2. Resultatet av ATAN2 kan tolkas som principalvärdet för argumentet till det från noll skilda komplexa talet x + iy. Det är funktionen arctan (y/x) och ligger i intervallet -pi < ATAN2(Y,X) <= pi. Om y är positivt blir resultatet positivt, om y är noll blir resultatet noll om x > 0 och pi om x < 0. Om y är negativt blir resultatet negativt. Om x är noll blir resultatet pi/2 eller -pi/2, beroende på tecknet på y. Om både x och y är noll blir resultatet odefinierat.
Samtliga trigonometriska funktioner arbetar med radianer.
Ett kommando med en liknande uppgift som en kommentar är den programsats som kan inleda ett program (egentligen ett huvudprogram). Den har utseendet
PROGRAM namn
och ger namnet namn på programmet. Detta programnamn utnyttjas av vissa
operativsystem, om
någon programsats ej är med får oftast programmet automatiskt
namnet MAIN eller
main. Programsatsen är frivillig, men jag rekommenderar den.
Inmatning sker med READ och utmatning med WRITE. Dessa operationer måste knytas till vissa fysiska enheter, det är lämpligt att använda * för dessa. Man kan även använda de historiska numren 5 för in-enheten och 6 för ut-enheten. Ett exempel följer.
PROGRAM INUT_RI
IMPLICIT NONE
REAL :: A
INTEGER :: I
WRITE(*,*) ' Ge värdena på flyttalet A och heltalet I'
READ(*,*) A, I
WRITE(*,*) A, I
WRITE(*,*) ' A = ', A, ' I = ', I
END
Den första stjärnan i parentesen ovan talar om att standardenheten skall användas,
den andra att list-styrd in- respektive utmatning skall användas. List-styrd innebär
att data tolkas i enlighet med vilka tal som skall matas in eller ut, matar man ut ett flyttal
skrivs det också ut som ett flyttal. Den första skrivsatsen ovan skriver ut texten
inom apostroferna, dvs en uppmaning att ge värdet av ett flyttal A och av ett heltal I. Man
ger värdena med mellanslag, komma eller vagnretur mellan värdena, och avslutar med
en vagnretur. Observera att om heltalet är stort, till exempel 7 283 550 så får
det inte skrivas så, man får inte ha några blanka inuti talen. Man kan ge flyttal
som heltal, heltal med decimaler, eller heltal med decimaler och exponent. Man kan således
ge det som 13 eller -5 eller 157.67 eller 2.38E7
eller 4.E-8
eller .2E5 (de båda sista varianterna
är faktiskt tillåtna och tolkas som 4.0E-8
respektive 0.2E5). Däremot är
det inte tillåtet att utelämna siffrorna både före
och efter decimalpunkten!
Den följande skrivsatsen skriver ut värdena, den därpå följande talar även om vilka värden som skrivits ut.
Vi övergår nu till de båda datatyperna logisk och textsträng.
PROGRAM INUT_LC
IMPLICIT NONE
LOGICAL :: B
CHARACTER(LEN=8) :: T
WRITE(*,*) ' Ge den logiska variabeln B '
READ(*,*) B
WRITE(*,*) ' Den logiska variabeln B är ', B
WRITE(*,*)
WRITE(*,*) ' Ge textsträngsvariabeln T (högst 8 tecken)'
WRITE(*,*) ' Observera att textsträngen måste ges inom'
WRITE(*,*) ' apostrofer '' eller citattecken "'
READ(*,*) T
WRITE(*,*) ' Textsträngen T är ', T
WRITE(*,*)
WRITE(*,*) ' Ge textsträngsvariabeln T (högst 8 tecken)'
WRITE(*,*) ' Observera att textsträngen nu skall ges'
WRITE(*,*) ' utan extra tecken'
READ(*,'(A8)') T
WRITE(*,*) ' Textsträngen T är ', T
END
När man skall mata in att en logisk variabel skall vara sann kan man välja en av representationerna
T eller .T. eller .TRUE.
När man skall mata in att en logisk variabel skall vara falsk kan man välja en av representationerna
F eller .F. eller .FALSE.
List-styrd utmatning av en logisk variabel sker med ett ensamt T eller F.
Vid list-styrd inmatning av en textsträng måste den omges med apostrofer ' eller citattecken ". Man kan valfritt välja om man vill ange en textsträng inom apostrofer eller citattecken, om texten skall innehålla ett av dessa tecken är det praktiskt att omge texten med det andra tecknet, i annat fall måste det tecken som skall skrivas ut dubbelskrivas, dvs '' respektive "".
Det är oftast opraktiskt att behöva omge textsträngar med apostrofer, man måste ju då svara 'JA' i stället för JA på en JA/NEJ fråga. Man kommer ifrån det kravet genom att i stället för list-styrd inmatning använda format-styrd inmatning. Detta sker i den sista delen av ovanstående exempel, där den andra stjärnan utbytts mot '(A8)', vilket talar om för systemet att åtta tecken förväntas. Jag kommer i kapitel 7 utbreda mig mycket om format, allt för mycket enligt många, men det är ett mycket kraftfullt hjälpmedel, främst för att erhålla en prydlig utmatning.
DIMENSION A(20)
I stället för ordet DIMENSION kan man använda
någon av deklarationerna
CHARACTER, INTEGER, LOGICAL, REAL.
Fältet får då angiven typ. I annat fall användes den implicita typen. Man kan även typdeklarera variabeln på vanligt sätt. Även de senare behandlade datatyperna COMPLEX och DOUBLE PRECISION kan naturligtvis bilda vektorer.
Från och med Fortran 77 behöver fältet ej längre börja i position 1, man anger då exempelvis
DIMENSION C(-7:5)
för vektorn C(-7), C(-6), ..., C(-1), C(0), C(1), ..., C(5).
Många gamla program utnyttjar restriktionen att index måste börja på ett, varför det matematiska indexet ofta har kanat ett steg. Många gamla programmerare lever likaså kvar i den föreställningen att index börjar på ett.
I deklarationen av dimensionen kan normalt endast konstanter användas. I funktioner och subrutiner kan i vissa sammanhang vanliga heltalsvariabler, eller en asterisk, användas. Även till detta återkommer jag i nästa kapitel.
Deklarationen är ganska ändrad i Fortran 90 jämfört med tidigare (men de gamla möjligheterna finns kvar). I stället för att ge dimensioneringen som ett kommando ger man den nu som ett attribut. Det fanns tidigare följande möjligheter att deklarera en flyttalsvektor med 20 element:
DIMENSION A(20) ! Metod 1, med implicit
! typdeklaration
REAL A(20) ! Metod 2 (vanligast)
REAL A ! Metod 3, med explicit
DIMENSION A(20) ! typdeklation
Nu tillkommer den nya möjligheten, som är praktisk i det att alla egenskaper för
en viss variabel samlats i en enda rad.
REAL, DIMENSION(20) :: A ! Metod 4
Elementen i en vektor lagras i en entydigt bestämd ordning,
om vektorn är deklarerad
(j:k) finns elementet (s) i position 1+(s-j).
Här användes således det första
elementet som referensposition.
DIMENSION A(-1:8), B(10:25)
A(2) = B(21)
Här identifierar A(2) det fjärde elementet i vektorn A,
och sättes till värdet
av B(21), det tolfte elementet i vektorn B.
Vid användning av vektorer kan som index användas heltalskonstanter, heltalsvariabler, eller allmänna heltalsuttryck.
En del Fortran system innehåller möjlighet att slå på indexkontroll, varvid konstiga tilldelningar av ovanstående natur kan konstateras. Om tilldelningen sker med konstanter kan "felet" hittas redan vid kompileringen, om variabler användes som index kan "felet" normalt konstateras först vid exekveringen. Då måste oftast en avlusare (eng. debugger) användas.
DO 100 index = n1, n2, n3
! satser
100 CONTINUE
Siffrorna, i detta exempel 100, får väljas godtyckligt
från 1 till 99 999, men det får inte finnas mer än en
sats CONTINUE med aktuellt nummer. Numret i DO-sats och före
CONTINUE måste naturligtvis vara samma. Det är
(tyvärr) tillåtet för flera DO-slingor att sluta
på samma CONTINUE.
Det är inte absolut nödvändigt att avsluta en gammaldags DO-slinga med en CONTINUE sats. Det går även med en vanlig sats, till exempel en vanlig tilldelningssats, som då har getts motsvarande nummer till vänster. En rekommendation i Fortran 90 och ett troligt krav nästa årtusende är att inte avsluta en gammaldags DO-slinga på annat sätt än med CONTINUE. Den nya och rekommenderade varianten är
DO index = n1, n2, n3
! satser
END DO
I båda fallen ovan gäller att storheterna n1, n2
och n3 ej
får ändras under slingans utförande. De skall
alltså betraktas som konstanter under exekveringen av slingan,
men det är tillåtet att de är variabler eller
variabeluttryck. Det viktiga är att delresultat under slingans
gång, inklusive aktuellt värde av index, ej får
påverka dem.
Båda dessa slingor startar med att sätta index till n1 och utföra satserna som följer. När exekveringen når CONTINUE (med rätt nummer) eller END DO hoppar exekveringen upp till DO-satsen igen och räknar upp index med n3. Om resultatet blir större än n2 avbrytes slingan med hopp till satsen efter CONTINUE eller END DO. Annars exekveras satserna ytterligare en gång. Det ovanstående förutsätter att n1 är högst lika med n2 och att n3 är positivt. Om n1 är större än n2 sker hopp direkt till satsen efter CONTINUE eller END DO.
Om n3 är negativ så gäller att ingen exekvering av satserna sker om n1 är mindre än n2. Annars blir det i princip samma sak som om n3 är positiv, det blir nu en nedräkning av n1 till n2.
Tidigare (till och med Fortran 66) gällde att värdet på indexet efter normalt genomlöpt slinga var odefinierat, nu (från och med Fortran 77) är det i stället "nästa värde", dvs det första underkända. Många program förutsätter tyvärr felaktigt att det är det sista godkända värdet som är tillgängligt utanför slingan.
DO
! satser
END DO
Den fjärde möjligheten är att ha en DO WHILE slinga.
Man får då en
slinga som utföres så länge som ett visst logiskt
villkor är uppfyllt.
Till skillnad från parametrarna i den vanliga varianten av
DO-slingan kan (och bör)
storheter i detta villkor ändras under slingans exekvering.
DO WHILE (logiskt villkor)
! satser
END DO
I Fortran 90 tillkom två helt nya kommandon att användas i
DO-slingor, nämligen CYCLE och EXIT.
Dessa kan läggas in
bland de vanliga satserna inne i en DO-slinga. Om CYCLE
påträffas fortsätter exekveringen direkt med
nästa värde på indexet respektive direkt från
början. Om EXIT påträffas fortsätter exekveringen
direkt med nästa sats efter DO-slingan.
En DO-slinga kan tilldelas namn, vilket sker genom att före DO ge ett namn följt av ett kolon. Dessutom bör avslutande END DO följas av namnet. Kommandona CYCLE och EXIT kan utnyttja namnet för att upprepa eller avsluta specificerad slinga. Om inget namn ges i EXIT eller CYCLE avses automatiskt den innersta slinga man just befinner sig i. Dessa kommandon ersätter GO TO till den sats som avslutade den gammaldags DO-slingan (vilken oftast är en CONTINUE-sats).
En ny sats FORALL infördes i Fortran 95 för att till skillnad från en DO-slinga kunna utföras i godtycklig ordning (och därmed parallellt om den fysiska möjligheten finns). Den hämtades från tillägget HPF = Hög-Prestanda Fortran, vilket tillägg aldrig blivit någon succe. Ett par enkla exempel på FORALL-satser följer
FORALL (I = 1:N, J = 1:N) H(I,J) = 1.0/REAL(I+J-1)
FORALL (I = 1:N, J = 1:N, Y(I,J) .NE. 0.0) &
X(I,J) = 1.0/Y(I,J)
FORALL (I = 1:N) A(I,I+1:N) = 4.0*ATAN(1.0)
Den första av dessa definierar Hilbertmatrisen av ordning N, den andra inverterar elementen
i en matris med undvikande av eventuella nollor. I den tredje tilldelas alla element över
huvuddiagonalen i matrisen A ett approximativt värde på pi.
I dessa satser kan FORALL sägas vara en dubbelslinga som kan utföras i godtycklig ordning. Det allmänna utseendet är
FORALL ( v1 = l1:u1:s1, ... , vn = ln:un:sn, mask ) &
a(e1, ... , em) = right_hand_side
och beräknas enligt väl definierade regler, i princip beräknas alla index först.
Dessutom finns en FORALL-konstruktion. Denna skall tolkas som om de ingående satserna i stället vore skrivna som FORALL-satser i samma ordning. Denna restriktion är väsentlig för att få ett entydigt beräkningsresultat. I en FORALL-konstruktion är anrop av funktioner och subrutiner tillåtna om dessa är rena (eng. pure). Med direktivet PURE kan man ange att så är fallet. Två enkla exempel på en FORALL-konstruktion:
REAL, DIMENSION(N,N) :: A, B
...
FORALL (I = 2:N-1, J = 2:N-1)
A(I,J) = 0.25*(A(I,J-1)+A(I,J+1)+A(I-1,J)+A(I+1,J))
B(I,J) = A(I,J)
END FORALL
Efter dessa satser har A och B exakt samma värden i alla inre punkter, medan B har kvar sina
eventuella gamla värden på ränderna.
REAL, DIMENSION(N,N) :: H, B
...
FORALL (I = 1:N, J = 1:N)
H(I,J) = 1.0/REAL(I+J-1)
B(I,J) = H(I,J)**2
END FORALL
Först tilldelas alla elementen i Hilbertmatrisen H sina värden, därefter tilldelas
matrisen B dessa värden i kvadrat.
Även IF-konstruktioner och CASE-konstruktioner kan tilldelas namn i Fortran 90, liksom FORALL-konstruktionen i Fortran 95.
Anm. Det har tyvärr visat sig svårt att implementera FORALL på så sätt att den blir effektivare än en vanlig DO-sats.
Den första av IF-satserna hör intimt samman med den satsnumrering som var nödvändig i vissa sammanhang upp till och med Fortran 77. Den går ut på att de satser man vill "hoppa" till ges ett nummer till vänster om den egentliga satsen, detta nummer skall vara mellan 1 och 99 999 (extremvärdena är tillåtna). Den första varianten av IF-satserna kallas aritmetisk, eftersom den tittar på tecknet hos den betraktade numeriska storheten (heltal eller flyttal). Om denna är negativ sker hopp till det första satsnumret, om den är noll till det andra, och om den är positiv till det tredje satsnumret.
IF (X) 10, 20, 30
10 ! Satser som utföres om X är negativt
GO TO 100
20 ! Satser som utföres om X är noll
GO TO 100
30 ! Satser som utföres om X är positivt
100 CONTINUE
! Satser som utföres i samtliga fall
I ovanstående exempel har jag även introducerat den
ovillkorliga hoppsatsen GO TO, vilken gör det möjligt att
helt särskilja exekveringen i de tre fallen. Om X är noll
sker således först ett hopp till sats 20, där de
satser som hör till detta fall utföres, varefter ett hopp
sker till sats 100, där satser för samtliga fall kan
följa.
IF (logiskt uttryck) sats
Om det logiska uttrycket är sant så utföres satsen,
annars exekveras nästa sats direkt. Som sats i en logisk IF-sats
användes exempelvis en vanlig tilldelningssats, en ovillkorlig
hoppsats eller ett subrutinanrop. Däremot är följande
förbjudna i detta sammanhang: DO-slinga, IF-sats eller
IF-konstruktion eller CASE-konstruktion.
IF (logiskt uttryck) THEN
! Satser som utföres om det logiska
! uttrycket är sant
ELSE
! Satser som utföres om det logiska
! uttrycket är falskt
END IF
Här kan ELSE och efterföljande satser utelämnas,
så att en förenklad
variant erhålles.
IF (logiskt uttryck) THEN
! Satser som utföres om det logiska
! uttrycket är sant
END IF
Alternativt kan ELSE utbytas mot ELSE IF, så
att en utvidgad variant erhålles.
IF (logiskt uttryck) THEN
! Satser som utföres om det logiska
! uttrycket är sant
ELSE IF (nytt logiskt uttryck) THEN
! Satser som utföres om det första logiska
! uttrycket är falskt och det andra är sant
ELSE
! Satser som utföres om båda de logiska
! uttrycken är falska
END IF
Notera strukturen hos den allmänna
IF-konstruktionen, på första raden finns efter det
logiska uttrycket enbart THEN. Den logiska IF-satsen
ser likadan ut men istället för THEN finns där
en exekverbar sats. De nya raderna efter THEN och både
före och efter ELSE är väsentliga för att
konstruktionen skall kännas igen.
En IF-sats kan namnsättas. Då bör även motsvarande END IF följas av namnet.
SELECT CASE (IVAR)
CASE (:-1) ! Alla negativa heltal)
WRITE (*,*) ' Negativt tal'
CASE (0) ! Nollfallet
WRITE (*,*) ' Noll'
CASE (1:9) ! Ental
WRITE (*,*) ' Siffran ', IVAR
CASE (10:99) ! Tvåsiffrigt tal
WRITE (*,*) ' Talet ',IVAR
CASE DEFAULT ! Alla resterande fall
WRITE (*,*) ' För stort tal'
END SELECT
Det är inte tillåtet med överlappande argument i den
meningen att ett enda argument satisfierar mer än ett fall av
CASE. Fallet DEFAULT behöver inte finnas med, om inget giltigt
alternativ finns så fortsätter exekveringen med första
sats efter END SELECT. Jag rekommenderar dock att alltid ha med ett
DEFAULT, som då bör ge en felutskrift om argumentet har ett
otillåtet värde.
Dessutom finns två föråldrade villkorliga hoppsatser. Dessa presenteras i avsnitt 16.7 samt i Appendix 4, dvs. styrd hoppsats och tilldelad hoppsats.
REAL :: A
INTEGER :: I, J, K
DATA A /17.0/, I, J, K /1, 2, 3 /
Dessa satser tilldelar flyttalet A begynnelsevärdet 17.0 och
heltalen I, J och K värdena
1, 2 respektive 3. Man kan även initiera dessa variabler ekvivalent
utnyttjande de nya egenskaperna
i Fortran 90, utan explicit DATA.
REAL :: A = 17.0
INTEGER :: I = 1, J = 2, K = 3
För vektorer kan man likaså använda både en gammal
variant liksom en ny.
REAL VEK1(10)
DATA VEK1 /10*0.0/
REAL, DIMENSION(1:10) :: VEK2 = (/ ( 0.0, I = 1, 10 ) /)
Likaså om det är fråga om olika värden som skall
tilldelas kan både
det gamla och det nya systemet användas.
REAL, DIMENSION(3) :: B
DATA B /12.0, 13.0, 14.0/
REAL, DIMENSION(3) :: B = (/ 12.0, 13.0, 14.0 /)
I ovanstående konstruktion skall teckenkombinationerna (/ och /)
tänkas som parenteser. Avsikten var från början att
använda de raka parenteserna [ och ], men jag påpekade att
dessa användes för Ä och Å i svensk 7-bits kod,
varför kommittén ändrade till dessa tråkiga
kombinationssymboler.
REAL, PARAMETER :: PI = 3.141592653589793
REAL, PARAMETER :: PIHALF = PI/2.0
REAL, PARAMETER :: SQRT_E = 1.648721270700128
Den gamla varianten skrevs
REAL PI, PIHALF, SQRT_E
PARAMETER (PI = 3.141592653589793)
PARAMETER (PIHALF = PI/2.0)
PARAMETER (SQRT_E = 1.648721270700128)
I båda fallen kan således en tidigare parameter utnyttjas
vid definitionen av en senare.
Som första exempel tittar vi på en funktion f(x) som beräknar exp(-x)·cos x. Vi skriver då en programenhet av typ FUNCTION och med namnet F.
REAL FUNCTION F(X)
IMPLICIT NONE
REAL :: X
REAL, INTRINSIC :: EXP, COS
F = EXP(-X) * COS(X)
RETURN
END FUNCTION F
I ovanstående funktion är faktiskt det mesta frivilligt. I
den första raden behöver ordet REAL ej vara med på
grund av reglerna om implicit typ-deklaration, jag rekommenderar att
ordet är med. Den andra raden är likaså frivillig men
jag rekommenderar den varmt. Notera att den måste upprepas i
varje programenhet där den skall ha verkan. Den tredje raden
blir nödvändig under dessa förutsättningar. Den
fjärde raden med deklaration av de inbyggda funktionerna är
onödig och jag rekommenderar faktiskt att den inte får vara
med. Den är bara av värde i ett mer komplicerat fall. Den
femte raden är den egentliga funktionsraden, som tilldelar
funktionsvärdet. Notera att det här kallas F, dvs
funktionsnamnet utan argument. Den sjätte raden talar om för
systemet att exekveringen av programmet är slut och att
exekveringen skall återgå till anropande programenhet
(ofta huvudprogrammet). Den sista raden talar om för kompilatorn
att funktionen är slut.
Att ha med orden FUNCTION F ger kompilatorn en möjlighet att klaga om man har strukturerat sitt program fel. Kompilatorn kontrollerar nämligen då att det är en funktion, och att namnet på denna stämmer. Om raden RETURN saknas så gäller att Fortran 77 och Fortran 90 fungerar så att END återsänder exekveringen till den anropande programenheten. Under Fortran 66 och tidigare fick man kompileringsfel. Jag upprepar att RETURN avslutar exekveringen och END avslutar kompileringen. Satsen END måste därför ligga sist, men satsen RETURN behöver inte alls ligga näst sist.
Nu gäller det att skriva ett huvudprogram för att utnyttja denna funktion.
PROGRAM TVA
IMPLICIT NONE
REAL :: X, Y
REAL, EXTERNAL :: F
1 READ(*,*) X
Y = F(X)
WRITE(*,*) X, Y
GO TO 1
END PROGRAM TVA
Om man vill köra detta exempel på en UNIX-maskin kan man antingen
ha båda programmen
i samma fil prog.f90, eller huvudprogrammet i filen tva.f90
och funktionen i filen f.f90.
Man kompilerar då med
f90 prog.f90
respektive
f90 tva.f90 f.f90
och kör med
a.out
Programmet är skrivet så att det ständigt ber om mer indata.
Man kan avbryta
med Kontroll-z eller Kontroll-d eller ge ett sådant värde på
X att programmet
bryter på grund av spill i beräkningen av exp(-x ).
På Sun gick det med -89. Ett "riktigt"
program skall naturligtvis innehålla en mer användarvänlig
utgång.
Nu följer ett mer meningsfullt exempel, baserad på ett exempel vid en kurs hos IBM 1967. Exemplet går ut på att skriva ett program för att beräkna kubikroten ur ett flyttal. Som bekant kan vi bara beräkna kvadratrötter ur icke-negativa flyttal (om vi håller oss till reella tal), men kubikroten blir en rent udda funktion, ty (-2)3 = -8.
Beräkningen sker nedan med hjälp av omskrivning med exp(x) och ln x. Man kan naturligtvis även använda upphöjt till med **, sedan man ordnat att basen (den som skall upphöjas) är positiv.
Vid kompilering av nedanstående program kommer åtminstone NAG:s kompilator att klaga på att den föråldrade aritmetiska IF-satsen användes. Jag tycker dock den passar utmärkt här. Man måste nämligen undvika att beräkna ln(0).
PROGRAM KUBIKROT
IMPLICIT NONE
REAL :: X, Y
REAL, EXTERNAL :: KUBROT
1 READ(*,*) X
Y = KUBROT(X)
WRITE(*,*) X, Y
GOTO 1
END PROGRAM KUBIKROT
REAL FUNCTION KUBROT(X)
IMPLICIT NONE
REAL :: X
LOGICAL :: SKIFTE
SKIFTE = .FALSE.
IF (X) 2, 1, 3
1 KUBROT = 0.0
RETURN
2 SKIFTE = .TRUE.
! Alternativ 1
X = ABS(X)
3 KUBROT = EXP(LOG(X)/3.0)
! Alternativ 2
!3 KUBROT = EXP(LOG(ABS(X))/3.0)
IF (SKIFTE) KUBROT = -KUBROT
RETURN
END FUNCTION KUBROT
Beträffande ovanstående program kan man ge två
viktiga kommentarer. Den första är att Alternativ 1 är
klart olämpligt, eftersom det ändrar inargumentet X, dvs X
ändras i det anropande programmet, huvudprogrammet. Man bör
därför byta till Alternativ 2 (genom att ta bort
utropstecknet framför den 3-an samt i stället sätta in
utropstecken framför de båda satserna i Alternativ 1).
Den andra kommentaren avser att det kan vara frestande att ändra
LOGICAL :: SKIFTE
SKIFTE = .FALSE.
till
LOGICAL :: SKIFTE = .FALSE.
men då använder man en implicit DATA sats, vilket
ger att SKIFTE blir initierat till falskt när programmet startas,
men inte varje gång som subrutinen anropas, vilket faktiskt
krävs.
Jag har ovan diskuterat den vanliga externa Fortran-funktionen samt något om de inbyggda funktionerna. Dessa båda bör deklareras med EXTERNAL respektive INTRINSIC då de användes som argument vid funktions- eller subrutinanrop.
Dessutom finns två typer av funktioner som ej kan användas som argument, nämligen satsfunktioner och interna funktioner. Till dessa lokala funktioner återkommer jag i avsnitten 11.3 respektive 11.4.
Man bör undvika att låta en funktion ha någon annan verkan än att returnera ett funktionsvärde, en funktion bör således inte ha som sidoeffekt att vissa av argumenten ändras.
Jag ger här ett enkelt exempel på en subrutin, nämligen för lösning av en andragradsekvation med koefficienten 1 för andragradstermen (vilket garanterar att det verkligen är en andra-gradare).
SUBROUTINE ANDRA( A, B, X1, X2, NANTAL)
! LÖS EKVATIONEN X^2 + A*X + B = 0
IMPLICIT NONE
REAL :: A, B, X1, X2
INTEGER :: NANTAL ! Antalet reella rötter
REAL :: DISK ! Diskriminanten
DISK = A*A - 4.0*B
IF (DISK > 0.0 ) THEN
DISK = SQRT(DISK)
X1 = 0.5*(-A + DISK)
X2 = 0.5*(-A - DISK)
NANTAL = 2
RETURN
ELSE IF (DISK == 0.0 ) THEN
X1 = -0.5*A
NANTAL = 1
RETURN
ELSE
NANTAL = 0
RETURN
END IF
END
Subrutinen har således koefficienterna i andragradsekvationen
som inparametrar och antalet reella rötter, samt deras eventuella
värden, som utparametrar. Även om man använder
principen att allt skall deklareras, genom att ge satsen IMPLICIT NONE
först i programenheten, så är det fortfarande mycket
vanligt i Fortran att använda konventionen om att allt som
börjar på I till N är heltal. Därför har
antalet rötter getts som NANTAL i stället för ANTAL.
För att använda denna subrutin behövs en anropande programenhet, jag ger den
här i form av ett huvudprogram.
PROGRAM HUVUD
! Huvudprogram för lösning av andragradsekvationen
! X^2 + B*X + C = 0
IMPLICIT NONE
REAL :: B, C, ROT1, ROT2
INTEGER :: N
WRITE(*,*) " Ge koefficienterna B och C"
READ(*,*) B, C
CALL ANDRA( B, C, ROT1, ROT2, N)
IF ( N == 2 ) THEN
WRITE(*,*) " De två rötterna är ", ROT1, ROT2
ELSE IF ( N == 1) THEN
WRITE(*,*) " Den enda roten är ", ROT1
ELSE
WRITE(*,*) " Inga reella rötter "
END IF
STOP
END
I detta program har jag valt att använda olika namn på de
verkliga parametrarna i huvudprogrammet, nämligen B, C, ROT1,
ROT2 och N, jämfört med de formella parametrarna i
subrutinen A, B, X1, X2 och NANTAL. Observera speciellt att den
verkliga parametern B svarar mot den formella parametern A och den
verkliga parametern C svarar mot den formella parametern B. Detta
är tillåtet, men naturligtvis förvirrande. Det
enklaste är om man har samma namn på de verkliga och
formella parametrarna, det näst enklaste om man har helt olika
namn.
Om de ovanstående programenheterna finns på filerna
andra.f90
och huvud_an.f90 kompileras,
länkas och körs programmet under
UNIX med kommandona
f90 huvud_andra.f90 andra.f90 -o program
program
Om man använder
MS-DOS
blir aktuella kommandon i stället
FTN90 HUVUD.F90
FTN90 ANDRA.F90
LINK77
$LOAD HUVUD
$LOAD ANDRA
$FILE PROGRAM.EXE
PROGRAM
Under MS-DOS genererar länkningsprogrammet LINK77 automatiskt
dollarsymbolen först på varje rad, vilket avslutas då
man ger kommandot FILE. Detaljerna ovan är från
NAG:s
system för Fortran under MS-DOS, och kan vara något
annorlunda vid andra leverantörer.
** (upphöjt till)
* och / (multiplikation och division)
+ och - (addition och subtraktion)
** (upphöjt till)
* och / (multiplikation och
division)
+ och - (addition och subtraktion)
.LT. .LE. .EQ. .NE. .GE. .GT. (jämförelse)
.NOT. (negation)
.AND. (skärning)
.OR. (union)
.EQV. .NEQV. (ekvivalens och
icke-ekvivalens)
(3.5) Upprepa föregående uppgift men använd nu
flyttal. Provkör och beräkna 10! och 30! Prova även att
beräkna 33!, 34! och 35!
Lösning.
(3.6) Skriv ett program för tabellering av
sinus-funktionen. Skriv gärna huvudprogrammet så att det
ber om det intervall för vilket funktionen önskas
beräknad. Mata ut en tabell med x och sin x för
x = 0.0,
0.1, ..., 1.0. Se till att utmatningen ser snygg ut! Provkör!
Lösning.
(3.7) Skriv ett program som utför en enkel kontroll av de trigonometriska funktionerna genom
att beräkna den trigonometriska ettan, och skillnaden mellan denna och en exakt etta. För
ett antal argument x skall således
max(sin2 x + cos2 x -1) och
min(sin2 x + cos2 x -1) beräknas.
Lösning.
(3.8) Skriv ett program i Fortran 90 för lösning av ett system av ordinära differentialekvationer med Runge-Kuttas klassiska metod. Givet är systemet
y'(t) = z(t)
z'(t) = 2*y(t)*(1+y(t)^2) + t*sin(z(t))
y(0) = 1
z(0) = 2
Beräkna en approximation till y(0.2)
genom att använda steglängden 0,04. Upprepa med
steglängderna 0,02 och 0,01. Låt respektive
derivata-funktioner finnas som externa (vanliga)
funktioner. Startvärden på t, y och z bör ges
interaktivt, liksom steglängden h och antalet steg N.