5. Funktioner och subrutiner

5.0 Inledning

I detta kapitel skall vi gå igenom de regler som gäller för funktioner och subrutiner i Fortran 90. Vi skall härvid skilja på inbyggda, externa och lokala funktioner och subrutiner.

5.1 Inbyggda

De inbyggda funktionerna och subrutinerna kallas i Fortrans engelska språkbruk för "intrinsic", vilket är ett starkare uttryck än det ganska intetsägande "built in". Min engelsk-svenska ordbok översätter "intrinsic" med inre eller inneboende. Det utmärkande för de inbyggda funktionerna är att någon speciell åtgärd ej behöver vidtas för att länka in dem. De behöver ej heller deklareras, utom då de användes som argument vid anrop av andra funktioner eller subrutiner, varvid de skall deklareras att vara just INTRINSIC. För ett exempel på detta se avsnitt 5.2.1.1, Funktioner som argument.

5.1.1 Funktioner

Bland de inbyggda funktionerna finns främst de elementära matematiska funktionerna (typ sinus), samt några funktioner som kallas numeriska (typ absolutbelopp). En kort diskussion av dessa fanns redan i avsnitt 3.5, en fullständig presentation finns i Appendix 5. Dessutom finns där presentation av de många övriga inbyggda funktionerna.

De inbyggda funktionerna är generiska, dvs datatypen hos argumentet anger vilken rutin som skall väljas. Under Fortran 66 var man tvungen att skriva SIN(X) om X var REAL, DSIN(X) om X var DOUBLE PRECISION och slutligen CSIN(X) om X var COMPLEX. Från Fortran 77 fungerar SIN(X) i alla tre fallen. Om funktionsnamnet användes som argument vid funktions- eller subrutinanrop måste däremot det explicita namnet anges.

5.1.2 Subrutiner

De inbyggda subrutinerna kom först med Fortran 90 och är bara fem stycken, två för tidsangivelser, en för bitkopiering och två för pseudoslumptal. Dessa presenteras fullständigt i slutet av Appendix 5.

5.2 Externa

De externa funktionerna och subrutinerna är de man närmast tänker på när man talar om funktioner och subrutiner i Fortran. De bildar egna programenheter, som kan kompileras separat. Data kan överföras med argument eller utnyttjande ett COMMON block, eller utnyttjande externa filer. Den viktigaste metoden är att data överföres via argument.

5.2.1 Funktioner

Liksom i matematiken så är i Fortran en funktion något som har ett eller flera argument och som returnerar ett funktionsvärde. Från Fortran 90 kan funktioner vara rekursiva och/eller fältvärda. Det finns även vissa funktioner som saknar argument.

Vanliga enkla funktioner diskuterades redan i avsnitt 3.13. Vi skall därför här diskutera funktioner utan argument, fältvärda funktioner och rekursiva funktioner. Vidare skall vi ta upp "avsikten" hos olika argument, frivilliga argument och argument med nyckelord. Användning av funktioner som argument skall likaså diskuteras. Vi börjar med det senare.

5.2.1.1 Funktioner som argument

För att illustrera användningen av funktioner som argument vid anropet av en annan funktion väljer vi att titta på ett program för bestämning av nollstället till en funktion f(x) av en reell variabel, utnyttjande intervallhalveringsmetoden. För att förenkla programmet förutsätter vi att det betraktade intervallet är [a, b], och att a < b, f(a) > 0, f(b) < 0, samt att epsilon > 0.
        REAL FUNCTION NOLL(F, A, B, EPS)
        IMPLICIT NONE
! Beräknar nollställe till en funktion f(x) om
! a < b och f(a) > 0 och f(b) < 0 och eps > 0.
        REAL :: F, A, B, EPS
        REAL :: V, H, MITT
        V = A
        H = B
        DO
                IF ( H - V >= EPS ) THEN
                        MITT = 0.5*(V+H)
                        IF ( F(MITT) > 0.0 ) THEN
                                V = MITT
                        ELSE
                                H = MITT
                        END IF
                        CYCLE     ! Ny iteration behövs.
                END IF
                EXIT    ! Konvergens har erhållits.
        END DO
        NOLL = 0.5*(V+H)
        RETURN
        END FUNCTION NOLL
