Programski jezik FORmulaTRANslation je bil prvotno razvit 1950 in nato standardiziran 1977. Kasnejše posodobitve vsebujejo Fortran 90.
Glavne prednosti so:
Slabosti:
Fortran 90 je vpelljal naslednje novosti:
program hello
! Display a message to standard output
implicit none
write (unit = *, fmt= *) ”Hello World!”
end program hello
Formalno se sestoji glavni program iz
[program program-name] [specification-statements] [executable-statements] end [program [program-name]]
Kjer pomeni besedilo v oglatih oklepajih [] opcijsko uporabo. Več stavkov v eni vrstici ločimi s podpičjem;. Če pa želimo daljše stavke zlimiti v vstice dodamo na kondu znak &, ki pomeni nadaljevanje vrstice v naslednji
write (unit = *, fmt= *) &
”Nekoliko daljši pozdrav v FORTRANU.!”
implicit none ! Prisili strogo preverjanje tipov
integer :: i! 10
real :: a! 3.14159
character :: letter! a
character (len= 12) :: month ! Januar
logical :: switch! .false.
complex :: z0, z1! (1.0, 1.0)
Spremeljivke
S stavkom implicit none prisilimo strogo preverjanje tipov in ne veljajo privzeti tipi, ki so rezervirani za cela števila. Pravilo je
Implicitna raba imen spremenljivk e nevarna in se jo moramo izogibati.
Polja vsebujejo skupine vrednosti. Elementi polj so dostopna z indeksi. V Fortranu so polja shranjena v spominu kot kolone in ne kot vrstice, kot v jeziku C - column major.
Polja podajamo z atributom dimension
integer, dimension(4) :: n4
Kar rezervira 4 elemente: n4(1), n4(2), n4(3), n4(4), Prvi element je privzeto z indeksom 1. Lahko pa določimo tudi spodnje in zgornje meje:
real, dimension(-5:4) :: r
Večdimenzijska polja določimo z dodatkom dimenzij. Npr.
complex, dimension(1:10, 1:20) :: z
Dovoljeno je do 7 dimenzij:
real, dimension(2, 3, 4, 5, 6, 1) :: veliko
So deklarirana podobno kot numerični tipi. Znakovne spremenljivke lahko
Naslednja so pravilne deklaracije
character :: sex character (len= 20) :: name character (len= 10), dimension(10,10) :: carray
Za prirejanje konstantnih nizov lahko uporabimo enojne ali dvojne narekovaje.
Se imenjejo konstante in so lahko vseh tipov. Njihova vrednost se ne more spremeniti.
integer, parameter :: n = 100 real, dimension(2*n) :: r real, parameter :: pi = 3.14
Spremenljivke se kahko inicializira na poljubnem mestu v programu,
program initial_declare implicit none integer:: i = 10 real :: pi = 3.14159 character (len= 12) :: month = ”Januar” end program initial_declare
ali v glavnem programu
complex :: ci logical:: iostatus ci= (0.0, 1.0) iostatus= .true.
S tipom lahko izpeljemo lastne tipe iz obstoječih. Primer take rabe je primeren npr za spremenljivke tipa real, ki so običajno 4 byte, vendar to ni nikjer določeno. Z mehanizmom tipa pa lahko določimo izbrani tip. Npr. s specificiranjem danga decimalne vejice, kar naredimo s parametrom kind.
integer, parameter :: sp= kind(1.0) real (kind = sp), dimension(10) :: variable
Razširimo ločljivost na double
integer, parameter :: dp= kind(1.0d0) real (kind = dp) :: variable
Fortran 90 omogoča tudi izpeljane tipe s katerimi združujemo osnovne tipe. P
type person character (len=10):: name real:: age integer:: id end type person type(person) :: you, me you = person(”Janez Kranjec”, 21, 1234)
Elemente izpeljanih tipov lahko naslavljamo z znakom %. Npr za popreje deklarirani tip person
you%name! vsebuje ime you%age! starost you%id! oznaka
Pri izračunavanju izrazov z različnimi tipo spremenljivk velja pravilo, da je izračunan izraz vedno tipa, ki je višjega reda. Možno pa je tudi ročno prirejanje tipov z vgrajenimi funkcijami int(), real(), cmplx().
Fortran ima zgodovinsko gledano, množico funkcij, ki si vgrajene v sam jezik. Teh je preko 100 in operirajo s polji, biti, znaki,..., kot tudi funkcije za
V 90/95 so poleg že zastarelih opisnih relacijski operatorjev uvedeni še "normalni" izrazi za operatorje, kot jih poznamo v ostalih "modernih" jezikij. Ti so:
< ! less than <= ! less than or equal > ! greater than >= ! greater than or equal == ! equal /= ! not equal
in logični operatorji
.true. .false. .not. ! unary not .and. ! logical and .or. ! logical or .eqv. ! equivalent .neqv. ! not equivalent
if (logical-expression) then block [else if (logical-expression) then block]... [else block] end if
Primer:
if (t < 0) then ! Mraz je led = .true. else if (t > 100) then ! Toplo je para = .true. else voda = .true. mokro = .true. end if
Poleg tega so možne še druge oblike pogojnih stavkov, kot je npr elseif ali izbirni if z case. Na primer:
seasons: select case (mesec)! mesec je tipa integer case (1:2,12)! Zima, Dec, Jan, Feb write(*,*)”Zima” case(3:5)! Pomlad, Mar, Apr, May write(*,*)”Pomlad je case(6:8)! Poletje, Jun, Jul, Aug write(*,*)”Poletje” case(9:11) ! Jesen, Sep, Oct, Nov write(*,*)”Jesen” case default! če je mesec izven 1-12 write(*,*)"Vnesti je potrebno 1-12" end select seasons
do n = 1, 100 ! računaj end do
Formalno je oblika stavka zanke:
do [variable= expr1, expr2[, expr3]] blok end do
kjer je expr3 izraz za korak. Možen je tudi negativni korak:
do n = 10, 1, -1 ! računaj end do
Uporabljamo ukaz print v osnovni obliki.
print*,”Hello World”
Lahko pa uporabljamo tudi write(), ki je bolj uporaben.
result = dist(a, b, c) ... real function dist(x, y, z) real, intent(in) :: x, y, z ! zaščiteni argumenti dist = sqrt(x*x + y*y + z*z)! Priredba rezultata end function dist
call sort(nmax, a, ipass) ... subroutine sort(nmax, a, ipass) integer, intent(in):: nmax real, dimension(nmax), intent(inout) :: a integer, intent(out) :: ipass ! ... tukaj računamo ... return end subroutine sort
Podprogrami nikoli ne vračajo vrednosti. Če želimo izhod iz podprograma prej uporabimo ukaz return. Argumenti so lahko:
Neupoštevanje predpisanega obnašanja se javi pri prevajanju, kar je dobra programerska praksa.
Modul je skupek ali zbirka spremenljivk in procedur z naslednjo zgradbo:
module sort implicit none ! specifikacija spremenljivk ... contains ! specifikacija procedur subroutine sort_sub1() ... end subroutine sort_sub1 ... end modulesort
Z moduli dosežemo modularno programiranje. Module v drugih delih kode uporabimo s stavkom use:
program main use sort implicit none ... call sort_sub1() end program main
Primer uporabe modula:
program scope_main use scope_mod implicit none real :: a = 10.0, b=20.0 write(*,*)”Pred klicanjem sub1 a = ”,a,”b = ”,b call sub1(a) write(*,*)”Po klicanju sub1 a = ”,a,”b = ”,b end program scope_main
module scope_mod implicit none ! Velja za vse kar sledi contains subroutine sub1(x) real, intent(in) :: x real :: b = 5.0 b = b + 1 write(*,*)”Znotraj sub1 x = ”,x,”b = ”,b return end subroutine sub1 end module scope_mod
V primeru imamo celoten porogram iz dveh delov. Glavni (main.f90), ki uporablja sortiranje (sort.f90) sta ločeni datoteki.
f95 sort.f90 main.f90 –o progsort
Ker main uporablja sort, je pri večini prevajalnikov potrebno napisati sort.f90 pred main.f90
module another_module implicit none private ! nastavi privzeto obravnavo public :: nlimit! javno dostopna spramenljivka modula integer, parameter :: nlimit= 20! public integer :: nlocal= 0! privatna real, public :: tmp! javna spremenljivka contains ... end module another_module
Privzeta vidnost spremenljivk je public.
V naslednjem primeru
program main ... call sub1() end program main subroutine sub1() ... end subroutine sub1
sta ta dva programa povsem ločeni prevajalni enoti, ki si ne delita vidnosti. Vsa skupna informacija se prenaša z argumenti, katere se na da kontrolirati na pravilnost tipa. Tak način modularnega programiranja je bil nekoč edino možen in zazo se imanuje tudi zunanji podprogram. Primer takega programa je:
program scope_external implicit none real :: a = 10.0, b=20.0 write(*,*)”Pred klicanjem sub1 a = ”,a,”b = ”,b call sub1(a) write(*,*)”Po klicanju sub1 a = ”,a,”b = ”,b end program scope_external subroutine sub1(x) implicit none real, intent(in) :: x real :: b= 5.0 b = b + 1 write(*, *)”Znotraj sub1 x = ”,x,”b = ”,b return end subroutine sub1
V primeru je razvidno, da prevajalnik ne testira tipov. Če želimo to doseči (kar je dobra praksa), to dosežemo z ekplicitno navedbo funkcije ali podprograma.
V glavnem programu lahko pišemo tudi interne podprograme, ki jih označimo z "internal" Primer:
program main ... call sub1() contains subroutine sub1() ... end subroutine sub1 end program main
Zarani privzetega prenašanja argumentov z referenco morajo rekurzivni podprogrami
Primer rekurzivne funkcije:
res = factorial(m) ... recursive function factorial(n) result(nfact) implicit none integer, intent(in) :: n integer :: nfact if (n > 0) then nfact= n * factorial(n-1) else nfact= 1 end if end function factorial
Primer rekurzivnega podprograma:
call factorial(m,res) ... recursive subroutine factorial(n,result) integer, intent(in) :: n integer, intent(inout) :: result if (n > 0) then call factorial(n-1,result) result = result * n! Need to update result else result = 1 end if return end subroutine factorial
Fortran v vsebuje različne programske enote:
Ob pravilni rabi lahko dobimo dobro struktuirano kodo, ki je zanesljiva, razumljiva in se jo da vzdrževati.
Polja deklratiramo i atributom dimension. Pomnite, da so polja shranjena v spominu kot kolone, kar pri izpisu izgleda kot transponiran izpis. Tako bo pri definiciji
real, dimension(3,2) :: a
write(*,*) a
izpisalo polja v naslednjem vrstnem redu: 1,3, 5, 2, 4, 6
Če želimo pravilen matematični izpis po vrsticah je potrebno izpisati v zanki
do i = 1,3
write(*,*) (a(i,j), j = 1,2) ) ! vgrajena zanka
end do
Za primer polja
real, dimension(1:10) :: a
lahko dele polja naslovimo z
Tako lahko tudi cele range uporabljamo v artimetiki
Lahko tudi inicializiramo ob začetku programa s konstruktorjem (/.../)
ali z vsebovano zanko
Za medsebojno delo morajo biti polja skladna (konformna)
Za polja obstajajo tudi pogojni operatorji, ji se izvajajo na polju kot celota
where (logical array-expression) array assignments [else where] (logical array-expression) array assignments end where
Namesto
do i = 1, n
a(i, i) = x(i)
end do
lahko uporabimo tudi
forall(i = 1:n) a(i, i) = x(i)
ali celo bolj zahtevne izvedbe
forall(i = 1:n)
where (a(i, :) == 0) a(i, :) = i
b(i, :) = i / a(i, :)
end forall
lahko apliciramo tudi na polja kot celoto s tem da npr. a = sqrt(a) izračuna korene za vse elemente polja. Za polja imamo tudi vgrajene funkcije za poizvedovanje (size, lbound, ubound,shape) in pretvorbo:
Fortran omogoča prilagodljive velikosti polj
real, dimension(:) :: a! 1D polje
real, dimension(:,:) :: b! 2D polje
real, dimension(:,:,:) :: c ! 3D polje
Lokalne spremenljivke, ki so spremenljive velikosti so avtomatske. Velikost je lahko predpostavljana
subroutine swap(a, b)
real, dimension(:), intent(inout) :: a, b
Deklarira se jih z:
real, dimension(:), allocatable:: names ! 1D polje
real, dimension(:,:), allocatable:: grid ! 2D polje
Spomin se alocira (priredi) in dealocira (sprosti) z ukazom
allocate(work(n,2*n,3*n))
deallocate(work)
Primer dimaničih polj v modulu:
module work_array
implicit none
integer :: n
real, dimension(:,:,:), allocatable:: work
end module work_array
program main
use work_array
implicit none
read(*,*) n
allocate(work(n,2*n,3*n))
. . .
deallocate(work)
end program main
Velokokrat želimo poravnan izpis, kar lahko naredimo s formatnim stavkom
write(*, fmt=”(i2,2x,f5.1,1x,a4)”) i,temp,name
Podobni lahko pišemo z ločenim formatnim stavkom, ki ga oštevilčimo
write(*,10) i, temp, name
10 format (i2,2x,f5.1,1x,a4)
Podaja interno presdtavitev. Formatiran izpis ima več slabosti:
Neformatiran I/O omogoča ohranitev interne strukture.
Primer pisanja v datoteko:
open(unit=10, file="unformatted.dat",& action="write", form="unformatted")
. . .
do i = 1, imax
write(10, err=100) a(i)
end do
100 continue
close(10)
Datoteke se naslavljajo s številkami odprtih enot. Številke enot so pozitivne in se lahko uporabljajo kot cela števila v programu.Običajo enota 5 predstavlja vhod, enota 6 pa izhod.
Preverimo delovanje programa z najkrajšim programom.
program hello
!Izpiše klasični pozdrav na terminal
implicit none
write (unit = *, fmt= *) "Hello World!"
end program hello
Program prevedemo in poženemo z ukazom
$ f95 hello.f90 -o hello $ ./hello Hello World
Poleg vgrajenih trigonometrijskimi funkcijami (sin(), cos(), tan, ...) si želimo še razne pomožne funkcije, kot je pretvorba stopinj.
Napišimo modul imenovan trig, ki vsebuje te funkcije za modularno rabo v drugih programih. Osnutek programa trig.f90 je naslednji
module trig
implicit none
contains
real function degtorad
...
end function degtorad
real function radtodeg
...
end function radtodeg
end module trig
Napišimo še osnutek programa anglecov.f90, ki prebere 3 kote (v stopinjah) iz zaslona
program anglecov
use trig
! preberi tri kote
! Pretvori kote iz stoping v radiane
! izpiši kote
! pretvori kote iz radianov v stopinje
! izpiši kote
end program anglecov
Napišite program, ki izračuna oba korena kvadratne funkcije
ax2+bx+c=0
za a=1, b=0, c=-4 s tem, da je potrebno vključiti pogojni stavek (if), pri katerem se kontrolira pozitivnost diskrimimante b2-4ac in javiti napako v primeru napake vhodnih podatkov. Lahko pa ta primer razširite še na kompleksna števila, ki mora za a=4, b=1, c=1 izpisati x=-0.125+0.48i in x=-0.125-0.48i.