10. Segédprogramok

Amikor egy program már több száz sor hosszú, nehéz már követni. Sõt, a valódi mérnöki (vagy akármilyen más) számításoknál sokszor több ezer sorból álló programokat kell alkalmazni. Az egyetlen módja, hogy e hatalmas "kód-zuhatagot" kezelni tudjuk a modulátorok és a programba beágyazott szub(vagy segéd vagy al)programok használata, amikkel programunk szövegét több kisebb részre bonthatjuk.

A szubprogram egy kisebb kódrészlet, ami egy konkrét segédfeladatot old meg. Egy nagyobb programban elõfordulhat, hogy ugyanazt azt alproblémát kell megoldani többször, de különbözõ adattokkal. Ilyenkor is alkalmazhatunk szubprogramot, a parancsok ismétlése helyett, azaz az egyszer megírt segédprogramot kell elõhívni, miután a bemenõ adatokat megfelelõen módosítottuk.

A Fortranban kétféle szubprogram van: a függvények és a subroutine-ok.

Függvények

Egy Fortran-függvény hasonló jelentésû, mint egy matematikai: mindkettõhõz meg kell adni a bemenõ argumentumot (paramétereket) és a egy függvényalakot, hogy értéket nyerjünk belõle. A Fortran esetében beszélhetünk a felhasználó által definiált szubprogramokról (ezekrõl volt fent is szó), de léteznek beépített függvények is.

Egy egyszerû példa a függvények használatára:

      x = cos(pi/3.0)
Itt a cos a cosinus függvény, tehát x értékére 0.5 adódik majd. (feltéve ha pi értékét korrekten definiáltuk, mivel a Fortran 77-nek nincsenek beépített konstans értékei). Nézzünk példákat a Fortran 77 saját, beépített függvényeire:
      abs     abszolútérték
      min     minimum érték
      max     maximum érték
      sqrt    gyökvonás
      sin     sinus      
	  cos     cosinus      
	  tan     tangens
      atan    cotanges
      exp     exponenciális (természetes, e)
      log     logaritmus (természetes alapú, ln)
Általában a függvényeknek típusuk van. A legtöbb beépített függvény, amiket elõbb is is láttunk, általános tipusú. Azaz, például a fenti esetet tekintve a pi és x értéke lehet real vagy double precision is. A programolvasó ellenõrzi az adatok típusát és a helyes cos verziót fogja alkalmazni (real vagy double precision-t). Sajnos a Fortran nem elég rugalmas nyelv e téren, ezért nagyon fontos a változók típusainak és a függvények korrekt megválasztása!

Most térjünk vissza a felhasználó által írt függvényekre. Tekintsük az alábbi problémát: Egy meteorológus miután tanulmányozta a Balaton-vidék havi csapadék viszonyait, segítségével egy modellt alkot:: r(m,t)-et, ahol r a havi csapadékösszeg, az m a hónap száma, és t a földrajzi helyre utaló paraméter, Ha r-re egy formulát és t-nek egy értéket adunk az éves csapadékösszeget kiszámolhatjuk.

A legegyszerübb megoldás egy ciklus megírása, ami havonként összegzi a csapadékmennyiségeket. Itt most r-et nem mérjük, hanem számítjuk. Az r kiszámítása egy külön elszigetelt probléma, ezért célszerû egy szubprogram megírása értékének meghatározásához. A fõprogram alakja az alábbi (más megoldás is lehet):

      program eso
      real r, t, sum
      integer m
 
      read (*,*) t
      sum = 0.0
      do 10 m = 1, 12
         sum = sum + r(m, t)
  10  continue
      write (*,*) 'Eves csapadekosszeg ', sum, 'mm'

      stop
      end
Megjegyzés: r-et egy valós változónak definiáltuk (hiszen egy konkrét értéket ad) A szubprogramban majd r-et Fortran függvénynek kell definiálni. A meteorológus az alábbi formulát találta:
      r(m,t) = t/10 * (m**2 + 14*m + 46) (ha ez pozítiv)
      r(m,t) = 0                         (egyébként)
Ez Fortranban lekódolva:
      real function r(m,t)
      integer m
      real t

      r = 0.1*t * (m**2 + 14*m + 46)
      if (r .LT. 0) r = 0.0

      return
      end