Funktionen ovan fungerar så att först testas om vänster- och högerpunkterna ligger tillräckligt nära varandra, i så fall utförs inte IF-satsen och den "eviga" DO-slingan avbrytes med EXIT. Ett slutligt nollställe beräknas som medelvärdet av vänster- och högerpunkterna. Om punkterna ej ligger tillräckligt nära beräknas en mittpunkt, som väljes som ny vänster- eller högerpunkt, och en ny iteration begäres med CYCLE. Funktionen täcker som synes inte alla fall, och svarar inte alltid så tidigt som möjligt. Anledningen är att vi sökt göra den så enkel som möjligt för att illustrera hur funktioner med funktioner som argument fungerar. Ett par utvidgningar föreslås i övningarna nedan.

Den funktion F(X) som användes som argument till funktionen NOLL måste också finnas, och då varken som intern funktion eller som satsfunktion, utan som en extern funktion, eller som en inbyggd funktion. Ett exempel på en extern funktion ges nedan, med namnet G.

        REAL FUNCTION G(X)
        IMPLICIT NONE
        REAL :: X 
        G = COS(X) - LOG(X)
        RETURN
        END FUNCTION G
För att använda dessa funktioner kräves även ett huvudprogram. Härvid är det väsentligt, på grund av Fortrans separatkompilering, att informera systemet om att funktionen G(X) är en extern funktion, vilket sker med satsen EXTERNAL G.
        PROGRAM NOLLSTAELLE
        IMPLICIT NONE
        REAL :: NOLL, A, B, G, EPS, Y
        EXTERNAL G
        A = 1.0
        B = 2.0
        EPS = 2.0E-7
        Y = NOLL(G, A, B, EPS)
        WRITE(*,*) Y
        STOP
        END PROGRAM NOLLSTAELLE
Om funktionen i stället är en inbyggd funktion användes satsen INTRINSIC funktion. I exemplet ovan passar det bra att byta ut satsen EXTERNAL G mot INTRINSIC COS och satsen Y = NOLL(G, A, B, EPS) mot Y = NOLL(COS, A, B, EPS), varvid programmet räknar ut att pi/2 är ett nollställe till cosinus. I detta fall behövs naturligtvis inte den externa funktionen G(X). Notera även att den inbyggda funktionen ej skall typdeklareras.

Övningar.

(5.1) Modifiera funktionen NOLL till att göra ett direkt uthopp med rätt nollställe som resultat om F(MITT) råkar bli exakt noll vid beräkningen.
Lösning.

(5.2) Modifiera funktionen NOLL till att klara även andra teckenkombinationer på f(a) och f(b).
Lösning.

(5.3) Modifiera funktionen NOLL till att använda en mer avancerad algoritm än intervallhalvering, till exempel regula falsi.
Kommentar.

5.2.1.2 Funktioner utan argument

En funktion kan faktiskt helt sakna argument. Skillnaden mot en subrutin är då främst att anropet sker med funktionsnamnet, och ej med CALL subrutinnamnet. Vanliga exempel på funktioner som fungerar bra utan argument är sådana som returnerar aktuell tidpunkt eller ett slumptal. De skrives med en tom argumentlista, men parentesen måste vara med både i funktionsdefinitionen och i funktionsanropet.

