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 Zde 10 siffrorna
0 1 2 3 4 5 6 7 8 9samt 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 MAII 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 O123456789ABCDEFHIJKLMNOPQRSTUVDe 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 :: TEXT3Dessa 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. ENDI 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) ENDI 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 namnoch 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 ENDDen 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 ENDNä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) ! typdeklationNu 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 4Elementen 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 CONTINUESiffrorna, 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 DOI 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 DODen 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 DOI 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_sideoch 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 FORALLEfter 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 FORALLFö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 fallI 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) satsOm 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 IFHä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 IFAlternativt 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 IFNotera 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 SELECTDet ä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 = 3Fö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.648721270700128Den 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 FI 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 TVAOm 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.f90respektive
f90 tva.f90 f.f90och kör med
a.outProgrammet ä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 KUBROTBeträ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 ENDSubrutinen 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 ENDI 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 programOm 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 PROGRAMUnder 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) = 2Berä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.