Láttuk, hogy egy függvény formailag szinte teljesen megegyezik a fõprograméval. Azonban vannak különbségek, melyek közül a fontosabbak: Összefoglalásképp egy Fortan 77 függvény általános alakja:
      típus function név (kül. változó típusok szerint)
      deklarációk
      utasítások
      return
      end
A függvény típusát pontosan kell megadni a hívó programban. Ha olyan függvényt alkalmazunk, amit elõtte nem deklaráltunk, a Fortran pótlásképp saját maga fogja deklarálni, nem kizárt, hogy hibásan. Egy függvényt nevének és paramétereinek leírásával (a paramétereket zárójelben a név mögött soroljuk föl) hívhatjuk meg.

Meg kell jegyezni, hogy a gyári, alapértelmezett Fortran 77 nem engedi meg a rekurzív (önmagát generáló számításokat), de némelyik fordító már elfogadja használatukat.

Subroutin-ok

Egy Fortran függvény csak egy értéket ad vissza. Azonban gyakran elõfordul, hogy kettõ vagy több értéket (sõt néha semmilyet sem) akarunk megkapni segédprogram segítségével. A subroutine utasítást erre használjuk. A szintaxis az alábbi:
      subroutine név (argumentumok listája)
      deklarációk
      utasítások
      return
      end
Fontos, hogy a subroutin-oknak nincsen típusuk és nem is lehet deklarálni a fõprogramban. Ezért meghívásuk is más, mint a függvényeknél: a call (=hívás) szót kell leírnunk nevük és paramétereik elé.

Nézzünk egy egyszerû subroutine-t. A feladat két egész szám megcserélése:

      subroutine csere (a, b)
      integer a, b
c Lokalis valtozok
      integer tmp

      tmp = a
      a = b
      b = tmp

      return
      end
Itt kétféle deklarációt is alkalmazunk. Elõször a bemenõ/kimenõ paramétereket kell, amik közösek a hívó és a hívott programban. De ezután deklarálni kell a lokális (helyi) változókat, amiket csak ebben a szubprogramban használhatunk. Ezért nyugodtan használhatunk ugyanolyan változó-neveket a különbözõ segédprogramokban, a programfordító tudni fogja, hogy ezek különbözõ változók, habár nevük formailag ugyanaz.

A Call-by-hivatkozás

A Fortran 77 használ ún.call-by-reference (=a hivatkozás alapján hívás) formát is. Ez azt jelenti, hogy nem csak a függvény/subroutine argumentumjait (call-by-value=érték alapján hívás) , hanem vele együtt az argomentumok memória-címeit is meghívja. A következõ rövid példa bemutatja a kettõ közötti különbséget:
      program callex
      integer m, n
c
      m = 1
      n = 2 

      call csere(m, n)
      write(*,*) m, n

      stop
      end
Ennek a programnak az eredménye "2 1", ahogy el is várjuk. De, ha a Fortran 77 call-by-value típusú hívást alkalmazott volna az eredmény "1 2" lenne, azaz m és n felcseréletlen maradt volna! Ennek az az oka, hogy a második esetben csak az értéket másolta volna a csere subroutine-ba, ahol habár a és b értékét megcserélte volna, az új (felcserélt) értékeket nem adta volna vissza a fõprogramba.

A fenti példa is rávilágosított, hogy jobb a call-by-reference hívás De bánjunk óvatosan ezzel is, mert könnyen elronthatjuk vele programunkat. Például néha csábító lehet a szubprogramba bemenõ változót egyben lokális változónak is használni, és így változtatni meg az értékét. Azonban a szubprogramból kimenõ érték már ez a megváltozott lesz, ami többnyire nem kívánatos. Bizonyos esetektõl eltekintve (amikor direkt akarjuk megváltoztatni a fõprogrambeli változónk értékét, lásd elõzõ példa) kerüljük ezt a megoldást.

Késõbb még folytatjuk e téma taglalását, de elõtte tisztáznunk kell a tömbök (mint argumentumok) szerepét a segédprogramokban.

 

Copyright © 1995-7 by Stanford University. All rights reserved.

Fordították: Seres András Tamás és Szalai Szilvia (ELTE-TTK)


[Tovább] [Tartalomjegyzék]