Som exempel har jag skrivet en enkel funktion utan argument. För att få ut något resultat ur den har jag valt att använda tidsrutinen i Fortran 90 (se Appendix 5). I heltalsvektorn VALUES lagras år, datum och tidpunkt, vilka jag summerar med fältadderaren SUM för att få något som påminner om ett slumptal. Anropet av tidsrutinen sker med nyckelordsargument, vilket förklaras senare.

        PROGRAM TEST_INGET_ARGUMENT
        IMPLICIT NONE
        REAL :: NOLL_ARG, Y
        Y = NOLL_ARG()
        WRITE(*,*) Y
        STOP
        END PROGRAM TEST_INGET_ARGUMENT

        REAL FUNCTION NOLL_ARG()
        IMPLICIT NONE
        INTEGER, DIMENSION(8) :: VALUES
        CALL DATE_AND_TIME(VALUES=VALUES)
        NOLL_ARG = SUM(VALUES)
        RETURN
        END FUNCTION NOLL_ARG

5.2.1.3 Rekursiva funktioner

En helt ny möjlighet i Fortran 90 är rekursion. Notera att det krävs att man i funktionsdeklarationen ger en helt ny egenskap RESULT (utvariabel). Denna resultat-variabel eller utvariabel användes inuti funktionen för att lagra funktionens värde. Vid själva anropet av funktionen, både externt och internt, användes däremot i stället det yttre eller "gamla" funktionsnamnet. Användaren kan därför vid anrop av den rekursiva funktionen strunta i att det finns en resultat-variabel. Här följer två exempel, dels rekursiv beräkning av fakulteten, dels rekursiv beräkning av Fibonacci-talen. Den senare är mycket ineffektiv.
        RECURSIVE FUNCTION FAKULTET(N) RESULT (FAK_RESULTAT)
        IMPLICIT NONE
        INTEGER, INTENT(IN)   :: N
        INTEGER                 :: FAK_RESULTAT
        IF ( N <= 1 ) THEN
                FAK_RESULTAT = 1
        ELSE
                FAK_RESULTAT = N * FAKULTET(N-1)
        END IF
        END FUNCTION FAKULTET

        RECURSIVE FUNCTION FIBONACCI(N) RESULT (FIBO_RESULTAT)
        IMPLICIT NONE
        INTEGER, INTENT(IN)   :: N
        INTEGER                 :: FIBO_RESULTAT
        IF ( N <= 2 ) THEN
                FIBO_RESULTAT = 1
        ELSE
                FIBO_RESULTAT = FIBONACCI(N-1) + FIBONACCI(N-2)
        END IF
        END FUNCTION FIBONACCI
Anledningen till att ovanstående beräkning av Fibonacci-talen blir så ineffektiv är att anrop med ett visst värde på N genererar två anrop av rutinen, som i sin tur generar fyra, osv. Gamla värden (anrop) återutnyttjas ej!

En intressantare användning av rekursiv teknik är beräkning av exponentialfunktionen av en matris. I stället för det omedelbara uttrycket med successiv multiplikation med en matris kan man använda en rekursiv metod där man plockar ut 2-potenser för att optimera beräkningen. Rekursion är vidare utmärkt för att koda adaptiva algoritmer, se Övning (5.5) nedan.

En annan viktig användning av det nya begreppet resultat-variabel är vid fält-värda funktioner, då det är lätt att deklarera denna variabel att lagra funktionens värde(n). Det är faktiskt kombinationen rekursivitet och fält som tvingat fram det nya begreppet.

I programmet ovan användes det nya begreppet "avsikt" eller INTENT, vilket förklaras i avsnitt 5.2.1.5.

Övningar.

(5.4) Skriv en rutin för beräkning av tribonacci-talen. Dessa bildas som Fibonacci-talen, men man utgår från tre tal (alla 1 vid starten) och adderar hela tiden de tre senaste talen för att få nästa. Provkör och beräkna TRIBONACCI(15). Notera att beräkningstiden växer mycket snabbt med argumentet.
Lösning.

(5.5) Skriv en adaptiv rutin för kvadratur, dvs beräkning av den bestämda integralen över ett visst intervall.
Lösning.

5.2.1.4 Fältvärda funktioner

