11. Avancerad användning av subrutiner och funktioner

11.0 Inledning

Det finns nu inte bara "vanliga" subrutiner och funktioner, utan dels finns flera varianter, som olika typer av lokala funktioner och subrutiner, dels ett antal attribut till de ingående argumenten. Inte minst intressant är den nya möjligheten med rekursiva programenheter.

11.1 Avsikt (INTENT)

De flesta funktioner och subrutiner har ett antal argument. Dessa kan delas in i tre kategorier:
  1. Variabler som överför värden från den anropande programenheten till den anropade programenheten, men inte tvärtom. Dessa variabler kallas in-variabler och har således avsikten "in". De kan betecknas i Fortran 90 med INTENT(IN).
  2. Variabler som överför värden från den anropade programenheten till den anropande programenheten, men inte tvärtom. Dessa variabler kallas ut-variabler och har således avsikten "ut". De kan betecknas i Fortran 90 med INTENT(OUT).
  3. Variabler som både överför värden från den anropande programenheten till den anropade programenheten, liksom tvärtom. Dessa variabler kallas in/ut-variabler och har således avsikterna "in" och "ut". De kan betecknas i Fortran 90 med INTENT(INOUT).
Genom att ge dessa attribut i koden ges systemet en möjlighet att kontrollera att de båda programenheterna samverkar på rätt sätt. Detta innebär att vid anropet måste en ut-variabel vara en variabel som kan ta emot ett värde, till exempel A eller B(1,J) men inte SIN(A) eller C + 3.0.

Å andra sidan bör vid anropet en in-variabel redan ha tilldelats ett värde.

För en variabel som är både in och ut måste båda villkoren ovan vara uppfyllda.

Inuti den anropade programenheten bör värdet på en in-variabel användas, men inte ändras. För en utvariabel gäller att den vid inhoppet saknar värde, och således inte bör användas före den första tilldelningen.

För allt detta gäller att systemet har en möjlighet att kontrollera dessa olika villkor, i begränsad omfattning vid kompileringen (statisk kontroll) och i full omfattning vid exekvering (dynamisk kontroll). I praktiken så utför de nuvarande systemen ännu inte så mycket av dessa kontroller. Enligt standarden är det inget krav på att systemet utför dem! Det är dock större chans att kontrollerna sker om man inte bara använder INTENT utan även gränssnitt INTERFACE.

Vid dessa kontroller utnyttjas i stor omfattning tekniken med dataflödesanalys, se avsnittet 17.1.

11.2 Gränssnitt (INTERFACE)

Vid ett par tillfällen saknar gamla Fortran möjlighet att kontrollera allt det som finns i nya Fortran, eftersom Fortran av tradition är baserat på separatkompilering av varje programenhet. För att råda bot på detta utnyttjas i stor omfattning tekniken med gränssnitt eller INTERFACE.

Med hjälp av ett gränssnitt överföres information mellan olika programenheter.

Eftersom de olika programenheterna i ett Fortran-program behandlas helt självständigt måste all information om argumenten i princip föras över manuellt. I Fortran 77 skedde detta med otympliga argumentlistor. I Fortran 90 kan i stället ett gränssnitt kallat INTERFACE användas. Detta måste användas vid

11.3 Satsfunktioner

Satsfunktion är en funktion som är lokal till en viss programenhet, och som finnes efter deklarationerna och före de exekverbara satserna. Den har en mycket enkel form, och fanns redan i FORTRAN I. En satsfunktion kan ej användas som argument! Se vidare avsnittet 5.3.1.

En generellare form är den interna funktionen efter CONTAINS, se nästa avsnitt.

11.4 Interna funktioner

En intern funktion är en funktion som är lokal till en viss programenhet, och som finnes efter en CONTAINS sats. Alla variabler i den överordnade programenheten är direkt tillgängliga. En intern funktion kan ej användas utanför den direkt överordnade programenheten. Detta kan vara bra för att undvika namnkonflikter mellan funktioner och subrutiner från olika bibliotek. Interna funktioner är generellare än satsfunktioner. En intern funktion kan ej användas som argument! Det finns även interna subrutiner.

11.5 De nya inbyggda funktionerna

De inbyggda funktionerna är en mycket viktig del av Fortran. Som tidigare nämnts är de generiska, dvs de känner av argumentets datatyp och returnerar ett resultat baserat på denna typ, normalt även av denna typ. I vissa sammanhang (användning av funktion som argument) måste dock det specifika namnet användas (t ex DSIN istället för SIN för att ta sinus för ett dubbelprecisions-flyttal). Se vidare Appendix 5.

11.6 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. Två enkla exempel gavs i kapitel 5. Det första respektive det andra exemplet nås enklast genom klickning!.

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.

11.7 Underförstådda argument och nyckelordsargument

Rutiner kan anropas med nyckelordsargument och kan utnyttja 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 standardvärde.

Användningen av nyckelord och underförstådda argument är inte riktigt så enkel som det borde vara. Det är ett av de fall då ett explicit gränssnitt INTERFACE erfordras. Jag har gett ett liknande exempel, utnyttjande en funktion, redan i kapitel 5. Nu tittar jag i stället på en subrutin.

Som nyckelord användes de formella parametrarna i gränssnittet, vilka ej behöver ha samma namn som de i den verkliga subrutinen. Dessa skall ej deklareras i anropande programenhet (utom i gränssnittet).

        IMPLICIT NONE
        INTERFACE
                SUBROUTINE SOLVE (A, B, N)
                        INTEGER, INTENT (IN)        :: N
                        REAL, INTENT(OUT)           :: A
                        REAL, INTENT(IN), OPTIONAL  :: B
                END SUBROUTINE SOLVE
        END INTERFACE

        REAL X
        CALL SOLVE(B=10.0,N=50,A=X)
        WRITE(*,*) X
        CALL SOLVE(B=10.0,N=100,A=X)
        WRITE(*,*) X
        CALL SOLVE(N=100,A=X)
        WRITE(*,*) X
        END

        SUBROUTINE SOLVE(A,B,N)
        IMPLICIT NONE
        REAL, OPTIONAL, INTENT (IN) :: B
	REAL :: A, TEMP_B
        INTEGER :: N
        IF (PRESENT(B)) THEN
                TEMP_B = B
        ELSE
                TEMP_B = 20.0
        END IF
        A = TEMP_B + N
        RETURN
        END
Notera att IMPLICIT NONE för huvudprogrammet ej verkar i subrutinen SOLVE, varför denna har kompletterats med det kommandot och deklaration av de ingående variablerna. Körning på Sun-datorn sker med
        f90 program.f90
        a.out
          60.0000000
           1.1000000E+02
           1.2000000E+02
Gränssnittet INTERFACE placeras lämpligen i en modul. 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.

Ytterligare datatyper Innehåll Pekare


Senast modifierad: 3 maj 1999
boein@nsc.liu.se