En helt ny möjlighet i Fortran 90 är fältvärda funktioner. Notera att man även här måste utnyttja den nya egenskapen RESULT (utvariabel), se 5.2.1.3. Denna användes inuti funktionen för att på ett enkelt sätt deklarera funktionens typ. Vid själva anropet av funktionen, både externt och internt, användes däremot i stället det yttre eller "gamla" funktionsnamnet. Användaren kan därför vid anrop av den fältvärda funktionen strunta i att det finns en resultat-variabel.

Fältvärda funktioner kräver den tidigare diskuterade nyheten, nämligen ett explicit gränssnitt (INTERFACE) i den anropande programenheten. Gränssnittet består i princip av deklarationerna i funktionen, se exemplet nedan. Gränssnitt kräves vid flera olika tillfällen och är faktiskt det svåraste i Fortran 90. Se vidare avsnittet 11.2.

Nedanstående enkla funktion ger som resultat en vektor med de första åtta potenserna av det skalära argumentet.

            PROGRAM TEST_FAELTVAERD_FUNKTION
            IMPLICIT NONE
            INTERFACE   ! Detta är gränssnittet för den 
			! fältvärda funktionen POTENS(X).
                  FUNCTION POTENS(X) RESULT(FAELT)
                  REAL :: X
                  REAL, DIMENSION(8) :: FAELT
                  END FUNCTION POTENS
            END INTERFACE
            REAL :: X
            REAL, DIMENSION(8) :: Y
            X = 2.0
            Y = POTENS(X)
            WRITE(*,*) Y
            STOP
            END PROGRAM TEST_FAELTVAERD_FUNKTION

            FUNCTION POTENS(X) RESULT(FAELT)
            IMPLICIT NONE
            REAL :: X
            REAL, DIMENSION(8) :: FAELT     
            INTEGER :: I
            DO I = 1, 8
               FAELT(I) = X**I
            END DO
            RETURN
            END FUNCTION POTENS
Vi kan ganska enkelt justera ovanstående funktion till att klara godtyckliga potenser, där ordningen ges endast som indata i huvudprogrammet. Dimensionen överföres i nästa exempel automatiskt med argumentet, en flyttalsvektor, och dimensionen för resultatet sättes lika med denna dimension. Det går naturligtvis minst lika bra att överföra dimensionen med ett vanligt heltalsargument.

Det är inte nödvändigt att här låta argumentet vara en flyttalsvektor. Man kan även klara sig med ett skalärt argument, men då måste man använda pekare vid deklarationen av fältet, se sektion 4.3.2.

            PROGRAM NY_TEST_FAELTVAERD_FUNKTION
            IMPLICIT NONE
            INTERFACE
                  FUNCTION POTENS(X) RESULT(FAELT)
                  REAL, DIMENSION(:) :: X
                  REAL, DIMENSION(SIZE(X)) :: FAELT
                  END FUNCTION POTENS
            END INTERFACE
            REAL, DIMENSION(:), ALLOCATABLE :: X
            REAL, DIMENSION(:), ALLOCATABLE :: Y
            INTEGER :: DIM
            WRITE(*,*) " Ge ordning "
            READ(*,*) DIM
            ALLOCATE(X(DIM))
            ALLOCATE(Y(DIM))
            X = 2.0         ! Detta är en vektoroperation.
            Y = POTENS(X)
            WRITE(*,*) Y
            STOP
            END PROGRAM NY_TEST_FAELTVAERD_FUNKTION

            FUNCTION POTENS(X) RESULT(FAELT)
            IMPLICIT NONE
            REAL, DIMENSION(:) :: X
            REAL, DIMENSION(SIZE(X)) :: FAELT
            INTEGER :: I
            DO I = 1, SIZE(X)
                FAELT(I) = X(I)**I
            END DO
            RETURN
            END FUNCTION POTENS

5.2.1.5 Avsikten hos olika funktionsargument

I variabeldeklarationer till subprogram har för argumenten tillkommet möjligheten att ange avsikten hos en variabel, dvs om den är invariabel, utvariabel, eller båda samtidigt. Detta anges med INTENT som kan vara IN, OUT eller INOUT. Om IN gäller så kan det verkliga argumentet vid anropet vara ett uttryck som X+Y eller SIN(X) eller en konstant som 37, eftersom ett värde skall överföras till subprogrammet men ej åter till anropande enhet. Variabeln får i detta fall ej tilldelas något nytt värde i subprogrammet. Om OUT gäller så måste däremot det verkliga argumentet vara en variabel. Vid inträde i subprogrammet anses då variabeln som odefinierad. Det tredje fallet täcker båda möjligheterna, ett värde in, ett annat (eller eventuellt samma) ut. Även då måste naturligtvis det verkliga argumentet vara en variabel. Implementeringen av INTENT är dock ännu ej fullständig, dvs alla system gör ännu ej alla tester som är tänkbara. Sannolikheten för att tester sker ökar om man har med ett fullständigt INTERFACE i den anropande programenheten, som i nedanstående exempel. Ett speciellt avsnitt om avsikt eller INTENT finns i 11.1.

Om argumentet har ett pekar-attribut POINTER så får INTENT ej sättas.


        PROGRAM TEST_AV_AVSIKT
        IMPLICIT NONE
        INTERFACE
             FUNCTION FUN(X,Y,Z) RESULT(UT)
             REAL :: UT
             REAL, INTENT(IN)    :: X
             REAL, INTENT(OUT)   :: Y
             REAL, INTENT(INOUT) :: Z
             END FUNCTION FUN
        END INTERFACE
        REAL :: Y, Z             
        Z = 12.0
        WRITE(*,*) Z        
        Y = FUN(1.0,Y,Z)
        WRITE(*,*) Y, Z
        STOP
        END PROGRAM TEST_AV_AVSIKT

        FUNCTION FUN(X,Y,Z) RESULT (UT)
! Varning! Denna funktion har sido-effekter, vilket är
! olämpligt. Sidoeffekterna är här för att illustrera 
! avsikt eller INTENT.        
           IMPLICIT NONE
           REAL, INTENT(IN)    :: X
           REAL, INTENT(OUT)   :: Y
           REAL, INTENT(INOUT) :: Z
           REAL :: UT
           Y = SIN(X) + COS(Z)
           Z = SIN(X) - COS(Z)
           UT = Y + Z
           RETURN
           END FUNCTION FUN
Om man ovan exempelvis byter ut Y = FUN(1.0,Y,Z) mot Y = FUN(1.0,Y,3.0) klagar NAG:s Fortran 90 kompilator på att det ej getts något ut-argument på plats 3.

5.2.1.6 Frivilliga argument och argument utnyttjande nyckelord

Funktioner kan anropas med nyckelordsargument och de kan utnyttja frivilliga eller underförstådda argument, dvs en del argument kan ges med nyckelord i stället för med position, och en del behöver ej ges alls utan utnyttjar i stället ett skönsvärde (eng. default value).

Användningen av nyckelord och underförstådda argument är ett av de fall då ett explicit gränssnitt INTERFACE erfordras. Jag ger därför här ett fullständigt exempel. Som nyckelord användes de formella parametrarna i gränssnittet, vilka ej behöver ha samma namn som de i den verkliga subrutinen. Dessa nyckelord skall ej deklareras i anropande programenhet.

        IMPLICIT NONE
        INTERFACE
           FUNCTION SUMMA (A, B, N) RESULT (UT)
              REAL                       :: UT
              INTEGER, INTENT (IN)       :: N
              REAL, INTENT(OUT)          :: A
              REAL, INTENT(IN), OPTIONAL :: B
           END FUNCTION SUMMA
        END INTERFACE

        REAL :: X, Y
        Y = SUMMA(X,100.0,20)       ! Normalt anrop
        WRITE(*,*) X, Y
        Y = SUMMA(B=10.0,N=50,A=X)  ! Anrop med nyckelord
        WRITE(*,*) X, Y
        Y = SUMMA(B=10.0,A=X,N=100) ! Anrop med nyckelord
        WRITE(*,*) X, Y
        Y = SUMMA(N=100,A=X)        ! Anrop med nyckelord
                                    ! och ett skönsvärde.
        WRITE(*,*) X, Y
        END

        FUNCTION SUMMA(A,B,N) RESULT (UT)
        IMPLICIT NONE
        REAL :: A, UT
        REAL, OPTIONAL, INTENT (IN) :: B
        INTEGER :: N
        REAL :: TEMP_B
        IF (PRESENT(B)) THEN
                TEMP_B = B
        ELSE
                TEMP_B = 20.0
        END IF
        A = TEMP_B + N
        UT = A + TEMP_B + N
        RETURN
        END
Notera att IMPLICIT NONE för huvudprogrammet ej verkar i funktionen SUMMA, varför denna har kompletterats med det kommandot och deklaration av de ingående variablerna.

Den inbyggda funktionen PRESENT är sann om dess argument finns med i anropet, och falsk om det inte finns med. I det senare fallet användes i stället skönsvärdet (eng. the default value).

Körning på UNIX sker med

   f90 program.f90
   a.out
      1.2000000E+02     2.4000000E+02
    60.0000000    1.2000000E+02
      1.1000000E+02     2.2000000E+02
      1.2000000E+02     2.4000000E+02
Gränssnittet INTERFACE placeras lämpligen i en modul, så att användaren inte behöver bry sig. Gränssnitten blir ett naturligt komplement till rutinbiblioteken, Fortran 90 söker automatiskt efter moduler i aktuell filkatalog, eventuella filkataloger i I-listan, samt /usr/local/lib/f90. Begreppet I-lista förklaras i Appendix 6. Om man glömmer INTERFACE eller har ett felaktigt sådant erhålles ofta felet "Segmentation error". Detta fel kan dock även erhållas vid ett i princip korrekt INTERFACE, men då man glömt att allokera något av de ingående fälten i den anropande programenheten.

Notera att om en utmatningsvariabel anges som OPTIONAL och INTENT (OUT) så måste den vara med i anropslistan om programmet vid exekveringen lägger ut ett värde på denna variabel. Man måste därför i sitt program använda test med PRESENT av aktuell variabel, och endast om den är med i anropet använda den för tilldelning, för att på så sätt få den önskade valfriheten om man bara ibland vill ha ut en viss variabel.

5.2.1.7 Generiska funktioner

Detta begrepp innefattar dels de inbyggda funktionerna, vilkas generiska egenskaper har diskuterats redan i avsnitt 5.1.1, dels möjligheten att skriva egna generiska funktioner och subrutiner.

Grundprincipen för de inbyggda generiska funktionerna tål dock att upprepas, dvs att datatypen hos argumentet anger vilken rutin som skall väljas. Under Fortran 66 var man tvungen att skriva SIN(X) om X var REAL, DSIN(X) om X var DOUBLE PRECISION och slutligen CSIN(X) om X var COMPLEX för att få resultat av rätt typ och noggrannhet. Från Fortran 77 fungerar SIN(X) i alla tre fallen.

Jag ger här ett fullständigt exempel på en generisk subrutin, nämligen en rutin SWAP(A,B) som byter plats på värdena hos variablerna A och B, utnyttjande olika underliggande rutiner beroende på om de båda argumenten är av typ REAL, INTEGER eller CHARACTER.

Subrutinen finns således i detta fall i tre varianter, nämligen för flyttal, heltal och textsträng (med längden ett). De är skrivna på vanligt sätt och heter SWAP_R, SWAP_I respektive SWAP_C. Samtliga är dessutom med i huvudprogrammets INTERFACE med sina deklarationer, men detta gränssnitt har namnet SWAP. När man i huvudprogrammet vill utnyttja någon av subrutinerna använder man enbart namnet SWAP och argument av viss typ. Systemet väljer då bland de tre tillgängliga funktionerna ut den som svarar mot argumenten i anropet. Om dessa båda argument är av olika typ blir det ett fel.

        PROGRAM SWAP_HUVUD
        IMPLICIT NONE
        INTEGER   :: I, J, K, L
        REAL      :: A, B, X, Y
        CHARACTER :: C, D, E, F
        INTERFACE SWAP
                SUBROUTINE SWAP_R(A,B)
                REAL, INTENT (INOUT)      :: A, B
                END SUBROUTINE SWAP_R
                SUBROUTINE SWAP_I(A,B)
                INTEGER, INTENT (INOUT)   :: A, B
                END SUBROUTINE SWAP_I
                SUBROUTINE SWAP_C(A,B)
                CHARACTER, INTENT (INOUT) :: A, B
                END SUBROUTINE SWAP_C
        END INTERFACE

        I = 1   ; J = 2    ;   K = 100  ; L = 200
        A = 7.1 ; B = 10.9 ;   X = 11.1 ; Y = 17.0
        C = 'a' ; D = 'b'  ;   E = '1'  ; F = '"'

        WRITE (*,*) I, J, K, L, A, B, X, Y, C, D, E, F
        CALL SWAP(I,J) ; CALL SWAP(K,L)
        CALL SWAP(A,B) ; CALL SWAP(X,Y)
        CALL SWAP(C,D) ; CALL SWAP(E,F)
        WRITE (*,*) I, J, K, L, A, B, X, Y, C, D, E, F
        END

        SUBROUTINE SWAP_R(A,B)
        IMPLICIT NONE
        REAL, INTENT (INOUT)          :: A, B
        REAL                          :: TEMP
                TEMP = A ; A = B ; B = TEMP
        END SUBROUTINE SWAP_R

        SUBROUTINE SWAP_I(A,B)
        IMPLICIT NONE
        INTEGER, INTENT (INOUT)       :: A, B
        INTEGER                       :: TEMP
                TEMP = A ; A = B ; B = TEMP
        END SUBROUTINE SWAP_I

        SUBROUTINE SWAP_C(A,B)
        IMPLICIT NONE
        CHARACTER, INTENT (INOUT)     :: A, B
        CHARACTER                     :: TEMP
                TEMP = A ; A = B ; B = TEMP
        END SUBROUTINE SWAP_C
Ovanstående fungerar utmärkt, men användaren är inte så glad åt att behöva släpa med all information om SWAP och dess olika varianter i sitt program. Lösningen är att flytta över allt som rör SWAP till en modul, vilken sedan kan användas i huvudprogrammet med satsen USE modulnamnet. Vi återkommer till detta i kapitel 14.

5.2.2 Subrutiner

Vanliga enkla subrutiner diskuterades redan i avsnitt 3.14. De mer komplicerade möjligheterna hos funktioner gäller även subrutiner, skillnaden är att subrutinnamnet inte återför något värde i sig. Vi skall här nämna de båda specialfallen subrutiner utan argument och rekursiva subrutiner, liksom avsikten hos olika argument, frivilliga argument och argument med nyckelord.

5.3 Lokala

Eftersom de externa funktionerna och subrutinerna automatiskt får globalt giltiga namn finns det behov av funktioner som bara är tillgängliga i den programenhet där de behövs. I Fortran 77 och tidigare tillämpades för detta de så kallade satsfunktionerna. Detta begrepp har generaliserats i Fortran 90 till interna funktioner och även interna subrutiner.

För de lokala funktionerna och subrutinerna gäller att de ej kan användas som argument vid anrop av externa funktioner eller subrutiner (eller andra lokala).

5.3.1 Satsfunktioner

Satsfunktioner kom mycket tidigt i Fortran, men är inte så väldigt populära hos programmerarna. De är mycket praktiska och lättanvända, satsfunktioner placeras i aktuell programenhet mellan deklarationerna och de exekverbara satserna, och ser ut som tilldelningssatsen inne i en extern funktion. Observera att trots att de måste kunna skrivas på en logisk rad så kan de vara ganska avancerade, eftersom de kan anropa såväl inbyggda funktioner som tidigare definierade satsfunktioner. Ett enkelt exempel följer.
        IMPLICIT NONE
        REAL :: X, Y, PI, COT, NCOT
! Efter deklarationer kan satsfunktioner placeras.
        Y(X) = X + 2.0*X - 3.0*X**2
        COT(X) = 1.0/TAN(X)
        NCOT(X) = COT(PI*X)/PI
! Nu följer de exekverbara satserna.
        PI = 3.141592654
        READ(*,*) X
        WRITE(*,*) X, Y(X), COT(X), NCOT(X)
        END
Här är satsen PI = 3.14159264 den första exekverbara satsen, men den borde nog hellre ha placerats bland deklarationerna utnyttjande PARAMETER satsen eller attributet. Denna form ger dock en god information om att satsfunktionerna utnyttjar argument (här X) och konstanter (här PI) som tilldelas senare, men satsfunktioner som definierats tidigare! De kan naturligtvis även använda konstanter och variabler som initierats redan bland deklarationerna.

Fördelarna gentemot en extern funktion är dels att lokala variabler delas med den aktuella programenheten, dels att funktionsnamnet blir lokalt (vilket innebär minskad risk för namnkonflikt).

5.3.2 Interna funktioner och subrutiner

Interna funktioner och subrutiner är en modernisering av de gamla satsfunktionerna. I en intern funktion eller subrutin kan mer komplicerade satser skrivas, ej enbart i form av en logisk rad. De placeras i slutet av aktuell programenhet (dock ej funktion), omedelbart före END placeras satsen CONTAINS följd av interna funktioner och subrutiner. Liksom hos satsfunktioner kan lokala konstanter och variabler delas med programenheten.

Ett enkelt exempel följer, nämligen en omskrivning av satsfunktionerna ovan. Notera att de tre interna funktionerna Y, COT och NCOT nu ej får deklareras på rad 3, den med REAL :: X, PI.

PROGRAM INTERN
        IMPLICIT NONE
        REAL :: X, PI
! Nu följer de exekverbara satserna.
        PI = 3.141592654
        READ(*,*) X
        WRITE(*,*) X, Y(X), COT(X), NCOT(X)
CONTAINS
! Här placeras interna funktioner och subrutiner.
        FUNCTION Y(X)
                REAL :: Y
                REAL, INTENT(IN) :: X
                Y = X + 2.0*X - 3.0*X**2
        END FUNCTION Y
        FUNCTION COT(X)
                REAL :: COT
                REAL, INTENT(IN) :: X
                COT = 1.0/TAN(X)
        END FUNCTION COT
        FUNCTION NCOT(X)
                REAL :: NCOT
                REAL, INTENT(IN) :: X
                NCOT = COT(PI*X)/PI
        END FUNCTION NCOT
END PROGRAM INTERN
I verkligheten kan de olika interna funktionerna och subrutinerna vara mycket komplicerade enheter med alla satser i Fortran, de kan dock ej i sin tur innehålla interna funktioner eller subrutiner.

OBS! Var försiktig vid deklaration av interna funktioner och subrutiner. De deklareras i samband med att de införes, de skall ej ges någon "egen" deklaration.

Ett eventuellt IMPLICIT NONE i den överordnade programenheten gäller automatiskt även alla interna funktioner och subrutiner, liksom naturligtvis för eventuella satsfunktioner.

Övningar.

(5.6) Skriv en rutin för beräkning av integralen av en funktion. Använd nyckelordsargument och underförstådda argument så att Lösning.

(5.7) Skriv det gränssnitt (interface) som behövs i anropande rutin för att kunna använda ovanstående integrationsrutin.
Lösning.

Fält Innehåll Text (CHARACTER)


Senast modifierad: 29 maj 2002
boein@nsc.liu.se