You are on page 1of 339

For t r an 95/2003

Juha Haataja, Jussi Rahola ja Juha Ruokolainen

FORTRAN95_2003.indd 3

23.4.2007 12:48:23

Fortran 95/2003
Juha Haataja
Jussi Rahola
Juha Ruokolainen
Tieteen tietotekniikan keskus CSC

Tmn teoksen tekijnoikeudet kuuluvat CSC Tieteellinen laskenta Oy:lle. Teoksen tai osia siit voi kopioida ja tulostaa vapaasti henkilkohtaiseen kyttn sek Suomen yliopistojen ja korkeakoulujen kurssikyttn edellytten, ett kopioon tai tulosteeseen liitetn tm ilmoitus teoksen tekijst ja tekijnoikeuksista. Teosta ei saa myyd tai sisllytt osaksi muita teoksia ilman
CSC:n lupaa.

c Juha Haataja, Jussi Rahola, Juha Ruokolainen ja



CSC Tieteellinen laskenta Oy
2007
Neljs uudistettu painos
ISBN 978-952-5520-23-1 (nid.)
ISBN 978-952-5520-24-8 (pdf)
http://www.csc.fi/csc/julkaisut/oppaat

Fortran 95/2003

Esipuhe
Fortran on trkein ja kytetyin ohjelmointikieli numeerisessa laskennassa.
Fortran 2003 on kielen uusin standardi. Koska uuden standardin kaikkia
piirteit ymmrtvi kntji ei viel ole markkinoilla, keskitymme uuden
standardin siihen osajoukkoon, joka sisltyy Fortran 95 -standardiin.
Fortran 95 ja sit edeltnyt Fortran 90 -standardi kuvaavat aikaisempia Fortranin versioita monipuolisemman ohjelmointikielen, joka sislt modernit
ohjaus- ja tietorakenteet. Koska Fortran koki monilta osin perusteellisen
muodonmuutoksen, on varsin aiheellista esitell Fortran 95 kokonaan uutena ohjelmointikielen. Tss teoksessa on ksitelty erillisess luvussa siirtymist FORTRAN 77:st uuteen standardiin. kaikkia ominaisuuksia, vaan
ppaino on trkeimmiksi katsomillamme piirteill. Vanha Fortran-kielen
standardi (FORTRAN 77) muodostaa Fortran 95:n osajoukon, joten kieli on
yhteensopiva vanhojenkin koodien kanssa.
Lukijalta ei edellytet Fortran-kielen aiempaa tuntemusta, vaikkakin ohjelmoinnin perusksitteet on syyt tuntea. Fortran-kntjn kyttesimerkit
on esitetty Unix-ympristss, mutta Unixiin liittyvt yksityiskohdat on pyritty minimoimaan.
Tmn teoksen ensimminen painos ilmestyi vuonna 1996 ja kertoi aiemmasta Fortran 90 -standardista. Tss kirjassa kerrotaan posin Fortran 95
-standardista eli uuden Fortran 2003 -standardin osajoukosta. Erillisess luvussa on tiivis kuvaus Fortran 2003:n uusista piirteist. Lisksi esittelemme
lyhyesti rinnakkaisohjelmointia High Performance Fortran -kielell (HPF) ja
OpenMP:ll.
Teoksen syntyyn ovat vaikuttaneet kokemuksemme tieteellisen ja teknisen
laskennan asiantuntijatehtviss. Olemme valinneet kyttmmme nkkulman ja esitystavan tieteen tietotekniikan keskuksen CSC:n asiakasprojekteista kertyneiden kokemusten perusteella. Oma vaikutuksensa teokseen on
ollut aiemmin tuottamistamme kursseista ja julkaisuista saadulla palautteella. Suurimman osan tekstist on kirjoittanut Juha Haataja, joka mys
toimitti kirjan painokuntoon.
Kiitmme kaikkia kirjan sisltn vaikuttaneita tytovereitamme. Janne
Kontkanen antoi hyvi kommentteja oppikirjan ensimmisest raakaversiosta. Yrj Leino, Esko Jrvinen, Jari Jrvinen ja Peter Rback oikolukivat
teosta, mist heille kiitokset. Lisksi Jukka Korpela, Raija Kukkonen ja Markku Lindroos TKK:n atk-keskuksesta antoivat palautetta oppikirjan sisllst

Fortran 95/2003

ja esitystavasta. Erityisesti Jukka Korpelan kommentit ja parannusehdotukset auttoivat selkeyttmn teoksen sislt.
HPF: ksittelev luku perustuu Jussi Heikosen ja Janne Kontkasen kirjoittamaan artikkeliin. OpenMP:n esittely perustuu Jussi Heikosen kirjoittamaan artikkeliin. Kari Vasko osallistui Fortran 2003: esittelevn tekstin
aiemman version kirjoittamiseen. Olemme ottaneet mys huomioon CSC:n
jrjestmilt Fortran-kursseilta tulleen palautteen.
Toista painosta oikolukivat Tiina Kupila-Rantala, Yrj Leino ja Ville Savolainen. Kolmanteen painokseen listtiin ja laajennettiin muutamia harjoitustehtvi, ja samalla erit kirjan esimerkkej muokattiin numeerisesti stabiilimmiksi. Neljnness painoksessa keskitytn Fortran 95:een ja hahmotellaan uuden Fortran 2003:n sislt. Samalla joitakin virheit on korjattu
ja teksti stilisoitu. Erityiset kiitoksemme ansaitsee Robert Pich, jonka jrjestm Fortran-kurssi TTKK:ssa tuotti runsaasti palautetta ja korjausehdotuksia. Lisksi Janne Ignatius, Jussi Rahola ja Satu Tissari antoivat arvokasta
palautetta teoksen kolmatta painosta valmisteltaessa.
Otamme mielellmme vastaan kaikki thn kirjaan liittyvt kommentit. Ne
voi osoittaa Juha Haatajalle shkpostiosoitteeseen Juha.Haataja@csc.fi.
Toivotamme teoksen lukijoille lytmisen iloa sek antoisia hetki Fortranin
parissa.
Espoossa 24.4.2007
Tekijt

Sislt

Sislt
Esipuhe

Teoksen sislt ja merkinnt


1.1
Oppikirjan sisllst . . . . . . . . . . . . . . . . . . . . . . . . .
1.2
Kytetyt merkinnt ja esimerkkiohjelma . . . . . . . . . . . .
1.3
Listietoja . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

12
12
13
14

Ohjelmoinnin perusksitteet
2.1
Mihin tarvitaan ohjelmointia . . . .
2.2
Tietokoneet ja ohjelmointi . . . . .
2.3
Mit tarkoitetaan tietokoneella . .
2.4
Ohjelmoinnin eri vaiheet . . . . . .
2.5
Ohjelman kntminen ja linkitys
2.6
Yhteenveto . . . . . . . . . . . . . . .

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

16
16
17
17
18
19
21

Johdatus ohjelmointiin Fortran 95:ll


3.1
Fortran-kielen merkitys . . . . . . . . . .
3.2
Fortran 95:n trkeimmt uudet piirteet
3.3
Miksi Fortran-kielt kannattaa kytt .
3.4
Fortran-ohjelman perusrakenne . . . . .
3.5
Tunnusten nimeminen . . . . . . . . . .
3.6
Vakiot ja peruslaskutoimitukset . . . .
3.7
Muuttujat . . . . . . . . . . . . . . . . . .
3.8
Muuttujien tyypit . . . . . . . . . . . . . .
3.9
Sijoituslauseet . . . . . . . . . . . . . . .
3.10 Omien funktioiden mrittely ja kytt
3.11 Toistorakenteet . . . . . . . . . . . . . . .
3.12 Taulukot ja rakenteiset tyypit . . . . . .
3.13 Tulostuksen muotoilu . . . . . . . . . . .
3.14 Esimerkki: asuntolainan lyhennykset .
3.15 Ehtolauseet . . . . . . . . . . . . . . . . .
3.16 Esimerkki: simulointitehtv . . . . . . .
3.17 Fortran-kielen moduulit . . . . . . . . . .
3.18 Listietoja . . . . . . . . . . . . . . . . . .
3.19 Yhteenveto . . . . . . . . . . . . . . . . . .

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

23
23
24
25
25
26
27
28
29
29
30
32
33
35
35
37
38
40
42
42

Ohjelman perusrakenne
4.1
Lhdekoodin muoto . . . . . . . . . . . . . . . . . . . . . . . . .
4.2
Fortranin merkkivalikoima . . . . . . . . . . . . . . . . . . . . .
4.3
Muuttujien ja vakioiden nimet . . . . . . . . . . . . . . . . . . .

44
44
45
46

.
.
.
.
.
.

.
.
.
.
.
.

Fortran 95/2003

4.4
4.5
4.6
5

Ohjelmakoodin lauseiden jrjestys . . . . . . . . . . . . . . . .


INCLUDE-lause ja moduulit . . . . . . . . . . . . . . . . . . . . .
Yhteenveto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

47
48
49

Perustyypit
5.1
Perustyypit ja vakiot . . . . . . . . . .
5.2
Muuttujien mrittely . . . . . . . . .
5.3
Nimetyt vakiot . . . . . . . . . . . . .
5.4
IMPLICIT NONE ja alkukirjainsnt
5.5
Perustyyppien lajimreet . . . . . .
5.6
Esimerkki: sarjan summa . . . . . . .
5.7
Yhteenveto . . . . . . . . . . . . . . . .

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

51
51
53
54
55
56
60
63

Lausekkeet ja sijoituslauseet
6.1
Aritmeettiset lausekkeet . . . . . . .
6.2
Numeeriset standardifunktiot . . . .
6.3
Merkkijonolausekkeet . . . . . . . . .
6.4
Merkkitiedon ksittelyfunktioita . .
6.5
Merkkitieto ja taulukot . . . . . . . .
6.6
Loogiset lausekkeet . . . . . . . . . .
6.7
Esimerkkej loogisista lausekkeista
6.8
Merkkijonojen vertailu . . . . . . . .
6.9
Esimerkki: merkkijonojen lajittelu .
6.10 Sijoituslauseet ja laskentajrjestys .
6.11 Funktioiden sivuvaikutukset . . . . .
6.12 Operaattorien sidontajrjestys . . .
6.13 Yhteenveto . . . . . . . . . . . . . . . .

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

67
67
69
73
74
75
76
77
78
80
81
83
83
83

Ohjausrakenteet
7.1
Mihin tarvitsemme ohjausrakenteita . . . . .
7.2
Ehdollinen suoritus: IF-rakenne . . . . . . . .
7.3
Tapausten ksittely: SELECT CASE -rakenne
7.4
Toisto: DO-rakenne . . . . . . . . . . . . . . .
7.5
Esimerkki: Euklideen algoritmi . . . . . . . .
7.6
Poikkeusten ksittely . . . . . . . . . . . . . .
7.7
Hyppylause: GOTO-lause . . . . . . . . . . . .
7.8
Tiedonsiirron erikoistilanteet . . . . . . . . .
7.9
Yhteenveto . . . . . . . . . . . . . . . . . . . . .

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

86
86
86
88
90
93
94
95
95
96

Funktiot ja aliohjelmat
8.1
Tehtvn jakaminen osiin . . . . . . . . . . . . . . . . .
8.2
Proseduurit, funktiot ja aliohjelmat . . . . . . . . . . .
8.3
Mihin proseduureja kytetn . . . . . . . . . . . . . . .
8.4
Funktioiden mrittely ja kytt . . . . . . . . . . . . .
8.5
Funktion otsikkolauseen muodot . . . . . . . . . . . . .
8.6
Funktion sivuvaikutukset . . . . . . . . . . . . . . . . . .
8.7
Aliohjelmien mrittely ja kytt . . . . . . . . . . . . .
8.8
Todellisten ja muodollisten argumenttien vastaavuus
8.9
Tiedon vlittminen proseduureihin . . . . . . . . . . .
8.10 Paikallisten muuttujien silyminen . . . . . . . . . . . .
8.11 Sisiset ja ulkoiset proseduurit . . . . . . . . . . . . . .
8.12 Rekursio . . . . . . . . . . . . . . . . . . . . . . . . . . . .

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

99
99
100
100
100
102
103
104
105
106
107
108
111

Sislt

8.13
8.14
8.15
8.16
8.17
8.18
8.19
8.20
8.21
8.22

Proseduurit argumentteina . . . . . . . . . . . . .
PURE-mre . . . . . . . . . . . . . . . . . . . . . .
ELEMENTAL-mre . . . . . . . . . . . . . . . . . .
Kutsumuodon mrittely . . . . . . . . . . . . . .
Esimerkki: NAG-aliohjelmakirjaston kytt . . .
Valinnaiset ja avainsana-argumentit . . . . . . .
Esimerkki: keskiarvon laskeminen . . . . . . . .
Milloin tarvitsemme kutsumuodon mrittely
Nkyvyysalueet . . . . . . . . . . . . . . . . . . . .
Yhteenveto . . . . . . . . . . . . . . . . . . . . . . .

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

113
115
115
116
117
118
118
121
121
123

Moduulit ja operaattorit
9.1
Modulaarinen ohjelmointi . . . . . . . . . . . . .
9.2
Ohjelmayksikkjen vliset suhteet . . . . . . . .
9.3
Esimerkki: vakioiden mrittely . . . . . . . . . .
9.4
Esimerkki: pituusyksikkjen muunnokset . . .
9.5
Moduulin kyttminen toisista moduuleista . .
9.6
Julkisuus ja yksityisyys . . . . . . . . . . . . . . .
9.7
USE-lauseen eri kytttavat . . . . . . . . . . . .
9.8
Esimerkki: muunnosohjelma . . . . . . . . . . . .
9.9
Esimerkki: nollakohdan haku . . . . . . . . . . .
9.10 Moduulissa mritellyt globaalit muuttujat . .
9.11 Esimerkki: normaalijakautuneet satunnaisluvut
9.12 Geneeriset proseduurit . . . . . . . . . . . . . . .
9.13 Operaattoreiden mrittely . . . . . . . . . . . .
9.14 Yhteenveto operaattoreiden mrittelyst . . .
9.15 Funktioiden ja operaattorien kytttavat . . . .
9.16 Muita moduulien kyttmahdollisuuksia . . . .
9.17 Yhteenveto . . . . . . . . . . . . . . . . . . . . . . .

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

126
126
127
128
128
131
131
133
134
135
137
138
140
142
147
147
148
148

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

152
152
155
158
159
159
161

11 Taulukot ja osoitinmuuttujat
11.1 Taulukoiden mrittely ja kytt . . . . . . . . . .
11.2 Taulukon alkioiden ksittely . . . . . . . . . . . . .
11.3 Taulukkoalustin . . . . . . . . . . . . . . . . . . . .
11.4 Taulukon alkioiden jrjestys . . . . . . . . . . . . .
11.5 Esimerkki: funktion arvojen talletus taulukkoon
11.6 Taulukoiden vlitys aliohjelmiin . . . . . . . . . .
11.7 Taulukkoarvoiset funktiot . . . . . . . . . . . . . .
11.8 Taulukkojen ksittely kokonaisuuksina . . . . . .
11.9 Taulukkojen ksittely standardifunktioilla . . . .
11.10 FORALL-rakenne . . . . . . . . . . . . . . . . . . . .
11.11 Dynaaminen muistinvaraus . . . . . . . . . . . . .
11.12 Esimerkki: Erastotheneen seula . . . . . . . . . . .
11.13 Osoitinmuuttujat . . . . . . . . . . . . . . . . . . . .

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

164
164
166
168
169
169
170
173
174
174
177
178
182
183

10 Rakenteiset tietotyypit
10.1 Rakenteisten tyyppien mrittely . . . . . .
10.2 Esimerkki: vektorilaskentamoduuli . . . . .
10.3 NULL-funktio . . . . . . . . . . . . . . . . . .
10.4 Julkisuus ja yksityisyys . . . . . . . . . . . .
10.5 Esimerkki: vektorimoduulin jatkokehittely
10.6 Yhteenveto . . . . . . . . . . . . . . . . . . . .

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

10

Fortran 95/2003

11.14 Esimerkki: listarakenne . . . . . . . . . . . . . . . . . . . . . . . 187


11.15 Yhteenveto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 189
12 Tiedon sytt ja tulostus
12.1 Sytt ja tulostus . . . . . . . . . . . . . . . . . . . .
12.2 Yksinkertaiset sytt- ja tulostuslauseet . . . . .
12.3 Listan ohjaama muotoilu . . . . . . . . . . . . . . .
12.4 Sytt- ja tulostuslauseet . . . . . . . . . . . . . . .
12.5 Muotoilukoodit . . . . . . . . . . . . . . . . . . . . .
12.6 Muotoilukoodien yhdistminen ja tulostuslistat .
12.7 Tietueen osan tulostaminen tai lukeminen . . . .
12.8 Tiedostojen ksittely . . . . . . . . . . . . . . . . .
12.9 Luku merkkijonoista ja kirjoitus merkkijonoihin
12.10 NAMELIST-rakenne . . . . . . . . . . . . . . . . . . .
12.11 Virhetilanteiden ksittely . . . . . . . . . . . . . . .
12.12 Yhteenveto . . . . . . . . . . . . . . . . . . . . . . . .

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

192
192
193
194
196
197
202
204
204
209
210
210
211

13 Standardiproseduurit
13.1 Standardiproseduurien perusksitteit
13.2 Standardiproseduurien kuvauksia . . .
13.3 Esimerkki: kellonaika ja pivmr . .
13.4 Esimerkki: satunnaisluvut ja kellonaika
13.5 Esimerkki: bittienksittelyoperaatiot . .
13.6 Esimerkki: data pakkaaminen . . . . . .
13.7 Standardifunktioiden erityisnimet . . .
13.8 Listietoja . . . . . . . . . . . . . . . . . .
13.9 Yhteenveto . . . . . . . . . . . . . . . . . .

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

215
215
216
218
219
226
227
229
230
231

14 Esimerkkiohjelmia
14.1 Nelilaskin . . . . . . . . . . . . . . . . . . . . . . . . . . . .
14.2 Datan lajittelu . . . . . . . . . . . . . . . . . . . . . . . . . .
14.3 Sokkelon tekeminen . . . . . . . . . . . . . . . . . . . . . .
14.4 Yksiulotteinen soluautomaatti . . . . . . . . . . . . . . .
14.5 Life-soluautomaatti . . . . . . . . . . . . . . . . . . . . . .
14.6 Binripuu . . . . . . . . . . . . . . . . . . . . . . . . . . . .
14.7 Sukupuu . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
14.8 Optimointitehtvien ratkaiseminen: evoluutiostrategiat
14.9 Lineaaristen yhtlryhmien ratkaisu . . . . . . . . . . . .

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

234
234
237
243
247
253
256
261
263
269

15 Siirtyminen Fortran 95 -kieleen


15.1 Miten Fortran 95:een kannattaa siirty . . . . . . . . . .
15.2 Fortran 95:n uudet piirteet . . . . . . . . . . . . . . . . . .
15.3 FORTRAN 77:n huonoja puolia . . . . . . . . . . . . . . .
15.4 FORTRAN 77 -koodien muuntaminen uuteen muotoon
15.5 Ohjausrakenteiden kytt . . . . . . . . . . . . . . . . . .
15.6 COMMON-alueiden korvaus moduuleilla . . . . . . . . .
15.7 Fortranin vanhentuvat piirteet . . . . . . . . . . . . . . .
15.8 Vltettvi Fortranin piirteit . . . . . . . . . . . . . . . .

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

275
275
276
276
279
281
283
284
287

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

16 Fortran 2003
291
16.1 Fortran 2003:n trkeimmt lisykset ja parannukset . . . . . 291
16.2 Fortran 2003 ja olio-ohjelmointi . . . . . . . . . . . . . . . . . 295

Sislt

16.3
16.4

11

Fortran 2003:n tuki olio-ohjelmoinnille . . . . . . . . . . . . . 296


Listietoa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 298

17 Rinnakkaislaskenta HPF:ll ja OpenMP:ll


17.1 Vlineit rinnakkaislaskentaan . . . . . . . . . . . . . . . . . .
17.2 HPF-ohjelmoinnin perusteet . . . . . . . . . . . . . . . . . . . .
17.3 OpenMP: rinnakkaislaskenta yhteisen muistin koneissa . . .

299
299
299
305

Liite

309

Ohjelmoinnin kultaiset snnt


A.1
Yleisohjeita . . . . . . . . . . .
A.2
Ohjausrakenteet . . . . . . . .
A.3
Mrittele . . . . . . . . . . . .
A.4
Strukturoi . . . . . . . . . . . .
A.5
Standardoi . . . . . . . . . . . .
A.6
Dokumentointi . . . . . . . . .
A.7
Luettavuus . . . . . . . . . . . .
A.8
Vianetsint ja koodin testaus
A.9
Ohjelmakoodin optimointi . .
A.10 Sytt ja tulostus . . . . . . . .
A.11 Yhteenveto . . . . . . . . . . . .

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

310
310
310
311
311
311
311
312
312
313
313
314

Fortran 95:n hyvt, pahat ja rumat piirteet


315
B.1
Hyvt piirteet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 315
B.2
Toisinaan hydylliset piirteet . . . . . . . . . . . . . . . . . . . 316
B.3
Mahdollisesti vaaralliset piirteet . . . . . . . . . . . . . . . . . 316

Yhteenveto Fortran 95 -kielen lauseista

317

ASCII-merkist

324

Sanasto

325

Kirjallisuutta

329

Hakemisto

331

12

Fortran 95/2003

Teoksen sislt ja
merkinnt

Kerromme tss luvussa oppikirjan tarkoituksesta ja annamme neuvoja lukijalle. Kappaleesta 1.2 lydt kirjassa kytetyt merkinttavat.

1.1

Oppikirjan sisllst

Tm oppikirja on tiivis johdatus ohjelmointiin Fortran-kielell. Teos on


tarkoitettu opiskelijoille sek yliopistojen ja yritysten tutkijoille. Kirjaa voi
kytt oppikirjana Fortran-kursseilla ja itseopiskeluun.
Luotamme esimerkin voimaan. Siksi asiat on pyritty havainnollistamaan esimerkeill. Kunkin luvun loppuun on sijoitettu muutamia tehtvi tai kysymyksi luvun aihepiirist. Teoksessa on tehtvi erityisesti tieteellisen ja
teknisen laskennan alalta.
Tm kirja on tarkoitettu luettavaksi lpi jrjestyksess alusta loppuun. Koska Fortran-kielen monet piirteet liittyvt useampaan kuin yhteen aihepiiriin,
kydn oppikirjan alussa nopeasti lpi kielen trkeimmt piirteet. Toisaalta kukin luku on suunniteltu yhteniseksi kokonaisuudeksi, joten teosta voi
kytt mys ksikirjan tapaan.
Korostamme ohjelmoinnin kytnnn seikkoja, kuten koodin luettavuutta,
selkeytt ja yllpidettvyytt. Pyrimme esittmn kokonaisia ohjelmaesimerkkej pienten ohjelmakoodin katkelmien sijaan. Tmn ansiosta voit
helposti kokeilla esimerkkien toimintaa kytnnss. Esimerkkiohjelmat on
tarkoitettu luettavaksi yhdess niihin liittyvn tekstin kanssa. Tst johtuen
esimerkeiss on vhemmn selittvi kommentteja kuin itsenisiss ohjelmakoodeissa tulisi olla.
Luvussa 15 kydn lpi Fortran-kielen vanhempia piirteit. Luvussa kerrotaan, miten nm usein virheit aiheuttavat kielen piirteet voi korvata uuden
Fortran-standardin paremmilla ominaisuuksilla.

1. Teoksen sislt ja merkinnt

13

1.2 Kytetyt merkinnt ja esimerkkiohjelma


Fortran-kielen omat tunnukset on tss teoksessa kirjoitettu isoilla kirjaimilla. Kyttjn mrittelemien muuttujien ym. symbolien nimet on kirjoitettu
pienill kirjaimilla. Suosittelemme tt kytnt muutoinkin sovellettavaksi, vaikka se ei ole osa Fortranin mritelm.
Seuraavassa on esimerkki Fortran-ohjelmakoodista:
PROGRAM testaus
IMPLICIT NONE
REAL :: x
INTRINSIC SIN
WRITE (*,*) Anna x:
READ (*,*) x
WRITE (*,*) x = , x, SIN(x) = , SIN(x)
END PROGRAM testaus

Tm ohjelma lukee luvun x arvon ja tulostaa sinifunktion arvon SIN(x).


Fortran-ohjelmien yksityiskohtiin palataan seuraavissa luvuissa.
Tss teoksessa kytetn kirjasintyyppi teletype, kun esitetn ohjelmakoodia tai tietokoneen tuottamaa tulostusta. Kuitenkin jos halutaan selvsti
erottaa, mik teksti on kehotetta, mik kyttjn sytett ja mik tietokoneen tulostusta, on kytetty seuraavanlaisia kirjasintyyppej:
% f90 testaus.f90
% ./a.out
Anna x:
5
x =
5.0000000 SIN(x) =

-0.9589243

Tss tietokoneen kehote (prompt, merkki % yll) ja tulostus on ladottu


teletype-kirjasintyypill, ja kyttjn antamat komennot ja sytteet on
lihavoitu. Tss esimerkiss knsimme ohjelmakoodin testaus.f90 komennolla f90 ja ajoimme syntyneen ajokelpoisen ohjelman a.out. Ohjelmalle annettiin sytteen luku 5.
Teoksessa kytetn knnskomentoa f90, joka useissa jrjestelmiss on
identtinen komennon f95 kanssa. Fortran-ohjelmien tiedostopte voi olla
esimerkiksi .f90, .f95, .f03 tai .f riippuen kntjst.
Sellaiset nimet, joiden paikalle tulee kirjoittaa esimerkiksi tiedoston nimi,
on ladottu vinokirjaimilla:
f90 tiedosto.f90

Jos jokin mrittelyn osa voidaan jtt pois, kytmme hakasulkumerkint ([]) seuraavaan tapaan:
funktio([argumentti][, argumentti]...)

Kolme pistett (...) tarkoittaa tss nolla, yksi tai useampi kertaa toistettavaa osaa. Seuraavat ovat esimerkkej edellisen merkinnn kuvaamista funktiokutsuista:
h()

14

Fortran 95/2003

h(a)
h(a,b)
h(a,b,c)

Tekstiss esiintyvt lyhenteet ja erisnimet on yleens kirjoitettu kuten Fortran. Uusien termien esittelyyn ja muutenkin tekstin korostamiseen on kytetty kursiivia.
Tmn oppikirjan Fortran 90 -kielisten esimerkkiohjelmien kntmiseen ja
testaamiseen on kytetty NAGWareTM f90 -kntj, joka on aito Fortran 90
-kntj eik sisll standardin ulkopuolisia laajennuksia. Ohjelmat on testattu mys Macintoshissa kytten Absoftin Fortran 90 -kntj. Fortran 95 -piirteit sisltvt ohjelmat on testattu Digital Fortran 95 -kntjll.
Oppaan neljnness painoksessa on kytetty kntjn Sun Studio 9 Fortran 95 -kntjn versiota 8.0, joka ei tue Fortran 2003:n uusia piirteit.

1.3

Listietoja

Tmn teoksen aihepiirist lydt listietoja World Wide Web -jrjestelmst


eli www:st osoitteesta
http://www.csc.fi/oppaat/f95/

Tarjolla on mm. oppikirjan esimerkkiohjelmien lhdekoodeja, harjoitustehtvien ratkaisuja, mahdollisten painovirheiden korjauksia sek tietoa CSC:n
tarjoamista palveluista. Annamme mys viitteit muihin Fortrania ja ohjelmointia ksitteleviin www-palveluihin.
CSC:n www-kotisivu on osoitteessa
http://www.csc.fi

Hydyllisi Fortran-kielen ksikirjoja ovat teokset Fortran 95/2003 Explained [MRC04], Fortran 95 Language Guide [Geh96] ja Fortran 95 Handbook
[ABM+ 97]. Johdatukseksi kieleen sek oppikirjaksi sopivat teokset Introduction to Programming with Fortran [CS06] ja Fortran 90 Programming [EPL94].
Teos Programmers Guide to Fortran 90 [BGA94] on hyvin laaja ja monipuolinen johdatus Fortran-ohjelmointiin. Teos Migrating to Fortran 90 [Ker93]
sopii ohjenuoraksi vanhojen Fortran-koodien muuntamiseen uuden standardin mukaisiksi. Fortran 90:een perustuvaa F-kielt ksitelln teoksessa The
F Programming Language [MR96]. Artikkelissa The New Features of Fortran
2003 [Rei] kuvataan tiiviiss muodossa Fortran 2003:n uudet piirteet.
Fortran-standardeihin liittyvi dokumentteja lydt www-osoitteesta
http://www.nag.co.uk/sc22wg5/

Tietokoneohjelmissa kytettyj algoritmeja on esitelty mm. teoksissa Introduction to Algorithms [CLR90] ja Algorithms [Sed84].
CSC on tmn Fortran 95/2003 -oppikirjan lisksi julkaissut mm. matemaattista mallintamista ja numeerisia menetelmi ksittelevi teoksia:

1. Teoksen sislt ja merkinnt

15

Alkurjhdyksest knnykkn: nkkulmia laskennalliseen tieteeseen


[Haa02]
Laskennallinen tuotekehitys: suunnittelun uusi ulottuvuus [HJKR02]
Sillanrakennuksesta lkeainesuunnitteluun: Matemaattinen mallintaminen suomalaisissa yliopistoissa [HJL00]
Numeeriset menetelmt kytnnss [HHL+ 02]
Optimointitehtvien ratkaiseminen [Haa04]
Datan ksittely [Kar01].

16

Fortran 95/2003

Ohjelmoinnin
perusksitteet

Kerromme tss luvussa tietokoneen toiminnasta ja ohjelmointikielten kytst tietokoneen ohjaamiseen.

2.1

Mihin tarvitaan ohjelmointia

Ohjelmointitaito on ers trkeimmist teknis-tieteellisill aloilla tarvittavista


osaamisen alueista. Ohjelmointi ei tietenkn ole pmr sinns, vaan
vline, jonka avulla voidaan ratkoa kytnnss esiin tulevia tehtvi.
Ohjelmistoja tarvitaan lentokoneiden ja autojen suunnitteluun, prosessien
ohjaamiseen, datan kermiseen instrumenteista ja ihmisen geeniperimn
selvittmiseen (katso CSC:n julkaisemia teoksia [Haa02, HJKR02, HJL00]).
Jopa matematiikan teoreemojen todistamisessa kytetn apuna tietokoneohjelmia. Autojen, kameroiden ja pyykinpesukoneiden ohjelmistot tekevt nist laitteista monipuolisempia ja lykkmpi vaikkakin parantamisen varaa viel on!
Kaikkea ei kuitenkaan ole valmiiksi ohjelmoitu. Varsinkin eturivin tutkimusja tuotekehitystehtviss tarvitaan ohjelmointia ja olemassaolevien koodien
muokkausta. Kaikkea ei tietenkn tarvitse keksi uudestaan, sill on hyv ohjelmointityyli kytt apuna valmiiksi testattuja ja luotettavia ohjelmistoja. Olemassaolevien ohjelmistojen muokkaaminen tehtvn ratkaisemiseksi onkin tyypillist ohjelmoijan tykentt.
Silloin tllin kuulee vitteen: Jos osaan oman sovellusalueeni tarpeeksi hyvin, ei minun tarvitse osata ohjelmoida. Kun tehdn jotain uutta ja ennen
ratkaisematonta, ohjelmointia tarvitaan kuitenkin monilla aloilla muodossa tai toisessa. Lisksi erillisen ohjelmoijan palkkaaminen sovellusalueen
osaajan lisksi on melko kallista.

2. Ohjelmoinnin perusksitteet

17

2.2 Tietokoneet ja ohjelmointi


Vaikka tietokoneiden perusrakenne ei ole muuttunut paljonkaan viimeisten
40 vuoden aikana, on tietokoneiden kyttalue laajentunut nopeasti. Tm
voidaan lukea sek koneiden halpenemisen ett ohjelmointikielten kehittymisen ansioksi, sill ohjelmointia tarvitaan tietokoneiden kaikkien toimintojen ohjaamiseen.
Tietokoneen ymmrtm kieli koostuu joukosta konekskyj, joita suorittamalla tietokone saadaan tekemn haluttuja asioita. Tietokoneet pystyvt
suorittamaan miljardeja konekskyj sekunnissa. Erityyppiset tietokoneet
ymmrtvt kukin omaa konekieltn, joten konekielisen ohjelman siirto
koneesta toiseen on hankalaa ellei perti mahdotonta. Lisksi ohjelmointi
konekskyjen avulla on aikaaviev ja virhealtista puuhaa.
Tietokoneen ohjaaminen hoidetaan konekielen sijaan yleens ohjelmointikielen avulla. Tm mahdollistaa konekielt paremman ohjelmistojen siirrettvyyden. Ohjelmointikielen kntj (compiler) hoitaa automaattisesti
konekskyjen tuottamisen ohjelmakoodista. Tten ohjelmointikielen kytt
helpottaa huomattavasti tehtvien ratkaisua tietokoneella. Tm ei tarkoita
tietenkn sit, etteik ohjelmoinnissa silti tarvittaisi suurta huolellisuutta
ja tarkkuutta: kntj osaa lukea vain tarkasti ohjelmointikielen sntj
noudattavaa koodia eik pysty arvaamaan ohjelmoijan alkuperist tarkoitusta, jos sit ei ole ilmaistu yksiksitteisesti.
Eri ohjelmointikielet on suunniteltu erilaisiin tehtviin. Ohjelmointikieli on
kehitetty tuhansia, mutta vain muutamat niist ovat laajassa kytss.

2.3 Mit tarkoitetaan tietokoneella


Kuvassa 2.1 on esitetty tietokoneen toiminta yksinkertaisena kaaviona.

keskusmuisti

nppimist

keskusyksikk

kuvaruutu

tiedostot
Kuva 2.1: Tietokoneen toiminnan periaate.

Tietokoneen keskusyksikk toimii ohjelmakoodin sisltmien kskyjen mukaisesti. Ohjelma lukee tavallisesti joukon sytttietoja, jotka vaihtelevat ohjelman ajokerrasta toiseen. Ohjelma lukee sytttiedot sisn esimerkiksi

18

Fortran 95/2003

tiedostoista tai kyttjn nppimistlt. Sytttietoja voisivat olla esimerkiksi shavaintopisteist kertyt mittausarvot kuten lmptila, ilmanpaine
ja kosteus. Sennusteen laskemiseen kehitetty ohjelma voisi niden sytttietojen perusteella laskea ja tulostaa ennusteen seuraavien 48 tunnin ajaksi.
Tietokoneen keskusyksikk ksittelee keskusmuistissa sijaitsevaa dataa. Esimerkiksi sennusteen laskennan eri vaiheissa tarvittava data voidaan pit
tietokoneen keskusmuistissa. Keskusyksikk voi mys kirjoittaa ja lukea
tiedostoja, jotka mahdollistavat tiedon silyttmisen ohjelman suorittamisen loppuessa: kun ohjelman suoritus pttyy, kytetn vapautunut keskusmuisti seuraavien ohjelmien ajamiseen. Tt ennen tulee laskentatulokset tallettaa tiedostoihin, sill pysyvien tiedostojen sislt silyy ohjelman
suorittamisen loputtuakin. Tiedostojen ksittely on yleens hitaampaa kuin
keskusmuistin kytt.
Keskusyksikk voi tiedostoihin tallettamisen lisksi tulostaa tietoa kyttjn laitteen (ptteen, mikron tai tyaseman) kuvaruudulle. Tekstimuotoisen
tulostuksen lisksi tietokoneeseen voi olla kytkettyn graasten kuvaajien
piirtmiseen kykenevi laitteita.

2.4

Ohjelmoinnin eri vaiheet

Kun kehitt ohjelmistoa annettuun tehtvn, pid mieless seuraavat ohjelmoinnin vaiheet:
1. tehtvn tavoitteen mrittely
2. tehtvn analyysi ja ohjelmakoodin suunnittelu
3. ohjelmakoodin kirjoittaminen
4. testaaminen ja virheiden korjaaminen
5. dokumentointi
6. ohjelmiston kytt ja yllpitminen.
Tehtvn mrittelyss selvitetn tarkkaan, mit ohjelman tulisi tehd.
Mrittely kannattaa tehd huolellisesti, jottei koodia kirjoitettaessa tai testattaessa tarvitsisi huomata, ettei ohjelmisto ratkaise annettua tehtv.
Ohjelmoinnin trkein vaihe on tehtvn analyysi ja ohjelmakoodin suunnittelu. Ennen ohjelmakoodin kirjoittamista tytyy selvitt mm. ohjelmassa kytetyt tietorakenteet ja algoritmit. Thn vaiheeseen kannattaa kytt
runsaasti aikaa pienesskin tehtvss. Suunnitteluun kytetty aika saadaan
yleens runsain mitoin takaisin ohjelmakoodin kirjoittamisessa ja testaamisessa. Tehokas ja luotettava ratkaisumenetelm mys vhent ohjelman
korjailemiseen tarvittavaa aikaa.

2. Ohjelmoinnin perusksitteet

19

Kirjoita ohjelmakoodi pienin osasina, joista kukin tekee oman tehtvns


hyvin. Kyt Fortran 95:n kehittyneit piirteit ohjelmakoodin modulaarisuuden lismiseksi. Minimoi riippuvuudet ohjelmiston eri osasten vlill ja
dokumentoi koodisi huolellisesti.
Kun kukin ohjelman osa on saatu koodattua, testaa sit ensin erikseen. Kiinnit testaukseen huomiota jo varhaisessa vaiheessa. Kytetyt testiaineistot
ja testiohjelmistot on syyt silytt ohjelmiston myhemp testausta varten. Hyvst testiaineistosta on apua esimerkiksi siirrettess ohjelmisto
uudelle koneelle tai tehtess ohjelmistoon muutoksia myhemmin.
Aloita ohjelmakoodin dokumentointi jo tehtvn mrittelyvaiheessa. Pid
mys huolta ohjelmakoodin luettavuudesta. Kyt sisennyst ja tyhji rivej
apuna koodin selkeyttmisess. l kommentoi suoraan ohjelman lauseiden
toimintaa, vaan kerro, mit ohjelmiston eri osissa tapahtuu.
Muista ohjelmoinnin kaikissa vaiheissa, ett ohjelmiston yllpitoon voi kulua
vuosien aikana paljon enemmn aikaa kuin ohjelmiston kehittmiseen alunperin. Pienillkin ohjelmakoodin ptkill on taipumus osoittautua yllttvn
pitkikisiksi. Ehkp vuosien kuluttua tytoverisi tulee kysymn, mit kehittmsi ohjelmakoodi oikeastaan tekee. Siis: dokumentoi huolellisesti ja
kirjoita selke koodia. Vlt kikkailua!

2.5 Ohjelman kntminen ja linkitys


Tmn kohdan sislt on oppaan muita osia riippuvaisempi kytettviss
olevasta Fortran-kntjst ja koneen kyttjrjestelmst. Tss esitellyt
asiat ptevt kuitenkin useimmissa koneympristiss pienin muutoksin.
Kuvassa 2.2 on esitetty ajettavan ohjelman tekemiseen tarvittavat vaiheet.
Kytnnss Fortran-koodista saa ajokelpoisen ohjelman seuraavasti:
1. kirjoita suunnitelman mukainen ohjelmakoodi yhteen tai useampaan
tiedostoon
2. knn lhdekooditiedostot objektikooditiedostoiksi
3. linkit objektikooditiedostot ja mahdolliset aliohjelmakirjastot suorituskelpoiseksi ohjelmaksi
4. aja syntynyt ohjelma ja tarkista tulokset.
Yksinkertaisissa tapauksissa voi kntmisen ja linkittmisen (vaiheet 2 ja 3)
tehd samalla komennolla.
Selvit kyttmsi koneen kyttoppaasta tai ksikirjasta, miten kntminen, linkittminen ja ohjelman ajaminen kytnnss tapahtuu. Useimmissa Unix-ympristiss yhdess tiedostossa olevan Fortran-ohjelmakoodin
kntminen ja ajaminen onnistuu seuraavasti:
% f90 tiedosto.f90
% ./a.out

20

Fortran 95/2003

Tss knnskomento oli nimeltn f90. Jos kytettviss on Fortran 95


-kntj, on knnskomento luultavasti nimeltn f95. Lhdekooditiedoston nimi pttyi merkkeihin .f90, mik on tyypillinen kytnt Unixissa.
Syntynyt ajokelpoinen ohjelma a.out ajettiin komennolla ./a.out.
Ohjelman kntminen ja linkittminen ajokelpoiseksi ohjelmaksi voidaan
tehd mys useassa vaiheessa:
%
%
%
%

f90 -c ali.f90
f90 -c ohj.f90
f90 -o ohj ali.o ohj.o -lnag
./ohj

Tss knsimme (ilman linkityst) ohjelmakoodin tiedostoissa ali.f90 ja


ohj.f90. Syntyneet objektikooditiedostot ali.o ja ohj.o linkitettiin ajokelpoiseksi ohjelmaksi nimelt ohj. Otimme ohjelmaan lisksi linkitysvaiheessa mukaan ohjelman kyttmt NAG-aliohjelmakirjaston rutiinit [Haa98].
Kyt ohjelmakoodin kirjoittamiseen sellaista editoria, joka osaa automaattisesti sisent koodirivit ja mahdollisesti mys tunnistaa Fortran 95 -kielen
avainsanat. Emacs on esimerkki tllaisesta editorista.
Kukin ohjelmayksikk (pohjelma, ulkoinen proseduuri tai moduuli) kannattaa yleens kirjoittaa omaan tiedostoonsa. Tllin voit knt kunkin
ohjelmayksikn erikseen, ja tehdesssi muutoksia ei aina ole vlttmtnt
knt koko ohjelmistoa uudestaan. Riitt, ett knnt uudestaan muuttuneet ohjelmayksikt (ja niit kyttvt ohjelmayksikt), jonka jlkeen tarvitsee vain linkitt ohjelmisto ajokelpoiseksi.
Fortran-kntjt tuottavat yleens tarvittaessa knnslistauksen, josta voi
olla hyty jljitettess ohjelmakoodiin pujahtaneita virheit. Kyt kn-

lhdekoodi
INCLUDE-tiedostot
kntj

tarvittaessa
knnslistaus

moduulit
objektikoodi

aliohjelmakirjastot

linkittj

tarvittaessa
linkitystaulu

ajettava
ohjelma
Kuva 2.2: Ohjelman kntmisen ja linkittmisen vaiheet. Moduuleja esitelln
luvussa 9 ja INCLUDE-lausetta kappaleessa 4.5.

2. Ohjelmoinnin perusksitteet

21

nksess lisksi tarkkaa Fortran 95:n mukaista syntaksitarkistusta, sill


kntjkohtaiset kielen laajennukset vhentvt koodin siirrettvyytt. Syntaksitarkistus kytketn plle kntjn valitsimilla.
Fortran-kntjt optimoivat ohjelmakoodia pyydettess. Tllin pyritn
nopeuttamaan koodin suoritusta. Koodin optimointi kannattaa ottaa kyttn varsinaisissa tuotantoajoissa. Varmista sit ennen, ettei optimointi
muuta ohjelman toimintaa ja tuota virheellisi tuloksia.
Knnsaikana voi valita, halutaanko ohjelmaan mukaan ajonaikaiset taulukkorajojen tarkistukset. Yleens nist on niin paljon hyty, ett ne on
syyt kytke plle ainakin ohjelman kehittelyn aikana.
Kun eri ohjelmayksikt on knnetty objektikooditiedostoiksi, tarvitaan viel linkitys, jotta koodia voidaan ajaa. Tss yhteydess ottaa linkittj ohjelmaan mukaan mys mahdollisesti kytettvt aliohjelmakirjastot. Esimerkiksi Fortranin standardifunktiot ja -aliohjelmat linkitetn tarpeen mukaan
ohjelmaan.
Fortran-kielisi ohjelmia voi olla mahdollista linkitt muilla ohjelmointikielill kirjoitettujen ohjelmien yhteyteen. Vastaavasti voi olla mahdollista kutsua Fortranista muilla ohjelmointikielill kirjoitettuja rutiineita. Kytnnn
yksityiskohdat vaihtelevat laitteistosta toiseen.
Koska ohjelmankehityksess harvoin saadaan suoraan kirjoitettua virheetnt koodia, joudutaan ohjelmaa korjailemaan ja tekemn knns uudestaan. Kytettess Fortran 95:n moduuleita ja tyypinmrittelyit monet
virheet lydetn onneksi jo knnsaikana.
Mys linkitysvaiheessa voi ilmet virheit esimerkiksi vrin kirjoitetun aliohjelman nimen vuoksi. Tarvittaessa voit pyyt linkittj tulostamaan linkitystaulun, josta nkyy ohjelmassa kytettyjen proseduurien nimet.
Vaikeimpia jljitettvi ovat vasta ohjelman ajon aikana ilmenevt virheet.
Ohjelma voi lisksi kyttyty vrin vain joissakin erikoistilanteissa. Tmn
vuoksi onkin syyt testata ohjelmakoodia huolellisesti ennen sen ottamista
tuotantokyttn. Jos ohjelma on iso ja monimutkainen, kannattaa sen eri
osia testata erikseen.
Kytettvisssi voi olla mys tutkinta- ja virheenjljitysohjelmia (debugger),
joiden avulla voi etsi ohjelmakoodiin pujahtaneita virheit.

2.6 Yhteenveto
Ohjelmointia tarvitaan tietokoneen kaikkien toimintojen ohjaamiseen.
Trkein ohjelmoinnin vaihe on tehtvn analyysi ja ohjelmakoodin suunnittelu.
Muista mys ohjelmakoodin testaaminen virheiden lytmiseksi.

22

Fortran 95/2003

Harjoitustehtvi
1. Mik on ohjelmointikielen kntjn tehtv?
2. Kokeile kappaleessa 1.2 (sivu 13) esitetyn ohjelmakoodin kntmist
ja ajamista kytettvisssi olevalla laitteistolla.
3. Muuta edellisen tehtvn ohjelmakoodia siten, ett se tulostaa luvun
x nelijuuren SQRT(x). Syt ohjelmalle negatiivinen luku. Mit tapahtuu?

3. Johdatus ohjelmointiin Fortran 95:ll

23

Johdatus ohjelmointiin
Fortran 95:ll

Esittelemme trkeimmt Fortran 95 -ohjelmointikielen piirteet yksinkertaisten esimerkkien avulla. Alussa kerromme lyhyesti Fortran-kielen merkityksest ja historiasta. Luku kannattaa silmill lpi, vaikka Fortran-kielen vanhemmat versiot olisivat tuttuja. Esiteltvt asiat kydn yksityiskohtaisemmin lpi myhemmiss luvuissa.

3.1 Fortran-kielen merkitys


Fortran on vanhin ja eniten kytetty tieteellisen ja teknisen laskennan ohjelmointikieli. Fortran-kielen kehitys lhti kyntiin IBM:ll vuonna 1954, jolloin
esiteltiin suunnitelma ohjelmointikielest nimelt FORmula TRANslator eli
FORTRAN.
Fortranin kytetyimpi versioita ovat olleet FORTRAN IV, FORTRAN 66 ja
FORTRAN 77. Fortran oli mys ensimminen ohjelmointikieli, josta luotiin standardi (FORTRAN 66 vuonna 1966). Vuonna 1978 valmistui viel
nykynkin kytetty vaikkakin jo vanhahtava FORTRAN 77 -standardi.
Fortran 90 -kielen ISO- ja ANSI-standardi hyvksyttiin vuosina 1991 ja 1992
(standardit ISO/IEC1539:1991 ja ANSI X3.198-1992). Tss kirjassa posin
ksiteltv Fortran-kielen versio, Fortran 95, hyvksyttiin vuonna 1997 kansainvliseksi standardiksi (ISO/IEC1539:1997). Kansainvlinen (ISO) ja amerikkalainen (ANSI) standardi ovat sisllltn samat. Teoksessa kytetn
Fortran-nimest kirjoitusasua FORTRAN, jos viitataan aiempiin Fortrankielen versioihin. Kirjoitustapa Fortran tarkoittaa yleens Fortran-kielt ja
erityisesti Fortran 95 -standardin sislt. Uusin standardi, Fortran 2003,
julkaistiin vuonna 2004 (ISO/IEC 1539-1:2004(E)).
Fortran 90 oli merkittv edistysaskel Fortran-kielen kehityksess. Standardi toi Fortranin muiden ohjelmointikielten tasolle. Osaa Fortran 90 -kielen
uusista ominaisuuksista ei edes lydy muista ohjelmointikielist. Fortran 95
oli sen sijaan pienehk pivitys standardiin; suurimmat lisykset liittyvt
rinnakkaislaskentaan.
Uusin standardi eli Fortran 2003 on suuri muutos kieleen sislten mm.

24

Fortran 95/2003

olio-ohjelmointiin liittyvi ominaisuuksia. Seuraava pivitys standardiin on


tynimeltn Fortran 2008, ja tuo tarjolle entisestn uusia piirteit. Tss
oppaassa keskitytn Fortran 95:n sisltn.
Fortran-ohjelmointikieli on kehitetty tehokkaaseen numeeriseen laskentaan.
Koska Fortran 95 sislt osajoukkonaan FORTRAN 77:n, voit kytt vanhoja koodeja osana uutta ohjelmistoa. Lisksi Fortran-kieli soveltuu entistkin
paremmin suuriin ohjelmistoprojekteihin.
Koska Fortran-kieli on standardoitu, on ohjelmakoodi helppo siirt uuteen
tietokoneeseen, jos kyseisell koneella on kytettviss Fortran-kielen kntj. Koodien siirrettvyytt voikin pit yhten ohjelmointikielen trkeimmist ominaisuuksista. Tm korostuu entisestn uusien tietokonesukupolvien seuratessa toisiaan kiihtyvll vauhdilla: vaikka laitteisto vaihtuu,
useimmiten uudessa koneessa halutaan ajaa samoja ohjelmistoja kuin aikaisemminkin.
Fortran-kieli on koodien siirrettvyyden kannalta parhaimpia ohjelmointikieli. Standardia noudattavan ohjelmakoodin siirtminen halutulle koneelle onnistuu usein ilman koodin muuttamista. Koneriippuvia ohjelman osia
ovat lhinn sytt- ja tulostuslauseet sek tiedostojen ksittely.

3.2

Fortran 95:n trkeimmt uudet piirteet

Vanhaan Fortran-kieleen tottuneille nkyvin Fortran 95:n muutos on uuden


lhdekoodimuodon kyttnotto. Tm vapaa lhdekoodin muoto ei en
ole sarakesidonnainen, vaan kielen lauseet voi sijoittaa vapaasti maksimissaan 132 merkin pituisille riveille. Tunnuksien nimiss saa olla 31 merkki
(entisen kuuden sijasta!), ja alaviiva _ on luvallinen symboli tunnuksissa
kirjainten (A . . . Z) ja numeroiden (0 . . . 9) lisksi.
Ohjelmointia tehostavat monipuolistuneet muuttujien mrittelyt. Nyt voi
luoda omia rakenteisia muuttujatyyppej, joiden avulla monet aiemmin hankalasti ratkaistut ohjelmointitehtvt onnistuvat suoraviivaisesti ja helposti.
Lisksi taulukkomuuttujien tilan voi varata ohjelman ajon aikana dynaamisesti, joten aikaisempien Fortran-versioiden vaatimista hankalista tytilavektoreista pstn vihdoin eroon.
Uudet ohjausrakenteet sek esimerkiksi taulukkosyntaksi mahdollistavat
luettavamman ja selkemmn koodin kirjoittamisen. Voit mys kytt taulukkosyntaksia rinnakkaisuuden esittmiseen ohjelmakoodin tasolla.
Moduulirakenteet ja sisiset aliohjelmat mahdollistavat ohjelmakoodin pilkkomisen erillisiin, riippumattomiin osiin (modulaarisuus). Nm ominaisuuksien avulla tehdn mys proseduurien argumenttilistojen knnsaikaisia tarkistuksia. Nm piirteet hydyttvt sek aloittelevaa ohjelmoijaa
ett suurten ohjelmistoprojektien parissa tyskentelevi ammattilaisia.
Fortran-kieli silytt uusien piirteiden lisksi mys perinteiset vahvuuteensa: esimerkkein tehokkuus numeerisessa laskennassa sek monipuoliset ja
tehokkaat sytt- ja tulostustoiminnot.

3. Johdatus ohjelmointiin Fortran 95:ll

25

3.3 Miksi Fortran-kielt kannattaa kytt


Fortran-ohjelmointikielen trkein etu on kielen yleisesti hyvksytty standardi. Koska lhestulkoon kaikille tietokoneille on saatavissa standardin mukainen Fortran-kntj, on koodien siirtminen mahdollista melkein mihin
tahansa koneympristn.
Fortran-kielen vahvuuksia on soveltuvuus numeeriseen laskentaan. Kieless
on paljon sellaisia piirteit, jotka ovat muissa kieliss ylimrisi laajennuksia tai hankalasti listtvi piirteit (esimerkkein kompleksilukujen
aritmetiikka ja taulukkosyntaksi).
Fortran-kielt kehitetn aktiivisesti edelleen. Esimerkiksi rinnakkaislaskentaa varten on kehitetty Fortran 95 -kielen laajennus High Performance Fortran (HPF). Rinnakkaisohjelmointi onnistuu mys OpenMP:n avulla. Rinnakkaislaskentaa ksitelln luvussa 17 (sivu 299).
Fortran 95 -standardi on posin yhteensopiva aiempiin Fortran-kielen standardeihin nhden. Tten aiemmin kehitetyt Fortran-koodit toimivat mys
jatkossa. Pieni osa FORTRAN 77 -kielen piirteist mriteltiin vanhentuneiksi, mutta useimmat Fortran 95 -kntjt sallivat silti niden piirteiden kytn.
Saatavilla on paljon valmiita Fortran-kielisi ohjelmistoja, joita voi kytt
hyvksi omassa ohjelmointityss. Usein ohjelmointi onkin olemassaolevan
koodin muokkaamista uusia tarpeita vastaavaksi.

3.4 Fortran-ohjelman perusrakenne


Ohjelmoimaan oppii vain ohjelmoimalla. Tss teoksessa esitettyj esimerkkej kannattaa kokeilla itse. Pienten muutosten tekeminen esimerkkiohjelmiin on hyv tapa oppia ymmrtmn Fortranin mahdollisuuksia.
Fortran-kielt voi kytt taskulaskimen tapaan tekemn yksinkertaisia
laskutoimituksia. Seuraavassa on alkeellinen esimerkkiohjelma:
PROGRAM laskuesimerkki
WRITE (*,*) 7*52 + 1
END PROGRAM laskuesimerkki

Tm ohjelmakoodi koostuu kolmesta rivist, joista kullakin on yksi Fortranin lause. Lauseessa esiintyvt tunnukset erotetaan toisistaan vlilynneill.
WRITE-lauseen sisentmiseen on kytetty vlilyntej, jolloin koodi on selkempi lukea.
PROGRAM-lause antaa ohjelmalle nimen, ja END PROGRAM -lause lopettaa ohjelmayksikn suorituksen. WRITE-lausetta kytetn tulostukseen. Merkint
7*52 tarkoittaa kertolaskua ja merkki + yhteenlaskua. Lause WRITE (*,*)
7*52 + 1 tulostaa laskutoimituksen 7 52 + 1 tuloksen kuvaruudulle.
Kun olemme kirjoittaneet tmn ohjelmakoodin tiedostoon laskut.f90,

26

Fortran 95/2003

saamme ohjelman knnetty ajokelpoiseksi ohjelmaksi Unix-koneissa tyypillisesti komennolla


% f90 -o laskut laskut.f90

Tiedoston nimi pttyy merkkeihin .f90, mist kntj tunnistaa tiedoston


Fortran-lhdekooditiedostoksi. Jos knnksess ei ollut ongelmia, voimme
ajaa syntyneen ohjelman laskut esimerkiksi seuraavasti:
% ./laskut
365

Ohjelma siis tulosti laskutoimituksen 7 52 + 1 tuloksen.


Fortran-ohjelma voi tulostaa mys merkkijonoja:
PROGRAM laskuesimerkki_2
WRITE (*,*) 7*52 + 1 =, 7*52 + 1
END PROGRAM laskuesimerkki_2

Tmn ohjelman tulostus nytt tlt:


7*52 + 1 = 365

Siis lainausmerkeiss ( . . . ) ollut merkkijono tulostettiin sellaisenaan.

3.5

Tunnusten nimeminen

Edellisess esimerkiss aloitettiin pohjelma PROGRAM-lauseella. Ohjelmakoodi lopetettiin lauseella END PROGRAM. Jokaisessa ajokelpoisessa ohjelmassa on vain yksi pohjelma.
Ohjelman tunnus oli ensimmisess esimerkiss laskuesimerkki. Tunnuksen pituus saa olla korkeintaan 31 merkki, ja tunnukseen kelpaavat kirjaimet A . . . Z, alaviiva _ sek numerot. Nm snnt koskevat kyttjn
mrittelemien tunnusten lisksi mys Fortranin omia tunnuksia.
Tss oppaassa kytetn itse mritellyiss tunnuksissa pieni kirjaimia.
Fortran-kielen kannalta nimet lasku, Lasku ja LASKU ovat identtisi, mutta
pienet kirjaimet parantavat koodin luettavuutta. Fortran 95 -standardi ei
ehdottomasti vaadi, ett kntj hyvksyisi pienet kirjaimet, mutta kaikki
kntjt nyttvt sen tekevn.
Tunnusten nimien on syyt olla jrkevi ja helposti ymmrrettvi. Tunnusten nimeminen on osa ohjelman dokumentointia! Esimerkiksi jos tunnuksen nimi viittaa eri asiaan kuin mihin tunnusta kytetn, aiheuttaa tm
sekaannuksen vaaran.

3. Johdatus ohjelmointiin Fortran 95:ll

27

3.6 Vakiot ja peruslaskutoimitukset


Edellisess esimerkiss laskimme lausekkeen 7*52 + 1 arvon. Lausekkeessa esiintyy kolme kokonaislukuvakiota. Mys lausekkeen tulos 365 on kokonaisluku. Kokonaislukuvakiolla voi mys olla etumerkki (+ tai -), esimerkkin tst on vakio -5.
Kokonaislukujen lisksi Fortran osaa laskea mys reaaliluvuilla. Seuraavassa
tulostetaan reaalilukuvakioita sisltvn laskutoimituksen tulos:
WRITE (*,*) 52.0/7.0

Operaatio / tekee jakolaskun. Tmn lauseen tulostus voisi olla


7.4285712

Tietokone laskee rellisell tarkkuudella. Tss tapauksessa tuloksen tarkkuus on noin kuusi desimaalia, vaikka ohjelma tulostaa kahdeksan numeroa.
Reaalikuluvakiot erotetaan kokonaisluvuista desimaalipisteen avulla. Reaaliluvut voi esitt mys eksponenttiesityksen avulla:
WRITE (*,*) 0.52E2/0.7E1

Tm lauseke tulostaa saman kuin edell. Merkint 0.52E2 tarkoittaa arvoa


0.52 102 = 52.0. Merkint 520E-1 tarkoittaisi mys samaa arvoa: 520
101 = 52.0.
Fortranin ymmrtmt peruslaskutoimitukset ovat +, -, *, / ja **. Nm
operaatiot vastaavat yhteen-, vhennys-, kerto- ja jakolaskua sek potenssiin
korottamista. Esimerkiksi lausekkeen 2**3 arvo on 23 = 8.
Laskutoimitusten tulos riippuu argumenttien tyypist. Lause
WRITE (*,*) 52.0/7.0 =, 52.0/7.0, , 52/7 =, 52/7

tulostaa seuraavaa:
52.0/7.0 =

7.4285712 , 52/7 = 7

Siis kokonaislukujen jakolaskun 52/7 tulos on mys kokonaisluku, ja murtoosa jtettiin huomiotta. Tarkista siis aina jakolaskun argumenttien tyypit!
Jos jakolaskun argumenteista toinen on reaalilukutyyppi, on tuloskin reaaliluku. Siten lausekkeen 52.0/7 arvoksi saadaan 7.4285712.
Sulkumerkkej ( ja ) voi kytt laskutoimitusten ryhmittelyyn:
WRITE (*,*) 52.0/7.0 + 1.0, (52.0/7.0)+1.0, 52.0/(7.0 + 1.0)

Tm lause tulostaa seuraavaa:


8.4285717

8.4285717

6.5000000

Koska jakolasku suoritetaan ennen yhteenlaskua, ovat edell kaksi ensimmist lauseketta identtiset, joten sulkujen kytt ei siis ole vlttmtnt.
Kolmannessa lausekkeessa ovat sulut vlttmttmt, muuten lausekkeen
tarkoitus muuttuu.
Merkkijonovakiot voi esitt joko yksinkertaisilla lainausmerkeill (tyyliin

28

Fortran 95/2003

merkkijono) tai kaksinkertaisilla lainausmerkeill ("merkkijono"). Jos


merkkijono sislt lainausmerkin, tytyy se kahdentaa tai laittaa toisentyyppisten lainausmerkkien sisn:
WRITE (*,*) yhtkki oli kes
WRITE (*,*) "yhtkki oli kes"

3.7

Muuttujat

Edellisiss esimerkeiss kytettiin pelkstn ohjelmakoodiin sijoitettuja


vakioita. Jos ohjelman halutaan laskevan jotain muuta, joudutaan tllin
muuttamaan ohjelmakoodia ja kntmn ohjelma uudestaan. Muun muassa tmn takia ohjelmoinnissa kytetn muuttujia, jotka voivat saada eri
arvoja ohjelman ajokerrasta toiseen.
Olkoon tehtvn kirjoittaa seuraavasti toimiva ohjelma:
1. pyydetn syttmn kaksi lukua
2. luetaan lukuarvot nppimistlt
3. tulostetaan sytetyt luvut ja niiden tulo.
Tm voidaan tehd seuraavalla ohjelmalla:
PROGRAM kertolasku
! Ohjelma lukee kaksi kokonaislukua sek
! laskee ja tulostaa niiden tulon.
IMPLICIT NONE
INTEGER :: i, j
WRITE (*,*) Syt luku i:
READ (*,*) i
WRITE (*,*) Syt luku j:
READ (*,*) j
WRITE (*,*) Luku i:, i
WRITE (*,*) Luku j:, j
WRITE (*,*) Tulo i*j:, i*j
END PROGRAM kertolasku

Huutomerkill (!) alkavat rivit ovat kommentteja ja ne on tarkoitettu ohjeeksi ohjelman lukijalle ja kyttjlle. Mrittely IMPLICIT NONE parantaa
kntjn tekemi virheentarkistuksia. Se on syyt laittaa jokaiseen ohjelmayksikkn otsikkolauseen (PROGRAM tms.) jlkeen. Tm lause poistaa
kytst tunnuksen ensimmiseen kirjaimeen perustuvan tyyppimrittelyn, josta kerrotaan tarkemmin kappaleessa 5.4 (sivu 55).
Ohjelmassa mritelln kokonaislukumuuttujat i ja j rivill
INTEGER :: i, j

Ensimminen WRITE-lause tulostaa kehotteen sytt muuttujalle i arvo.


Nppimistlt lukemiseen kytetn lausetta

3. Johdatus ohjelmointiin Fortran 95:ll

29

READ (*,*) i

Tllin muuttuja i saa arvokseen kyttjn antaman luvun. Sama tehdn


muuttujalle j. Lopuksi ohjelma tulostaa muuttujien i ja j arvot ja kertolaskun i*j tuloksen. Muuttujia voi siis kytt vakioiden tavoin aritmeettisissa
lausekkeissa.
Ohjelman toiminta voisi olla seuraavaa:
Syt luku i:
11
Syt luku j:
13
Luku i: 11
Luku j: 13
Tulo i*j: 143

Kyttj siis sytti ohjelmalle luvut 11 ja 13, ja ohjelma tulosti arvon 11


13 = 143.

3.8 Muuttujien tyypit


Edell mrittelimme kokonaislukutyyppisi (INTEGER) muuttujia. Kokonaislukujen lisksi voimme mritell reaalilukutyypin (REAL) muuttujia:
PROGRAM potenssi
! Ohjelma laskee ja tulostaa numeerisen lauseekkeen arvon.
IMPLICIT NONE
REAL :: k, x
INTEGER :: n
WRITE (*,*) Syt lausekkeen k*x**n luvut k, x ja n:
READ (*,*) k, x, n
WRITE (*,*) Luku k:, k, Luku x:, x, Luku n:, n
WRITE (*,*) Arvo k*x**n:, k*x**n
END PROGRAM potenssi

Muuttujat k ja x ovat reaalilukuja ja muuttuja n on kokonaisluku. Ohjelma


lukee muuttujien arvot, tulostaa ne sek laskee lausekkeen kx n arvon.
Voimme kytt ohjelmaa seuraavasti:
Syt lausekkeen k*x**n luvut k, x ja n:
5.0 1.23 3
Luku k:
5.0000000 Luku x:
1.2300000
Arvo k*x**n:
9.3043346

Luku n: 3

3.9 Sijoituslauseet
Edellisess ohjelmassa luimme tietoa muuttujiin k, x ja n sek tulostimme lausekkeen k*x**n arvon. Yleens tarvitsemme tt monipuolisempia
lausekkeita.

30

Fortran 95/2003

Sijoituslauseella voimme antaa muuttujalle arvon. Seuraavassa on pieni esimerkkiohjelma:


PROGRAM neliojuuri
! Ohjelma laskee nelijuurilausekkeen arvon.
IMPLICIT NONE
REAL :: x, y
INTRINSIC SQRT
WRITE (*,*) Syt luku x:
READ (*,*) x
y = x**2 + 1
WRITE (*,*) Luku x:, x
WRITE (*,*) Arvo x**2 + 1:, y
WRITE (*,*) Arvo SQRT(x**2 + 1):, SQRT(y)
END PROGRAM neliojuuri

Tss ohjelmassa mrittelimme kaksi reaalilukumuuttujaa rivill


REAL :: x, y

Lauseella INTRINSIC SQRT kerrotaan, ett ohjelmassa kytetn Fortranin


standardifunktiota SQRT, joka laskee luvun nelijuuren arvon.
Muuttujaan x luetaan arvo nppimistlt, ja tmn jlkeen sijoitetaan lausekkeen x**2 + 1 arvo muuttujaan y rivill
y = x**2 + 1

Lopuksi ohjelma tulostaa muuttujan y arvon sek lausekkeen SQRT(y) arvon. Seuraavassa on esimerkki ohjelman kytst:
Syt luku x:
2.345
Luku x:
2.3450000
Arvo x**2 + 1:
6.4990253
Arvo SQRT(x**2 + 1):
2.5493186

Ohjelma tulostaa reaaliluvut kahdeksan numeron pituisina. Tulostetuissa


arvoissa on noin kuusi merkitsev desimaalinumeroa, koska reaaliluvuilla
lasketaan rellisell tarkkuudella.

3.10

Omien funktioiden mrittely ja kytt

Edell kytimme Fortranin standardifunktiota SQRT nelijuuren laskemiseksi. Mys omien funktioiden mritteleminen on mahdollista. Seuraavassa ohjelmakoodissa mrittelemme funktion f, joka vastaa matemaattista mritelm f (a) = a2 + 1:
PROGRAM funktio_esimerkki
! Ohjelma lukee reaaliluvun x ja tulostaa
! funktion f arvon tss pisteess.

3. Johdatus ohjelmointiin Fortran 95:ll

31

IMPLICIT NONE
REAL :: x
WRITE (*,*) Syt luku x:
READ (*,*) x
WRITE (*,*) Luku x:, x
WRITE (*,*) Arvo f(x):, f(x)
CONTAINS
FUNCTION f(a) RESULT(f_arvo)
IMPLICIT NONE
REAL :: a, f_arvo
f_arvo = a**2 + 1
END FUNCTION f
END PROGRAM funktio_esimerkki

Tm esimerkkiohjelma toimii seuraavasti:


Syt luku x:
2.345
Luku x:
2.345000
Arvo f(x):
6.499025

Funktion f arvo lasketaan funktiokutsulla f(x). Funktio f on mritelty pohjelman sisisen funktiona CONTAINS-lauseen jlkeen.
Funktioista kerromme lis luvussa 8 sivulta 99 alkaen.
Seuraavassa on ote funktion f mrittelyst:
FUNCTION f(a) RESULT(f_arvo)
REAL :: a, f_arvo

Funktion f muodollinen argumentti a on mritelty funktion otsikkorivill. Muodollinen argumentti saa arvokseen funktiokutsussa f(x) esiintyvn
todellisen argumentin x arvon.
Funktion f arvo lasketaan muuttujaan f_arvo seuraavasti:
f_arvo = a**2 + 1

Sek muodollinen argumentti a ett funktion palauttama arvo f_arvo ovat


tss tapauksessa tyyppi REAL.
Voimme kytt funktiota f esimerkiksi seuraavasti:
y = f(x)
y = f(1.0)
y = f(1.23E2)

Toisaalta funktiokutsut
y = f(1)
y = f(1,0)

johtavat knnsaikaiseen virheeseen: ensimmisess kutsussa argumentti


on vr tyyppi (kokonaisluku) ja toisessa kutsussa on lisksi vr mr
argumentteja.
Knnsaikaiset argumenttilistojen tarkistukset ovat hydyllisi ohjelmankehityksen kaikissa vaiheissa. Jos edellisess esimerkkikoodissa desimaali-

32

Fortran 95/2003

piste olisi muuttunut vahingossa pilkuksi, huomataan virheellinen funktiokutsu f(1,0) jo knnsaikana. Tt ominaisuutta ei ollut FORTRAN 77:ss.

3.11

Toistorakenteet

Fortranissa hoidetaan lauseiden toistaminen DO-silmukkojen avulla. Laskemme esimerkkin lausekkeen




1 x
1+
x
arvot, kun x on 21 , 22 , . . . , 210 eli 2, 4, . . . , 1024. Seuraava ohjelmakoodi tulostaa halutut arvot:
PROGRAM silmukka
IMPLICIT NONE
REAL :: x
INTEGER :: i
DO i = 1, 10
x = REAL(2**i)
WRITE (*,*) arvot:, x, (1 + 1/x)**x
END DO
END PROGRAM silmukka

Muuttuja x on mritelty REAL-tyyppiseksi, jolloin lausekkeessa 1/x vltetn kokonaislukujen jakolasku. Funktiokutsu REAL(2**i) muuttaa puolestaan kokonaislukulausekkeen 2**i reaalilukutyyppiseksi.
DO-silmukka mritelln seuraavasti:
DO silmukkamuuttuja = alaraja, ylraja
lauseet
END DO

Silmukkamuuttuja ky jrjestyksess lpi arvot


alaraja, alaraja + 1, alaraja + 2, . . . , ylraja.
Tss tytyy silmukkamuuttujan sek ala- ja ylrajojen olla kokonaislukutyyppi.
Edellisess esimerkiss silmukkamuuttuja i ky lpi arvot 1, 2, 3, . . . , 10. Ohjelma tulostaa esimerkiksi seuraavaa (tulostusta muotoillaan paremman nkiseksi jatkossa):
arvot:
arvot:
arvot:
arvot:
arvot:
arvot:
arvot:
arvot:

2.0000000
2.2500000
4.0000000
2.4414062
8.0000000
2.5657845
16.0000000
2.6379285
32.0000000
2.6769900
64.0000000
2.6973450
1.2800000E+02
2.7077391
2.5600000E+02
2.7129917

3. Johdatus ohjelmointiin Fortran 95:ll

arvot:
arvot:

5.1200000E+02
1.0240000E+03

33

2.7156320
2.7169557

Kun muuttujan x arvo kasvaa, lhestyy lausekkeen (1 + 1/x)**x arvo matemaattista vakiota e 2.71828.

3.12

Taulukot ja rakenteiset tyypit

Ksittelemme seuraavassa esimerkiss kurssille osallistujien nimi- ja arvosanatietoja. Sijoitamme merkkijonomuuttujiin nimi_1 . . . nimi_4 osallistujien nimet. Nimess saa olla korkeintaan 30 merkki. Arvosanat sijoitamme
muuttujiin arvosana_1 . . . arvosana_4.
CHARACTER (LEN=30) :: nimi_1, nimi_2, nimi_3, nimi_4
REAL :: arvosana_1, arvosana_2, arvosana_3, arvosana_4
REAL :: keskiarvo
nimi_1 = Iiro V.
arvosana_1 = 7.25
nimi_2 = Sauli N.
arvosana_2 = 7.0
jne.

Voimme laskea arvosanojen keskiarvon seuraavasti:


keskiarvo = (arvosana_1 + arvosana_2 + arvosana_3 + &
arvosana_4)/4

Yll merkki & kertoo, ett sijoituslause jatkuu seuraavalle riville.


Jos osallistujia olisi esimerkiksi 120, tytyisi mritell 120 muuttujaa sek nimi ett arvosanoja varten. Yksittisten muuttujien sijaan kannattaakin kytt taulukkoja, joiden alkioiden lukumr ilmoitetaan mreell
DIMENSION:
INTEGER, PARAMETER :: lkm = 120
CHARACTER(LEN=30), DIMENSION(lkm) :: nimet
REAL, DIMENSION(lkm) :: arvosanat

Mrittelimme tunnuksen lkm kokonaislukutyyppisen nimetyn vakion tunnukseksi kyttmll mrett PARAMETER. Vakion lkm arvo on 120. Tmn jlkeen mrittelimme taulukot nimet ja arvosanat, joissa kummassakin on lkm alkiota. Taulukko nimet on merkkijonotyyppi, ja taulukko
arvosanat on reaalilukutyyppi.
Taulukkojen alkioille voimme antaa arvot seuraavasti:
nimet(1) = Iiro V.
arvosanat(1) = 7.25
nimet(2) = Sauli N.
arvosanat(2) = 7.0
jne.

34

Fortran 95/2003

Merkint tunnus(n) viittaa taulukon n:nteen alkioon. Tss esimerkiss taulukkojen indeksi n voi olla vlill 1, 2, . . . , 120.
Fortranin standardifunktio SUM laskee taulukon alkioiden summan. Taulukkoon arvosanat sijoitettujen arvosanojen keskiarvo voidaan siis laskea seuraavasti:
REAL :: keskiarvo
keskiarvo = SUM(arvosanat)/lkm

Edell kytimme erillisi taulukkoja nimille ja arvosanoille. Seuraavassa mrittelemme rakenteisen tyypin, joka sislt erilliset kentt oppilaan nimelle
ja arvosanalle:
TYPE kurssilainen
CHARACTER(LEN=30) :: nimi
REAL :: arvosana
END TYPE kurssilainen

FORTRAN 77 ei sisltnyt mahdollisuutta kytt rakenteisia tyyppej.


Nyt voimme mritell 120 alkiosta koostuvan taulukon, jonka alkiot ovat
tt tyyppi:
INTEGER, PARAMETER :: lkm = 120
TYPE(kurssilainen), DIMENSION(lkm) :: oppilaat

Voimme kytt taulukkoa oppilaat seuraavasti:


oppilaat(1)%nimi = Iiro V.
oppilaat(1)%arvosana = 7.25
oppilaat(2)%nimi = Sauli N.
oppilaat(2)%arvosana = 7.0
jne.

Viittasimme taulukon alkioihin edell, mutta nyt kukin alkio on rakenteista


tyyppi. Tyypin kurssilainen kenttiin viitataan merkinnll alkio % kentt.
Siten lauseke oppilaat(i)%arvosana viittaa i:nnen oppilaan arvosanaan.
Arvosanojen keskiarvo voitaisiin laskea seuraavasti:
keskiarvo = SUM(oppilaat%arvosana)/lkm

Tss merkint oppilaat%arvosana viittaa kaikkien 120 oppilaan arvosanoihin. Merkin % ymprill saa kytt vlilyntej:
keskiarvo = SUM(oppilaat % arvosana)/lkm

Edell esitetty monipuolisempi esimerkki rakenteisten tyyppien kytst


lytyy kappaleesta 14.7 sivulta 261. Tss esimerkiss sytetn tietokoneeseen perheen sukupuu, josta voi tehd hakuja vaikkapa etunimen perusteella.

3. Johdatus ohjelmointiin Fortran 95:ll

3.13

35

Tulostuksen muotoilu

Kappaleen 3.11 esimerkiss tulostettiin merkkijono, reaaliluku ja kokonaisluku kytten lausetta


WRITE (*,*) arvot:, x, (1 + 1/x)**x

Tmn lauseen pitempi muoto on


WRITE (UNIT=*,FMT=*) arvot:, x, (1 + 1/x)**x

Ensimminen thti vastaa oletusarvoista tulostuskanavaa (unit), joka on


yleens kyttjn laitteen nytt. Toinen thti tarkoittaa, ett kntj valitsee tulostettavalle datalle sopivan oletusarvoisen tulostusasun (format).
Oletusmuotoilun * sijaan voidaan tulostusta ohjata mys erityisell muotoilukoodilla. Tulostuslause voi olla seuraava:
WRITE (*,(A6,F7.1,F7.4)) arvot:, x, (1 + 1/x)**x

Ohjelma tulostaa tllin seuraavan:


arvot:
2.0
arvot:
4.0
arvot:
8.0
arvot:
16.0
arvot:
32.0
arvot:
64.0
arvot: 128.0
arvot: 256.0
arvot: 512.0
arvot: 1024.0

2.2500
2.4414
2.5658
2.6379
2.6770
2.6973
2.7077
2.7130
2.7156
2.7170

Merkkijonovakio tulostettiin kuuden merkin levyiseen kenttn (muotoilukoodi A6). Muuttujan x arvo tulostettiin seitsemn merkin levyiseen kenttn
siten, ett desimaalipisteen oikealla puolella oli yksi numero (F7.1). Lauseke
(1 + 1/x)**x tulostettiin siten, ett desimaalipisteen oikealla puolella oli
nelj numeroa (F7.4).

3.14

Esimerkki: asuntolainan lyhennykset

Tasasuuruisin maksuin (annuiteetti) maksettavan lainan kuukausiert P


voidaan laskea lausekkeesta
P=

rL
,
1 (1 + r )m

miss

P = kuukausier

r = R/12, miss R = lainan vuosikorko

L = alkuperinen lainasumma

m = laina-aika kuukausina.

36

Fortran 95/2003

Teemme seuraavasti toimivan ohjelman:


1. luemme lainan vuosikoron prosentteina, alkuperisen lainasumman suuruuden markoissa sek laina-ajan pituuden kuukausina
2. tulostamme kyttjn antamat arvot
3. laskemme kuukausiern suuruuden
4. tulostamme kuukausiern suuruuden.
Seuraavassa on maksuern laskeva ohjelmakoodi:
PROGRAM annuiteetti
! Ohjelma laskee annuiteettilainan kuukausiern.
IMPLICIT NONE
REAL :: vuosikorko, kk_korko, lainasumma, kk_era
INTEGER :: laina_aika
WRITE (*,*) Anna vuosikorko prosentteina (%):
READ (*,*) vuosikorko
WRITE (*,*) Anna alkuperinen lainasumma (mk):
READ (*,*) lainasumma
WRITE (*,*) Anna laina-aika kuukausissa (kk):
READ (*,*) laina_aika
WRITE (*,*) Vuosikorko (%):, vuosikorko
WRITE (*,*) Lainasumma (mk):, lainasumma
WRITE (*,*) Laina-aika (kk):, laina_aika
kk_korko = (vuosikorko/100.0)/12.0
kk_era = kk_korko*lainasumma/ &
(1 - (1 + kk_korko)**(-laina_aika))
WRITE (*,*) Kuukausimaksu (mk):, kk_era
END PROGRAM annuiteetti

Varsinainen laskenta tehdn riveill


kk_korko = (vuosikorko/100.0)/12.0
kk_era = kk_korko*lainasumma/ &
(1 - (1 + kk_korko)**(-laina_aika))

Kytmme tss jatkorivin merkki & osoittamaan, ett sijoituslause jatkuu


seuraavalle riville. Aluksi muutamme prosenteissa annetun vuosikoron kuukausikoroksi kk_korko. Tmn jlkeen laskemme kuukausiern suuruuden.
Huomaa sulkujen kytt!
Seuraavassa on esimerkki ohjelman kytst (muokkaamme tulostusta paremman nkiseksi jatkossa):
Anna vuosikorko prosentteina (%):
8.4
Anna alkuperinen lainasumma (mk):
100000
Anna laina-aika kuukausissa (kk):
36
Vuosikorko (%):
8.3999996

3. Johdatus ohjelmointiin Fortran 95:ll

37

Lainasumma (mk):
1.0000000E+05
Laina-aika (kk): 36
Kuukausimaksu (mk):
3.1521265E+03

Ohjelma siis laski kuukausiern suuruudeksi noin 3152 mk.


Tulostuksessa on hiukan parantamisen varaa. Voimme kirjoittaa tulostuslauseet seuraavaan muotoon:
WRITE
WRITE
WRITE
WRITE

(*,(A20,
(*,(A20,
(*,(A20,
(*,(A20,

F12.2)) Vuosikorko (%):, vuosikorko


F12.2)) Lainasumma (mk):, lainasumma
I12)) Laina-aika (kk):, laina_aika
F12.2)) Kuukausimaksu (mk):, kk_era

Tss kytmme merkin * sijaan muotoilukoodia (A20, F12.2), joka tulostaa merkkijonoja 20 merkin levyiseen kenttn (koodi A20) sek reaalilukuja 12 merkin levyiseen kenttn kahden desimaalin tarkkuudella (koodi
F12.2). Koodi I12 puolestaan tulostaa kokonaislukuja 12 merkin levyiseen
kenttn.
Seuraavassa on esimerkki muutetun ohjelman tulostuksesta:
Vuosikorko (%):
Lainasumma (mk):
Laina-aika (kk):
Kuukausimaksu (mk):

8.40
100000.00
36
3152.13

Tulostus on nyt paljon luettavampaa!

3.15

Ehtolauseet

Fortranin IF-rakenne mahdollistaa ehdollisten toimintojen suorittamisen.


Seuraavassa kytmme IF-rakenteesta muotoa
IF (ehto) THEN
lohko1
ELSE
lohko2
END IF

Tietokone suorittaa lohkoon lohko1 kuuluvat lauseet siin tapauksessa, ett


loogisen lausekkeen ehto arvo on tosi. Muussa tapauksessa tietokone suorittaa lohkon lohko2.
Seuraavassa koodissa testaamme, etteivt kyttjn syttmt arvot ole virheellisi:
IF (vuosikorko > 0 .AND. lainasumma > 0 &
.AND. laina_aika > 0) THEN
! Sytttiedot ovat kunnossa
kk_korko = (vuosikorko/100.0)/12.0
kk_era = kk_korko*lainasumma/ &
(1 - (1 + kk_korko)**(-laina_aika))
WRITE (*,(A20,F12.2)) Kuukausimaksu (mk):, kk_era

38

Fortran 95/2003

ELSE
WRITE (*,*) Virhe: arvojen pit olla positiivisia!
END IF

Vertailulausekkeen vuosikorko > 0 arvo on tosi, jos muuttujan arvo on


suurempi kuin nolla. Totuusarvot voimme yhdist operaattorilla .AND. seuraavasti:
lauseke1 .AND. lauseke2

Tmn lausekkeen arvo on tosi vain silloin, kun kumpikin argumentti on


tosi. Kerromme lis loogisista operaatioista luvussa 6 (sivu 67).
Esimerkin IF-lauseen ehto on tosi vain siin tapauksessa, ett kaikki testatuista muuttujista ovat positiivisia. Kun ehto on tosi, suoritetaan IF-lauseen
ensimminen lohko. Tllin lasketaan ja tulostetaan kuukausittainen maksuer. Muussa tapauksessa (ehto eptosi) suoritetaan IF-lauseen toinen lohko eli tulostetaan kyttjlle virheilmoitus.

3.16

Esimerkki: simulointitehtv

Seuraava tehtv liittyy komponenttien sijoitteluun shkpiirille. Ratkaisussa kytmme hyvksi edellisiss kappaleissa esiteltyj Fortran-kielen piirteit.
Sijoitamme yksikknelin kaksi satunnaista pistett a ja b. Pisteiden koordinaatit ovat a = (a1 , a2 ) ja b = (b1 , b2 ), joille siis ptevt epyhtlt
0 ai 1 ja 0 bi 1, i = 1, 2. Pisteiden vlimatka d on

d = (a1 b1 )2 + (a2 b2 )2 .
Tehtvn on arvioida, mik on etisyyden d odotusarvo E[d]. Kuva 3.1
havainnollistaa tehtv.

1
a

d = (a1 b1 )2 + (a2 b2 )2

d
b
0

1
Kuva 3.1: Simulointitehtvn geometrinen havainnollistus.

Voimme simuloida pisteiden sijoittamista ohjelmalla, joka sijoittaa satunnaisesti n pisteparia yksikknelin ja laskee keskiarvon pisteiden keskinisille etisyyksille:

3. Johdatus ohjelmointiin Fortran 95:ll

39

PROGRAM pisteparit
! Ohjelma simuloi pisteparin sijoittamista
! yksikknelin.
IMPLICIT NONE
REAL, DIMENSION(2) :: a, b
REAL :: d, s = 0.0
INTEGER :: i, n
WRITE (*,*) Anna lukuparien lkm:
READ (*,*) n
WRITE (*,*) Pistepareja:, n, kpl
IF (n > 0) THEN
DO i = 1, n
CALL RANDOM_NUMBER(a)
CALL RANDOM_NUMBER(b)
s = s + SQRT(SUM((a - b)**2))
END DO
d = s/n
WRITE (*,*) Etisyyden keskiarvo:, d
ELSE
WRITE (*,*) Lukumr ei ole positiivinen!
END IF
END PROGRAM pisteparit

Pisteiden koordinaatit sijoitetaan kahden alkion reaalilukutaulukoihin a ja b.


Ohjelmakoodissa on muuttujamrittelyn yhteydess annettu muuttujalle s
alkuarvoksi nolla:
REAL :: d, s = 0.0

Ohjelma pyyt kyttjlt simuloitavien pisteparien lukumrn n. Tmn


jlkeen suoritetaan toistolausetta eli DO-silmukkaa n kertaa. Silmukkamuuttuja i ky jrjestyksess lpi arvot 1, 2, 3, . . . , n toistolauseen eri suorituskerroilla.
CALL-lauseella suoritetaan satunnaislukuja tuottava aliohjelmakutsu seuraavasti:
CALL RANDOM_NUMBER(muuttuja)

Aliohjelma RANDOM_NUMBER palauttaa argumentin muuttuja alkioissa tasajakautuneita satunnaislukuja vlilt [0, 1). Aliohjelman eroavat funktioista
siten, ett funktiolla on arvo, joka vlittyy kutsuvaan ohjelmayksikkn. Tavallisesti funktio vain laskee sille annetuista argumenteista jonkin tuloksen,
kun taas aliohjelman argumentit voivat olla sek lhttietoja ett muuttujia,
joiden kautta aliohjelma vlitt tuloksia kutsuvalle ohjelmayksiklle.
Rivill
s = s + SQRT(SUM((a - b)**2))

listn muuttujan s aikaisempaan arvoon uuden pisteparin etisyys.


Taulukko-operaatio a - b tarkoittaa taulukkojen alkioiden vlisten erotusten laskemista. Lauseke (a - b)**2 laskee alkioiden vlisten etisyyksien
nelit. Funktio SUM laskee tuloksena saadun taulukon arvot yhteen, ja funk-

40

Fortran 95/2003

tio SQRT laskee nelijuuren. Taulukko-operaatioista kerromme enemmn luvussa 11 (sivu 164).
Silmukan jlkeen laskemme etisyyksien keskiarvon d sijoituslauseessa
d = s/n

Seuraavassa on esimerkkej ohjelman kntmisest ja ajamisesta Unix-tietokoneella:


% f90 -o pisteparit pisteparit.f90
% ./pisteparit
Anna lukuparien lkm:
100
Pistepareja: 100 kpl
Etisyyden keskiarvo:
0.5217210
% ./pisteparit
Anna lukuparien lkm:
10000
Pistepareja: 10000 kpl
Etisyyden keskiarvo:
0.5244793

Etisyyksien keskiarvo nyttisi siis olevan noin 0.52. Tarkka odotusarvo on


(katso viitett [RR96])
E[d] =

3.17

1
1
ln(1 + 2) +
(2 + 2) 0.5214 . . .
3
15

Fortran-kielen moduulit

Kun ohjelma on vhnkin laajempi, kannattaa ohjelma jakaa moduuleihin,


joissa kussakin lasketaan jokin tarkasti rajattu laskentatehtvn osa. Moduulissa voidaan mritell mm. nimettyj vakioita, muuttujia, rakenteisia
tyyppej ja proseduureja. Samaa moduulia voidaan usein kytt hyvksi
muissakin yhteyksiss, joten ohjelmoijan tymr vhenee.
Ajatellaan esimerkkin lukuvlin [a, b] jakamista n osaan: (a, a + d, a +
2d, . . . , b d, b) miss d = |b a|/n on osavlien pituus. Ohjelmoimme
pistejoukon laskemista varten funktion nimelt jako, jota kutsutaan seuraavasti:
arvot = jako(a, b, n)

Tss argumentit a ja b ovat reaalilukuja ja argumentti n on kokonaisluku.


Funktio palauttaa arvonaan taulukon, jossa on n+1 alkiota. Voimme sijoittaa
funktion jako moduuliin nimelt taulukko_operaatiot:
MODULE taulukko_operaatiot
CONTAINS
FUNCTION jako(a, b, n)
IMPLICIT NONE
REAL :: a, b
INTEGER :: n
REAL, DIMENSION(n+1) :: jako

3. Johdatus ohjelmointiin Fortran 95:ll

41

REAL :: pituus
INTEGER :: i
pituus = ABS(a-b)
jako = MIN(a,b) + (/ (i, i = 0, n) /)*pituus/n
END FUNCTION jako
END MODULE taulukko_operaatiot

Moduulin mrittely alkaa lauseella MODULE ja pttyy vastaavaan END-lauseeseen. Moduulin sisltm funktio jako kirjoitetaan CONTAINS-lauseen jlkeen.
Moduulissa on kytetty hyvksi Fortran 95:n uusia ominaisuuksia, jotka esitelln myhemmin luvuissa 9 (moduulit) ja 11 (taulukoiden ksittely). Funktion jako mrittelyss kytetty merkint
REAL, DIMENSION(n+1) :: jako

tarkoittaa, ett funktio palauttaa yksiulotteisen taulukon, jonka pituus riippuu argumentista n.
Voimme kytt edell mritelty moduulia seuraavasti:
PROGRAM jakaminen
USE taulukko_operaatiot
IMPLICIT NONE
INTEGER, PARAMETER :: n = 5
REAL, DIMENSION(n+1) :: arvot
REAL :: pii
pii = 4*ATAN(1.0)
arvot = jako(0.0, pii, n)
WRITE (*,*) arvot: , arvot
WRITE (*,*) SIN(arvot): , SIN(arvot)
END PROGRAM jakaminen

Otimme kyttn moduulin taulukko_operaatiot komennolla USE, jonka


on oltava heti ohjelmayksikn alussa. Lauseen ansiosta saamme kyttmme moduulissa mritellyn funktion jako.
Ohjelmassa kytetn Fortranin standardifunktiota ATAN piin likiarvon laskemiseen. Vli [0, ] jaetaan osiin moduulissa taulukko_operaatiot mritellyll funktiolla jako. Funktio SIN laskee sinifunktion arvot jakopisteiss.
Ohjelmamme voidaan knt kahdessa vaiheessa. Ensiksi knnmme moduulin, joka on tiedostossa taulukko_operaatiot.90:
% f90 -c taulukko_operaatiot.f90

Tmn jlkeen knnmme pohjelman tiedostosta jakaminen.f90 ja linkitmme ohjelmaan kyttmmme moduulin:
% f90 jakaminen.f90 taulukko_operaatiot.o

Tmn jlkeen voimme testata ohjelmaamme:


% ./a.out
arvot:
0.0000000E+00 0.6283185
1.256637
1.884956
2.513274
3.141593

42

Fortran 95/2003

SIN(arvot):
0.9510565

0.0000000E+00
0.5877852

0.5877852
0.9510565
-8.7422777E-08

Ohjelma siis jakoi lukuvlin [0, ] viiteen osavliin ja tulosti jakopisteet


sek sin-funktion arvot niss pisteiss.

3.18

Listietoja

Seuraavissa luvuissa kerromme tarkemmin tss luvussa esitellyist Fortran-kielen piirteist. Ksittelemme mm. funktioita ja aliohjelmia, moduuleita, taulukkoja sek rakenteisia tietotyypej. Luvussa 15 (sivu 275) kerromme
Fortranin vanhentuvista ja vltettvist piirteist.

3.19

Yhteenveto

Fortran on eniten kytetty kieli tieteellisess ja teknisess laskennassa.


Fortran-kieliset ohjelmat voi laajasti kytss olevan standardin ansiosta siirt melkein mille tahansa tietokoneelle.
Fortranin tulostus- ja syttlauseet ovat WRITE ja READ.
Muuttujatyypill INTEGER ksitelln kokonaislukuja ja tyypill REAL
ksitelln reaalilukuja. Muuttujatyyppi CHARACTER kytetn merkkijonojen mrittelyyn.
Fortran-kielen standardiproseduurien lisksi voit mritell omia funktioita ja aliohjelmia.
Voit ohjata ohjelman suoritusta DO-silmukan ja IF-lauseen avulla.
Kyt moduuleja ohjelmakoodin jakamiseen helpommin hallittaviin ja
yllpidettviin osiin.

Harjoitustehtvi
1. Kuinka vanha ohjelmointikieli Fortran on tll hetkell?
2. Jos tunnet ennestn Fortran-kielt, etsi kappaleen 1.2 (sivu 13) esimerkist Fortran-kielen uusia piirteit (verrattuna FORTRAN 77 -kieleen).
3. Kirjoita ohjelma, joka laskee sinifunktion SIN(x) arvot pisteiss
{0, /8, 2 /8, . . . , }.

3. Johdatus ohjelmointiin Fortran 95:ll

43

4. Muuta edellist ohjelmaa siten, ett arvot lasketaan pisteiss


{ , 9 /10, 8 /10, . . . , 0, . . . , }.
5. Mrittele seuraava ohjelma:
luetaan kolmion sivujen pituudet a, b, c
lasketaan kolmion pinta-ala A kaavasta

A = p(p a)(p b)(p c),
miss p = (a + b + c)/2
tulostetaan sivujen pituudet ja pinta-ala.
Laske pinta-ala kolmiolle, jonka sivujen pituudet ovat 3, 4 ja 5. Mit
ohjelma tekee, jos sivujen pituudet ovat 3, 4 ja 8? Mik menee pieleen?
6. Testaa edellisess esimerkiss kyttjn antaman syttdatan jrkevyys. Tutki lisksi nelijuuren sisll olevan lausekkeen merkki, ja ilmoita virheest, jos arvo on negatiivinen.
7. Muuta kappaleen 3.14 asuntolainaesimerkki siten, ett kuukausiern
lisksi tulostetaan kuukausittain jljell oleva lainapoma. Huomaa,
ett kuukausiern sisltyy sek lainan korkoa ett lyhennyst. Korko
oletetaan vakioksi laina-ajalla.
8. Muuta kappaleen 3.16 simulointiesimerkki siten, ett mritelmn

d = (a1 b1 )2 + (a2 b2 )2
sijaan etisyyden d laskemiseen kytetn maksiminormia:
d = max |ai bi |.
i{1,2}

9. Muuta kappaleen 3.16 simulointiesimerkki siten, ett etisyyksien dj


keskiarvon lisksi lasketaan mys keskihajonta . Kyt apuna kaavoja
n
=

j=1

dj

n
,

j=1

d2j n 2

n1

Siis saat laskettua keskihajonnan laskemalla etisyyksien neliiden summan.


10. Muuta kappaleen 3.16 simulointiesimerkki siten, ett yksikknelin
sijasta pisteparit sijoitetaan suorakulmioon, jonka sivujen pituudet ovat
(1, t), t 0. Mit saat etisyyden odotusarvoksi, kun t = 0 tai kun t on
hyvin suuri?
11. Toteuta kappaleen 3.16 simulointiesimerkki yksikknelin sijaan yksikkkuutiossa eli kolmiulotteisessa tapauksessa.

44

Fortran 95/2003

Ohjelman perusrakenne

Esittelemme tss luvussa Fortran 95 -ohjelmakoodin rakenteen ja perussyntaksin. Keskitymme esityksess Fortran 95 -standardin mukaiseen lhdekoodin esitystapaan.

4.1

Lhdekoodin muoto

Fortran-kntj ksittelee ohjelmakoodia sisltvi lhdekooditiedostoja.


Seuraavassa kuvataan Fortran 95:n vapaata lhdekoodin muotoa (free source
form). Aiemmissa Fortranin versioissa kytettiin tst poikkeavaa sarakesidonnaista lhdekoodin muotoa (xed source form), joka periytyi reikkorttien aikakaudelta. Sarakesidonnaisen lhdekoodin muodon muuttamista vapaaseen muotoon kuvataan luvussa 15 (sivu 275).
Fortran-ohjelma koostuu jonosta lauseita. Lauseet kirjoitetaan riveille, joiden pituus voi olla 0 . . . 132 merkki. Lausetta voi jatkaa seuraavalle riville
kyttmll jatkorivin merkki & seuraavasti:
kk_era = kk_korko*lainasumma/ &
(1 - (1 + kk_korko)**(-laina_aika))

Jos merkin & jlkeen rivin lopussa on vlilyntej, ne jtetn huomiotta.


Jatkorivi kannattaa sisent vlilynneill tss esitettyyn tapaan ohjelmakoodin selkeyttmiseksi. Rivill saa olla jatkorivej korkeintaan 39.
Jos merkkijono halutaan jatkaa usealle riville, voi jatkorivin alkuun sijoittaa
vlilyntej sek merkin &, jolloin merkkijono jatkuu vasta tst kohtaa:
WRITE (*,*) "Tm on todella pitk & monimutkainen&
& merkkijono."

Merkki & ksitetn rivin jatkamisen tunnukseksi vain rivin lopussa, joten
edellinen lause tulostaa ensimmisen &-merkin:
Tm on todella pitk & monimutkainen merkkijono.

Riville voi laittaa useamman lauseen erottamalla ne puolipisteell (;):


x = 1.0; b = 1.0

Rivin lopussa ei tarvita puolipistett, koska rivin loppu automaattisesti lopettaa lauseen. Puolipisteen kytt kannattaa vltt, sill turha tiivistmi-

4. Ohjelman perusrakenne

45

nen (rivimrn minimointi) johtaa vaikeasti luettavaan koodiin. Sen sijaan


tyhji rivej kannattaa kytt ryhmittelemn ohjelmayksikn eri osia.
Ohjelmakoodiin kannattaa sijoittaa ohjelman toimintaa kuvaavia kommentteja, jotka aloitetaan huutomerkill (!):
PROGRAM mahtava
! Tm ohjelma ratkaisee maailmankaikkeuden perusyhtlt
! kytten 10-ulotteista supersieteoriaa.

Kommentteja voi sijoittaa mys jatketun rivin loppuun:


REAL :: nopeus = 0.0, &
! raketin nopeus alussa (m/s)
kiihtyvyys = 24.0 ! raketin kiihtyvyys (m/s**2)

Tss kerrottiin ohjelmassa kytettyjen muuttujien mrittelyn yhteydess


muuttujien merkitys.
Jos kommenttimerkki on merkkijonon sisll, se tulkitaan osaksi merkkijonoa.
Vlilynnit ovat merkitsevi Fortran 95 -kielen vapaassa lhdekoodin muodossa. Yht tai useampaa vlilynti kytetn erottamaan tunnuksia toisistaan. Pohjelman voi aloittaa vaikkapa seuraavilla tavoilla:
PROGRAM omaohjelma
PROGRAM
omaohjelma

Seuraava ei ole laillista vapaassa lhdekoodin muodossa:


PROGRAMomaohjelma

Kukaan tuskin haluaisikaan kirjoittaa tllaista koodia.


Useimmat Fortranin avainsanat eivt sisll vlilyntej, mutta poikkeuksen
muodostavat ohjelmayksikiden ja ohjausrakenteiden lopetuslauseet:
END PROGRAM omaohjelma
END DO
END IF

Omissa ohjelmayksikiden, muuttujien jne. tunnuksissa ei saa kytt vlilyntej.

4.2 Fortranin merkkivalikoima


Fortran 95 -kielen tunnuksen tulee alkaa kirjaimella. Pienet kirjaimet ovat
valinnaisesti osa merkkivalikoimaa, ja niiden merkitys ohjelmakoodin tunnuksissa on sama kuin isojen kirjainten.
Kytnnss kaikki Fortran-kntjt hyvksyvt pienet kirjaimet, vaikka
tm ei olekaan vlttmtnt. Tss teoksessa on kytetty isoja kirjaimia
Fortranin valmiiden tunnusten nimiss ja pieni kirjaimia itse mritellyiss
tunnuksissa.

46

Fortran 95/2003

Tunnuksissa kytettvien merkkien lisksi Fortran-kieleen kuuluu muutamia muita symboleita. Fortranin erikoismerkit on lueteltu taulukossa 4.1.
Taulukko 4.1: Fortranin erikoismerkit.

Merkki

Selitys

Merkki

Selitys

=
+
*
/
(
)
,
.

vlilynti
yhtlisyysmerkki
plusmerkki
miinusmerkki
asteriski
vinoviiva
oikea sulje
vasen sulje
pilkku
piste
heittomerkki

:
!
"
%
&
;
<
>
?
$

kaksoispiste
huutomerkki
lainausmerkki
prosenttimerkki
et-merkki
puolipiste
pienempi kuin -merkki
suurempi kuin -merkki
kysymysmerkki
dollarin merkki

Kaikki taulukossa 4.1 esitetyt merkit sisltyvt ASCII-merkistn (liite D


sivulla 324). Merkkijonoissa ja kommenteissa voi olla mahdollista kytt
mys muita erikoismerkkej, vaikkapa skandinaavisia kirjaimia , jne.
Yleens Fortran-kntj tukee niit merkistj mit tietokoneessa muutenkin kytetn.

4.3

Muuttujien ja vakioiden nimet

Muuttujien ja vakioiden nimien tulee alkaa kirjaimella, ja nimen maksimipituus on 31 merkki. Seuraavassa on esimerkkej laillisista nimist: a, a2,
a_2 ja a_b_c. Sen sijaan seuraavat nimet eivt ky: 2a, _a ja a b.
Fortran-kieless ei ole ns. varattuja sanoja, joten mys Fortranin tunnukset
voi mritell vaikkapa omien muuttujien nimiksi. Tm ei kuitenkaan ole
suositeltavaa!
PROGRAM crazy
IMPLICIT NONE
REAL :: sin, max = 1.0, real = 1.23
INTEGER :: program, precision
sin = max + real
program = 12
precision = 10
END PROGRAM crazy

Tss esimerkiss eivt Fortranin sisnrakennetut funktiot SIN, MAX ja


PRECISION ole en kytettviss, koska nimet on varattu ohjelman muuttujille sin, max ja precision.

4. Ohjelman perusrakenne

47

4.4 Ohjelmakoodin lauseiden jrjestys


Trkeimmt Fortran-kielen lauseet voidaan jakaa seuraaviin tyyppeihin:
ohjelmayksikn otsikkolauseet:
PROGRAM, FUNCTION, SUBROUTINE ja MODULE
kommentit
USE-lauseet
lause IMPLICIT NONE
tyyppien, muuttujien ja nimettyjen vakioiden mrittelyt
kutsumuodon mrittelyt INTERFACE-lohkossa
muut mrittelylauseet: EXTERNAL, INTRINSIC, OPTIONAL jne.
FORMAT-lauseet
suoritettavat lauseet ja rakenteet
CONTAINS-lause
ohjelmayksikon lopettava END-lause.
Kukin ohjelmayksikk alkaa otsikkolauseella (PROGRAM, FUNCTION jne.) ja
pttyy vastaavaan END-lauseeseen. Ohjelmayksikn sisll lauseet voivat
esiinty kuvan 4.1 havainnollistamassa jrjestyksess. Kuvaan ei ole otettu
mukaan vanhaa BLOCK DATA -rakennetta, jonka voi korvata moduulilla.
PROGRAM-, FUNCTION-, SUBROUTINE- tai MODULE-lause
USE-lauseet
IMPLICIT NONE -lause

FORMAT-lauseet

vakioiden mrittelyt

tyyppien ja muuttujien mrittelyt


INTERFACE-lohkot

suoritettavat lauseet ja rakenteet


CONTAINS-lause
sisiset tai moduulin proseduurit
ohjelmayksikn pttv END-lause

Kuva 4.1: Fortran 95:n lauseiden jrjestys ohjelmayksikss.

Seuraava ohjelma havainnollistaa Fortranin pohjelman omaohjelma ja sisisen funktion f rakennetta:

48

Fortran 95/2003

PROGRAM omaohjelma
! Ohjelma laskee lausekkeen a**i + b**j arvon.
IMPLICIT NONE
REAL :: a, b
INTEGER :: i, j
WRITE (*,*) Anna a ja b:
READ (*,*) a, b
WRITE (*,*) Anna i ja j:
READ (*,*) i, j
WRITE (*,*) f(x) = , f(a, i, b, j)
CONTAINS
FUNCTION f(x, x_potenssi, y, y_potenssi) RESULT(f_arvo)
IMPLICIT NONE
REAL :: x, y
INTEGER :: x_potenssi, y_potenssi
REAL :: f_arvo
f_arvo = x**x_potenssi + y**y_potenssi
END FUNCTION f
END PROGRAM omaohjelma

Pohjelman sisinen funktio f mritelln CONTAINS-lauseen jlkeen. Sisisill funktiolla ei voi olla omia sisisi funktioita tai aliohjelmia. Funktioiden mrittely ksitelln tarkemmin luvussa 8 (sivu 99).

4.5

INCLUDE-lause ja moduulit

Fortran 95:ss voidaan ohjelmakoodiin sisllytt toisessa tiedostossa olevia


mrittelyj kyttmll INCLUDE-lausetta:
PROGRAM sisalto
IMPLICIT NONE
INCLUDE vakiot.inc
REAL :: x = pii
WRITE (*,*) Piin arvo: , x
END PROGRAM sisalto

Tiedoston vakiot.inc sislt voisi olla seuraava:


REAL, PARAMETER :: pii = 3.14159265358979

Ohjelmamme siis tulostaa :n arvon.


INCLUDE-lauseiden sijaan kannattaa kytt moduuleita ja USE-lausetta. Etuna tst on mm. parempi knnsaikainen virheiden tarkistus. Seuraavassa
on esimerkki moduulin vakiot kytst:
PROGRAM sisalto
USE vakiot ! Trkeit matematiikan vakioita
IMPLICIT NONE
REAL :: x = pii
WRITE (*,*) Piin arvo: , x
END PROGRAM sisalto

4. Ohjelman perusrakenne

49

Edellisess pohjelmassa kytetty moduuli vakiot voitaisiin mritell


seuraavasti:
MODULE vakiot
IMPLICIT NONE
REAL, PARAMETER :: &
pii = 3.14159265358979, &
e = 2.71828182845905
END MODULE vakiot

Moduuleissa voi mritell muuttujien ja vakioiden lisksi mm. proseduureja. Lyhyt esimerkki tst lytyy kappaleesta 3.17 sivulta 40. Kerromme
moduulien kytst enemmn luvussa 9 (sivu 126).

4.6 Yhteenveto
Voit kirjoittaa ohjelmakoodin 132 merkin pituisille riveille. Kytettviss on lisksi jatkorivit (&), kommentit (!) ja mahdollisuus sijoittaa useita
lauseita riville (;).
Tunnukset muodostuvat kirjaimista A, . . . Z, numeroista 0, 1, . . . , 9
sek alaviivasta _. Tunnuksen tulee alkaa kirjaimella. Pienten kirjainten
merkitys tunnuksissa on sama kuin isojen kirjainten.
Kyt vlilyntej erottamaan tunnukset ja lausekkeiden osat toisistaan.
Aloita ohjelmayksikk otsikkolauseella PROGRAM, MODULE, FUNCTION ja
SUBROUTINE. Pt ohjelmayksikk vastaavaan END-lauseeseen.
Kyt moduuleja ohjelmayksikkjen jsentelyyn.

Harjoitustehtvi
1. Mit seuraava koodirivi tekee?
a = 0.0 ; b = 370 ! Alustus ; c = 17.0 ; d = 33.0

2. Onko seuraava oikein kirjoitettua Fortran 90 -koodia?


y = SIN(MAX(x1,x2)) * EXP(-COS(x3)**i) - TAN(AT&
& AN(x4))

3. Onko seuraava mrittely oikein?


REAL :: real; REAL :: price

4. Selvit itsellesi, mit kappaleessa 4.4 esitetty pohjelma tekee (knn


ohjelma ja aja se).
5. Ovatko seuraavat Fortranin lauseet kirjoitettu oikein?

50

Fortran 95/2003

merkkijono = Aamun virkku,


& illan torkku.
x = 0.4-6
vastaus = Tosi & eptosi
ala-raja = 0.0005E10
y = E6

6. Etsi seuraavasta ohjelmakoodista kaikki virheet.


PROGARM testaus_ohjelma
IMPLICIT NONE
INTEGER :: i
REAL :: pi = 3*ARCTAN(1.0)
WRITE (*,*) pii on pii
i = 4
j = 10.0 - i
WRITE (*,*) "i/(10-i):, i/j
END PROGRAM testaus_ohelma

7. Kokeile kappaleessa 4.5 esitetyn moduulin vakiot kytt. Lis moduuliin muita matemaattisia ja luonnonvakioita.

5. Perustyypit

51

Perustyypit

Esittelemme tss luvussa Fortranin perustyyppien kytt. Nit kutsutaan


mys nimell standardityypit. Esittelemme rakenteiset tietotyypit myhemmin luvussa 10 (sivu 152). Taulukoiden kytst kerromme luvussa 11
(sivu 164).

5.1 Perustyypit ja vakiot


Tietokoneohjelmilla ksitelln tieto-olioita, jotka rakentuvat luvuista ja
merkkijonoista sek niden yhdistelmist. Fortran sislt viisi perustyyppi
(intrinsic type):
kokonaisluvut (INTEGER)
reaaliluvut (REAL)
kompleksiluvut (COMPLEX)
totuusarvot (LOGICAL)
merkkijonot (CHARACTER).
Nit tietotyyppej ksitelln Fortran-kielen operaatioilla, proseduureilla
ja lauseilla.

5.1.1 Kokonaisluvut
Kokonaislukutyypill esitetn positiivisia tai negatiivisia kokonaislukuja eli
joukon {. . . , 2, 1, 0, 1, 2, . . . } alkioita. Fortranin kokonaislukutyypill voi
esitt vain rellisen osajoukon kokonaisluvuista.
Ohjelmassa voi esiinty kokonaislukuvakioita, jotka koostuvat numeroista
0, 1, . . . , 9:
WRITE (*,*) 40 + 2

Tss vakiot 40 ja 2 lasketaan yhteen tulostusta varten.

52

Fortran 95/2003

Edell luetelluista perustyypeist voi olla kytettviss eri lajeja (kind).


Kaikki Fortran-toteutukset tuntevat ainakin yhden kokonaislukulajin (integer kind). Monissa toteutuksissa on kuitenkin useita eri lajeja kokonaislukuja. Lajimreist kerrotaan kappaleessa 5.5 sivulla 56.
Kutakin kokonaislukulajia vastaa sill esitettviss olevien kokonaislukujen
joukko, joka tyypillisesti koostuu luvuista {m, m + 1, . . . , m 2, m 1}.
Tss m on kokonaisluvun lajista riippuva luku, esimerkiksi 231 tai 263 .

5.1.2 Reaaliluvut
Fortran-kielen reaaliluvut ovat kokonaislukutyypin tapaan vain rellinen
osajoukko todellisista reaaliluvuista.
Reaalilukuvakiot erotetaan kokonaislukuvakioista desimaalipisteen tai eksponenttinotaation avulla:
WRITE (*,*) 42.0, 0.42E2, 0.012345, 12345E-6

Yksinkertaisen desimaalinotaation lisksi reaalilukuvakiot voidaan siis esitt eksponenttimuodossa. Vakio 0.42E2 tarkoittaa reaalilukua 0.42 102 =
42.0 ja vakio 12345E-6 tarkoittaa reaalilukua 12345 106 = 0.012345.
Edellisen lauseen tulostus voisi olla
42.0000000

42.0000000

1.2345000E-02

1.2345000E-02

Reaalilukutyypist on vhintn kaksi eri lajia. Lajimreist kerrotaan kappaleessa 5.5 sivulla 56.

5.1.3 Kompleksiluvut
Kompleksilukutyyppi vastaa matemaattista kompleksiluvun ksitett. Siten
kompleksiluku
z koostuu reaaliosasta x ja imaginriosasta y eli z = x+iy,

miss i = 1.
Kompleksilukuvakiot kirjoitetaan sulkujen sisn:
WRITE (*,*) (0, -1)*(1.2345E-2, 8)

Tss lasketaan kompleksilukujen 1.0i ja 0.012345 + 8.0i tulo. Tulostus


voisi nytt tlt:
(8.000000,-1.2345000E-02)

Jos kummatkin sulkujen sisss olevat luvut ovat kokonaislukutyyppi, vakio on oletuslajin kompleksiluku. Jos sulkujen sisll on reaalilukuja, on
kompleksiluku suuremman tarkkuuden reaaliluvun mukaista lajia.

5.1.4 Totuusarvot
Fortranissa voidaan esitt totuusarvot tosi ja eptosi vakioilla .TRUE. ja
.FALSE.. Vakioiden alussa ja lopussa olevat pisteet ovat tarpeen!

5. Perustyypit

53

Totuusarvoja voi ksitell loogisilla operaatioilla .NOT., .AND., .OR., .EQV.


ja .NEQV.. Totuusarvojen ksittelyst kerrotaan enemmn sivulla 76.

5.1.5 Merkkijonot
Merkkijonotyypill voidaan esitt merkkijonoja. Merkkijonovakio sijoitetaan ohjelmakoodiin yksinkertaisten tai kaksinkertaisten lainausmerkkien
sisn:
WRITE (*,*) "kki ern pivn..."
WRITE (*,*) kki ern pivn...

Lainausmerkki voidaan sisllytt osaksi merkkijonoa sijoittamalla se toisentyyppisiin lainausmerkkeihin tai kahdentamalla se:
WRITE (*,*) "Yhtkki tuli pime."
WRITE (*,*) Yhtkki tuli pime.

Merkkijonoja voidaan liitt perkkin liitosoperaatiolla // seuraavasti:


WRITE (*,*) Yksi // // kaksi

Tss tulostettiin merkkijono Yksi kaksi.

5.2 Muuttujien mrittely


Muuttujat esitelln ohjelmayksikn alussa ennen suoritettavia lauseita.
Muuttujat voivat olla Fortranin sisisi tietotyyppej tai nist johdettuja
tyyppej.
Muuttujan mrittelylause koostuu tyypinmrittelyst ja listasta muuttujien nimi. Mrittelemme seuraavassa kolme kokonaislukumuuttujaa ja yhden merkkijonon, jonka pituus on kymmenen merkki:
INTEGER :: luku, alku, loppu
CHARACTER (LEN=10) :: merkkijono

Tyypinmrittely voi koostua pilkuilla erotetusta listasta tyyppi koskevia


mreit. Jos nit mreit ei ole (kuten edell), voi mrittelyst jtt
osan :: pois. Suosittelemme kuitenkin edell esitty mrittelytapaa, koska
tllin on tarvittaessa helppo list mreit tyypin tunnuksen pern.
Seuraava esimerkkiohjelma lukee ptteelt reaali- ja kokonaisluvut x ja n,
ja tulostaa lausekkeen x n arvon:
PROGRAM syotto
IMPLICIT NONE
REAL :: x
INTEGER :: n
WRITE (*,*) Anna x ja n:
READ (*,*) x, n

54

Fortran 95/2003

WRITE (*,*) x**n = , x**n


END PROGRAM syotto

Ohjelmassa mritelln reaalilukumuuttuja x ja kokonaislukumuuttuja n.


Seuraavassa on esimerkki ohjelman ajamisesta:
Anna x ja n:
1.2345 3
x**n =
1.8813663

Muuttujalle voidaan antaa alkuarvo alustuslausekkeessa (initialization expression) seuraavaan tapaan:


PROGRAM muuttujat
IMPLICIT NONE
LOGICAL :: tulosta = .TRUE.
COMPLEX :: z = (0.5, 1.0)
CHARACTER (LEN=20) :: selitys = Kompleksiluku:
IF (tulosta) THEN
WRITE (*,*) selitys, z
END IF
END PROGRAM muuttujat

Loogisen muuttujan tulosta arvoksi asetetaan tosi. Kompleksilukumuuttuja z sai alkuarvoksi luvun 0.5 + 1.0i.
Merkkijonomuuttujassa selitys on 20 merkki. Alustuslausekkeessa
CHARACTER (LEN=20) :: selitys = Kompleksiluku:

sijoituslauseen oikea puoli on lyhyempi, ja tllin kntj lis merkkijonon selitys loppuun tarpeellisen mrn vlilyntej.
Ohjelman tulostus voisi nytt tlt:
Kompleksiluku:

(0.5000000000,1.000000000)

Alustuslausekkeissa saa kytt vakioita ja peruslaskutoimituksia sek


osaa standardifunktioista. Esimerkiksi potenssiinkorotusoperaatiota (**)
saa kytt vain, jos potenssi on kokonaisluku. Alkioittaisen standardifunktion (esimerkkin MAX ja LEN) argumentti ja palautettava arvo saa olla
kokonaisluku- tai merkkijonotyyppi. Esimerkiksi funktiota SIN ei voi kytt alustuslausekkeessa.

5.3

Nimetyt vakiot

Edellisess esimerkiss mrittelimme joukon muuttujia, joiden alkuarvot


annoimme mrittelylauseessa. Muuttujien sijaan voimme mritell nimettyj vakioita, joiden arvoa ei voi muuttaa ohjelman ajon aikana.
Nimetyill vakioilla kannattaa korvata ohjelmakoodissa useassa kohdassa
esiintyvi luku- ym. vakioita. Tllin ohjelmakoodin muuttaminen on helpompaa. Riitt, ett nimetyn vakion mrittely muutetaan.

5. Perustyypit

55

Nimetyt vakiot erotetaan muuttujista mreen PARAMETER avulla. Seuraavassa kytmme hyvksi nimettyj vakioita kehote ja tuloste:
PROGRAM liittolainen
IMPLICIT NONE
CHARACTER (LEN=*), PARAMETER :: &
kehote = Syt kompleksiluku z:, &
tuloste = Luku z ja SIN(z):
COMPLEX :: z
WRITE (*,*) kehote
READ (*,*) z
WRITE (*,*) tuloste, z, SIN(z)
END PROGRAM liittolainen

Tss mrittelimme ohjelman tulostaman kehotteen ja selitteen merkkijonovakioiksi. Ohjelman kytt nytt seuraavalta:
Syt kompleksiluku:
(0.5, 1.0)
Luku z ja SIN(z): (0.5000000,1.000000) (0.7397923,1.031336)

Merkkijonovakion kehote pituudeksi tulee mrittelyn (LEN=*) ansiosta


merkkijonon Syt kompleksiluku z: pituus eli siis 22 merkki. Merkkijonovakion tuloste pituus on puolestaan 17 merkki.
Jos nimetylle vakiolle yritetn antaa uusi arvo ohjelmassa, ilmoittaa kntj virheellisest koodista.
Esimerkiksi mriteltess vakiokokoisia taulukkoja on taulukkojen koot
hyv mritell nimetyiksi vakioiksi:
INTEGER, PARAMETER :: n = 10
REAL, DIMENSION(n) :: b
REAL, DIMENSION(n,n) :: c, d

Tss mritelty nimetty vakiota n voi kytt mys DO-silmukan toistokertojen mrmiseen:
INTEGER :: i, j
DO i = 1, n
DO j = 1, n
d(j,i) = 1.0 - c(j,i)
END DO
END DO

Jos haluamme muuttaa tmn esimerkin taulukkojen b, c ja d kokoa, selvimme tst muuttamalla nimetyn vakion n mrittely.

5.4 IMPLICIT NONE ja alkukirjainsnt


Vanhoissa Fortran-koodeissa on kytetty hyvksi muuttujien alkukirjainsnt (implicit typing), jolloin muuttujan tyypin mrsi tunnuksen en-

56

Fortran 95/2003

simminen kirjain. Muuttujat, joiden ensimminen kirjain oli I . . . N, olivat


kokonaislukuja. Kaikki muut muuttujat olivat reaalilukuja.
Alkukirjainsnnn kytll vltettiin muuttujien tyypin mrittely ohjelmayksikn alussa. Valitettavasti tm voi johtaa erittin hankalasti jljitettviin virhetilanteisiin. Jos muuttujan mrittely unohtuu, saattaa tuloksena olla vrn tyyppinen muuttuja. Toinen virhelhde ovat kirjoitusvirheet
tunnusten nimiss: tuloksena on uusi muuttuja, jonka tyypin mr alkukirjainsnt. Alkukirjainsnnn kytt tuottaakin epselv ja vaikeasti
yllpidettv koodia.
Ohjelmayksikn alkuun onkin syyt aina laittaa lause
IMPLICIT NONE

Ainoastaan USE-lause sijoitetaan ennen IMPLICIT-lausetta.


Lause IMPLICIT NONE kytkee alkukirjainsnnn pois kytst. Tllin kntj tarkistaa, ett jokainen tunnus on mritelty. Vrin kirjoitetut muuttujien nimet paljastuvat siten usein jo knnsaikana.

5.5

Perustyyppien lajimreet

Tietokoneiden prosessorit mahdollistavat laskemisen usean eri tarkkuuden


kokonais- ja reaaliluvuilla. Tarkkuuksia on kytettviss yleens kaksi tai
kolme erilaista. Fortran-kielen lajiparametrien avulla on helppo valita ohjelmassa kytettv laskentatarkkuus. Lisksi lajiparametrit mahdollistavat
laskentatarkkuuden silymisen siirrettess ohjelma toiseen koneeseen.
Fortranin perustyyppeihin voidaan liitt lajimre (kind), joka kertoo, millaisen version tyypist haluamme kyttn esimerkiksi mik on reaalilukujen haluttu esitystarkkuus ja lukualue. Tllaisia mrittelyj kutsutaan
parametrisoiduiksi tyyppimrittelyiksi. Numeeristen tyyppien eri lajit liittyvt yleens lukujen tarkkuuteen. Merkkijonojen lajit liittyvt vaikkapa kiinalaisten kirjoitusmerkkien kyttn: kytetty laitteisto voi normaalin ASCIImerkistn lisksi tukea muitakin merkkijonolajeja.
Lajiparametri annetaan osana muuttujan mrittely esimerkiksi seuraavasti:
REAL (KIND=lajiparametri) :: x
INTEGER (KIND=lajiparametri) :: i
COMPLEX (KIND=lajiparametri) :: z
LOGICAL (KIND=lajiparametri) :: testi
CHARACTER (LEN=80, KIND=lajiparametri) :: rivi

Lajiparametri on Fortran-kntjst riippuva positiivinen kokonaislukuvakio, joka kannattaa selvitt tarkoitukseen varatuilla funktiokutsuilla (esimerkiksi SELECTED_INT_KIND).

5. Perustyypit

57

5.5.1 Kokonaisluvut
Kokonaislukujen lajiparametri mr luvun esitystarkkuuden. Funktiokutsu
SELECTED_INT_KIND(r)

tuottaa lajiparametrin arvon sellaisille kokonaisluvuille i, joiden arvo saa


vaihdella vlill 10r < i < 10r .
Lajiparametrin arvo kannattaa mritell nimetyksi kokonaislukuvakioksi
seuraavaan tapaan:
INTEGER, PARAMETER :: isoluku = SELECTED_INT_KIND(9)
INTEGER (KIND=isoluku) :: m, n
INTEGER (KIND=isoluku), PARAMETER :: max_iter = 10000000

Edell mritellyll kokonaislukulajilla isoluku voidaan esitt vhintn


sellaiset kokonaisluvut, joissa on 9 desimaalinumeroa.
Jos haluttua tarkkuutta ei ole kytettviss, palauttaa funktiokutsu
SELECTED_INT_KIND lajiparametrille negatiivisen arvon. Jos tt arvoa yritetn kytt muuttujien parametrisoiduissa mrittelyiss, saadaan knnsaikainen virhe.
Kokonaislukuvakion lajiparametri voidaan list kokonaislukuvakion pern alaviivan jlkeen:
WRITE (*,*) 199600000_isoluku

5.5.2 Reaaliluvut
Reaalilukujen tarkkuuden voi valita samaan tapaan kuin kokonaislukujenkin. Tarkkuuden mrittelyss oletetaan, ett reaaliluvut esitetn kytten
eksponenttinotaatiota
d.ddddd 10e ,
miss mantissassa d.ddddd on p merkitsev desimaalia ja eksponentin e
vaihteluvli on r e r . Tllaista reaalilukuesityst kutsutaan liukulukuesitykseksi.
Liukulukujen lajiparametrin voi valita funktiokutsulla
SELECTED_REAL_KIND(p,r)

Tss p on liukuluvun tarkkuus desimaalinumeroina ja r on eksponentin


vaihteluvli.
Jos halutaan esitt reaalilukuja, joiden eksponentin vaihteluvli on vhintn 30 ja joiden mantissassa on ainakin kuusi merkitsev desimaalia,
saadaan sopiva lajiparametri selville funktiokutsulla
SELECTED_REAL_KIND(6, 30)

58

Fortran 95/2003

Saatu lajiparametrin arvo takaa, ett kyseisen lajin reaaliluvulla voidaan esitt vhintn halutun tarkkuuden ja eksponentin vaihteluvlin reaaliluvut.
Fortran-kntj tuntee ainakin kaksi eri lajia reaalilukuja. Oletuksena on
pienemmn tarkkuuden reaaliluvut. Reaalilukuvakion laji voidaan ilmoittaa
lismll vakion pern alaviiva ja lajiparametri samoin kuin kokonaisluvuillekin:
WRITE (*,*) 3.141592653589793_reaalilukulaji

Lisksi on kytettviss standardifunktio KIND, joka palauttaa tunnusta vastaavat tietotyypin lajiparametrin. Seuraavassa on esimerkki funktion kytst:
INTEGER :: i
REAL :: x
x = REAL(i, KIND=KIND(x))

Tss muutettiin kokonaislukumuuttujan i arvo muuttujan x lajia vastaavaksi reaaliluvuksi kyttmll funktiota REAL.

5.5.3 Kompleksiluvut
Kompleksiluvun lajimre ilmaisee sen reaali- ja imaginriosan lajin samoin
kuin reaaliluvuille:
INTEGER, PARAMETER :: tarkka = SELECTED_REAL_KIND(10,100)
COMPLEX (KIND=tarkka) :: z

5.5.4 Merkkijonot ja totuusarvot


Yleens tyypille LOGICAL kytetn oletuslajin tyyppi, samoin merkkijonoille. Jos koneessa on kytettviss useita eri lajeja merkkijonoja (vaikkapa ASCII-merkit ja kiinalaiset kirjoitusmerkit), voitaisiin ne ottaa kyttn
seuraavasti:
CHARACTER (LEN=80, KIND=lajiparametri) :: rivi

Tss mriteltiin 80 merkin mittainen merkkijono, jonka lajiparametri annetaan mreess KIND=lajiparametri.
Merkkijonovakion laji annetaan ennen merkkijonovakiota:
lajiparametri_Merkkijono

Fortran-standardi mahdollistaa usean merkkijonolajin kytn, mutta ei standardoi tt milln tavalla. Sama ptee totuusarvoihin. Siten Fortranissa ei
ole helppoa tapaa saada selville merkkijonojen tai totuusarvojen lajiparametreja; parhaiten nm selvivt kunkin Fortran-kntjn ksikirjoista.

5. Perustyypit

59

5.5.5 Lajimrittelyn eri muodot


Parametrisoidun tyyppimrittelyn voi antaa numeeristen tyyppien yhteydess mys lyhyemmss muodossa:
INTEGER, PARAMETER :: isoluku = SELECTED_INT_KIND(9)
INTEGER, PARAMETER :: tarkka = SELECTED_REAL_KIND(10,100)
INTEGER (isoluku) :: m, n
REAL (tarkka) :: x, y
COMPLEX (tarkka) :: z

Suosittelemme kuitenkin mrittelyn KIND=laji kyttmist:


INTEGER (KIND=isoluku) :: z

5.5.6 Esimerkki parametrisoiduista tyypeist


Seuraavassa kytmme hyvksi parametrisoituja tyyppimrittelyj piin arvon laskemisessa:
PROGRAM pii
IMPLICIT NONE
INTEGER, PARAMETER :: &
pienitarkkuus = SELECTED_REAL_KIND(6, 30), &
suuritarkkuus = SELECTED_REAL_KIND(12)
REAL (KIND=suuritarkkuus) :: pi1
REAL (KIND=pienitarkkuus) :: pi2
COMPLEX (KIND=suuritarkkuus) :: pi3
pi1 = 4*ATAN(1.0_suuritarkkuus)
pi2 = 4*ATAN(1.0_pienitarkkuus)
pi3 = 6*ASIN(0.5_suuritarkkuus)
WRITE (*,*) pii: , pi1, pi2
WRITE (*,*) pii: , pi3
END PROGRAM pii

Lajiparametria suuritarkkuus laskettaessa jtettiin eksponentin vaihteluvli antamatta, koska ainoastaan merkitsevien numeroiden lukumrll
(12) on esimerkiss merkityst. Kntj valitsee tllin sopivan vaihteluvlin eksponentille.
Ohjelma tulostaa esimerkiksi seuraavan:
pii:
pii:

3.141592653589793
3.141593
(3.141592653589793,0.0000000000000000E+00)

Tss kytetyll laitteistolla vastaavat ohjelmassa kytetyt reaalilukujen lajiparametrit suuritarkkuus ja pienitarkkuus eri tarkkuuden lajeja.
Toisella laitteistolla voisi laji suuritarkkuus olla pienin esitettviss oleva
reaalilukujen tarkkuus, jolloin suuritarkkuus ja pienitarkkuus esittisivt samaa lajia. Tllaisella koneella voisi olla kytettviss mys vaikkapa
28 desimaalin tarkkuuteen pystyv reaalilukulaji.

60

Fortran 95/2003

5.5.7 Kaksoistarkkuus
Aikaisemmassa Fortran-standardissa oli kytss kaksoistarkkuuden tyyppi
DOUBLE PRECISION, joka aiheutti suuria siirrettvyysongelmia: toisen koneen REAL-tyyppi saattoi vastata toisen koneen kaksoistarkkuutta. Kaksoistarkkuuden mrittelyt kannattaa korvata parametrisoiduilla tyypinmrittelyill.
Seuraavassa on esimerkki vanhasta Fortran-koodista:
DOUBLE PRECISION X, Y
X = 1.D0
Y = 2.D0*ACOS(X)

Tss merkint 1.D0 tarkoittaa kaksoistarkkuuden vakiota, jota vastaa yksinkertaisen tarkkuuden vakio 1.E0.
Edellinen ohjelmakoodi on parempi esitt vaikkapa seuraavassa muodossa:
INTEGER, PARAMETER :: dp = SELECTED_REAL_KIND(12)
REAL (KIND=dp) :: x, y
x = 1.E0_dp
y = 2.E0_dp*ACOS(x)

Nyt voimme olla varmoja siit, ett kytetty reaalilukulaji pystyy esittmn
lukuja 12 numeron tarkkuudella riippumatta kytetyn koneen ominaisuuksista.
Huomaa, ett Fortran-kntjt eivt pysty esittmn lukuja mielivaltaisella
tarkkuudella. Esimerkiksi reaalilukujen eri lajeja on tyypillisesti kaksi tai
kolme.

5.6

Esimerkki: sarjan summa

Olkoon laskettavana arvio sarjan summalle


s=

1
.
2
i
i=1

Tarkka arvo on s = 2 /6 1.6449341. Koska emme voi laskea rellisess


ajassa yhteen retnt mr termej, otamme mukaan summaan vain n
termi:
s =

1
.
2
i
i=1

Seuraava ohjelmakoodi tekee tarvittavat laskut:


PROGRAM summaus
IMPLICIT NONE
REAL :: s = 0.0

5. Perustyypit

61

INTEGER :: i, n
WRITE (*,*) Anna termien lukumr:
READ (*,*) n
WRITE (*,*) Termej: , n
DO i = 1, n
s = s + 1/(REAL(i)**2)
END DO
WRITE (*,*) Summa on:, s
END PROGRAM summaus

Funktiokutsu REAL(i) muuttaa kokonaisluvun i reaaliluvuksi, jotta jakolaskun tulos olisi mys reaaliluku. Seuraavassa on ajoesimerkki:
Anna termien lukumr:
100
Termej: 100
Summa on:
1.6349840

Jos laskemme mukaan lis termej, saamme tulokset:


Termej: 1000
Summa on:
1.6439348
Termej: 10000
Summa on:
1.6447253
Termej: 1000000
Summa on:
1.6447253

Miljoonalla termill lasketussa tuloksessa on vain 4 desimaalia oikein, samoin kuin 10000 desimaalin tapauksessa. Ohjelmakoodimme nyttisi siis
tuottavan eptarkkoja tuloksia.
Tulosten huonous johtuu siit, ett summauksessa hvi liikaa informaatiota pyristysvirheiden takia. Tm puolestaan johtuu kytetyn laskentamenetelmn ominaisuuksista.
DO-silmukassa lasketaan lauseessa
s = s + 1/(REAL(i)**2)

yhteen luku s ja luku 1/(REAL(i)**2). Jos muuttuja i on luokkaa 1000, on


jlkimmisen luvun suuruusluokka 1/10002 106 . Luvun s arvo on puolestaan noin 1.6. Seuraavassa havainnollistetaan tt yhteenlaskua (symboli
d esitt nollasta poikkeavia desimaaleja):
1.60000
+0.00000d
1.60000d 1.60000
Koska yhteenlaskettavien suuruusero on iso verrattuna merkitsevien numeroiden mrn, niin myhemmill iteraatiokierroksilla laskettavat termit
eivt en vaikuta muuttujan s arvoon!
Lasketaanpa termit knteisess jrjestyksess! Tllin alussa summataan
yhteen pienet termit ja vasta lopussa listn suurimmat termit. Siis seuraavasti:

62

Fortran 95/2003

DO i = n, 1, -1
s = s + 1/(REAL(i)**2)
END DO

Lause
DO i = n, 1, -1

tarkoittaa, ett silmukkamuuttujan i askel on 1. Silmukkamuuttuja i ky


siis lpi arvot n, n-1, . . . , 1. Tmn ohjelman tulostus on:
Termej: 1000
Summa on:
1.6439345
Termej: 10000
Summa on:
1.6448340
Termej: 1000000
Summa on:
1.6449330

Nyt viimeisess tuloksessa on jo 6 oikeaa desimaalia, mik johtuu siit, ett


laskimme saman suuruusluokan termej yhteen.
Algoritmin muuttamisen lisksi voimme muuttaa reaalilukumuuttujan s lajia tarkemmaksi:
PROGRAM summaus
IMPLICIT NONE
INTEGER, PARAMETER :: tarkkuus = SELECTED_REAL_KIND(12)
REAL (KIND=tarkkuus) :: s = 0.0
INTEGER :: i, n
WRITE (*,*) Anna termien lukumr:
READ (*,*) n
WRITE (*,*) Termej: , n
DO i = n, 1, -1
s = s + 1/(REAL(i, KIND=tarkkuus)**2)
END DO
WRITE (*,*) Summa on:, s
END PROGRAM summaus

Tss mriteltiin reaalilukumuuttujan s vhimmistarkkuudeksi 12 desimaalia. Standardifunktio REAL muuttaa kokonaisluvun reaaliluvuksi. Huomaa, ett KIND-mre on syyt antaa mys funktiokutsun REAL argumenttina, koska lauseke REAL(i) tuottaa oletustarkkuuden reaaliluvun.
Ohjelma tulostaa seuraavaa:
Termej: 1000000
Summa on:
1.6449330668487263

Jos kytetn alkuperist DO-silmukan jrjestyst (DO i = 1, n), saadaan


tulokseksi
Summa on:

1.6449330668487701

Eri jrjestyksess lasketut arvot poikkeavat nyt 15. desimaalissa. Jos laskujrjestyksen on parempi vaihtoehto eli DO i = n, 1, -1, saadaan viel
termej lismll seuraava tulos:

5. Perustyypit

63

Termej: 10000000
Summa on:
1.6449339668482315

Siten sarja suppenee hyvin hitaasti kohti tarkkaa arvoa.


Laskutarkkuuden kasvattamisen sijasta (tai lisksi) on parempi muuttaa laskentamenetelm siten, ettei reaalilukujen rellinen tarkkuus johda huonoihin tuloksiin. Edellisess laskentatehtvss olisimme voineet kytt
summan arvioimiseen kaavaa
s

1
1
+ .
2
i
n
i=1

Tmn arvion virhe on pienempi kuin 1/n2 . Esimerkiksi arvolla n = 1000


saamme tulokseksi s 1.6449345, joka on oikea kuuden desimaalin tarkkuudella.

5.7 Yhteenveto
Fortranissa on viisi perustyyppi: INTEGER, REAL, COMPLEX, LOGICAL ja
CHARACTER.
Mreell PARAMETER luodaan nimettyj vakioita, joiden arvoa ei voi
muuttaa ohjelman ajon aikana.
Perustyyppien lajimre kertoo, millaisen version tyypist haluamme
kyttn.
Kytetty laskentamenetelm sek laskutarkkuus voivat vaikuttaa ratkaisevasti tuloksiin.

Harjoitustehtvi
1. Ovatko seuraavat mrittelyt laillista Fortrania:
DOUBLE :: x
CHARACTER (LEN=*), PARAMETER :: "Nimi"
REAL :: pi = 22/7
REAL :: x = 2., y = -3
REAL :: pii = 22.0/7.0
REAL x = 1.0

Jos mrittelyiss on virheit, korjaa ne.


2. Mrittele nimetty vakio k, jonka arvo on 0.75.
3. Mrittele reaalilukulaji (KIND), joka mahdollistaa laskemisen 10 desimaalin tarkkuudella. Mrittele tll tarkkuudella muuttujia ja vakioita.
4. Mit seuraava ohjelma tulostaa?

64

Fortran 95/2003

PROGRAM tulostus
IMPLICIT NONE
INTEGER, PARAMETER :: reaali = SELECTED_REAL_KIND(10)
REAL(KIND=reaali) :: x
x = 314/1000_reaali
WRITE (*,*) X
END PROGRAM tulostus

Korjaa ohjelmassa oleva virhe.


5. Tutki, millaisia kokonais- ja reaalilukuja kyttmsi Fortran-kntj
osaa ksitell. Ohje: tutki aliohjelmakutsujen
SELECTED_REAL_KIND
SELECTED_INT_KIND

palauttamia arvoja seuraavaan tapaan:


PROGRAM lajitesti
IMPLICIT NONE
INTEGER :: i, laji
DO i = 1, 24
laji = SELECTED_INT_KIND(i)
WRITE (*,*) i = , i, laji = , laji
END DO
END PROGRAM lajitesti

Huomaa, ett lajiparametrin arvot riippuvat kytetyst Fortran-kntjst!


6. Tulosta lausekkeen

1
1
ln(1 + 2) +
(2 + 2) 0.5214 . . .
3
15
arvo vhintn 6 desimaalin tarkkuudella. Kyt funktiokutsuja LOG ja
SQRT. Tulosta lausekkeen arvo lisksi suurimmalla kntjn tuntemalla
reaalilukujen tarkkuudella. Kyt hyvksesi edellisen tehtvn tuloksia.
7. Kappaleessa 3.11 sivulla 32 esiteltiin ohjelma, joka tulostaa arvoja lausekkeelle (1 + 1/x)**x.
Muuta ohjelmaa siten, ett se lukee arvon reaalimuuttujalle x ja tulostaa
lausekkeen (1 + 1/x)**x arvon. Kun muuttujan x arvo kasvaa, lausekkeen arvon pitisi lhesty arvoa e 2.71828. Kokeile ohjelmasi toimintaa syttarvoilla 103 , 106 , 1012 ja 1018 (siis 1E3, 1E6 jne.). Miten
ky?
Liukulukujen kone-epsilon  on pienin positiivinen liukuluku, jolle ptee
epyhtl 1.0 +  > 1.0. Reaalilukumuuttujan x lajia vastaavan koneepsilonin saa selville seuraavasti:
REAL :: x
REAL, PARAMETER :: eps = EPSILON(x)

Muuta ohjelmaasi siten, ett lasket lausekkeen (1 + 1/x)**x arvon


vain siin tapauksessa, ett ehto
1/x > 100*eps

on voimassa. Muussa tapauksessa ilmoitat, ett annettu syttarvo on


liian suuri.

5. Perustyypit

65

8. Kappaleen 3.16 (sivu 38) simulointiesimerkiss laskettiin yhteen pisteiden etisyyksi lauseessa
s = s + SQRT(SUM((a - b)**2))

Koska pisteiden etisyys on keskimrin noin 0.52, on summan s arvo


likimain 0.52*n, miss muuttuja n on yhteenlaskettujen termien lukumr. Jos luku n on vaikkapa miljoona, lasketaan silmukan viimeisess
summauksessa yhteen luvut, joiden suuruusluokat ovat 0.5 106 ja 0.5.
Jos reaalilukumuuttujassa s on tarkkuutta p merkitsev numeroa,
kuinka monta termi voit laskea yhteen ennen tarkkuuden hvimist? Laske arvio pisteiden vlisen etisyyden odotusarvolle kytten miljoonaa pisteparia. Valitse reaalilukumuuttujan s laji siten, ett tarkkuus
on riittv.
9. Liukulukujen yhtsuuruutta ei pitisi vertailla, vaan sen sijaan on syyt
tarkastella likimrist yhtsuuruutta. Seuraavassa on esimerkki tst:
PROGRAM yhtasuuruus
IMPLICIT NONE
REAL :: x, y
REAL, PARAMETER :: toleranssi = 10*EPSILON(x)
WRITE (*,*) anna x ja y:
READ (*,*) x, y
WRITE (*,*) arvot x ja y:, x, y
IF (ABS(x - y) <= toleranssi*ABS(x)) THEN
WRITE (*,*) x ja y likimain yht suuria
END IF
IF (x - y > toleranssi*ABS(x)) THEN
WRITE (*,*) x selvsti suurempi kuin y
END IF
IF (y - x > toleranssi*ABS(x)) THEN
WRITE (*,*) x selvsti pienempi kuin y
END IF
END PROGRAM yhtasuuruus

Miksi muuttujien erotusta verrataan lausekkeeseen


toleranssi*ABS(x)

eik vakioarvoon toleranssi?


10. Laske likiarvo seuraavan vuorottelevan sarjan summalle:

(1)k1
.
k
k=1

Kokeile eri summausjrjestyksi sek lisksi positiivisten ja negatiivisten alkioiden summausta erikseen. Tulos on ln 2 0.69314718 . . . .
11. Ns. maagisessa summauksessa voidaan kytt pienemmn tarkkuuden
muuttujia, mutta tulos on silti varsin tarkka. Seuraavassa on esimerkki
tarvittavasta koodista:

66

Fortran 95/2003

INTEGER, PARAMETER :: tarkkuus = SELECTED_REAL_KIND(6)


REAL (KIND=tarkkuus) :: summa, korjaus, &
korjattu_termi, uusi_summa
INTEGER :: i, n
...
summa = f(1)
korjaus = 0.0
DO i = 2, n
korjattu_termi = f(i) - korjaus
uusi_summa = summa + korjattu_termi
korjaus = (uusi_summa - summa) - korjattu_termi
summa = uusi_summa
END DO

Tss funktio f(i) laskee sarjan i:nnen alkion arvon. Kokeile summauksen tarkkuutta edellisess tehtvss.

6. Lausekkeet ja sijoituslauseet

67

Lausekkeet ja
sijoituslauseet

Lausekkeet ja sijoituslauseet ovat ohjausrakenteiden ohella tietokoneohjelmassa ne tykalut, joilla laskentatehtv saadaan ohjelmoitua. Tss luvussa esittelemme Fortran-kielen aritmeettiset, loogiset ja merkkijonolausekkeet. Sijoituslauseiden ksittelyn yhteydess puhumme lausekkeiden eri
osien laskentajrjestyksest. Taulukoita sisltvi lausekkeita ja sijoituslauseita ksittelemme luvussa 11 (sivu 164).

6.1 Aritmeettiset lausekkeet


Koska Fortran on alunperin suunniteltu numeeristen algoritmien ohjelmointiin, on kieless hyvt numeeriset tietotyypit ja operaattorit sek paljon matemaattisia standardifunktioita. Fortranin nimihn on perisin sanoista FORmula TRANslator, eli vapaasti knnettyn kaavankntj.
Fortranin aritmeettiset operaattorit ovat yhteenlasku (+), vhennyslasku (-),
kertolasku (*), jakolasku (/) ja potenssiin korotus (**). Lisksi miinusmerkki lausekkeen edess tarkoittaa etumerkin vaihtoa. Nm operaattorit on
mritelty kokonais-, reaali- ja kompleksiluvuille. Operaattorit on mritelty mys taulukoille, mutta tt ksitelln vasta luvussa 11 (sivu 164).
Operaattoreista etumerkin vaihto lasketaan ensin, sitten potenssiin korotus, kerto- ja jakolaskut ja lopuksi yhteen- ja vhennyslaskut. Lausekkeiden
sidontajrjestyst voi muuttaa sulkuja kyttmll.
Esimerkiksi lauseke
a**2+b/d-3*-5

tulkitaan kuten lauseke


((a ** 2) + (b / d)) - (3 * (-5))

Luvussa -5 esiintyv miinusmerkki kuuluu lukuesitykseen eik siis ole etumerkin vaihto-operaatio. Siten seuraavassa lausekkeessa sulut voi jtt pois
luvun -5 ymprilt mutta ei lausekkeen -c ymprilt:
a**(-5) + a**(-c)

68

Fortran 95/2003

Huomaa, ett aritmeettisissa lausekkeissa operaattorit potenssiin korotusta


lukuunottamatta tulkitaan vasemmalta oikealle. Tllin lauseke
a - b - c + d - e

tulkitaan kuten lauseke


(((a - b) - c) + d) - e

Vastaavasti jakolaskuja sisltv lauseke


a / b / c * d

tulkitaan seuraavasti:
((a / b) / c) * d

Kntjll on oikeus jrjestll lausekkeita uudestaan tehokkuuden lismiseksi. Tten yll esitetyt lausekkeet saatettaisiin laskea seuraavassa jrjestyksess:
(a + d) - (b + c + e)
(a * d) / (b * c)

Perkkiset potenssiinkorotusoperaattorit tulkitaan oikealta vasemmalle. Tten esimerkiksi


2 ** 3 ** 5

tulkitaan kuten lauseke


2 ** (3 ** 5)

Kyt aritmeettisissa lausekkeissa vlilyntej ja ylimrisi sulkumerkkej selkeyden lismiseksi. Esimerkiksi lauseke
a ** (b ** (-2)) + d / (e * f * g)

on paljon selkempi ja intuitiivisempi kuin


a**b**-2+d/e/f/g

Ole erityisen varovainen, kun lasket jakolaskua tai potenssiinkorotusta kokonaisluvuille. Kahden kokonaisluvun jakolaskun tulos katkaistaan nollaa
kohti kokonaisluvuksi. Siten esimerkiksi jakolaskun 2/3 tulos on 0 ja jakolaskun -5/3 tulos on -1.
Kun kokonaislukuja korotetaan negatiiviseen potenssiin, tulkitaan tulos ykksen ja vastaavan positiivisen potenssin jakolaskuna. Tm jakolasku tulkitaan kuten mik tahansa kokonaislukujen jakolasku, joten tuloksena on
useimmiten nolla. Lauseke 2**(-3) tulkitaan siis lausekkeeksi 1/(2**3)
eli 1/8, mist tulee tulokseksi nolla. Lauseke
2**(-1/2) tuottaa puolestaan

tulokseksi arvon 20 = 1 eik arvoa 1/ 2.


Jos aritmeettisissa lausekkeissa on erityyppisi operandeja, muutetaan argumentit ennen laskutoimitusta vahvimman ja tarkimman tyypin mukaisiksi. Siten jos esimerkiksi kertolaskussa on kokonaislukuja ja reaalilukuja
sekaisin, muutetaan kokonaisluvut reaaliluvuiksi ennen laskutoimitusta. Samoin jos lausekkeessa on reaali- ja kompleksilukuja, muutetaan kussakin
laskutoimituksessa reaaliluvut ensin kompleksiluvuiksi.

6. Lausekkeet ja sijoituslauseet

69

Eri tarkkuuden argumentit (KIND-parametri) muutetaan tarkimman argumentin mukaiseksi. Seuraavassa esimerkiss mrittelemme reaalilukujen
lajiparametrit t1 ja t2, joilla luvun esitystarkkuus on vhintn 6 ja 12 numeroa. Lisksi mrittelemme lajeja t1 ja t2 vastaavat muuttujat a ja b:
INTEGER, PARAMETER
INTEGER, PARAMETER
REAL(KIND=t1) :: a
REAL(KIND=t2) :: b
WRITE (*,*) a + b

:: t1 = SELECTED_REAL_KIND(6)
:: t2 = SELECTED_REAL_KIND(12)
= 3.5
= 4.2

Kun luvut a ja b lasketaan yhteen, muutetaan muuttujan a arvo tarkkuuteen


t2 ja tulos on samaa tarkkuutta.

6.2 Numeeriset standardifunktiot


Fortranissa on suuri joukko numeerisia standardifunktioita, joita on esitelty
luvussa 13 (sivu 215). Nit ovat muun muassa
katkaisu- ja pyristysfunktiot
itseisarvo, kompleksikonjugointi, luvun reaali- ja imaginriosa
lukujen tyyppimuunnokset
minimi, maksimi, modulo
logaritmi- ja eksponenttifunktio, nelijuuri
trigonometriset ja hyperboliset funktiot
funktiot, jotka kertovat liukulukuesityksen parametrit
binrilukujen bittioperaatiot
satunnaislukugeneraattori
kellonaika.
Fortran 95 -standardissa parannettiin numeerisia standarifunktioita verrattuna Fortran 90:een. Erityisesti on poistettu ristiriidat IEEE-liukulukuaritmetiikan suhteen: standardi tuntee nyt mm. positiivisen ja negatiivisen nollan
(+0, 0) jne. Funktio SIGN tuottaa negatiivisen nollan, kun ensimminen
argumentti on nolla ja toinen negatiivinen: SIGN(0.0,-1.0).
Seuraavat esimerkit havainnollistavat aritmeettisten lausekkeiden ja numeeristen funktioiden kytt.

6.2.1 Koordinaatiston muunnokset


Ensiksi teemme aliohjelmat, joilla voidaan muuttaa pallokoordinaatit karteesisiksi koordinaateiksi ja pinvastoin. Pallokoordinaatit (r , , ) muutetaan

70

Fortran 95/2003

karteesisiksi koordinaateiksi (x, y, z) seuraavien kaavojen mukaisesti:


x = r sin cos ,
y = r sin sin ,
z = r cos .
Karteesiset koordinaatit muunnetaan pallokoordinaateiksi seuraavasti:

r = x 2 + y 2 + z2 ,

x2 + y 2
= arctan
,
z
y
= arctan .
x
Seuraavassa moduulissa kytetn nit kaavoja muunnoksiin:
MODULE pallokoordinaatit
IMPLICIT NONE
INTEGER, PARAMETER :: prec = SELECTED_REAL_KIND(12, 50)
CONTAINS
SUBROUTINE pallo_xyz(r, theta, phi, x, y, z)
! Tm aliohjelma muuttaa pallokoordinaatit
! r, theta, phi karteesisiksi koordinaateiksi x, y, z
IMPLICIT NONE
REAL(prec), INTENT(IN) :: r, theta, phi
REAL(prec), INTENT(OUT) :: x, y, z
x
y
z
END

= r * SIN(theta) * COS(phi)
= r * SIN(theta) * SIN(phi)
= r * COS(theta)
SUBROUTINE pallo_xyz

SUBROUTINE xyz_pallo(x, y, z, r, theta, phi)


! Tm aliohjelma muuttaa karteesiset koordinaatit
! x, y, z pallokoordinaateiksi r, theta, phi
IMPLICIT NONE
REAL(prec), INTENT(IN) :: x, y, z
REAL(prec), INTENT(OUT) :: r, theta, phi
r = SQRT(x**2 + y**2 + z**2)
theta = ATAN2(SQRT(x**2+y**2),z)
IF (SQRT(x**2+y**2) < 10*EPSILON(x)) THEN
phi = 0
ELSE
phi = ATAN2(y,x)
END IF
END SUBROUTINE xyz_pallo
END MODULE pallokoordinaatit

Kytimme esimerkiss standardifunktiota ATAN2(y,x). Tm funktio palauttaa kompleksiluvun x + i y argumentin eli luvun arctan(y/x). Otimme
lukujen x ja y etumerkit huomioon siten, ett kulma tulee samaan xy-tason
neljnnekseen kuin alkuperinen kompleksiluku.

6. Lausekkeet ja sijoituslauseet

71

6.2.2 Pyristys- ja katkaisuoperaatiot


Seuraavassa esimerkiss havainnollistamme pyristys- ja katkaisuoperaatioiden toimintaa. Funktio FLOOR katkaisee alaspin, CEILING muuntaa lhinn suuremmaksi kokonaisluvuksi, INT katkaisee kohti nollaa ja NINT
pyrist lhimpn kokonaislukuun:
PROGRAM pyorista
IMPLICIT NONE
REAL, DIMENSION(6) :: numerot = &
(/ 1.4, 1.5, 1.6, -1.4, -1.5, -1.6 /)
INTEGER :: i
WRITE (*,(A))
F
C
I
N
DO i = 1, 6
WRITE (*,(F6.2, 4i4)) numerot(i), &
FLOOR(numerot(i)), CEILING(numerot(i)), &
INT(numerot(i)), NINT(numerot(i))
END DO
END PROGRAM pyorista

Ohjelman tulostus nytt tlt:


1.40
1.50
1.60
-1.40
-1.50
-1.60

F
1
1
1
-2
-2
-2

C
2
2
2
-1
-1
-1

I
1
1
1
-1
-1
-1

N
1
2
2
-1
-2
-2

6.2.3 Laskeminen kompleksiargumenteilla


Useat Fortranin standardifunktiot osaavat laskea kompleksiargumenteilla.
Tllaisia funktioita ovat muun muassa nelijuuri, eksponentti- ja logaritmifunktiot sek sini ja kosini.
Seuraavassa esimerkiss havainnollistamme kompleksiargumenttien kytt. Mrittelemme kompleksiluvut suluissa kirjoitettavina lukupareina. Tulostus tulee samassa muodossa.
PROGRAM kompleksi
IMPLICIT NONE
REAL, PARAMETER :: pii = 3.1415926535
COMPLEX, PARAMETER :: i = (0.0, 1.0)
WRITE (*,*) SQRT((-1.0, 0.0))
WRITE (*,*) EXP(i*pii)
WRITE (*,*) LOG((-1.0, 0.0))
WRITE (*,*) SIN(i)
END PROGRAM kompleksi

Ohjelman tulostus nytt tlt:


(0.0000000E+00,1.000000)
(-1.000000,-8.7422777E-08)

72

Fortran 95/2003

(0.0000000E+00,3.141593)
(0.0000000E+00,1.175201)

Laskemme
ohjelmassa numeerisesti likiarvot matemaattisille lausekkeille

1 = i, ei = 1, log(1) = i ja sin i = i sinh 1.

6.2.4 Toisen asteen yhtln ratkaiseminen


Seuraavassa toteutamme toisen asteen yhtln
ax 2 + bx + c = 0

(6.1)

ratkaisukaavan
x=

b2 4ac
2a

(6.2)

kompleksiargumenteilla.
PROGRAM juuret
IMPLICIT NONE
COMPLEX :: a, b, c, juuri1, juuri2
WRITE (*,*) Anna toisen asteen yhtln kompleksiset &
&kertoimet
WRITE (*, (A), ADVANCE=NO) a:
READ (*,*) a
WRITE (*, (A), ADVANCE=NO) b:
READ (*,*) b
WRITE (*, (A), ADVANCE=NO) c:
READ (*,*) c
CALL toinenaste(a, b, c, juuri1, juuri2)
WRITE (*,*) Juuret ovat , juuri1, juuri2
CONTAINS
SUBROUTINE toinenaste(a, b, c, j1, j2)
IMPLICIT NONE
COMPLEX, INTENT(IN) :: a, b, c
COMPLEX, INTENT(OUT) :: j1, j2
COMPLEX :: diskr
diskr = SQRT(b**2-4*a*c)
j1 = (-b+diskr)/(2*a)
j2 = (-b-diskr)/(2*a)
END SUBROUTINE toinenaste
END PROGRAM juuret

Kyttj sytt ohjelmalle yhtln kertoimet kompleksilukuina, joissa reaali- ja imaginriosa kirjoitetaan sulkuihin. Sytss voi kytt joko kokonaisluku- tai reaalilukuesityst.
Anna toisen asteen yhtln kompleksiset kertoimet
a: (2, 3)
b: (-3, -1)
c: (1.0, 1.0)
Juuret ovat (0.4392123,-0.8573956) (0.2530954,0.3189341)

6. Lausekkeet ja sijoituslauseet

73

Esimerkiss ratkaisimme yhtln (2 + 3i)x 2 + (3 i)x + (1 + i) = 0.


Huomaa, ett aliohjelmassa toinenaste kytetty ratkaisukaava (6.2) on numeerisesti epstabiili, jos b ja diskr ovat lhell toisiaan. Lisksi termit
b**2 ja 4*a*c voivat aiheuttaa ylivuodon, jos luku b tai luvut a ja c ovat
hyvin suuria. Ylivuodon voi vltt skaalaamalla yhtln (6.1) suurimmalla
kertoimella. Termin b ja nelijuurilausekkeen erotuksen voi laskea stabiilisti vaihtoehtoisella ratkaisukaavalla
x=

2c

.
b2 4ac

Tm on harjoitustehtvn.

6.3 Merkkijonolausekkeet
Tarvitsemme merkkijonolausekkeita sytn ja tulostuksen yhteydess sek
tekstimuotoista tietoa ksitteleviss sovelluksissa. Fortranissa on lukuisia
merkkijonoja ksittelevi standardifunktioita. Fortran 90 -standardin liitnnisstandardina (ISO/IEC 1539-2:1994) on olemassa tuki mys vaihtelevan
pituisille merkkijonoille.
Fortranin merkkijonomuuttujat mritelln lauseella CHARACTER. Ilman argumentteja lauseella voi mritell yhden merkin pituisia muuttujia, muuten
pituus on annettava LEN-parametrin avulla:
CHARACTER :: merkki
CHARACTER(LEN=10) :: merkkijono

Voimme poimia merkkijonosta alimerkkijonon tai yksittisi merkkej seuraavan ohjelman mukaisesti:
PROGRAM koe
IMPLICIT NONE
CHARACTER(LEN=10) :: merkkijono
merkkijono = Fortran
WRITE (*,*) merkkijono(2:4)
WRITE (*,*) merkkijono(6:6)
END PROGRAM koe

Jos sijoitettava merkkijono on pidempi kuin merkkijonomuuttuja, katkaistaan merkkej merkkijonon lopusta. Jos sijoitettava merkkijono on lyhyempi
kuin muuttuja, listn muuttujan loppuun vlilyntej.
Edellisen ohjelman tulostus nytt tlt:
ort
a

Merkkijonojen liitosoperaattori // muodostaa annetuista merkkijonoista uuden merkkijonon, joka on saatu liittmll nm kaksi perkkin. Tarkastelemme seuraavaa esimerkkiohjelmaa:

74

Fortran 95/2003

PROGRAM koe
IMPLICIT NONE
CHARACTER(LEN=30) :: etunimi = James
CHARACTER(LEN=30) :: sukunimi = Bond
CHARACTER(LEN=30) :: kone = @csc.fi
WRITE (*,*) Osoite: // TRIM(etunimi) // . &
// TRIM(sukunimi) // kone
END PROGRAM koe

Standardifunktio TRIM poistaa merkkijonon lopussa olevat vlilynnit. Ohjelma tulostaa merkkijonolausekkeen, joka saadaan liittmll nelj merkkijonolauseketta perkkin:
Osoite:James.Bond@csc.fi

6.4

Merkkitiedon ksittelyfunktioita

Fortran 95 ksitt mm. seuraavat standardifunktiot merkkijonojen ksittelyyn:


ADJUSTL(string) poistaa merkkijonon alussa olevat vlilynnit ja siirt ne merkkijonon loppuun.
ADJUSTR(string) siirt merkkijonon lopussa olevat vlilynnit merkkijonon alkuun.
INDEX(string,substring) palauttaa merkkijonon substring alkupaikan
merkkijonossa string tai nollan, jos yhteisi merkkej ei ole.
LEN(string) palauttaa merkkijonon pituuden.
LEN_TRIM(string) palauttaa merkkijonon pituuden ilman lopussa olevia vlilyntej.
TRIM(string) palauttaa alkuperisen merkkijonon ilman lopussa olevia
vlilyntej.
SCAN(string,set) palauttaa ensimmisen paikan, jossa merkkijonon set
merkki esiintyy merkkijonossa string, tai nollan, jos yhteisi merkkej
ei ole.
VERIFY(string,set) palauttaa nollan, jos kaikki annetun merkkijonon
string merkit esiintyvt merkkijonossa set, muuten ensimmisen poikkeavan merkin paikan.
Voit list funktioihin INDEX, SCAN ja VERIFY ylimrisen loogisen argumentin mask. Jos tmn argumentin arvo on .TRUE., merkkej etsitn
lopusta alkuun pin.
Voit kytt SCAN-funktiota esimerkiksi etsiesssi lukuja sytevirrasta. Seuraavassa lausekkeessa muuttujaan alku tulee merkkijonon rivi ensimmisen numeron paikka tai luku 0, jos merkkijonossa ei ole numeroita:

6. Lausekkeet ja sijoituslauseet

75

alku = SCAN(rivi, 0123456789)

Voit kytt VERIFY-funktiota tarkastamaan, ett merkkijonossa esiintyy


vain sallittuja merkkej. Seuraavassa lausekkeessa muuttuja tulos saa arvon nolla, jos merkkijono rivi voidaan tulkita oktaaliluvuksi, ja muuten
ensimmisen poikkeavan merkin paikan:
tulos = VERIFY(rivi, 01234567 )

Seuraava esimerkkiohjelma lukee nppimistlt etu- ja sukunimi ja tulostaa niiden perusteella muodostetun osoitteen. Ohjelmassa poistetaan merkkijonon lopussa olevat vlilynnit kahdella eri tavalla.
PROGRAM osoite
IMPLICIT NONE
CHARACTER(LEN=30) :: etunimi
CHARACTER(LEN=30) :: sukunimi
CHARACTER(LEN=30) :: kone = @csc.fi
WRITE (*, (A), ADVANCE=NO) Etunimi:
READ (*,*) etunimi
WRITE (*, (A), ADVANCE=NO) Sukunimi:
READ (*,*) sukunimi
WRITE (*,(5A)) Osoite: // TRIM(etunimi) // "." &
// sukunimi(1:LEN_TRIM(sukunimi)) // kone
END PROGRAM osoite

WRITE-lauseissa kytetty mre ADVANCE=NO est tulostuksen siirtymisen seuraavalle riville, kuten muuten tapahtuisi. Ohjelman kytt nytt
tlt:
Etunimi:Jaska
Sukunimi:Jokunen
Osoite:Jaska.Jokunen@csc.fi

6.5 Merkkitieto ja taulukot


Fortranissa on mahdollista ksitell mys merkkitaulukoita ja merkkijonotaulukoita. Kun tavallinen merkkijono sijoitetaan taulukkoon, sijoitetaan
Fortranin taulukkosyntaksin mukaisesti sama merkkijono taulukon kaikkiin
alkioihin.
Seuraavassa ohjelmassa muuttuja merkkijono on tavallinen merkkijono ja
muuttuja merkkitaulukko on taulukko, jossa on kymmenen yhden merkin
mittaista alkiota. Muuttuja merkkijonotaulukko puolestaan sislt viisi
alkiota, joista kukin on kymmenen merkin mittainen merkkijono.
PROGRAM taulut
IMPLICIT NONE
CHARACTER(LEN=10) :: merkkijono
CHARACTER, DIMENSION(10) :: merkkitaulukko
CHARACTER(LEN=10), DIMENSION(5) :: merkkijonotaulukko

76

Fortran 95/2003

merkkijono = Fortran
merkkitaulukko = +
merkkijonotaulukko = Fortran
WRITE (*,*) merkkijono
WRITE (*,*) merkkitaulukko
WRITE (*,*) merkkijonotaulukko
WRITE (*,*) merkkijono(2:4)
WRITE (*,*) merkkitaulukko(2:4)
WRITE (*,*) merkkijonotaulukko(1:2)(4:6)
END PROGRAM taulut

Viittaamme merkkijonoon ja merkkitaulukkoon tss esimerkiss samalla


tavalla eli kyttmll sulkuja ( . . . ). Merkkijonotaulukon viittauksessa
merkkijonotaulukko(1:2)(4:6)

ensimminen sulkulauseke valitsee taulukon alkiot 1 ja 2 ja toinen sulkulauseke nist alkioista merkit 4:st 6:een.
Ohjelman tulostus nytt tlt:
Fortran
++++++++++
Fortran
Fortran
ort
+++
tra
tra

Fortran

Fortran

Fortran

Huomaa, ett tulostus voi vaihdella koneesta toiseen, koska eri kntjt tulkitsevat muotoilukoodin * eli listan ohjaaman tulostuksen eri tavoin. Tst
kerrotaan lis kappaleessa 12.3 (sivu 194).

6.6

Loogiset lausekkeet

Loogisia lausekkeita kytetn IF-lauseen ja muiden ohjausrakenteiden yhteydess, ja niill siis toteutetaan ohjelman logiikka. Loogisiin lausekkeisiin
kuuluvat lukujen ja merkkijonojen vertailuoperaattoreiden lisksi loogiset
operaattorit.
Fortran-kielen vertailuoperaattorit on lueteltu taulukossa 6.1. Taulukossa
on annettu mys vanhat FORTRAN 77:n mukaiset operaattorit (.EQ. jne.).
Vertailuoperaattoreilla muodostettuja lausekkeita voi yhdistell loogisilla
operaattoreilla. Aritmeettiset operaatiot sitovat operandeja vahvemmin kuin
vertailuoperaattorit, jotka puolestaan ovat vahvempia kuin loogiset operaattorit. Loogisten operaattoreiden sidontajrjestys on taulukon 6.2 mukainen,
eli operaattori .NOT. suoritetaan ennen muita operaattoreita.
Loogisten operaattorien toimintaa on havainnollistettu taulukossa 6.3. Huomaa, ett operaattori .NEQV. on poissulkeva tai-operaattori (exclusive or).

6. Lausekkeet ja sijoituslauseet

77

Taulukko 6.1: Vertailuoperaattorit.

Vertailuoperaattori

Vanha muoto Selitys

==
/=
<
>
<=
>=

.EQ.
.NE.
.LT.
.GT.
.LE.
.GE.

Yhtsuuruus
Erisuuruus
Pienempi kuin
Suurempi kuin
Pienempi tai yhtsuuri kuin
Suurempi tai yhtsuuri kuin

Taulukko 6.2: Loogiset operaattorit.

Operaattori

Selitys

.NOT.
.AND.
.OR.
.EQV.
.NEQV.

Looginen negaatio
Looginen ja
Looginen tai
Looginen ekvivalenssi
Looginen epekvivalenssi

6.7 Esimerkkej loogisista lausekkeista


Havainnollistamme loogisten lausekkeiden kytt muutamilla esimerkeill.
Seuraava IF-rakenne keskeytt ohjelman suorituksen, jos luku x ei ole vlill [1, 5]:
IF (x < 1 .OR. x > 5) THEN
WRITE (*,*) Virhe: x ei ole vlill [1, 5]
STOP
END IF

Seuraava IF-rakenne tutkii, onko pistepari (x, y) yksikknelin sisll:


IF (x >= 0 .AND. x <= 1 .AND. y >= 0 .AND. y <= 1) THEN
WRITE (*,*) Piste (x,y) on yksikkneliss
END IF

Seuraava ehtolauseke testaa, pteek epyhtl ||x| 2| < 1:


IF (x > -3 .AND. x < -1 .OR. x > 1 .AND. x < 3) THEN
WRITE (*,*) x toteuttaa epyhtln | |x| - 2 | < 1
END IF

Ehto olisi tietysti voitu kirjoittaa mys muotoon


IF (ABS(ABS(x)- 2) < 1)

78

Fortran 95/2003

Taulukko 6.3: Loogisten operaattorien toiminta. Merkint F tarkoittaa arvoa eptosi


ja T arvoa tosi.

a
F
F
T
T

b
F
T
F
T

a .AND. b
F
F
F
T

a .OR. b
F
T
T
T

a .EQV. b
T
F
F
T

a .NEQV. b
F
T
T
F

Ohjelmassa ei pid verrata reaalilukujen yhtsuuruutta operaattorilla ==


vaan tutkia, ovatko reaaliluvut riittvn lhell toisiaan. l siis kirjoita ohjelmaasi lausetta
IF (x == a) THEN

vaan kyt vaikkapa lausetta


IF (ABS(x - a) < 10*EPSILON(a)*ABS(a)) THEN

Funktio EPSILON palauttaa muuttujan a tyyppi vastaavan kone-epsilonin 


arvon:  on pienin positiivinen luku, jolle ptee 1.0 +  > 1.0.
Voit sijoittaa loogisten lausekkeiden arvoja mys muuttujiin. Seuraavassa
ohjelmassa tutkimme, kuuluuko lukupari (x, y) nelin [0, 2] [0, 2], nelin [1, 3] [1, 3] vai kumpaankin.
PROGRAM numerot
IMPLICIT NONE
LOGICAL :: nelio1, nelio2
REAL :: x, y
WRITE (*,*) Anna lukupari x ja y
READ (*,*) x, y
nelio1 = (x >= 0 .AND. x
nelio2 = (x >= 1 .AND. x
IF (nelio1 .AND. nelio2)
WRITE (*,*) Piste on
ELSE IF (nelio1) THEN
WRITE (*,*) Piste on
ELSE IF (nelio2) THEN
WRITE (*,*) Piste on
ELSE
WRITE (*,*) Piste on
END IF
END PROGRAM numerot

6.8

<=2 .AND. y >= 0 .AND. y <= 2)


<=3 .AND. y >= 1 .AND. y <= 3)
THEN
kummassakin neliss
neliss 1
neliss 2
neliiden ulkopuolella

Merkkijonojen vertailu

Vertailuoperaattoreilla <, >, == jne. voi vertailla mys merkkijonoja, jolloin


vertailu tehdn merkki kerrallaan. Aluksi vertailtavista merkkijonoista teh-

6. Lausekkeet ja sijoituslauseet

79

dn yht pitki lismll lyhyemmn merkkijonon loppuun vlilyntej.


Siten esimerkiksi merkkijonot kukkuu ja kukkuu ovat Fortranin vertailuoperaattoreiden mielest samoja.
Yksittisten merkkien jrjestys on koneriippuvainen etenkin skandinaavisten kirjaimien ja muiden erikoismerkkien osalta. Merkkien jrjestys riippuu
merkkikoodista, joka voi olla eri koneissa erilainen. Jotta ohjelmat voitaisiin helpommin kirjoittaa siirrettviksi, on Fortranissa mahdollista vertailla
merkkej ja merkkijonoja joko konekohtaisen merkkikoodin tai standardoidun ASCII-koodin mukaan (katso liitett D sivulla 324). Kyttmll jlkimmist vaihtoehtoa saamme ohjelmasta siirrettvmmn joskin ehk hitaamman.
Jokaista merkki vastaa kokonaislukukoodi, jota kytetn selvitettess
merkkien jrjestyst. Yksittisen merkin jrjestysnumeron koneen sisisess merkistss saa selville standardifunktiolla ICHAR ja ASCII-merkistss
funktiolla IACHAR. Funktiot CHAR ja ACHAR puolestaan palauttavat jrjestysnumeroa vastaavan merkin.
Fortran edellytt, ett koneen sisisess merkistss numerot 09 sek kirjaimet AZ ovat luonnollisessa jrjestyksess, eli A tulee ennen B:t, Y ennen
Z:aa ja 0 tulee ennen ykkst. Lisksi edellytetn, ett vlilynti tulee ennen numeroita ja kirjaimia ja ett kaikki numerot tulevat joko ennen kaikkia
kirjaimia tai kaikkien kirjainten jlkeen. Standardi ei ota kantaa pienten ja
isojen kirjainten keskeniseen sijoitteluun eik erikoismerkkien ja skandinaavisten merkkien jrjestykseen.
ASCII-merkist mrittelee merkkikoodit 0127 ja siihen sisltyy numerot,
isot ja pienet kirjaimet sek tiettyj erikois- ja ohjausmerkkej. Funktiot
IACHAR ja ACHAR voivat tuntea mys 127: suurempia merkkikoodeja, mutta niden sislt on koneriippuvainen. Useissa tietokoneissa koneen sisinen
merkist on ASCII tai jokin sen laajennus.
Kun vertailet merkkijonoja operaattoreilla <, >, == jne., kytetn vertailujrjestyksen koneen sisist merkist. ASCII-merkist varten on olemassa
standardifunktiot LGE, LGT, LLE ja LLT. Nm funktiot vertailevat merkkijonojen ASCII-jrjestyst operaatioilla , >, ja <.
Seuraavassa esimerkiss vertailemme kahta merkkijonoa koneen sisist ja
ASCII-merkist kytten:
CHARACTER(LEN=10) :: mjono1, mjono2
...
IF (mjono1 < mjono2) THEN
WRITE (*,*) Sisinen merkist: , mjono1, &
on ennen jonoa , mjono2
END IF
IF (LLT(mjono1, mjono2)) THEN
WRITE (*,*) ASCII-merkist: , mjono1, &
on ennen jonoa , mjono2
END IF

80

Fortran 95/2003

6.9

Esimerkki: merkkijonojen lajittelu

Seuraava ohjelma lukee ptteelt rivien lukumrn ja lajiteltavat rivit. Rivit lajitellaan edell kuvattuun, koneen sisisen merkistn mukaiseen jrjestykseen kytten valintalajittelua (selection sort). Muuttuja sivu on taulukko, jonka kukin alkio on 80 merkki pitk merkkijonomuuttuja. Lauseke
sivu(1:rivit) viittaa taulukon riveihin 1-rivit ja lauseke sivu(j) tarkoittaa koko rivi j.
PROGRAM lajittele
! Ohjelma lukee sisn lajiteltavan tekstin
! sek tulostaa lajitellun tekstin.
IMPLICIT NONE
INTEGER, PARAMETER :: maxrivit = 100
CHARACTER(LEN=80), DIMENSION(maxrivit) :: sivu
CHARACTER(LEN=80) :: apu
INTEGER :: rivit, pienin, i, j
WRITE (*,*) Anna rivien lukumr:
READ (*,*) rivit
IF (rivit > maxrivit) THEN
WRITE (*,*) Liian monta rivi!
STOP
END IF
WRITE (*,*) Syt rivit:
READ (*, (A)) sivu(1:rivit)
DO i = 1, rivit-1
pienin = i
DO j = i+1, rivit
IF (sivu(j) < sivu(pienin)) pienin = j
END DO
apu = sivu(pienin)
sivu(pienin) = sivu(i)
sivu(i) = apu
END DO
WRITE (*,*) Lajiteltu taulukko:
WRITE (*,(A)) sivu(1:rivit)
END PROGRAM lajittele

Ohjelman kytt voisi nytt tlt:


Anna rivien lukumr:
4
Syt rivit:
sidewise
crosswise
clockwise
pairwise
Lajiteltu taulukko:
clockwise
crosswise
pairwise

6. Lausekkeet ja sijoituslauseet

81

sidewise

Jos haluat kytt edellisess lajitteluohjelmassa ASCII-merkistn perustuvaa vertailua, voit tehd sen seuraavalla IF-lauseella:
IF (LLT(sivu(j), sivu(pienin))) pienin = j

6.10 Sijoituslauseet ja laskentajrjestys


Olemme jo aikaisemmin kyttneet Fortranin sijoituslauseita. Nyt ksittelemme lyhyesti sijoituslauseissa esiintyvi tyyppimuunnoksia sek lausekkeiden osien suoritusjrjestyst.
Sijoituslauseissa voit sijoittaa eri tyyppisi lausekkeita muuttujiin, jos nille tietotyypeille on mritelty tyyppimuunnokset. Voit sijoittaa esimerkiksi
kokonaislukumuuttujiin reaali- tai kompleksimuuttujia, jolloin muuttujan
arvo katkaistaan kokonaisluvuksi nollaa kohti ja imaginriosa pudotetaan
pois. Sijoitettaessa isomman tarkkuuden reaaliluku pienemmn tarkkuuden
reaalilukuun joudutaan alkuperinen reaaliluku pyristmn.
Reaalimuuttujiin voi sijoittaa kokonais- ja kompleksilukulausekkeita. Vastaavasti kompleksimuuttujiin voi sijoittaa reaali- tai kokonaislukulausekkeita. Sijoitettaessa arvoja kokonais-, reaali- ja kompleksimuuttujiin kytetn
automaattisesti standardifunktioita INT, REAL ja CMPLX tarvittaviin muunnoksiin.
Taulukossa 6.4 on havainnollistettu sijoituslauseessa tapahtuvia numeeristen tyyppien muunnoksia. Loogisen lausekkeen arvoa ei voi sijoittaa muuhun
kuin loogiseen muuttujaan. Vastaavasti merkkijonolausekkeen voi sijoittaa
vain merkkijonomuuttujaan, jolloin tehdn tarvittaessa merkkijonon katkaisu tai vlilyntien lisminen merkkijonomuuttujan loppuun.
Taulukko 6.4: Automaattiset tyypinmuunnokset sijoituksessa a = b. Merkint =
tarkoittaa, ettei muunnosta tarvita.
a

REAL

COMPLEX

INTEGER

b: INTEGER
=

katkaisu

reaaliosan otto ja
katkaisu

REAL

esitysmuodon
muunnos

reaaliosan otto

COMPLEX

esitysmuodon
muunnos

esitysmuodon
muunnos

Seuraavassa ohjelmassa sijoitamme lausekkeita erityyppisiin muuttujiin ja


tulostamme muuttujien arvot:
PROGRAM numerot
IMPLICIT NONE
INTEGER :: i
REAL :: r

82

Fortran 95/2003

COMPLEX :: c
i = 7.3
r = (1.618034, 0.618034)
c = 2.7182818
WRITE (*,*) i, r, c
END PROGRAM numerot

Ohjelma tulostaa seuraavaa:


7

1.618034

(2.718282,0.0000000E+00)

Merkkijonolausekkeiden sijoituksessa liian pitk merkkijono katkaistaan, ja


liian lyhyt merkkijono tytetn lopusta vlilynneill:
PROGRAM sijoitus
IMPLICIT NONE
CHARACTER(LEN=10) :: merkit = Suutari
WRITE (*,*) merkit
merkit(1:4) = Se
WRITE (*,*) merkit
merkit(4:5) = Jeeves
WRITE (*,*) merkit
END PROGRAM sijoitus

Ohjelman tulostus nytt tlt:


Suutari
Se ari
Se Jeri

Lausekkeen oikea puoli lasketaan aina kokonaan, ennen kuin sijoitusoperaatiota ruvetaan suorittamaan. Erityisen trke tm on merkkijonomuuttujia
ksiteltess ja myhemmin luvussa 11 esiteltvien taulukoiden ksittelyss.
Seuraavassa esimerkiss merkkijonon merkit alimerkkijonot menevt pllekkin, mutta sijoituksessa kytetn muuttujan vanhaa arvoa:
PROGRAM siirto
IMPLICIT NONE
CHARACTER(LEN=10) :: merkit = Raskas
WRITE (*,*) merkit
merkit(3:6) = merkit(4:7)
WRITE (*,*) merkit
END PROGRAM siirto

Ohjelman tulostaa seuraavaa:


Raskas
Rakas

6. Lausekkeet ja sijoituslauseet

6.11

83

Funktioiden sivuvaikutukset

Kyttjn mrittelemien funktioiden sivuvaikutukset eivt vlttmtt tuota haluttua tulosta. Esimerkiksi voimme kirjoittaa funktion f, joka pit sisisell laskurilla kirjaa kutsukerroistaan. Kun ohjelmassa esiintyy lauseke
f(x) + f(x)

kntj voi optimoida tmn lausekkeeksi 2*f(x), jolloin laskuri toimii eri
tavalla.
Funktio ei saa muuttaa argumenttejaan, jos todellisia argumentteja kytetn muualla samassa lausekkeessa. Esimerkiksi lausekkeessa f(x) + f(x)
kytetty funktio f ei saa muuttaa argumenttia x.
Kntjn ei tarvitse laskea lausekkeen kaikkia osia, jos se pystyy pttelemn lausekkeen arvon muuten. Esimerkiksi ehtolauseessa
IF (x < 0 .AND. f(x) < 0) THEN

kntjn ei tarvitse kutsua f-funktiota, jos x 0, koska loogisen lausekkeen


arvo on tllin vlttmtt eptosi. Toisaalta kntj voi kutsua f-funktiota,
jos se haluaa.
Nollalla jakamista ei voi est seuraavan tyyppisell lausekkeella:
IF (a /= 0 .AND. 1/a < 1) THEN

vaan tm pit testata kahdessa eri ehtolausekkeessa:


IF (a /= 0) THEN
IF (1/a < 1) THEN
...

6.12

Operaattorien sidontajrjestys

Taulukossa 6.5 on lueteltu tss luvussa esitetyt operaattorit ja niiden tyypit.


Operaattorit on esitetty niiden sidontajrjestyksess eli ylempn esitetyt
operaattorit ovat vahvempia kuin alempana annetut. Mukaan on otettu mys
kyttjn mrittelemt operaattorit, joita tarkastellaan luvussa 9 (sivu 126).

6.13

Yhteenveto

Fortranin lausekkeissa voi esiinty vakioita, muuttujia, operaattoreita, standardifunktioiden kutsuja ja omien funktioiden kutsuja. Sijoituslausekkeen
oikea puoli lasketaan kokonaan ennen sijoitusta. Operaattorien sidontajrjestys on esitetty taulukossa 6.5.
Lausekkeiden suoritusjrjestys ei ole ennalta mrtty, vaan kntj voi

84

Fortran 95/2003

jrjest lausekkeita uudelleen ja optimoida pois tarpeettomia osia ja funktiokutsuja.


Taulukko 6.5: Operaattorien sidontajrjestys.

Operaattorin tyyppi

Operaattori

Numeerinen
Numeerinen
Numeerinen
Numeerinen
Merkkijono
Vertailu
Looginen
Looginen
Looginen
Looginen

kyttjn mrittelem yksiargumenttinen


**
* tai /
yksiargumenttinen + tai kaksiargumenttinen + tai //
== /= < <= > >=
.NOT.
.AND.
.OR.
.EQV. tai .NEQV
kyttjn mrittelem kaksiargumenttinen

Harjoitustehtvi
1. Mik on lausekkeen 2* -3-4** 2*3 arvo?
2. Mik on lausekkeen 2/3 * 2 arvo?
3. Paljonko on 1.0/2.0/3.0?
4. Miten seuraava lausekkeet poikkeavat toisistaan:
2 ** -3
2.0 ** -3

5. Ovatko seuraavat lausekkeet laillista Fortrania:


2.0
2.0
2.0
2.0

**
**
**
**

(-3)
-3
(-c)
-c

6. Miten seuraava lausekkeet poikkeavat toisistaan:


7 / (2 + 3)
7 / (2.0 + 3)

7. Laske arvo lausekkeille


4.0 / 3.0 / 2.0
4.0 ** 3.0 ** 2.0

8. Ohjelmakoodissa on mritelty
CHARACTER(LEN=8) :: a = Helsinki
CHARACTER(LEN=8) :: b = juurekas

6. Lausekkeet ja sijoituslauseet

85

Mink arvon saa lauseke a(1:2) // b(3:7)?


9. Jos 8-merkkisen merkkijonon merkit arvo on Onnela, niin mik on
sen arvo sijoituksen merkit(5:6) = merkit(6:8) jlkeen?
10. Miss jrjestyksess suoritetaan seuraava lauseke:
a .AND. .NOT. b .OR. .NOT. a .AND. b

Laadi lausekkeelle totuustaulukko. Mik looginen operaatio on kyseess?


11. Laadi ohjelma, joka lukee ptteelt heksadesimaalilukuja ja tarkistaa
niiden oikeellisuuden.
12. Laadi ohjelma, joka testaa, sijaitseeko annettu piste nelin [1, 1]
[1, 1] sisll ja yksikkympyrn ulkopuolella.
13. Kappaleessa 6.2.4 (sivu 72) ksitelln toisen asteen yhtln ratkaisemista. Toteuta ohjelma niin, ett ratkaisukaavan numeerinen epstabiilisuus vltetn.

86

Fortran 95/2003

Ohjausrakenteet

Ohjausrakenteiden avulla voimme toistaa laskutoimituksia niin usein kuin


on tarpeen tai suorittaa ne vain halutun ehdon toteutuessa. Fortran-kieli
sislt laajan valikoiman ohjausrakenteita. Seuraavassa kymme lpi eri
mahdollisuudet lyhyiden esimerkkien avulla.

7.1

Mihin tarvitsemme ohjausrakenteita

Kytimme edellisten lukujen esimerkeiss pasiassa perkkin kirjoitettuja


ohjelmakoodin lauseita, jotka suoritetaan annetussa jrjestyksess. Useimmin laskentatehtv ei kuitenkaan voi ratkaista jonona perkkisi lauseita,
vaan tarvitsemme testej ja toistorakenteita.
Tllaisia tehtvi varten on Fortranissa monipuolinen valikoima ohjausrakenteita, joissa yleens mritelln jokin lausejono eli ohjelmalohko suoritettavaksi ohjausrakenteen mrmll tavalla.
Perkkisyys tarkoittaa sit, ett ohjelmakoodiin kirjoitetut lauseet suoritetaan annetussa jrjestyksess. Valinnalla suoritetaan annetuissa ohjelmalohkoissa olevat lauseet vain sopivien ehtojen toteutuessa. Toisto mahdollistaa annetun ohjelmalohkon suorittamisen toistuvasti, esimerkiksi kunnes
toistolauseen lopetusehto toteutuu. Hyppylause siirtyy suorittamaan ohjelmayksikn annettua lausetta.
Ohjausrakenteet voivat olla mys toisiinsa nhden siskkin, jolloin esimerkiksi toistolause voidaan suorittaa vain annetun ehdon toteutuessa.
Proseduurin kutsu siirtyy suorittamaan proseduurissa (aliohjelma tai funktio) mriteltyj lauseita ja palaa lopulta takaisin kutsuvaan ohjelmayksikkn. Proseduureja ksitelln luvussa 8 (sivu 99).

7.2

Ehdollinen suoritus: IF-rakenne

IF-rakenteen avulla voimme antaa ehdon ohjelmalohkon lauseiden suorittamiselle. Taulukossa 7.1 on esitetty IF-rakenteen yleinen muoto.

7. Ohjausrakenteet

87

Taulukko 7.1: IF-rakenteen yleinen muoto.

[tunniste:] IF (ehto) THEN


lohko
[ELSE IF (ehto) THEN [tunniste]
lohko]
[ELSE [tunniste]
lohko]
END IF [tunniste]

Yksinkertaisin esimerkki IF-rakenteesta on seuraava:


IF (ehto) THEN
lohko
END IF

Tss lohko tarkoittaa jonoa suoritettavia lauseita tai ohjausrakenteita.


IF-rakenteen sisll olevaan lohkoon kuuluvat lauseet suoritetaan siin tapauksessa, ett loogisen lausekkeen ehto arvo on tosi. Esittelimme loogisia
lausekkeita kappaleessa 6.6 (sivu 76).
Vaihdamme seuraavassa esimerkiss muuttujien arvot, jos x on suurempi
kuin y:
IF (x > y) THEN
t = y
y = x
x = t
END IF

Jos haluamme tehd jotain muuta IF-rakenteen ehdon ollessa eptosi, voimme kytt ELSE-haaraa:
IF (x /= 0.0) THEN
y = 1.0/x
ELSE
WRITE (*,*) Nollalla jako
y = 0.0
END IF

Voimme sijoittaa IF-rakenteita mys siskkin. Lisksi voimme antaa IFrakenteelle tunnisteen, jolloin erityisesti siskkisten rakenteiden logiikka
voi tulla koodin lukijalle selkemmksi. Mys sisennyst kannattaa kytt, mutta se ei mr ohjelmakoodin logiikkaa. Seuraavassa on esimerkki
siskkisist IF-rakenteista:
ulompi: IF (x /= 0.0) THEN
sisempi: IF (y > 0.0) THEN
t = y/x
ELSE IF (y == 0.0) THEN sisempi
t = 0.0
ELSE sisempi

88

Fortran 95/2003

t = -y/x
END IF sisempi
END IF ulompi

Kytmme tss IF-lauseiden tunnisteita ulompi ja sisempi. Tunnisteiden


avulla voidaan pitkiss ja monimutkaisissa IF-rakenteissa merkit nkyviin,
mihin rakenteeseen esitetty ehto liittyy.
IF-rakenteen lisksi voimme kytt IF-lausetta, jossa suoritettavana on
vain yksi lause:
IF (ehto) lause

IF-lause vie vhemmn rivej kuin IF-rakenne, mutta se soveltuu vain yksinkertaisiin tapauksiin:
IF (x /= 0.0) y = 1/x

7.3

Tapausten ksittely: SELECT CASE -rakenne

SELECT CASE -rakenne on IF-lauseen vaihtoehto silloin, kun testaamme, kuuluuko annettu arvo tiettyyn joukkoon arvoja. Yleens testataan
kokonaisluku- tai merkkijonojoukkoja. Fortranin SELECT CASE -rakenne on
monipuolisempi kuin esimerkiksi Pascalin tai C-kielen vastaavat rakenteet.
Rakenteen yleinen muoto on esitetty taulukossa 7.2.
Taulukko 7.2: SELECT CASE -rakenteen yleinen muoto.

[tunniste:] SELECT CASE (lauseke)


[CASE (arvo[, arvo] . . . ) [tunniste]
lohko]...
[CASE DEFAULT [tunniste]
lohko]
END SELECT [tunniste]

Seuraavassa on esimerkki kokonaisluvun merkin selvittvst SELECT CASE


-rakenteesta:
INTEGER :: i, merkki
...
SELECT CASE(i)
CASE (:-1)
merkki = -1
CASE (0)
merkki = 0
CASE (1:)
merkki = 1
END SELECT

Tapausta CASE (:-1) vastaavat negatiiviset kokonaisluvut {1, 2, . . . }, ja


tapausta CASE (1:) vastaavat positiiviset kokonaisluvut {1, 2, . . . }. Jos i on

7. Ohjausrakenteet

89

nolla, saa muuttuja merkki arvon nolla.


Seuraavassa on hiukan monipuolisempi rakenne, jossa testataan alkulukuja:
INTEGER :: i
LOGICAL :: alkuluku
...
SELECT CASE(i)
CASE (2,3,5,7)
alkuluku = .TRUE.
CASE (1,4,6,8:10)
alkuluku = .FALSE.
CASE DEFAULT
alkuluku = testaa(i)
END SELECT

Siis jos luku i on joukossa {2, 3, 5, 7}, saa muuttuja alkuluku arvon tosi.
Jos luku on joukossa {1, 4, 6, 8, 9, 10} saa muuttuja arvon eptosi. Muutoin
(lause CASE DEFAULT) lukua i tutkitaan kyttjn mrittelemss funktiossa testaa, joka selvitt, onko annettu luku alkuluku.
Mys merkkijonoja voi kytt SELECT CASE -rakenteen testeiss:
INTEGER, PARAMETER :: mies = 1, nainen = 2, &
tuntematon = -1
CHARACTER(LEN=20) :: nimi
INTEGER :: laji
...
SELECT CASE(nimi)
CASE (Juha, Jussi)
laji = mies
CASE (Maarit, Marita)
laji = nainen
CASE DEFAULT
laji = tuntematon
END SELECT

Merkkej vertaillaan koneen oman merkkikoodin mukaisesti.


Mys merkkijonojen yhteydess voi kytt kaksoispistett vaihtoehtojen
ilmoittamiseen. Esimerkiksi lause
CASE (a:z,,,)

vastaa skandinaavisten merkkien pieni kirjaimia.


CASE-ehdoissa ei saa olla pllekkisi arvoja. Esimerkiksi seuraava on kielletty:
CASE (a:z,,,)
WRITE (*,*) pieni kirjain
CASE (aatami:eeva)
WRITE (*,*) sanakirjan osa 1

Esimerkiksi merkki b vastaa kumpaakin CASE-lauseen ehtoa: merkki kuuluu vlille a:z ja lisksi mys vlille aatami:eeva. Tuloksena on
knnsaikainen virheilmoitus.

90

Fortran 95/2003

Reaalilukumuuttujaa ei voi kytt SELECT CASE -rakenteen ehdoissa, sill


reaalilukujen luettelemisessa ei olisi paljon jrke.

7.4

Toisto: DO-rakenne

Toistoa tarvitaan esimerkiksi seuraavan tyyppisiss tehtviss. Iteraatiokaava


xi+1 = xi

f (xi )
f  (xi )

etsii funktion f (x) nollakohtaa ns. Newtonin menetelmll. Seuraavassa ohjelmakoodissa etsitn funktion f (x) = ex + x 5 nollakohtaa alkuarvauksena x0 = 0.
PROGRAM newt
IMPLICIT NONE
CHARACTER(LEN=*), PARAMETER :: muoto = (A,F9.6)
REAL :: x = 0.0
INTEGER :: i, n = 20
WRITE (*,(A,I3)) iteraatioita:, n
WRITE (*,muoto) x:n arvo alussa:, x
DO i = 1, n
x = x - (EXP(x) + x - 5)/(EXP(x) + 1)
WRITE (*,muoto) x:n arvo:, x
END DO
END PROGRAM newt

Toistorakenne aloitettiin DO-lauseella ja lopetettiin END DO -lauseeseen. Ohjelman tulostus voisi olla:
iteraatioita: 20
x:n arvo alussa: 0.000000
x:n arvo: 2.000000
x:n arvo: 1.476812
x:n arvo: 1.317715
x:n arvo: 1.306608
...
x:n arvo: 1.306559

Tss pstiin lhelle nollakohtaa jo neljnnell DO-silmukan toistokerralla.


Muuttujaa i kutsutaan silmukkamuuttujaksi lauseessa
DO i = 1, n

Silmukkamuuttujan on syyt olla kokonaislukutyyppi, sill reaalilukutyyppinen silmukkamuuttuja oli mritelty vanhentuvaksi Fortran 90 -standardin
piirteeksi, ja Fortran 95 -standardista tm piirre on jo poistettu.
DO-rakenteessa voi silmukkamuuttujalle antaa mys erikseen askeleen, mink verran silmukkamuuttujaa muutetaan kullakin iteraatiokierroksella:
DO i = 20, 1, -1

7. Ohjausrakenteet

91

Tss silmukkamuuttuja i saa arvot 20, 19, 18, . . . , 1. Silmukka


DO muuttuja = alku, loppu, askel

suoritetaan



loppu alku + askel
MAX 0, INT
askel
kertaa. Silmukkamuuttuja saa aluksi arvon alku, ja arvoa kasvatetaan lisyksell askel. Jos silmukkamuuttujan arvo ei ole vlill alku . . . loppu, silmukkaa ei en suoriteta.
Esimerkiksi silmukka DO i = 1, 32, 5 suoritetaan



32 1 + 5
MAX 0, INT
=7
5
kertaa ja silmukkaa DO i = 1, 32, -5 ei suoriteta kertaakaan.
Silmukkamuuttujaa ei saa muuttaa silmukan sisll. Silmukkamuuttujan arvoksi j viimeksi testattu arvo, esimerkiksi silmukassa
DO i = 1, 32, 5
lohko
END DO

muuttujan i arvo on silmukan jlkeen 36.


Aina emme tied etukteen, kuinka monta kertaa toistolausetta tytyy suorittaa. Tt varten on olemassa DO-rakenteesta muoto
DO
lohko
END DO

Tllaisenaan kyseess olisi ikuinen silmukka, jonka suoritus ei koskaan lopu.


Kytettviss on kuitenkin EXIT-lause, jolla DO-silmukan suoritus voidaan
lopettaa:
DO
READ (*,*) x
IF (x <= 0) EXIT
summa = summa + x
END DO

Tss luemme lukuja muuttujaan x ja lopetamme silmukan suorituksen,


jos luettu luku ei ole positiivinen. Muussa tapauksessa lismme luetun
lukuarvon muuttujan summa arvoon.
Voimme muuttaa edell esitetyn Newtonin iteraation seuraavaan muotoon:
PROGRAM newt
IMPLICIT NONE
REAL :: x = 0.0, d
REAL, PARAMETER :: toleranssi = 1e3*EPSILON(x)
INTEGER :: i, max_iter = 20
WRITE (*,(A,F9.6)) x:n arvo alussa:, x
DO i = 1, max_iter

92

Fortran 95/2003

d = (EXP(x) + x - 5)/(EXP(x) + 1)
x = x - d
WRITE (*,(A,I3,A,F9.6)) i:, i, , x:n arvo:, x
IF (ABS(d) <= toleranssi*ABS(x)) EXIT
END DO
END PROGRAM newt

Silmukan toistaminen siis lopetetaan, jos muuttujan x arvo muuttuu hyvin


vhn. Silmukalle on mys annettu maksimimr toistokertoja. Tulostus
voisi olla seuraava:
x:n
i:
i:
i:
i:
i:

arvo alussa:
1, x:n arvo:
2, x:n arvo:
3, x:n arvo:
4, x:n arvo:
5, x:n arvo:

0.000000
2.000000
1.476812
1.317715
1.306608
1.306559

Nollakohdan likimriseen lytmiseen tarvittiin viisi iteraatiota.


EXIT-lauseen lisksi kytettviss on CYCLE-lause, joka aloittaa vlittmsti
silmukan seuraavan toistokerran:
summaus: DO
READ (*,*) x
IF (x < 0.0) THEN
EXIT summaus
ELSE IF (x == 0.0) THEN
CYCLE summaus
END IF
summa = summa + 1.0/SQRT(x)
END DO summaus

Tss annettiin silmukkarakenteelle tunniste summaus. Silmukassa luetaan


muuttujalle x arvo, ja jos arvo on negatiivinen, poistutaan silmukasta. Jos
luettu arvo on nolla, siirrytn silmukan seuraavaan toistokertaan. Muussa
tapauksessa lasketaan summalauseke ja jatketaan silmukan toistoa.
Edell on annettu EXIT- ja CYCLE-lauseissa mys silmukan tunniste summaus.
Jos tunnistetta ei ole annettu, lauseet koskevat sisint DO-rakennetta. Tunnisteen avulla voidaan poistua mys ulommasta silmukasta. Kumpaakaan
lauseista EXIT ja CYCLE ei voi kytt DO-silmukan ulkopuolella.
Edellisten DO-rakenteiden lisksi kytettviss on DO WHILE -rakenne, jossa
silmukkaa suoritetaan niin kauan kuin annettu ehto on tosi:
READ (*,*) x
DO WHILE (x > 0)
summa = summa + x
READ (*,*) x
END DO

Testi x > 0 suoritetaan ennen silmukan kutakin toistokertaa. Tm DOrakenne on identtinen aiemmin esitetyn DO-rakenteen kanssa, jossa kytettiin silmukan lopetusehtoa
IF (x <= 0) EXIT

7. Ohjausrakenteet

93

Taulukossa 7.3 on esitetty DO-rakenteen yleinen muoto. Monissa tapauksissa DO WHILE -rakenne on luonnollinen tapa esitt ohjelmalohkon toisto.
Toisinaan selvi kuitenkin helpommin kyttmll EXIT- ja CYCLE-lauseita
DO-rakenteen sisll.
Taulukko 7.3: DO-rakenteen yleinen muoto.

[tunniste:] DO
lohko
END DO [tunniste]
[tunniste:] DO muuttuja = lauseke, lauseke[, lauseke]
lohko
END DO [tunniste]
[tunniste:] DO WHILE (ehto)
lohko
END DO [tunniste]

7.5 Esimerkki: Euklideen algoritmi


Seuraava ohjelma laskee kahden positiivisen kokonaisluvun suurimman yhteisen tekijn (s.y.t.) Euklideen algoritmilla:
PROGRAM syt
! Lasketaan kokonaislukujen m ja n suurin yhteinen
! tekij Euklideen algoritmilla.
IMPLICIT NONE
INTEGER, PARAMETER :: int_kind = SELECTED_INT_KIND(9)
INTEGER (KIND=int_kind) :: m, n, t
WRITE (*,*) Anna positiiviset kokonaisluvut m ja n:
READ (*,*) m, n
WRITE (*,*) m:, m, n:, n
positiivisuusehto: IF (m > 0 .AND. n > 0) THEN
DO WHILE (n /= 0)
t = MOD(m,n)
m = n
n = t
END DO
WRITE (*,*) Suurin yhteinen tekij:, m
ELSE
WRITE (*,*) Luvut eivt ole positiivisia!
END IF positiivisuusehto
END PROGRAM syt

Ohjelmassa kytetn siskkisi ohjausrakenteita. IF-lauseessa tarkistetaan kokonaislukujen positiivisuus:

94

Fortran 95/2003

positiivisuusehto: IF (m > 0 .AND. n > 0) THEN


...
ELSE
WRITE (*,*) Luvut eivt ole positiivisia!
END IF positiivisuusehto

Varsinainen ty tehdn DO WHILE -silmukassa:


DO WHILE (n /= 0)
t = MOD(m,n)
m = n
n = t
END DO

Silmukan sisll lasketaan funktionkutsu MOD(m,n). Tuloksena on jakojnns, joka saadaan, kun m jaetaan n:ll. Sen jlkeen otamme vanhan jakajan
uudeksi jaettavaksi ja jakojnnksen (t) uudeksi jakajaksi. Toisto pttyy,
kun jakojnnkseksi saatu luku on nolla, jolloin viimeisin jakaja on alkuperisten lukujen suurin yhteinen tekij.
Seuraavassa on esimerkki ohjelman kytst:
Anna positiiviset kokonaisluvut m ja n:
88664400 223355000
m: 88664400 n: 223355000
Suurin yhteinen tekij: 2200

7.6

Poikkeusten ksittely

Ohjelmakoodissa tarvitaan toisinaan normaalista poikkeavaa lauseiden suoritusjrjestyst erikoistilanteiden ksittelemiseksi. Usein kyseess on virhetilanne, josta ohjelmakoodin normaali logiikka ei selviydy.
Esimerkiksi silmukoista psee ulos EXIT-lauseella. Voit ksitell erikoistilanteita yhdistmll loogisia muuttujia ja IF-rakenteita:
LOGICAL :: tilanne_ok = .TRUE.
lohko1
IF (tilanne_ok) THEN
! normaali tilanne
lohko2
ELSE
! erikoistilanne
lohko3
END IF

Jos ensimmisess lohkossa tapahtuu jotain yllttv, asetetaan muuttujan


tilanne_ok arvoksi .FALSE.. Loppuosassa ohjelmayksikn koodia voidaan
tm tapaus ksitell erikseen IF-lauseen avulla. Voit kytt mys SELECT
CASE -rakennetta erikoistilanteiden ksittelyyn.
Erittin vakavien virheiden tapahtuessa voidaan ohjelman suoritus keskeytt STOP-kskyll. Aliohjelmasta tai funktiosta voi palata kutsuvaan ohjelmaan lauseella RETURN (aliohjelmista ja funktioista kerrotaan luvussa 8).

7. Ohjausrakenteet

95

7.7 Hyppylause: GOTO-lause


Hyppylause voi toisinaan soveltua poikkeusten ksittelyyn. Hyppylause siirt suorituksen ohjelmayksikn toiseen lauseeseen. Hyppylauseen kohde
tunnistetaan lausenumerolla. Usein hyppylauseen kohteena kytetn erityist CONTINUE-lausetta, joka ei tee mitn muuta kuin jatkaa suoritusta
seuraavasta lauseesta.
Seuraavassa on esimerkki hyppylauseen kytst:
! Tarkistetaan virhetilanne
IF (testi) GOTO 99
lohko1
99 CONTINUE
lohko2

Tss siis siirryttiin lauseeseen 99, jos ehto testi oli tosi. Huomaa, ett saman voi usein tehd IF-rakenteella. GOTO-lause voi olla kuitenkin joskus
tarpeen poistuttaessa siskkisten ohjausrakenteiden keskelt. Tsskin tapauksessa esimerkiksi DO-silmukan EXIT-lause on parempi ohjausrakenne
kuin GOTO, koska kntj pystyy optimoimaan DO-silmukkarakenteen suoritusta, mutta ei GOTO-lauseita.
Hyppylauseen normaalit muodot ovat
GOTO lausenumero
GO TO lausenumero

Hyppylauseen kohteena olevan lauseen tulee alkaa lausenumerolla:


lausenumero CONTINUE
lausenumero lause

Lausenumerossa on yhdest viiteen desimaalinumeroa, joista yhden tulee


olla nollasta poikkeava. Numeron alussa olevia nollia ei lasketa mukaan erotettaessa lausenumeroita toisistaan.
Hyppylausetta voi kytt siirtymiseen ulos ohjausrakenteesta, mutta hyppminen DO-silmukan tai muun ohjausrakenteen sislle on kielletty. Kuitenkin lohkon sisiset hyppylauseet ovat sallittuja, samoin hypyt lohkon
sislt ohjausrakenteen lopetuslauseeseen.
Vanhoissa Fortran-koodeissa on kytetty GOTO-lauseita toistorakenteiden tai
SELECT CASE -rakenteen simulointiin. Nm GOTO-rakenteet on syyt korvata paremmilla ohjausrakenteilla koodin selkeyden takia, mutta mys tehokkuussyist: kntj ei yleens pysty optimoimaan GOTO-kskyj sisltv
koodia.

7.8 Tiedonsiirron erikoistilanteet


Lausenumeroita voidaan joutua kyttmn mys tiedonsiirron erikoistilanteissa (tst kerrotaan tarkemmin kappaleessa 12.11 sivulla 210).

96

Fortran 95/2003

Usein selvimme kuitenkin kyttmll tilamuuttujaa. Seuraavassa on esimerkki syttoperaation erikoistilanteen ksittelyst:
INTEGER :: tila
tila = 0
READ (*,*,IOSTAT=tila) luku
IF (tila == 0) THEN
Tehdn normaali operaatio
ELSE
Virheen ksittely
END IF

Jos READ-lauseessa tapahtui virhe, saa muuttuja tila nollasta poikkeavan


arvon.

7.9

Yhteenveto

Fortran-kieli sislt kattavan valikoiman ohjausrakenteita lauseiden suoritusjrjestyksen muuttamiseksi. Erityisesti IF- ja SELECT CASE -rakenteet sek DO-silmukka mahdollistavat monimutkaisenkin ohjelmalogiikan selken
toteuttamisen.

Harjoitustehtvi
1. Mit seuraava koodirivi tekee?
IF (x /= 0.0) y = 1/x; z = y/x

2. Kirjoita SELECT CASE -rakenne, joka tekee kolme erityyppist toimintoa


sen mukaan, onko kokonaislukumuuttuja negatiivinen, nolla tai jokin
alkuluvuista (3, 5, 7, 11, 13). Muussa tapauksessa ei tehd mitn.
3. Ilmoita seuraavien DO-silmukkojen toistokertojen lukumr, silmukkamuuttujan i saamat arvot silmukan sisll sek silmukkamuuttujan
arvo DO-rakenteen jlkeen.
DO
DO
DO
DO
DO

i
i
i
i
i

=
=
=
=
=

1, 5
5, 0, -1
10, 1, -2
0, 30, 7
3, 2, 1

4. Kirjoita DO-silmukka, joka summaa 100 annetun reaaliluvun nelijuuret


poislukien negatiiviset luvut ja lopettaa summauksen, jos summattava
luku on pienempi kuin 1000.
5. Muuta suurimman yhteisen tekijn laskevaa ohjelmaa (katso kappaletta
7.5 sivulla 93) siten, ett ohjelma laskee ja tulostaa algoritmin tarvitsemien DO-silmukan toistokertojen lukumrn.

7. Ohjausrakenteet

97

Tarkista mys Lamn lause, jonka mukaan syt(m,n) vaatii korkeintaan


5*LOG10(max(m,n)) MOD-laskua.
6. Tulosta muunnostaulu tuumista millimetreihin (1" = 25.4 mm). Aloita
arvosta 0.0 ja tulosta arvoja puolen tuuman vlein 12 tuumaan asti.
7. Ratkaise toisen asteen yhtln ax 2 +bx +c = 0 reaaliset juuret kytten
apuna ratkaisukaavaa
x=

b2 4ac
.
2a

(7.1)

Tss a, b ja c ovat reaalilukuja. Jos ptee ehto b2 4ac = 0, on yhtlll vain yksi juuri, ja jos ptee ehto b2 4ac < 0, ei yhtlll ole
reaaliarvoisia juuria. Lue ohjelmassasi vakioiden a, b, c arvot ptteelt,
ja tarkista yhtln ratkaisujen lukumr ennen juurien laskemista.
Huomaa, ett termit b2 ja 4ac voivat aiheuttaa ylivuodon, jos kertoimet
ovat hyvin suuria. Ylivuodon voi vltt skaalaamalla yhtln suurimmalla kertoimella.

Termin b ja diskriminantin b2 4ac vlisess yhteen- tai vhennyslaskussa tapahtuvan pyristysvirheen voi minimoida kyttmll seuraavia ratkaisukaavoja:
diskr = SQRT(b**2-4*a*c)
q = -(b + SIGN(diskr,b))/2
j1 = q/a
j2 = c/q

Vertaile niden kaavojen ja yhtlss (7.1) esitetyn ratkaisukaavan tarkkuutta. Voit kytt esimerkkin yhtl x 2 bx + 1 = 0, kun kerroin
b
1.
8. Ns. Collatzin probleemassa tarkastellaan iteraatiota

ni+1 =

ni /2,
ni parillinen,
(3ni + 1)/2, ni pariton,

miss i = 0, 1, . . . , . Tss oletetaan, ett alkuarvo n0 > 0.


Jos n0 = 3, tuottaa iteraatio arvot
n1 = 5,

n2 = 8,

n3 = 4,

n4 = 2,

n5 = 1,

n6 = 2.

Siis iteraatio ptyy vuorottelemaan lukujen 1 ja 2 vlille. Nin ky kaikille positiivisille kokonaisluvuille (tt ei kuitenkaan ole matemaattisesti todistettu!).
Kirjoita ohjelma, joka laskee sykliin {1, 2} ptymiseen tarvittavien toistojen lukumrn luvuille n0 {1, 2, . . . , 100}.
9. Laajenna edellisen tehtvn ohjelmaa siten, ett iteraation alkuarvoksi
n0 ky mys negatiivinen kokonaisluku tai nolla. Laske, kuinka monta
iteraatiota tarvitaan, kunnes annetusta kokonaisluvusta (positiivinen tai

98

Fortran 95/2003

negatiivinen) pstn johonkin seuraavista sykleist:


S1 = {0}
S2 = {1}
S3 = {1, 2}
S4 = {5, 7, 10}
S5 = {17, 25, 37, 55, 82, 41, 61, 91, 136, 68, 34}.
Laske sykleihin ptyvien iteraatioiden pituudet, kun alkuarvoina n0 on
luvut {0, 1, 2, . . . , 100}.
10. Muuta seuraava GOTO-kskyj kyttv ohjelmakoodi IF-rakenteeksi:
INTEGER :: i, m, a, b
IF (i .EQ. 1) GOTO 100
IF (i .EQ. 3) GOTO 200
m = m + a
GOTO 500
100 m = m + b
GOTO 500
200 m = m - b
500 CONTINUE

Kumpi ohjelmakoodi on mielestsi selkempi?


11. Kirjoita funktio MSUM(f,n), joka summaa funktion f (i), i = 1, 2, . . . , n,
arvot maagisella summauksella (harjoitustehtv 11 sivulla 65). Funktio
f on tyyppi REAL. Laske summa mys kytten standardifunktiota SUM.
Tutki funktiota MSUM seuraavan sarjan avulla:

(1)k1
.
k
k=1

Mrittele kaksi eri tarkkuuden reaalilukulajia seuraavasti:


INTEGER, PARAMETER :: sp = KIND(1.0)
INTEGER, PARAMETER :: dp = &
SELECTED_REAL_KIND(2*PRECISION(1.0))

Mrittele tmn jlkeen taulukot arvot (laji sp) ja tarkat_arvot (laji


dp), joihin on laskettu funktion n ensimmist arvoa. Tulosta nyt seuraavien lausekkeiden arvot:
|SUM(arvot) SUM(tarkat_arvot)|
|MSUM(f,n) SUM(tarkat_arvot)|.

8. Funktiot ja aliohjelmat

99

Funktiot ja aliohjelmat

Kerromme tss luvussa Fortranin funktioiden ja aliohjelmien mrittelyst ja kytst. Funktioita ja aliohjelmia kutsutaan Fortran 95:ss yhteisell
nimell proseduurit. Itse mrittelemiesi proseduurien lisksi voit kytt
Fortranin standardiproseduureja.

8.1 Tehtvn jakaminen osiin


Tehtvn jakaminen pienempiin osiin on yleens paras lhestymistapa ratkaistessasi vhnkin suurempia tehtvi. Ohjelmankehityksess puhutaan
ksitteist top down ja bottom up, jotka kuvaavat, miss jrjestyksess tehtv jaetaan osiin.
Liikkeelle voi lhte ylhlt eli koko tehtvn tasolta. Tllin seuraavassa
vaiheessa ratkaistavana on muutama pienempi ja toivottavasti helpommin
ratkeava tehtv.
Toinen lhestymistapa on aloittaminen ruohonjuuritasolta, jolloin aluksi kirjoitetaan pieni apu- ja tykaluproseduureja. Nist osasista kootaan lopullisen tehtvn ratkaiseva ohjelmakoodi.
Yleens ohjelmankehityksess tarvitaan kumpaakin lhestymistapaa. Trkeint on ohjelmakoodin huolellinen ennakkosuunnittelu ja jo alkuvaiheissa aloitettu testaaminen.
Ohjelmankehityksess voi soveltaa komponenttiajattelua, jossa pyritn kehittmn mahdollisimman itsenisi ja uudelleen kytettviss olevia tykaluja. Ideana on mritell kunkin komponentin kutsurajapinta muihin ohjelmayksikihin sek komponentin tekemt operaatiot, mutta toteutuksen
yksityiskohdat piilotetaan ulkopuolisilta. Tllin voimme muuttaa toteutusta myhemmin ilman, ett komponenttiamme kyttvi ohjelmayksikit
tarvitsee mys muuttaa.
Fortran 95 -kieli tarjoaa hyvt mahdollisuudet ohjelmistokomponenttien
tuottamiseen. Tss luvussa esiteltvien aliohjelmien ja funktioiden lisksi kytettviss on seuraavassa luvussa (sivu 126) esiteltvt moduulit. Moduulien avulla voidaan proseduureja ja niihin liittyv dataa paketoida itsenisiksi kokonaisuuksiksi.

100

8.2

Fortran 95/2003

Proseduurit, funktiot ja aliohjelmat

Fortran 95:n yhteydess tarkoitetaan proseduurilla (procedure) joko funktiota (function) tai aliohjelmaa (subroutine). Funktio palauttaa arvon, joten
funktioita kytetn Fortranin lausekkeiden osana. Aliohjelma sen sijaan
siirt tietoa ainoastaan argumenttiensa vlityksell.
Fortranin ksitteist eroaa tlt osin esimerkiksi Pascal-kielen yhteydess
kytetyst terminologiasta: Pascalissa aliohjelma on yleisnimi funktioille ja
proseduureille. Kytmme Fortran-standardin mukaista terminologiaa, jotta Fortran-ohjelmakoodin kirjoittaminen ja lukeminen olisi helpompaa (esimerkiksi sanaa procedure kytetn Fortranin lauseiden osana). Lisksi
mys englanninkielisten ksikirjojen lukeminen on helpompaa, kun ksitteist on tuttu.

8.3

Mihin proseduureja kytetn

Proseduureja kyttmll tehtv voidaan jakaa pienempiin, nimettyihin


osatehtviin. Proseduurien kytt paitsi helpottaa ohjelman hahmottamista mys parantaa ohjelman yllpidettvyytt: usein tarvitsee muuttaa vain
osaa ohjelmakoodin proseduureista. Esimerkiksi muutettaessa ohjelman tulostusproseduureja ei vlttmtt tarvitse muuttaa ohjelman laskentaosaa,
ja pinvastoin.
Ohjelman jakaminen itsenisiin proseduureihin helpottaa mys ohjelmakoodin testausta. Kutakin proseduuria voidaan testata erikseen, ja vian lytyess se voidaan usein paikallistaa tiettyyn proseduuriin.
Fortran-kielt on kytetty jo vuosikymmeni ohjelmistoteollisuuden vlineen. Ohjelmoijan kytettviss onkin monipuolinen valikoima valmiita proseduureja. Saatavilla on esimerkiksi aliohjelmakirjastot IMSL ja NAG, jotka
sisltvt kumpikin yli tuhat proseduuria tieteellisen ja teknisen laskennan
perustehtviin. Fortran 95 -kieleen kuuluu 115 standardiproseduuria, joista
kerrotaan luvussa 13 (sivu 215).
Hyvin testattujen ja korkealaatuisten aliohjelmakirjastojen kytt sst
ohjelmoijan tyaikaa ja tuottaa yleens tehokkaita ja luotettavia ohjelmia.
Ei kannata keksi pyr uudestaan, kun se kerran on jo keksitty!

8.4

Funktioiden mrittely ja kytt

Funktioita kytetn Fortran-lausekkeissa tarvittavien arvojen laskemiseen.


Seuraavassa on esimerkki funktiosta, joka palauttaa arvonaan argumenttina
annetun kokonaisluvun i numeroiden mrn desimaalijrjestelmss:

8. Funktiot ja aliohjelmat

101

FUNCTION desimaalit(i) RESULT(desim)


IMPLICIT NONE
INTEGER :: i, desim
INTEGER :: apu
INTRINSIC ABS
desim = 1
apu = ABS(i)
DO WHILE (apu >= 10)
apu = apu/10
desim = desim + 1
END DO
END FUNCTION desimaalit

Funktion mrittely alkaa otsikkolauseella


FUNCTION tunnus(argumenttilista) RESULT(tunnus)

RESULT-mrittelyss esitelty tunnusta kytetn funktion palauttaman arvon mrittelemiseen. Myhemmin esittelemme otsikkolauseen vaihtoehtoisia muotoja.
Funktion arvo palautetaan kokonaislukumuuttujassa desim, joka on mainittu RESULT-mrittelyss. Tt muuttujaa saa kytt normaalin muuttujan
tapaan funktion sisll. Muuttujan desim viimeksi saama arvo palautetaan
funktiokutsun arvona.
Kokonaislukumuuttuja i on desimaalit-funktion muodollinen argumentti.
Huomaa, ett funktio desimaalit ei muuta muodollisen argumenttinsa i
arvoa!
Funktion sisll mritelty muuttuja apu on paikallinen muuttuja, joka on
kytettviss vain funktion sisll. Funktiossa voi vapaasti mritell paikallisia muuttujia avuksi tarvittaviin laskentaoperaatioihin.
Voimme kytt funktiota desimaalit pohjelman sisisen funktiona seuraavasti:
PROGRAM desit
IMPLICIT NONE
INTEGER :: n
WRITE (*,*) Anna n:
READ (*,*) n
WRITE (*,*) Luku n: , n
WRITE (*,*) Desimaaleja: , desimaalit(n)
CONTAINS
Funktion desimaalit edell esitetty mrittely
END PROGRAM desimaalit

Funktion desimaalit mrittely tulee sijoittaa CONTAINS-lauseen jlkeen


pohjelman loppuun. Jos sisisi proseduureja on enemmn kuin yksi, ne
mritelln perkkin. Sisisill proseduureilla ei voi olla omia sisisi proseduureja.
Lauseke desimaalit(n) on funktiokutsu, ja muuttujaa n sanotaan funktion todelliseksi argumentiksi. Funktiokutsun desimaalit(n) vaikutuksesta

102

Fortran 95/2003

funktion desimaalit muodollinen argumentti i saa arvokseen muuttujalla


n kutsuhetkell olevan arvon.
Seuraavassa on ohjelman desit kyttesimerkkej:
Anna n:
10101
Luku n:
Desimaaleja:
Anna n:
-199
Luku n:
Desimaaleja:

10101
5

-199
3

Koska Fortranin kokonaislukutyypill voi esitt vain rellisen kokoisia lukuja, ohjelmalle ei voi sytt mielivaltaisen pitki kokonaislukuja.
On mahdollista mritell mys funktio, jolla ei ole yhtn argumenttia. Sulkumerkkej on kuitenkin kytettv sek funktion mrittelyss ett kutsussa:
PROGRAM funtesti
IMPLICIT NONE
WRITE (*,*) Satunnaisluku: , rnd()
CONTAINS
FUNCTION rnd()
IMPLICIT NONE
REAL :: rnd
CALL RANDOM_NUMBER(rnd)
END FUNCTION rnd
END PROGRAM funtesti

Mrittelimme pohjelmalle sisisen funktion rnd, joka palauttaa arvonaan


tasajakautuneen satunnaisluvun vlilt [0, 1). Apuna kytmme standardialiohjelmaa RANDOM_NUMBER.

8.5

Funktion otsikkolauseen muodot

Edell esittelimme RESULT-lausekkeessa funktion arvon palauttavan muuttujan. Paluuarvon sisltv muuttuja voi olla saman niminen kuin funktio.
Tm onnistuu jttmll RESULT-lauseke pois:
FUNCTION desimaalit(i)
IMPLICIT NONE
INTEGER :: i, desimaalit

Tss mriteltiin funktion desimaalit tyypiksi kokonaislukutyyppi. Tyyppimrittelyn voi antaa mys osana otsikkolausetta:
INTEGER FUNCTION desimaalit(i)

Tllin ei en saa mritell desimaalit-nimist muuttujaa.

8. Funktiot ja aliohjelmat

103

RESULT-lausekkeen kytt on tarpeen erityisesti rekursiivisten (itsen kutsuvien) funktioiden tapauksessa. Tst kerrotaan lis kappaleessa 8.12 (sivu 111).
Tyyliseikoista voi olla monta mielt, mutta useimmiten RESULT-lausekkeen
kytt selkeytt ohjelmakoodia.

8.6 Funktion sivuvaikutukset


Seuraavassa ksittelemme funktioiden mahdollisia sivuvaikutuksia. Hyvss
ohjelmointityyliss funktioilla ei ole sivuvaikutuksia.
Funktion ei yleens ole syyt muuttaa argumenttiensa arvoja. Edell mritelty funktio desimaalit voitaisiin mritell virheellisesti seuraavasti:
FUNCTION desimaalit(i) RESULT(desim)
IMPLICIT NONE
INTEGER :: i, desim
INTRINSIC ABS
desim = 1
i = ABS(i)
DO WHILE (i >= 10)
i = i/10
desim = desim + 1
END DO
END FUNCTION desimaalit

Tss kytmme laskentaan muodollista argumenttia i lauseissa


i = ABS(i)

ja
i = i/10

Tllin muuttuu mys funktiokutsussa annetun todellisen argumentin arvo.


Esimerkiksi lauseen
WRITE (*,*) Desimaaleja: , desimaalit(n)

suorittamisen jlkeen olisi argumentin n arvo vlill [0, 9].


Entp mik olisi seuraavien lausekkeiden vaikutus:
desimaalit(10*n) + desimaalit(n+1)
desimaalit(1996)

Muuttuisiko muuttujan n arvo ylemmiss funktiokutsuissa? Tai muuttuisiko


vakion 1996 arvo? Nist syist funktioilla ei tulisi olla sivuvaikutuksia, eli
niiden ei tulisi muuttaa argumenttiensa arvoja.
Sivuvaikutuksen voi aiheuttaa mys vaikkapa funktion kutsukertoja laskevan muuttujan pivittminen funktion sisll sek funktiossa tehdyt tiedonsiirto-operaatiot (WRITE-lause jne.).

104

Fortran 95/2003

Seuraavassa on esimerkki tulostusoperaatiosta, jossa kytetn funktiota


desimaalit:
WRITE (*,*) Desimaaleja: , desimaalit(n)

Jos funktiossa desimaalit suoritetaan ptteelle tulostava WRITE-lause,


seurauksena on ajonaikainen virhetilanne, sill kytss on tllin kaksi
siskkist WRITE-lausetta.

8.7

Aliohjelmien mrittely ja kytt

Edellisess kappaleessa esitellyt funktiot palauttavat (yleens) vain funktiokutsun arvon eivtk muuta argumenttiensa arvoja. Usein tarvitaan tt monipuolisempaa tiedonsiirtoa proseduurin ja kutsuvan ohjelman vlill. Tt
varten Fortranissa voidaan mritell ja kytt aliohjelmia (subroutine).
Aliohjelman nimi mritelln SUBROUTINE-lauseessa, ja aliohjelmaa kutsutaan CALL-lauseella. Seuraavassa mrittelemme aliohjelman vaihda kahden reaaliluvun arvojen vaihtamiseksi keskenn:
SUBROUTINE vaihda(a,b)
IMPLICIT NONE
REAL :: a, b
REAL :: apu
apu = a
a = b
b = apu
END SUBROUTINE vaihda

Vaihdamme aliohjelman muodollisten argumenttien a ja b arvot keskenn


kytten apuna paikallista reaalilukumuuttujaa apu. Aliohjelmaa vaihda
voisi kytt seuraavasti:
PROGRAM vaihtotesti
IMPLICIT NONE
REAL :: x = 1.0, y = -1.0
WRITE (*,*) x,y: , x, y
IF (x > y) THEN
CALL vaihda(x,y)
END IF
WRITE (*,*) x,y: , x, y
CONTAINS
Aliohjelman vaihda edell annettu mrittely
END PROGRAM vaihtotesti

Aliohjelmakutsu tehdn pohjelman lauseessa


CALL vaihda(x,y)

Ohjelman tulostus voisi olla


x,y:
x,y:

1.000000
-1.000000

-1.000000
1.000000

8. Funktiot ja aliohjelmat

105

8.8 Todellisten ja muodollisten argumenttien


vastaavuus
Muutettaessa Fortranin proseduurien muodollista argumenttia muuttuu todellisen argumentin arvo. Erityisesti funktion tapauksessa tm voi johtaa
vaikeasti jljitettviin virhetilanteisiin. Virheiden vlttmiseksi on hyv kytt seuraavassa kappaleessa esiteltvi muodollisten argumenttien INTENTmreit.
Proseduurin todellisten ja muodollisten argumenttien tulisi vastata toisiaan
tyypiltn ja tarkkuudeltaan. Jos muodollista argumenttia muutetaan proseduurissa, tulee vastaavan todellisen argumentin olla muuttuja tai viittaus
taulukkomuuttujan tai rakenteisen muuttujan alkioon.
Fortranin proseduurikutsuissa ei tehd automaattisia tyypinmuunnoksia kuten esimerkiksi aritmeettisissa lausekkeissa. Mrittelemme esimerkin vuoksi funktion f seuraavasti:
FUNCTION f(x) RESULT(f_arvo)
IMPLICIT NONE
REAL :: x, f_arvo
f_arvo = EXP(x) + x - 5
END FUNCTION f

Nyt seuraavat funktiokutsut ovat virheellisi:


f(3)
f((1,0))
f(auto)

kokonaislukuargumentti
kompleksilukuargumentti
merkkitieto

Proseduureihin voi vlitt ns. oletetun pituisia merkkijonoja ja vaihtelevan


kokoisia taulukoita. Taulukkojen kytst proseduureissa kerrotaan enemmn luvussa 11 (sivu 164).
Seuraavassa on esimerkki funktiosta isot_merkit, joka muuttaa argumenttinaan saamansa merkkijonon isoiksi kirjaimiksi:
PROGRAM mjono_testi
IMPLICIT NONE
WRITE (*,*) isot_merkit(Merkkijono)
CONTAINS
FUNCTION isot_merkit(mjono) RESULT(uusi)
IMPLICIT NONE
CHARACTER(LEN=*) :: mjono
CHARACTER(LEN=LEN(mjono)) :: uusi
INTEGER, PARAMETER :: ero = IACHAR(A) - IACHAR(a)
INTEGER :: i
uusi = mjono
DO i = 1, LEN_TRIM(uusi)
SELECT CASE (uusi(i:i))
CASE(a:z)
uusi(i:i) = CHAR(IACHAR(uusi(i:i)) + ero)
END SELECT

106

Fortran 95/2003

END DO
END FUNCTION isot_merkit
END PROGRAM mjono_testi

Oletetun pituinen merkkijonoargumentti mjono on mritelty kytten merkint LEN=*. Funktio palauttaa merkkijonon uusi, joka on yht pitk kuin
argumentti mjono:
CHARACTER(LEN=LEN(mjono)) :: uusi

Funktiossa ei muuteta kirjaimia , ja isoiksi kirjaimiksi. Tm on harjoitustehtvn.

8.9

Tiedon vlittminen proseduureihin

Proseduurin mrittelyss on mahdollista kertoa, mihin suuntaan kukin proseduurin argumenteista siirt tietoa. Tm onnistuu kyttmll INTENTmrett. Seuraavassa aliohjelman mrittelyss kerromme, ett argumentit a ja b siirtvt tietoa kutsuvasta ohjelmayksikst sek aliohjelmaan ett
takaisin pin:
SUBROUTINE vaihda(a,b)
IMPLICIT NONE
REAL, INTENT(INOUT) :: a, b
REAL :: apu
apu = a
a = b
b = apu
END SUBROUTINE vaihda

Mrittely INTENT(INOUT) kertoo, ett aliohjelma vaihda kytt muodollisten argumenttien aliohjelmakutsussa saamia arvoja. Lisksi aliohjelma
muuttaa vastaavien todellisten argumenttien arvoja palattaessa aliohjelmasta kutsuvaan ohjelmayksikkn.
Jos INTENT-mreit ei ole annettu, kntj ei tarkista proseduurin argumenttien kytttapaa. Lisksi INTENT-mre auttaa hahmottamaan ohjelman toimintaa: mre kertoo, mihin suuntaan tietoa siirretn proseduurin
kussakin argumentissa.
INTENT-mritteen kytt est esimerkiksi seuraavan virheellisen aliohjelmakutsun:
CALL vaihda(1.0,2.0)

Tss vlitetn vaihda-aliohjelmaan kaksi vakiota vaihdettaviksi keskenn. Muodollisten argumenttien INTENT(INOUT)-mre johtaa tss tapauksessa knnsaikaiseen virheeseen, koska vakioiden muuttaminen on
kielletty.
INTENT-mreen vaihtoehtoiset muodot ovat
INTENT(IN)
INTENT(OUT)

siirretn tietoa sisn aliohjelmaan


siirretn tietoa ulos aliohjelmasta

8. Funktiot ja aliohjelmat

INTENT(INOUT)
INTENT(IN OUT)

107

siirretn tietoa molempiin suuntiin


siirretn tietoa molempiin suuntiin

Voimme muuttaa edell esitetty funktion desimaalit mrittely seuraavasti:


FUNCTION desimaalit(i) RESULT(desim)
IMPLICIT NONE
INTEGER, INTENT(IN) :: i
INTEGER :: desim
INTEGER :: apu
INTRINSIC ABS
desim = 1
apu = ABS(i)
DO WHILE (apu >= 10)
apu = apu/10
desim = desim + 1
END DO
END FUNCTION desimaalit

Mrite INTENT(IN) est muuttujan i muuttamisen funktion sisll. Tm


eliminoi funktion tahattomat sivuvaikutukset. Tss tapauksessa funktiokutsu
desimaalit(1996)

on tysin luvallinen, koska INTENT(IN)-mrite takaa, ettei funktio muuta


muodollista argumenttia i. Jos funktion koodiin olisi pujahtanut esimerkiksi
sijoituslause
i = ABS(i)

johtaa tm knnsaikaiseen virheilmoitukseen, koska argumentin i muuttaminen on kielletty.

8.10 Paikallisten muuttujien silyminen


Toisinaan olisi hyv, ett proseduurin paikallisen muuttujan arvo silyisi
kutsukertojen vlill. Olkoon esimerkkin seuraava funktio:
PROGRAM neliosumma
IMPLICIT NONE
WRITE (*,*) tulos: , nesu(1.0)
WRITE (*,*) tulos: , nesu(2.0)
CONTAINS
REAL FUNCTION nesu(x)
IMPLICIT NONE
REAL, INTENT(IN) :: x
REAL, SAVE :: nsum = 0.0
nsum = nsum + x**2
nesu = nsum
END FUNCTION nesu
END PROGRAM neliosumma

108

Fortran 95/2003

Funktio nesu laskee argumenttina x annettujen lukujen kumulatiivista nelisummaa kutsukerrasta toiseen.
Alustamme sisisen funktion paikallisen muuttujan nsum arvoltaan nollaksi
ja annamme muuttujalle mreen SAVE. Tmn johdosta muuttuja nsum saa
arvon 0.0 kutsuttaessa funktiota nesu ensimmisen kerran. Tmn jlkeen
muuttujan nsum arvo silyy funktiokutsujen vlill.
Edellisen ohjelman tulostus on
tulos:
tulos:

1.0000000
5.0000000

Edell kytettiin SAVE-mrett muuttujan arvon silyttmiseen funktion


kutsukertojen vlill. Asian olisi hoitanut mys mrittely
REAL :: nsum = 0.0

sill mrittelyss alustetut paikalliset proseduurin muuttujat oletetaan automaattisesti SAVE-tyyppisiksi.


Siis proseduurin paikallisen muuttujan arvo alustetaan mrittelylauseessa
vain kerran! Alustamattoman paikallisen muuttujan alkuarvo on mrittelemtn, eik muuttujan arvo sily kutsukertojen vlill, ellei kytet SAVEmrett.
Jos haluat, ett muuttujan arvo alustetaan aina proseduuria kutsuttaessa,
kyt suoritettavaa sijoituslausetta:
REAL :: nsum
nsum = 0.0

8.11

Sisiset ja ulkoiset proseduurit

Tss luvussa on thn asti kytetty yksinomaan pohjelman sisisi proseduureja (internal procedure). Ne mritelln pohjelman lopussa lauseen
CONTAINS jlkeen.
Sisisten proseduurien lisksi Fortranissa voi mritell ja kytt ulkoisia
proseduureja (external procedure) ja moduulin proseduureja (module procedure). Seuraavassa kerromme tarkemmin sisisist ja ulkoisista proseduureista. Moduulien kytt esitelln luvussa 9 (sivu 126).

8.11.1 Sisiset proseduurit


Sisiset proseduurit mritelln ohjelmayksikn lopussa CONTAINS-lauseen
jlkeen. Sisisill proseduureilla ei voi olla omia sisisi proseduureja.
Sisinen proseduuri mritelln isnnksi (host) kutsutun ohjelmayksikn sisll. Isntn voi toimia pohjelma, ulkoinen proseduuri tai moduulin proseduuri.

8. Funktiot ja aliohjelmat

109

Sisiset proseduurit voivat kytt hyvksi isnnss mriteltyj muuttujia


ja muita olioita. Poikkeuksena on sellainen tilanne, jossa isnnss mritelty nime kytetn mys sisisen proseduurin paikallisena tunnuksena.
Seuraavassa on esimerkki pohjelmassa mritellyn muuttujan kytst
sisisess aliohjelmassa:
PROGRAM vaihtotesti
IMPLICIT NONE
REAL, DIMENSION(3) :: taulu = (/ 0.5, -0.5, 1.0 /)
WRITE (*,*) taulu
CALL vaihda(1,2)
WRITE (*,*) taulu
CONTAINS
SUBROUTINE vaihda(i,j)
IMPLICIT NONE
INTEGER, INTENT(IN) :: i, j
REAL :: apu
apu = taulu(i)
taulu(i) = taulu(j)
taulu(j) = apu
END SUBROUTINE vaihda
END PROGRAM vaihtotesti

Pohjelmassa vaihdetaan kolmialkioisen taulukon taulu ensimminen ja


toinen alkio keskenn aliohjelmakutsulla
CALL vaihda(1,2)

Taulukkoa taulu kutsutaan pohjelman globaaliksi muuttujaksi, koska siihen voidaan viitata mys sisisist aliohjelmista. Globaaleja muuttujia tulisi
kytt harkiten, jottei synny tarpeettomia riippuvuuksia ohjelmayksikiden vlille.
Muuttuja apu on aliohjelman vaihda paikallinen muuttuja, eik se ny pohjelmaan tai pohjelman muihin sisisiin proseduureihin.
Aliohjelman vaihda mrittelyst voisi jtt pois lauseen
IMPLICIT NONE

sill pohjelmassa annettu lause vaikuttaa mys sisisiin proseduureihin.


Kuitenkin tm lause kannattaa sijoittaa mys sisisiin proseduureihin.
Muussa tapauksessa lause on erikseen muistettava list, jos sisisen proseduurin mrittely jatkossa siirretn tai kopioidaan ulkoiseksi tai moduulin
proseduuriksi.
Aliohjelma vaihda pystyy ksittelemn pohjelman taulukkoa taulu, koska aliohjelmassa ei ole mritelty saman nimist paikallista tunnusta. Jos
pohjelmassa olisi mritelty tunnus nimelt apu, ei tt tunnusta vastaava olio olisi kytettviss aliohjelmassa vaihda, koska aliohjelmassa on
mritelty paikallinen muuttuja apu.
Ohjelmayksikss nkyville tunnuksille on siis sisin mrittely se, joka on
kulloinkin voimassa.

110

Fortran 95/2003

8.11.2 Ulkoiset proseduurit


Ulkoiset proseduurit (external procedure) olivat ainoa Fortranin aikaisempien versioiden tuntema proseduurityyppi. Fortran 95:n yhteydess ulkoisia proseduureja tarvitaan lhinn kytettess valmiita aliohjelmakirjastoja. Yleens kannattaa kytt moduuleihin sijoitettuja proseduureja. Toinen
ulkoisten proseduurien kytttarve on kutsuttaessa Fortranista muilla ohjelmointikielill kirjoitettuja funktioita ja aliohjelmia.
Ulkoiset proseduurit tulee mainita ohjelmayksikn alussa EXTERNAL-lauseessa. Jos kyseess on funktio, tytyy lisksi mritell funktion palauttaman
arvon tyyppi. Tm onnistuu kyttmll EXTERNAL-mrett. Seuraavassa
on esimerkki ulkoisen funktion desimaalit kytst:
PROGRAM ulkolainen
IMPLICIT NONE
INTEGER :: n
INTEGER, EXTERNAL :: desimaalit
WRITE (*,*) Anna n:
READ (*,*) n
WRITE (*,*) Luku n: , n
WRITE (*,*) Desimaaleja: , desimaalit(n)
END PROGRAM ulkolainen

Ulkoinen funktio desimaalit tytyy knt erikseen ja linkitt pohjelman kanssa ajokelpoiseksi ohjelmaksi.
Koska ulkoinen aliohjelma ei palauta arvoa, tytyy mrittelyyn kytt tyypinmrittelylauseen sijaan EXTERNAL-lausetta vaikkapa seuraavasti:
EXTERNAL vaihda

8.11.3 Knnsaikaiset virheentarkistukset


Ulkoiset proseduurit eivt tarjoa samanlaista virheentarkistusta kuin sisiset tai moduulin proseduurit. Tmn vuoksi olisi aina pyrittv kyttmn
joko sisisi proseduureja tai moduulissa mriteltyj proseduureja.
Edellisess pohjelmassa voisi esiinty seuraavat ulkoisten funktioden kutsut ilman, ett kntj havaitsee virhett:
desimaalit(100.0)
desimaalit(1,2)

Ensimmisess funktiokutsussa vlitetn funktioon desimaalit reaalilukuvakio, vaikka funktiossa oletetaan ksiteltvn kokonaislukudataa. Toisessa tapauksessa vlitetn funktiokutsussa kaksi argumenttia, vaikka funktio tarvitsee vain yhden argumentin.
Kappaleessa 8.16 sivulla 116 kerrotaan lis kntjn tekemist proseduurien argumenttilistojen tarkistuksista.
Jos funktio on mritelty sisisen tai moduulin funktiona, huomaa kntj

8. Funktiot ja aliohjelmat

111

edell esitetyn tapaiset virheet. Tllin kntj tarkistaa mm. proseduurien


argumenttien lukumrn, tyypin ja INTENT-mritteiden noudattamisen.

8.12

Rekursio

Ohjelmoinnissa tarkoitetaan rekursiolla sit, ett proseduuri kutsuu itsen


joko suoraan tai epsuorasti. Monet matematiikassa kytetyt rekursiokaavat
voi toteuttaa rekursiivisilla proseduureilla. Toisaalta rekursio on usein kytnnss hitaampaa kuin yksinkertaiseen toistoon perustuva ohjelmakoodi.
Fortran 95:ss ovat rekursiiviset proseduurit sallittuja, toisin kuin FORTRAN 77:ss. Rekursiivisten funktioiden ja aliohjelmien otsikkorivill tytyy
kytt mrett RECURSIVE.
Esimerkiksi kertoma n! = n (n 1) 2 1 voidaan mritell rekursiivisesti:

n! = n (n 1)!, jos n > 0,


n! = 1,
jos n = 0.
(Mrittelemme kertomafunktion tss vain positiivisille kokonaisluvuille.)
Kertoma voidaan laskea kytten rekursiivista funktiota kert:
RECURSIVE FUNCTION kert(n) RESULT(kert_arvo)
IMPLICIT NONE
INTEGER, INTENT(IN) :: n
INTEGER :: kert_arvo
IF (n == 0) THEN
kert_arvo = 1
ELSE
kert_arvo = n * kert(n-1)
END IF
END FUNCTION kert

Funktion otsikkorivill on kytettv mrett RECURSIVE. Lisksi otsikkorivill on annettava RESULT-mrittely, jotta voimme kytt rekursiivisen
funktion tunnusta funktion sisll tekemn rekursiivisen funktiokutsun.
Kytmme funktion kert sisll rekursiivista kertomafunktion mritelm.
Jos muodollisen argumentin n arvo on nolla, saa funktio arvon 1. Muussa
tapauksessa kertomafunktion arvo on
kert_arvo = n * kert(n-1)

Tss kutsumme funktiota kert uudestaan, ja argumenttina on luku n-1.


Ehtoa n == 0 sanotaan rekursion lopetusehdoksi. Kaikissa rekursiivisissa ohjelmissa tytyy olla jokin lopetusehto, sill rekursio ei muuten pyshdy koskaan.
Voimme kytt edell mritelty funktiota kert seuraavasti:

112

Fortran 95/2003

PROGRAM kertoma
IMPLICIT NONE
INTEGER :: n
DO
WRITE (*,(A),ADVANCE=no) Anna n:
READ (*,*) n
WRITE (*,*) n = , n
IF (n < 0) EXIT
WRITE (*,*) n! = , kert(n)
END DO
CONTAINS
Edell esitetty funktion kert mrittely
END PROGRAM kertoma

Ohjelma toimii seuraavasti:


Anna n: 7
n =
7
n! =
5040
Anna n: 10
n =
10
n! =
3628800
Anna n: -1
n =
-1

Pohjelmassa tarkistetaan, ett luku n on suurempi tai yhtsuuri kuin nolla


ennen kertoman laskemista. Tarkistuksen voisi tietysti sijoittaa mys kertfunktion sislle. Huomaa, ett ohjelma tuottaa kokonaislukujen ylivuodon,
jos sytetty luku n on liian iso. Yleens kokonaisylivuoto tapahtuu ilman
ajonaikaista ilmoitusta, joten virhetilanteen tarkistus j ohjelmoijan vastuulle.
Voimme mritell rekursiivisen funktion otsikkolauseessa mys funktion
tyypin. Seuraavassa on esimerkki edell kytetyn funktion kert vaihtoehtoisesta mrittelyst:
RECURSIVE INTEGER FUNCTION kert(n) RESULT(kert_arvo)
IMPLICIT NONE
INTEGER, INTENT(IN) :: n
...
END FUNCTION kert

Otsikkolause voisi olla mys muotoa


INTEGER RECURSIVE FUNCTION kert(n) RESULT(kert_arvo)

Siis funktion paluuarvon tyyppimre voi olla RECURSIVE-mritteen edess


tai perss.
Huomaa, ett voimme laskea kertoman arvon rekursion sijaan nopeammin
kytten DO-rakennetta:
kertoma = 1
DO i = 2, n
kertoma = i*kertoma
END DO

Mys aliohjelma voi olla rekursiivinen, jolloin otsikkorivi nytt tlt:

8. Funktiot ja aliohjelmat

113

RECURSIVE SUBROUTINE tunnus[([argumentti][, argumentti]...])]

Laajempia ja realistisempia esimerkkej rekursion kytst lytyy luvusta 14 sivulta 234 alkaen.

8.13

Proseduurit argumentteina

Fortranin proseduureihin voi vlitt datan lisksi argumentteina mys toisia proseduureja. Seuraavassa mritelty funktio asteina muuttaa mink
tahansa funktion palauttaman arvon radiaaneista asteiksi. Kytmme esimerkkin Fortranin standardifunktioita ASIN, ACOS ja ATAN:
PROGRAM astesti
IMPLICIT NONE
INTRINSIC ASIN, ACOS, ATAN
WRITE (*,*) arcsin(0.5): , asteina(ASIN,0.5)
WRITE (*,*) arccos(0.5): , asteina(ACOS,0.5)
WRITE (*,*) arctan(1.0): , asteina(ATAN,1.0)
CONTAINS
REAL FUNCTION asteina(f, x)
IMPLICIT NONE
REAL, EXTERNAL :: f
REAL, INTENT(IN) :: x
INTRINSIC ATAN
asteina = 45*f(x)/ATAN(1.0)
END FUNCTION asteina
END PROGRAM astesti

Ohjelman tulostus nytt tlt:


arcsin(0.5):
arccos(0.5):
arctan(1.0):

30.00000
60.00000
45.00000

Funktion asteina mrittelyss esiteltiin muodollinen argumentti f kytten mrett EXTERNAL:


REAL, EXTERNAL :: f

Tm kertoo kntjlle, ett muodollista argumenttia f vastaa todellisena


argumenttina reaaliarvoinen funktio.
Mys aliohjelmien vlittminen argumentteina on mahdollista. Kun todellinen argumentti on aliohjelma, on muodollinen argumentti esiteltv lauseessa EXTERNAL, koska aliohjelma ei palauta arvoa.
Seuraavassa on esimerkki aliohjelmasta, joka tekee annetun operaation pareittain kaikille argumenttitaulukkojen alkioille. Mre DIMENSION(:) tarkoittaa, ett kyseinen muodollinen argumentti on yksiulotteinen taulukko ja
vastaava todellinen argumentti voi olla vaihtelevan kokoinen. Funktio SIZE
palauttaa taulukon alkioiden lukumrn. Taulukoista kerrotaan lis luvussa 11 (sivu 164).

114

Fortran 95/2003

SUBROUTINE suorita(operaatio, t1, t2)


IMPLICIT NONE
EXTERNAL operaatio
REAL, DIMENSION(:), INTENT(INOUT) :: t1, t2
INTEGER :: i
IF (SIZE(t1) /= SIZE(t2)) THEN
STOP t1 ja t2 erisuuret!
END IF
DO i = 1, SIZE(t1)
CALL operaatio(t1(i),t2(i))
END DO
END SUBROUTINE suorita

Operaationa voisi olla kappaleessa 8.11.1 (sivu 108) mritelty aliohjelma


vaihda. Koska aliohjelman suorita todellinen argumentti ei saa olla sisinen aliohjelma, tytyy aliohjelma vaihda mritell joko moduulissa tai
ulkoisena aliohjelmana, kuten seuraavassa:
PROGRAM argumentti
IMPLICIT NONE
REAL, DIMENSION(5) :: t1, t2
EXTERNAL vaihda
t1 = -1
t1(2) = 3
t2 = 2
CALL suorita(vaihda, t1, t2)
WRITE (*,*) t1
WRITE (*,*) t2
CONTAINS
Aliohjelman suorita edell esitelty mrittely
END PROGRAM argumentti

Sijoituslauseessa t1 = -1 sijoitetaan taulukon kaikkien alkioiden arvoksi


luku -1.
Aliohjelma vaihda tytyy knt erikseen ja linkitt pohjelman kanssa.
Ohjelman tulostus voisi nytt tlt:
2.0000000
-1.0000000

2.0000000
3.0000000

2.0000000
-1.0000000

2.0000000
-1.0000000

2.0000000
-1.0000000

Siis vaihto-operaatio tehtiin kaikille taulukkojen t1 ja t2 alkioille.


Ohjelmayksikn sisisi proseduureja ei saa antaa todellisiksi argumenteiksi.
Tm rajoitus on tehty tehokkuussyist: kntj voi tarvittaessa korvata
sisisten proseduurien kutsut niiden koodilla (inlining). Tm est sisisen
proseduurin kytn todellisena argumenttina.
Toinen rajoitus on, ett Fortranin standardialiohjelmia ei saa kytt todellisina argumentteina. Standardialiohjelman kutsun tosin voi sijoittaa itse
ohjelmoituun aliohjelmaan, jonka voi vlitt proseduurin todellisena argumenttina. Standardifunktioiden kyttminen todellisina argumentteina on
sallittua, kuten nimme tmn kappaleen alussa. Fortran 95:ss on kuusi
standardialiohjelmaa. Standardifunktioita on 109.

8. Funktiot ja aliohjelmat

8.14

115

PURE-mre

Proseduurien mre PURE mahdollistaa sivuvaikutuksettomien proseduurien


mrittelyn. Tllaisia proseduureja voi kytt helpommin rinnakkaislaskennassa, koska niiss ei ole sivuvaikutuksia. Esimerkkin tst on silmukka:
DO i = 1, n
a(i) = laske(i)
END DO

Jos funktio laske on mritelty sivuvaikutuksettomaksi funktioksi PUREmreell, voi kntj laskea silmukan funktiokutsut rinnakkain. PUREmre helpottaa kntjn toimintaa mys ohjelmakoodia optimoitaessa,
joten sen kytt on suositeltavaa.
Mys mrittelylausekkeissa (specication expression) saa kytt puhtaita
(PURE) funktioita:
SUBROUTINE s(a)
IMPLICIT NONE
REAL, DIMENSION(:,:) :: a
REAL, DIMENSION(rivit(a),sarakkeet(a)) :: apu
...
CONTAINS
PURE FUNCTION rivit(x)
REAL, INTENT(IN) :: x(:,:)
INTEGER :: rivit
rivit = SIZE(x,DIM=1)
END FUNCTION rivit
PURE FUNCTION sarakkeet(x)
...
END FUNCTION sarakkeet
END SUBROUTINE s

Edell mriteltiin puhtaat funktiot rivit ja sarakkeet, joiden avulla voimme mritell taulukon apu koon mrittelylauseessa.

8.15

ELEMENTAL-mre

Mre ELEMENTAL mahdollistaa alkioittaisten proseduurien mrittelyn. Tllin proseduurin argumenttina voi olla skalaari tai taulukko, ja kntj huolehtii tarvittavan koodin generoimisesta.
ELEMENTAL FUNCTION f(x, y)
IMPLICIT NONE
REAL, INTENT(IN) :: x, y
REAL :: f
f = SQRT(x**2 + y**2)
END FUNCTION f

116

Fortran 95/2003

ELEMENTAL-mreell varustettua proseduuria voi kutsua kaiken muotoisilla


taulukkoargumenteilla:
INTEGER, PARAMETER :: n = 10
REAL :: s
REAL, DIMENSION(n,n) :: a, b, c
REAL, DIMENSION(n) :: u, v, t
...
s = f(1.0, 2.0)
c = f(a, b)
t = f(u, v)

8.16

Kutsumuodon mrittely

Proseduurin tai operaattorin kutsumuodolla (interface) tarkoitetaan argumenttien lukumr ja tyyppi sek mahdollisesti palautettavan arvon tyyppi.
Tunnettu kutsumuoto (explicit interface) on kyseess silloin, kun kntj
pystyy knnsaikana selvittmn kutsumuodon sek tekemn thn liittyvi tarkistuksia. Ohjelmayksikn sisisill proseduureilla sek moduulin
proseduureilla on tunnettu kutsumuoto. Moduulien kytst kerrotaan tarkemmin luvussa 9 (sivu 126).
Oletusarvoinen kutsumuoto (implicit interface) on kyseess silloin, kun kntj ei kykene selvittmn olion kutsumuotoa. Ulkoisilla proseduureilla on
oletusarvoinen kutsumuoto.
Jos kutsumuoto ei ole automaattisesti tunnettu, voidaan se mritell ohjelmayksikn INTERFACE-lohkossa. Tm mahdollistaa esimerkiksi argumenttilistojen tarkistuksen kytettess ulkoisia proseduureja.
Voisimme muuttaa edell mritelty aliohjelmaa suorita siten, ett mrittelemme operaatio-argumentin kutsumuodon INTERFACE-lohkossa:
SUBROUTINE suorita(operaatio, t1, t2)
IMPLICIT NONE
INTERFACE
SUBROUTINE operaatio(a,b)
IMPLICIT NONE
REAL, INTENT(INOUT) :: a, b
END SUBROUTINE operaatio
END INTERFACE
REAL, DIMENSION(:), INTENT(INOUT) :: t1, t2
INTEGER :: i
IF (SIZE(t1) /= SIZE(t2)) STOP t1 ja t2 erisuuret!
DO i = 1, SIZE(t1)
CALL operaatio(t1(i),t2(i))
END DO
END SUBROUTINE suorita

Tmn jlkeen kntj huomaisi esimerkiksi seuraavan virheellisen mrittelyn muodollisille argumenteille t1 ja t2:

8. Funktiot ja aliohjelmat

117

REAL, DIMENSION(:), INTENT(IN) :: t1, t2

Lydtk virheen? Aliohjelman operaatio argumentit a ja b ovat kirjoittamamme INTERFACE-lohkon mukaan tyyppi
REAL, INTENT(INOUT) :: a, b

Tllin aliohjelmakutsu
CALL operaatio(t1(i),t2(i))

voisi muuttaa taulukoiden t1 ja t2 alkioita, mik taas on kielletty niden


argumenttien INTENT(IN)-mrittelyll.
Kutsumuodon mrittelylohkoa (INTERFACE) voi kytt mys geneeristen
proseduurien mrittelyyn. Tllin samaa proseduurikutsua voi kytt eri
tyyppisille argumenteille. Aiheesta kerrotaan enemmn moduulien yhteydess sivulla 140.

8.17

Esimerkki: NAG-aliohjelmakirjaston kytt

Seuraavassa on esimerkki NAG-aliohjelmakirjaston aliohjelman g05faf kutsumuodon esittmisest INTERFACE-lohkossa. Tm aliohjelma tuottaa tasajakautuneita satunnaislukuja vlilt [a, b].
PROGRAM nagrnd
IMPLICIT NONE
INTEGER, PARAMETER :: tarkkuus = SELECTED_REAL_KIND(12)
INTERFACE
SUBROUTINE g05faf(a, b, n, x)
IMPLICIT NONE
INTEGER, PARAMETER :: tarkkuus = SELECTED_REAL_KIND(12)
REAL(KIND=tarkkuus), INTENT(IN) :: a
REAL(KIND=tarkkuus), INTENT(IN) :: b
INTEGER, INTENT(IN) :: n
REAL(KIND=tarkkuus), INTENT(OUT), DIMENSION(n) :: x
END SUBROUTINE g05faf
END INTERFACE
REAL(KIND=tarkkuus), DIMENSION(5) :: taulukko
CALL g05faf(-1.0_tarkkuus, 1.0_tarkkuus, &
SIZE(taulukko), taulukko)
WRITE (*,*) taulukko
END PROGRAM nagrnd

Ohjelman tulostus voisi nytt tlt:


-0.6274038453462165 -0.7423877855038070 -0.2103735021112449
-0.8656148369960344 0.3577838993895552

Tss kytettiin NAG-aliohjelmakirjaston vanhempaa FORTRAN 77 -pohjaista versiota. Saatavilla on mys Fortran 95:een perustuva aliohjelmakirjasto, joka kytt hyvkseen moduuleita ja muita Fortranin kehittyneit
piirteit.

118

Fortran 95/2003

8.18

Valinnaiset ja avainsana-argumentit

Toisinaan on hyv, jos proseduurin kutsussa voi antaa vain osan argumenteista ja kytt oletusarvoja lopuille. Tllaisia argumentteja kutsutaan valinnaisiksi (optional). Useissa Fortranin standardiproseduureissa voi kytt
valinnaisia argumentteja. Seuraavassa on esimerkki tst:
INTEGER, PARAMETER :: tarkkuus = SELECTED_REAL_KIND(6)

Tss jtettiin antamatta eksponentin vaihteluvli lajiparametrin mrittelyss, jolloin funktio SELECTED_REAL_KIND kytt oletusarvoa tlle argumentille.
Avainsana-argumenteilla tarkoitetaan proseduurin argumentin ilmaisemista muodossa avainsana = arvo. Jos kytetn avainsana-argumentteja, on
argumenttien jrjestys vapaa, koska avainsana ilmaisee, mit muodollista
argumenttia kyseinen todellinen argumentti vastaa.
Seuraavassa on esimerkki avainsana-argumenttien kytst:
INTEGER, PARAMETER :: &
tarkkuus = SELECTED_REAL_KIND(r = 30, p = 6)

Tss annettiin funktiokutsun argumentit p ja r vaihtoehtoisessa jrjestyksess kytten avainsana-argumentteja.


Valinnaisia ja avainsana-argumentteja voi kytt samassa proseduurin kutsussa:
INTEGER, PARAMETER :: tarkkuus = SELECTED_REAL_KIND(r = 100)

Tss otamme kyttn reaalilukulajin, jonka eksponentin vaihteluvli on


vhintn 100. Argumentti p jtetn antamatta.
Jotta valinnaisia ja avainsana-argumentteja voisi kytt, on proseduurin
kutsumuodon oltava tunnettu. Standardiproseduurien kutsumuoto on automaattisesti tunnettu. Fortranin standardiproseduurien kutsumuodot ja
avainsanat on esitetty luvussa 13 (sivu 215).

8.19

Esimerkki: keskiarvon laskeminen

Olkoon tehtvn laskea reaalilukutaulukon lukujen keskiarvo. Tmn voi


tehd funktiolla keskiarvo:
PROGRAM keskiarvo_testi
IMPLICIT NONE
REAL, DIMENSION(6) :: x
x = (/ 1.0, -1.0, 0.5, 1.5, 2.5, 5.0 /)
WRITE (*,*) keskiarvo: , keskiarvo(x)
CONTAINS
REAL FUNCTION keskiarvo(x)
IMPLICIT NONE
REAL, DIMENSION(:), INTENT(IN) :: x

8. Funktiot ja aliohjelmat

119

keskiarvo = SUM(x)/SIZE(x)
END FUNCTION keskiarvo
END PROGRAM keskiarvo_testi

Haluaisimme muuttaa funktiota keskiarvo siten, ett keskiarvon laskemisesta voidaan jtt pois liian pienet tai liian suuret luvut. Valinnaiset argumentit mahdollistavat sen, ett samalla funktiolla voi laskea sek tavallisen
keskiarvon ett edell kuvatun muunnelman. Kytnnss tm tapahtuu
antamalla muodollisille argumenteille mre OPTIONAL. Funktiolla PRESENT
voi tarkistaa, onko valinnainen argumentti annettu funktiokutsussa.
Seuraavassa on muutetun funktion mrittely:
REAL FUNCTION keskiarvo(x, alaraja, ylaraja)
IMPLICIT NONE
REAL, DIMENSION(:), INTENT(IN) :: x
REAL, INTENT(IN), OPTIONAL :: alaraja, ylaraja
REAL :: a, b
INTEGER :: i, lkm
a = -HUGE(a)
b = HUGE(b)
IF (PRESENT(alaraja)) a = alaraja
IF (PRESENT(ylaraja)) b = ylaraja
keskiarvo = 0
lkm = 0
DO i = 1, SIZE(x)
IF (x(i) >= a .AND. x(i) <= b) THEN
keskiarvo = keskiarvo + x(i)
lkm = lkm + 1
END IF
END DO
IF (lkm /= 0) THEN
keskiarvo = keskiarvo/lkm
ELSE
keskiarvo = -HUGE(keskiarvo)
END IF
END FUNCTION keskiarvo

Funktiossa kytetn paikallisia muuttujia a ja b ala- ja ylrajojen tallentamiseen. Alussa rajoiksi asetetaan suurimmat negatiiviset ja positiiviset luvut
kyttmll funktiota HUGE. Jos valinnaiset argumentit on annettu, kytetn
rajoina annettuja arvoja:
IF (PRESENT(alaraja)) a = alaraja
IF (PRESENT(ylaraja)) b = ylaraja

Keskiarvo lasketaan muuttujaan keskiarvo DO-silmukassa. Silmukassa tarkistetaan, kuuluuko arvo x(i) halutulle vlille. Arvo x(i) listn muuttujaan keskiarvo vain tss tapauksessa:
IF (x(i) >= a .AND. x(i) <= b) THEN
keskiarvo = keskiarvo + x(i)
lkm = lkm + 1
END IF

Funktion lopussa palautetaan joko laskettu keskiarvo tai itseisarvoltaan suurin muuttujalla keskiarvo esitettviss oleva negatiivinen luku, jos yhtn

120

Fortran 95/2003

lukua ei kuulunut haluttuun vliin:


IF (lkm /= 0) THEN
keskiarvo = keskiarvo/lkm
ELSE
keskiarvo = -HUGE(keskiarvo)
END IF

Seuraavassa ohjelmassa testaamme edell mritelty funktiota:


PROGRAM keskiarvo_testi
IMPLICIT NONE
REAL, DIMENSION(6) :: a
a = (/ 1.0, -1.0, 0.5, 1.5, 2.5, 5.0 /)
write (*,(A,6(F7.3))) a: , a
WRITE (*,(A,F7.3)) keskiarvo: , keskiarvo(a)
WRITE (*,(A,F7.3)) keskiarvo: , keskiarvo(a,0.0)
WRITE (*,(A,F7.3)) keskiarvo: , &
keskiarvo(ylaraja=3.0,alaraja=-2.0,x=a)
WRITE (*,(A,F7.3)) keskiarvo: , keskiarvo(a,1.0,0.0)
CONTAINS
Funktion keskiarvo edell esitelty mrittely
END PROGRAM keskiarvo_testi

Ensimmisess funktiokutsussa jtmme valinnaiset argumentit antamatta.


Toisessa annamme ensimmisen valinnaisen argumentin eli alarajan:
keskiarvo(a,0.0)

Kolmannessa funktiokutsussa kytmme avainsana-argumentteja ja annamme molemmat valinnaiset argumentit:


keskiarvo(ylaraja=3.0,alaraja=-2.0,x=a)

Viimeisess funktiokutsussa annamme (virheellisesti) ylrajaa suuremman


alarajan (tm tilanne olisi ehk hyv tarkistaa funktiossa).
Ohjelman tulostus voisi olla seuraavaa:
a:
1.000 -1.000 0.500
keskiarvo:
1.583
keskiarvo:
2.100
keskiarvo:
0.900
keskiarvo: *******

1.500

2.500

5.000

Viimeinen funktiokutsu palautti vastauksena itseisarvoltaan suuren negatiivisen luvun, joka ei mahtunut annettuun tulostuskenttn.
Avainsana-argumentteja voi kytt aina, kun proseduurin kutsumuoto on
tunnettu. Valinnaisten argumenttien tapauksessa argumenttilistan keskelt
voi jtt pois argumentteja, jolloin loppuosalle argumenteista on kytettv
avainsanoja.

8. Funktiot ja aliohjelmat

121

8.20 Milloin tarvitsemme kutsumuodon


mrittely
Jos haluamme antaa ulkoiselle proseduurille valinnaisen tai avainsana-argumentin, on proseduurin kutsumuoto mriteltv INTERFACE-lohkossa.
Mys vaihtelevan pituisen merkkijonon, taulukon tai osoitintyypin palauttava ulkoinen proseduuri on mriteltv INTERFACE-lohkossa tai kutsumuodon on oltava muuten tunnettu. Taulukkoja ja osoitintyyppej ksitelln
luvussa 11 (sivu 164).
Kutsumuodon mrittelylohkoa tarvitaan mys, jos ulkoisella proseduurilla
on oletetun kokoinen taulukkoargumentti tai sen argumenttitaulukolla on
mre POINTER tai TARGET. Mys kytettess ulkoista proseduuria muodollisena tai todellisena argumenttina on kutsumuoto syyt mritell.
Lisksi proseduurin kutsumuoto on mriteltv INTERFACE-lohkossa, jos
moduulin proseduuria tai ulkoista proseduuria kytetn operaattorin tai
sijoituslauseen mrittelemiseen. Mys geneerisen nimen mrittelyyn tarvitaan kutsumuodon mrittelylohko. Tst kerrotaan kappaleessa 9.12 (sivu 140).

8.21

Nkyvyysalueet

Nkyvyysalue (scope) tarkoittaa sit ohjelman lauseiden kokonaisuutta, jossa kukin muuttujan, vakion tms. tunnus tai lausenumero on nkyviss. Kunkin ohjelmayksikn voi katsoa koostuvan joukosta nkyvyysalueita, jotka
eivt mene toistensa kanssa pllekkin.
Nkyvyysalue voi olla jokin seuraavista:
rakenteisen tyypin mrittely
proseduurin kutsumuodon mrittelylohko (interface body) lukuunottamatta kyseisen lohkon sisll olevia rakenteisten tyyppien mrittelyj, kutsumuodon mrittelylohkoja tai proseduureja
ohjelmayksikk (moduuli, pohjelma tai proseduuri) lukuunottamatta
kyseisen ohjelmayksikn sisll olevia rakenteisten tyyppien mrittelyj, kutsumuodon mrittelylohkoja tai proseduureja.
Taulukossa 8.1 on havainnollistettu nkyvyysalueita. Mrittelemme pohjelmassa rakenteisen tyypin kurssilainen, jota kytmme mys aliohjelmassa lue_tiedot. Mrittelemme lisksi funktion apurutiini kutsumuodon INTERFACE-lohkossa.
Pohjelmassa voitaisiin kytt tunnusta nimi vaikkapa muuttujan nimen, vaikka sama tunnus esiintyy tyypin kurssilainen mrittelyss. Tm

122

Fortran 95/2003

Taulukko 8.1: Esimerkki ohjelman sisltmist nkyvyysalueista.

PROGRAM oppi
IMPLICIT NONE
TYPE kurssilainen
CHARACTER(LEN=30) :: nimi
REAL :: arvosana
END TYPE kurssilainen
INTEGER, PARAMETER :: lkm = 120
TYPE(kurssilainen), DIMENSION(lkm) :: &
oppilaat
INTEGER :: luokan_koko
CALL lue_tiedot(luokan_koko, oppilaat)
CONTAINS
SUBROUTINE lue_tiedot(koko, taulukko)
INTEGER, INTENT(OUT) :: koko
TYPE(kurssilainen), DIMENSION(:), &
INTENT(OUT) :: taulukko
INTERFACE
FUNCTION apurutiini(lkm)
IMPLICIT NONE
REAL, INTENT(IN) :: lkm
jne.
END FUNCTION apurutiini
END INTERFACE
jne.
END SUBROUTINE lue_tiedot
END PROGRAM oppi

alue
alue
alue
alue
alue
alue
alue
alue
alue
alue
alue
alue
alue
alue
alue
alue
alue
alue
alue
alue
alue
alue
alue
alue
alue
alue

1
1
2
2
2
2
1
1
1
1
1
1
3
3
3
3
4
5
5
5
5
5
4
3
3
1

johtuu siit, ett tyypinmrittelyn sisll olevat tunnukset eivt kuulu pohjelman nkyvyysalueeseen eivtk siten mene pllekkin pohjelman
tunnusten kanssa.
Vastaavasti INTERFACE-lohkon sisll voi kytt vapaasti tunnuksia, kunhan ne ovat yksiksitteiset kunkin kutsumuodon mrittelylohkon sisll.
Esimerkiksi funktion apurutiini muodollinen argumentti lkm ei sekoitu
pohjelman saman nimiseen nimettyyn vakioon.
Kun tunnus on mritelty jossakin nkyvyysalueessa, se on kytettviss
kaikkialla nkyvyysalueessa. Jos nkyvyysalueessa otetaan kyttn moduuli USE-lauseella (tst kerrotaan seuraavassa luvussa), ovat kyseisess moduulissa mritellyt tunnukset kytettviss tmn nkyvyysalueen sisll.
Tllin nkyvyysalueessa ei en voi mritell saman nimisi paikallisia
tunnuksia.
Kussakin nkyvyysalueessa voi kytt sek nkyvyysalueen sisll mriteltyj tunnuksia ett sen isnnn nkyvyysalueessa mriteltyj tunnuksia.
Paikallinen tunnus korvaa aina isnnss mritellyn saman nimisen tunnuksen.

8. Funktiot ja aliohjelmat

123

Sisinen proseduuri voi viitata isnnn nkyvyysalueessa mriteltyihin tunnuksiin, jos proseduurissa ei ole mritelty saman nimist paikallista tunnusta. Taulukon 8.1 esimerkiss on pohjelman nimetty vakio lkm kytettviss aliohjelmassa lue_tiedot, jos aliohjelmassa ei mritell saman
nimist tunnusta.
Myskn INTERFACE-lohkon sisiset mrittelyt eivt vaikuta sisisen funktion lue_tiedot nkyvyysalueessa. Huomaa, ett INTERFACE-lohkon sislle
eivt ny lohkon isnnss tehdyt mrittelyt. Siten apurutiini-funktion
kutsumuodon mrittelyss (nkyvyysalue 5) ei ole kytettviss nkyvyysalueissa 1, 2 tai 3 tehtyj mrittelyj.

8.22

Yhteenveto

Fortranin proseduurit (aliohjelmat ja funktiot) voi jakaa kolmeen tyyppiin:


ohjelmayksikn sisiset proseduurit, ulkoiset proseduurit sek moduulin
proseduurit. Koska sisisten ja moduulin proseduurien kutsumuoto on tunnettu, pystyy kntj tekemn kutsumuodon virheentarkistukset ohjelman knnsaikana. Tst syyst ei ulkoisia proseduureja kannata kytt,
ellei muuta mahdollisuutta ole. Kyttmll INTERFACE-lohkoa voidaan kuitenkin mys ulkoisen proseduurin kutsumuoto tehd tunnetuksi.
Funktio mritelln seuraavasti:
[tyyppi] [RECURSIVE] [tyyppi] FUNCTION tunnus([argumentti] &
[, argumentti]...) [RESULT(tunnus)]
[mrittelylauseet]
[suoritettavat_lauseet]
[CONTAINS
sisinen_proseduuri
[sisinen_proseduuri]...]
END [FUNCTION [tunnus]]

Aliohjelma mritelln vastaavasti:


[RECURSIVE] SUBROUTINE tunnus[([argumentti][,argumentti]...])]
[mrittelylauseet]
[suoritettavat_lauseet]
[CONTAINS
sisinen_proseduuri
[sisinen_proseduuri]...]
END [SUBROUTINE [tunnus]]

Aliohjelmaa kutsutaan CALL-lauseessa:


CALL aliohjelma[(argumentti[, argumentti]...)]
CALL aliohjelma[([nimi=]argumentti[, [nimi=]argumentti]...)]

Jlkimmisess tapauksessa kytetn avainsana-argumentteja.

124

Fortran 95/2003

Harjoitustehtvi
1. Muuta kappaleen 8.8 esimerkkiohjelmaa siten, ett se muuttaa mys
skandinaaviset merkit , ja isoiksi kirjaimiksi.
2. Kirjoita funktio, joka laskee standardifunktioiden SIN, COS ja TAN arvon, kun reaalilukuargumentti annetaan asteina eik radiaaneina. Katso
mallia kappaleesta 8.13 sivulla 113.
3. Kirjoita kertoman laskeva rekursiivinen aliohjelma (ei funktio!), jonka
kutsusyntaksi voisi olla
CALL kertoma(n,tulos)

Kirjoita mys DO-silmukkaversio kertoman laskemiseksi. Vertaile rekursiivisen funktion ja silmukkaversion nopeutta. Voit kytt vertailuun
Unixin time-komentoa, Fortran 90:n standardialiohjelmaa SYSTEM_CLOCK
tai Fortran 95:n aliohjelmaa CPU_TIME.
4. Kirjoita funktio, joka laskee n termi sinifunktion sarjakehitelmst
pisteess x. Sinifunktion sarjakehitelm origon ympristss on
sin x = x

x 2i1
x3
x5
x7
(1)i+1
+

+ =
.
6
120
5040
(2i 1)!
i=1

Huomaa, ett tt sarjakehitelm voi kytt vain, kun |x| 0. Laske likiarvo lausekkeelle sin 0.5 kyttmll 2 tai 4 termi. Laske mys
lausekkeen SIN(0.5) arvo.
Huomaa, ett sarjakehitelmn n:s termi tn toteuttaa yhtln
t1 = x,
tn+1 = tn

x2
.
2n(2n + 1)

Kirjoita funktiosta rekursiivinen versio.


5. Kirjoita rekursiivinen funktio kahden kokonaisluvun suurimman yhteisen tekijn laskemiseksi. Kyt hyvksi identiteettej

syt(m, n) = syt(n, MOD(m, n)),


syt(m, 0) = m.
Vertaile rekursiivisen funktion ja kappaleessa 7.5 sivulla 93 esitetyn
silmukkaversion nopeutta. Voit kytt vertailuun Unixin time-komentoa, Fortran 90:n aliohjelmaa SYSTEM_CLOCK tai Fortran 95:n aliohjelmaa CPU_TIME.
6. Kirjoita aliohjelma kokonaisluvuista a ja b koostuvan murtoluvun a/b
sieventmiseksi. Kyt apuna edellisess harjoituksessa tekemsi suurimman yhteisen tekijn laskevaa funktiota.

8. Funktiot ja aliohjelmat

125

7. Kirjoita rekursiivinen funktio joka laskee Tribonaccin lukuja:

x = 1, x = 1, x = 1,
1
2
3
xn = xn1 + xn2 + xn3 , n > 3.
Laske x12 . Tutki laskenta-ajan kyttytymist n:n funktiona kyttmll Unixin time-komentoa, Fortran 90:n aliohjelmaa SYSTEM_CLOCK tai
Fortran 95:n aliohjelmaa CPU_TIME. Onko rekursiivinen funktio tehokas? Toteuta laskeminen mys silmukkarakenteen avulla.
8. Kirjoita rekursiivinen funktio positiivisen kokonaisluvun m muuntamiseksi kymmenjrjestelmst k-kantaiseen jrjestelmn (1 < k < 100).
Esimerkiksi luku 1996 on 8-jrjestelmss (3714)8 = 3 83 + 7 82 +
81 + 4. Kyt ohjelmassasi hyvksi identiteetti
m = (dn kn1 + dn1 kn2 + + d1 )k + d0
= (m/k) k + MOD(m, k).
Edell merkint m/k tarkoittaa kokonaislukujen jakolaskua.
Kirjoita mys silmukkaversio algoritmista. Vertaile rekursiivisen funktion ja silmukkaversion nopeutta. Voit kytt vertailuun Unixin timekomentoa, Fortran 90:n aliohjelmaa SYSTEM_CLOCK tai Fortran 95:n aliohjelmaa CPU_TIME.
9. Kirjoita proseduuri skalaarifunktion f (x) integroimista varten. Kyt
esimerkiksi Gaussin integroimiskaavaa
I



ba
a+b
a+b
ba
ba
f(
) + f(
+ ) ,
2
2
2
2 3
2 3

miss integroimisvli on [a, b]. Kyt valinnaisia argumentteja seuraavasti:


jos vasenta integroimisrajaa a ei ole annettu, asetetaan rajan arvoksi a = 0,
jos oikeaa integroimisrajaa b ei ole annettu, asetetaan rajan arvoksi
b = a + 1.
10. Muuta kappaleen 8.11.2 (sivu 110) pohjelmaa ulkolainen siten, ett
ulkoisen funktion desimaalit kutsumuoto esitelln ohjelman alussa
INTERFACE-lohkossa.

126

Fortran 95/2003

Moduulit ja operaattorit

Moduulit mahdollistavat tietorakenteiden ja niit ksittelevien algoritmien


sijoittamisen samaan ohjelmayksikkn. Lisksi toteutuksen yksityiskohdat voidaan piilottaa moduulin kyttjlt. Tllin vltytn tarpeettomilta
ja mahdollisesti yllttvilt riippuvuuksilta ohjelmayksikiden vlill. Esittelemme tss luvussa mys operaattorien mrittely moduulien avulla.

9.1

Modulaarinen ohjelmointi

Modulaarisuudella tarkoitetaan ohjelmointitehtvn jakamista toisistaan


minimaalisesti riippuviin osiin eli moduuleihin. Moduulissa voidaan mritell mm. nimettyj vakioita, muuttujia, rakenteisia tyyppej ja proseduureja. Kunkin moduulin sisinen toteutus voidaan piilottaa kyseist moduulia
kyttvilt ohjelmayksikilt. Ainoastaan moduulin kyttliittym on julkinen.
Fortranin moduuliin voi sijoittaa ohjelmiston eri ohjelmayksikiden tarvitsemia globaaleja vakioita ja muuttujia. Moduulissa voi mys mritell uusia
tietotyyppej ja niihin liittyvi operaatioita. Moduulissa mriteltyj tietoolioita (muuttujat, vakiot, proseduurit jne.) voi kytt useammassa kuin
yhdess ohjelma-yksikss.
Olkoon kuvitteellisena esimerkkin datan lajitteluun tarkoitettu moduuli.
Moduulissa mritellyt tunnukset voidaan mritell yksityisiksi, jolloin ne
ovat kytettviss vain moduulin sislt ksin. Julkisiksi voimme mritell
moduulin ja lajittelurutiinin nimi sek lajittelurutiinin kutsusyntaksi. Toteutuksen yksityiskohdat voimme jtt ohjelmoijan vapaasti valittaviksi.
Lajittelumoduulissa voidaan alussa kytt yksinkertaista ja helposti toteutettavaa lajitteluproseduuria. Datamrn kasvaessa voidaan moduulin sisinen toteutus uusia tehokkaammaksi.
Moduuleilla voimme toteuttaa abstrakteja tietorakenteita, jolloin kyttj
nkee tietorakenteelle mritellyt operaatiot mutta ei pse vaikuttamaan
tietorakenteen sisiseen toteutukseen. Tllin on mahdollista hajauttaa ohjelmankehitys pieniin erillisiin osatehtviin, joissa ohjelmoijat voivat halutessaan muuttaa toteutuksen yksityiskohtia ohjelmiston toiminnallisuuden
muuttumatta. Lisksi abstrakteja tietorakenteita on helpompi yllpit ja

9. Moduulit ja operaattorit

127

kehitt, koska toteutuksessa ei ole sitouduttu tiettyyn sisiseen datan esitystapaan.


Moduulien avulla voi mys korvata vanhoissa Fortran-ohjelmissa kytetyt
COMMON-alueet ja BLOCK DATA -ohjelmayksikt.

9.2 Ohjelmayksikkjen vliset suhteet


Kuva 9.1 havainnollistaa Fortranin eri ohjelmayksikkjen suhdetta toisiinsa.
pohjelma

moduuli
CONTAINS

CONTAINS

CONTAINS

ulkoinen proseduuri
CONTAINS
CONTAINS
moduulin
proseduuri
sisinen
proseduuri

Kuva 9.1: Fortran 95:n ohjelmayksikiden liittyminen toisiinsa.

Jokaisessa ohjelmassa tulee olla tsmlleen yksi pohjelma. Pohjelmalla


voi olla kytssn sisisi proseduureja, jotka mritelln pohjelman lopussa CONTAINS-lauseen jlkeen. Pohjelma voi lisksi kytt hyvkseen
ulkoisia proseduureja ja moduuleja. Ulkoisilla proseduureilla ja moduulin
proseduureilla voi olla omia sisisi proseduureja, mutta sisisill proseduureilla ei en voi olla sisisi proseduureja.
Moduuleissa voi mritell mm. nimettyj vakioita, muuttujia, uusia tietotyyppej, NAMELIST-ryhmi ja proseduureja. Moduulit voivat kytt toisia
moduuleita, jolloin moduuleista muodostuu hierarkkinen rakenne.
Ulkoiset proseduurit voi ohjelmoida toisella ohjelmointikielell. Esimerkiksi C-kielisten rutiinien kyttminen Fortranista ksin ei sislly Fortranin tai
C-kielen mrittelyyn, mutta onnistuu joissakin laiteympristiss, esimerkiksi useimmissa Unix-koneissa. Ulkoiset proseduurit voivat olla tietenkin
mys Fortran-kielell kirjoitettuja, mutta tmn sijaan kannattaa yleens
kytt sisisi proseduureja tai moduulirakenteita.

128

Fortran 95/2003

9.3

Esimerkki: vakioiden mrittely

Moduulissa voi mritell vaikkapa usein kytettyj vakioita. Seuraavaan


moduuliin on sijoitettu pituusyksikkjen muunnoskertoimia sisltv vakiotaulukko matkat (perusyksikkn on metri).
MODULE yksikot
IMPLICIT NONE
REAL, DIMENSION(6), PARAMETER :: &
matkat = (/ &
0.01, &
! sentit
0.0254, &
! tuumat
0.3048, &
! jalat
0.9144, &
! jaardit
1e3, &
! kilometrit
1609.34 /)
! mailit
END MODULE yksikot

Moduulin mrittely alkaa MODULE-lauseella ja pttyy vastaavaan END MODULE


-lauseeseen.
Tm moduuli voidaan ottaa kyttn lauseella USE:
PROGRAM vakiotesti
USE yksikot
IMPLICIT NONE
WRITE (*,*) tuumat senttein: , matkat(2)/matkat(1)
END PROGRAM vakiotesti

Huomaa, ett lause USE tulee antaa ennen IMPLICIT NONE -lausetta. Jotta
pohjelmassa voidaan kytt moduulia yksikot, on se knnettv ennen
pohjelmaa. Ohjelman kntminen ja ajaminen voisi tapahtua Unix-ympristss seuraavasti:
% f90 -c yksikt.f90
% f90 -o vakiotesti vakiotesti.f90 yksikot.o
% ./vakiotesti
tuumat senttein: 2.539999962

Jos moduulia yksikot kytettisiin USE-lauseella pohjelman lisksi mys


aliohjelmassa laske, olisi ohjelmayksikiden kyttkaavio kuvan 9.2 mukainen. Siten moduuli tarjoaa helpon tavan ottaa kyttn ohjelmiston tarvitsemaa globaalia dataa.

9.4

Esimerkki: pituusyksikkjen muunnokset

Edell mritelty moduuli yksikot on melko hankala kytt, koska kyttjn tulisi muistaa, mit vakioarvoja taulukon matkat alkiot vastaavat. Moduulia voi laajentaa siten, ett se sislt ohjelman, joka tekee muunnoksen
halutusta pituusyksikst toiseen. Jatkossa esitmme mys, miten taulukko
matkat voidaan mritell kytettvksi vain moduulin sislt ksin.

9. Moduulit ja operaattorit

129

MODULE yksikot
...
END MODULE yksikot

PROGRAM ohjelma
USE yksikot
...
CALL laske
...
END PROGRAM ohjelma

SUBROUTINE laske
USE yksikot
...
END SUBROUTINE laske

Kuva 9.2: Moduulin yksikot kytt useasta eri ohjelmayksikst. Sek pohjelma ett pohjelman kutsuma aliohjelma laske ottavat moduulin kyttn USElauseella.

Moduulin rakenne mritelln seuraavasti:


MODULE tunnus
[mrittelyt]
[CONTAINS
proseduuri
[proseduuri]...]
END MODULE tunnus

Edellisess kappaleessa emme tarvinneet CONTAINS-lauseen jlkeen tulevaa


osaa. Nyt laajennamme esimerkki lismll moduuliin funktion muunna_r,
joka tekee halutut muunnokset pituusyksikkjen vlill:
MODULE yksikot
IMPLICIT NONE
Edell esitetyt vakioiden mrittelyt
CONTAINS
RECURSIVE FUNCTION muunna_r(x, alkup, kohde) RESULT(tulos)
IMPLICIT NONE
REAL, INTENT(IN) :: x
CHARACTER(LEN=*), INTENT(IN) :: alkup, kohde
REAL :: tulos
SELECT CASE (alkup)
CASE (sentti, cm)
tulos = muunna_r(matkat(1)*x,
CASE (tuuma, in)
tulos = muunna_r(matkat(2)*x,
CASE (jalka, ft)
tulos = muunna_r(matkat(3)*x,
CASE (jaardi, yd)
tulos = muunna_r(matkat(4)*x,
CASE (kilometri, km)
tulos = muunna_r(matkat(5)*x,

metri, kohde)
metri, kohde)
metri, kohde)
metri, kohde)
metri, kohde)

130

Fortran 95/2003

CASE (maili, mile)


tulos = muunna_r(matkat(6)*x, metri, kohde)
CASE (metri, m)
SELECT CASE (kohde)
CASE (metri, m);
tulos = x
CASE (sentti, cm);
tulos = x/matkat(1)
CASE (tuuma, in);
tulos = x/matkat(2)
CASE (jalka, ft);
tulos = x/matkat(3)
CASE (jaardi, yd);
tulos = x/matkat(4)
CASE (kilometri, km); tulos = x/matkat(5)
CASE (maili, mile);
tulos = x/matkat(6)
CASE DEFAULT;
tulos = -HUGE(tulos)
END SELECT
CASE DEFAULT
tulos = -HUGE(tulos)
END SELECT
END FUNCTION muunna_r
END MODULE yksikot

Ennen CONTAINS-lausetta annetut mrittelyt ovat suoraan kytettviss


moduulin proseduureissa. Moduulin yksikot alussa mritelty vakiotaulukkoa matkat kytetn aliohjelmassa muunna_r.
Muunnosmoduuli yksikt muuntaa ensin alkuperisen yksikn metreihin
ja kutsuu tmn jlkeen itsen muuntaakseen tuloksen haluttuun yksikkn. Jos yksikkj ei tunnisteta, ohjelma palauttaa itseisarvoltaan hyvin
suuren negatiivisen luvun.
Moduulia kytetn seuraavaan tapaan:
PROGRAM muunnostesti
USE yksikot
IMPLICIT NONE
REAL :: luku
CHARACTER(LEN=10) :: yks1, yks2
WRITE (*,(A)) Anna arvo sek alkuperinen ja&
& haluttu yksikk:
READ (*, *) luku, yks1, yks2
WRITE (*,(A,G12.6)) Tulos: , muunna_r(luku,yks1,yks2)
END PROGRAM muunnostesti

Muunnamme nyt kolme tuumaa senteiksi:


Anna arvo sek alkuperinen ja haluttu yksikk:
3 tuuma cm
Tulos: 7.62000

Kytimme sytteess lainausmerkkej merkkijonojen ymprill, vaikka tss tapauksessa ne eivt ole tarpeen, sill merkkijonot tuuma ja cm eivt
sisll esimerkiksi vlilyntej.

9. Moduulit ja operaattorit

131

9.5 Moduulin kyttminen toisista moduuleista


Seuraavassa moduulissa mrittelemme reaalilukulajin reaaliluku, joka
pystyy esittmn reaalilukuja vhintn kuuden numeron tarkkuudella:
MODULE tarkkuus
IMPLICIT NONE
INTEGER, PARAMETER :: reaaliluku = SELECTED_REAL_KIND(6)
! INTEGER, PARAMETER :: reaaliluku = SELECTED_REAL_KIND(12)
END MODULE tarkkuus

Moduulissa on kommentoitu pois kytst vaihtoehtoinen lajiparametrin


mritelm, jossa mritelln 12 numeron tarkkuus.
Muissa ohjelmayksikiss voimme ottaa kyttn halutun reaalilukujen tarkkuuden lauseella
USE tarkkuus

Mrittelemme moduulissa tarkkuus joko suuren tai pienen tarkkuuden


lajiparametrin reaaliluku. Reaalimuuttujien mrittelyt voidaan tehd tmn jlkeen seuraavaan tapaan:
USE tarkkuus
IMPLICIT NONE
REAL(KIND=reaaliluku) :: x, y, z

Jos haluamme myhemmin muuttaa kytetty laskentatarkkuutta suuremmaksi, voimme yksinkertaisesti muuttaa moduulia tarkkuus seuraavasti:
MODULE tarkkuus
IMPLICIT NONE
! INTEGER, PARAMETER :: reaaliluku = SELECTED_REAL_KIND(6)
INTEGER, PARAMETER :: reaaliluku = SELECTED_REAL_KIND(12)
END MODULE tarkkuus

Kun knnmme uudestaan tmn moduulin ja kaikki siit riippuvat moduulit, toimii koko ohjelmistomme suuremmalla tarkkuudella.

9.6 Julkisuus ja yksityisyys


Seuraavassa moduulissa kytetn edell mritelty tarkkuus-moduulia:
MODULE vakiot
USE tarkkuus
IMPLICIT NONE
REAL(KIND=reaaliluku), PRIVATE :: x
REAL(KIND=reaaliluku), PARAMETER, PUBLIC :: &
toleranssi = 100*EPSILON(x), &
maksimiarvo = HUGE(x)
END MODULE vakiot

Muuttuja x on mritelty yksityiseksi kytten mrett PRIVATE. Tllin


muuttuja x nkyy vain moduulin vakiot sisll. Vakiot toleranssi ja

132

Fortran 95/2003

maksimiarvo mriteltiin julkisiksi kyttmll mrett PUBLIC, jolloin vakioita voidaan kytt moduulin ulkopuolella USE-lauseen avulla.
Standardifunktio EPSILON palauttaa pienimmn positiivisen liukuluvun ,
jolla ptee 1 +  > 1. Standardifunktio HUGE palauttaa suurimman reaaliluvun, joka on esitettviss argumenttia vastaavalla tietotyypill.
Oletuksena on, ett moduulissa mritellyt oliot ovat julkisia. Tllin ne nkyvt niiss ohjelmayksikiss, joissa moduuli otetaan kyttn USE-lauseella.
Moduulia vakiot voisi kytt seuraavasti:
PROGRAM vakiotesti
USE vakiot
IMPLICIT NONE
WRITE (*,*) toleranssi: , toleranssi
WRITE (*,*) maksimi: , maksimiarvo
END PROGRAM vakiotesti

Ohjelman tulostus nytt tlt:


toleranssi:
1.1920929E-05
maksimi:
3.4028235E+38

Kuvassa 9.3 on havainnollistettu pohjelman sek moduulien vakiot ja


tarkkuus hierarkkista kytt.

pohjelma vakiotesti

USE vakiot

USE tarkkuus
Kuva 9.3: Moduulien kyttkaavio.

PUBLIC- tai PRIVATE-mreen sijaan voi kytt erillisi lauseita, jotka asettavat moduulin tunnuksen julkiseksi tai yksityiseksi:
MODULE vakiot
USE tarkkuus
IMPLICIT NONE
PRIVATE
REAL(KIND=reaaliluku) :: x
REAL(KIND=reaaliluku), PARAMETER :: &
toleranssi = 100*EPSILON(x), &
maksimiarvo = HUGE(x)
PUBLIC :: toleranssi, maksimiarvo, reaaliluku
END MODULE vakiot

Jos PUBLIC- tai PRIVATE-lauseelle ei anneta argumentteja, asettaa lause oletusarvon moduulin tunnusten julkisuudelle. Edell mriteltiin tunnukset
oletusarvoisesti yksityisiksi lauseella

9. Moduulit ja operaattorit

133

PRIVATE

Tunnukset toleranssi, maksimiarvo ja reaaliluku mriteltiin julkisiksi


lauseessa
PUBLIC :: toleranssi, maksimiarvo, reaaliluku

9.7 USE-lauseen eri kytttavat


Lauseella
USE moduuli

otetaan kyttn kaikki moduulissa mritellyt julkiset tunnukset. Jos osalle kyttn otetun moduulin tunnuksista halutaan antaa uusi nimi, tm
onnistuu seuraavasti:
USE moduuli[, paikallinen_tunnus => tunnus_moduulissa]...

Tten otettaessa kyttn edellisess kappaleessa mritelty moduuli vakiot


voidaan osalle moduulin tunnuksista antaa uudet nimet:
USE vakiot, tyyppi => reaaliluku, maksimi => maksimiarvo
IMPLICIT NONE
REAL(KIND=tyyppi) :: x = maksimi

Tss mriteltiin muuttuja x kytten moduulista vakiot perisin olevia


vakioita tyyppi ja maksimi.
Jos halutaan ottaa kyttn vain osa moduulissa mritellyista tunnuksista,
onnistuu tm kyttmll mrett ONLY:
USE moduuli, ONLY : tunnus_moduulissa[, tunnus_moduulissa]...

Mys nille tunnuksille voi antaa uudet nimet:


USE vakiot, ONLY : tyyppi => reaaliluku, &
maksimi => maksimiarvo

Saman moduulin voi ottaa kyttn useassa eri USE-lauseessa:


USE vakiot, ONLY : tyyppi => reaaliluku
USE vakiot, ONLY : maksimi => maksimiarvo
IMPLICIT NONE
REAL(KIND=tyyppi) :: x = maksimiarvo

Jos sama moduuli otetaan kyttn useaan kertaan, ksitelln kyseiset USElauseet yhten kokonaisuutena. Huomaa, ett moduulin olion voi ottaa kyttn vain yhdell nimell. Seuraava on siis kielletty:
USE vakiot, ONLY : maksimiarvo
USE vakiot, ONLY : maksimi => maksimiarvo

Mrittely ONLY kannattaa kytt, sill tllin USE-lauseesta nkee suoraan, mit moduulin tunnuksia ohjelmayksikss kytetn. Lisksi tllin
on helpompi vltt koniktit moduulissa mriteltyjen tunnusten ja ohjelmayksikn omien tunnusten kesken.

134

Fortran 95/2003

Jos USE-lausetta kytetn moduulissa, voi kyttn otettujen tunnusten


julkisuuden mritell uudelleen kyttmll lausetta PUBLIC tai PRIVATE:
MODULE vakiot2
USE vakiot, ONLY : tyyppi => reaaliluku
USE vakiot, ONLY : maksimi => maksimiarvo
IMPLICIT NONE
PRIVATE
PUBLIC :: tyyppi, maksimi
END MODULE vakiot2

Lause PRIVATE muuttaa moduulin tunnukset oletusarvoisesti yksityisiksi.


PUBLIC-lauseella puolestaan mritelln tunnukset tyyppi ja maksimi julkisiksi.

9.8

Esimerkki: muunnosohjelma

Kappaleissa 9.3 ja 9.4 esitellyn yksikot-moduulin vakiotaulukko matkat nkyy moduulin ulkopuolelle. Tm johtaa virhetilanteeseen esimerkiksi seuraavassa tapauksessa:
USE yksikot
IMPLICIT NONE
REAL :: matkat

Moduulissa mritelty tunnusta matkat yritettiin siis kytt paikallisen


muuttujan nimen. Tm johtaa knnsaikaiseen virheeseen.
Jos moduulia kyttvss ohjelmassa kytetn saman nimisi tunnuksia
kuin moduulissa, on sekaannuksen vaara olemassa. Tmn vuoksi on aina
syyt mritell julkisiksi vain ne moduulin tunnukset, jotka ovat tarpeellisia moduulin kyttmiseen.
Voisimme ottaa kyttn moduulista yksikot vain muunnosohjelman:
USE yksikot, ONLY : muunna_r

Toinen mahdollisuus on nimet moduulin sisltm taulukko matkat uudestaan USE-lauseessa:


USE yksikot, pituusvakiot => matkat

Tss moduulin yksikot vakiotaulukolle matkat annetaan paikallinen nimi


pituusvakiot.
Taulukon matkat voi piilottaa moduulia kyttvilt ohjelmayksikilt. Niidenhn ei ole tarpeellista tiet muunnosohjelman toteutuksen yksityiskohtia. Tmn saa aikaan seuraavilla mrittelyill:
MODULE yksikot
IMPLICIT NONE
PRIVATE
REAL, DIMENSION(6), PARAMETER :: &
matkat = (/ &
0.01, &
! sentit

9. Moduulit ja operaattorit

135

0.0254, &
0.3048, &
0.9144, &
1e3, &
1609.34 /)
PUBLIC :: muunna_r
CONTAINS
Funktion muunna_r mrittely

!
!
!
!
!

tuumat
jalat
jaardit
kilometrit
mailit

END MODULE yksikot

Moduulin ainoa julkinen olio on funktio muunna_r. Lisetuna paikallisten


vakioiden piilottamisessa on se, ett voimme vapaasti muuttaa toteutuksen
yksityiskohtia. Ainoastaan muunna_r-funktion kyttliittymn tulee sily.

9.9 Esimerkki: nollakohdan haku


Etsimme ratkaisua yhtllle ex = 5 x. Saamme muuttujan x arvon selville
hakemalla nollakohdan funktiolle f (x) = ex + x 5 (kuva 9.4).

150

100
f (x)
50

465
1

2
x

Kuva 9.4: Funktion f (x) = ex + x 5 nollakohdan hakeminen.

Lausekkeen ex +x5 arvon laskeva funktio fun voidaan sijoittaa moduuliin:


MODULE yhtalo
CONTAINS
FUNCTION fun(x) RESULT(fun_arvo)
IMPLICIT NONE
REAL, INTENT(IN) :: x
REAL :: fun_arvo
fun_arvo = EXP(x) + x - 5
END FUNCTION fun
END MODULE yhtalo

Jatkuvan funktion nollakohta voidaan hakea puolitushaulla (bisection). Aiemmin kappaleessa 7.4 sivulla 90 kytimme nollakohdan hakuun Newtonin
menetelm. Puolitushaussa lhdetn vlist [a, b], jossa funktion arvojen

136

Fortran 95/2003

f (a) ja f (b) tiedetn olevan erimerkkisi. Kullakin iteraatiolla vli puolitetaan, ja jatkoon otetaan se vli, jolla funktion arvot ovat edelleen erimerkkisi ptepisteiss. Funktiolle f (x) = ex + x 5 voidaan valita haun
aloittamiseen esimerkiksi vli [0, 5], koska f (0) = 4 < 0 ja f (5) = e5 > 0.
Seuraava moduuli sislt funktion puolitushaku nollakohdan etsintn:
MODULE nollakohta
IMPLICIT NONE
PRIVATE
REAL, PARAMETER :: toleranssi = 1e3*EPSILON(1.0)
PUBLIC :: puolitushaku
CONTAINS
FUNCTION puolitushaku(f, a, b) RESULT(hakupiste)
IMPLICIT NONE
INTERFACE
FUNCTION f(x) RESULT(f_arvo)
REAL, INTENT(IN) :: x
REAL :: f_arvo
END FUNCTION f
END INTERFACE
REAL, INTENT(IN) :: a, b ! Hakuvlin ptepisteet
REAL :: x1, x2, hakupiste, arvo
LOGICAL :: merkki
x1 = a
x2 = b
merkki = f(x1) < 0.0
IF (merkki .EQV. (f(x2) < 0)) THEN
WRITE (*,*) Ptepistearvot samanmerkkiset!
hakupiste = -HUGE(hakupiste)
ELSE
DO WHILE (ABS(x1 - x2) > toleranssi*ABS(x1))
hakupiste = 0.5*(x1+x2)
arvo = f(hakupiste)
IF ((arvo < 0.0) .EQV. merkki) THEN
x1 = hakupiste
ELSE
x2 = hakupiste
END IF
END DO
END IF
END FUNCTION puolitushaku
END MODULE nollakohta

Funktiossa puolitushaku mriteltiin muodollisen argumentin f kutsumuoto INTERFACE-lohkossa. Tten funktion sisll tehdn kutsumuodon
mukaiset tarkistukset kytettess funktiota f.
Lausekkeen f (x) = ex + x 5 nollakohta voidaan nyt hakea puolitushaulla
seuraavasti:
PROGRAM ratkaisu
USE yhtalo, ONLY : fun
USE nollakohta, ONLY : puolitushaku
IMPLICIT NONE

9. Moduulit ja operaattorit

137

REAL :: x
x = puolitushaku(fun, 0.0, 5.0)
IF (ABS(x) < HUGE(x)) THEN
WRITE (*,*) Nollakohta:, x, fun(x)
END IF
END PROGRAM ratkaisu

Edell annoimme ONLY-mrittelyn mys moduulille yhtalo, koska tllin


pohjelmasta nkee suoraan, mist tunnus fun on perisin.
Ohjelman tulostus voisi olla seuraava:
Nollakohta:

1.3066101

2.4127960E-04

Ohjelman toimintaa on havainnollistettu kuvassa 9.4. Kyrn ylpuolelle


piirretyt luvut 1, 2, . . . , 6 ovat menetelmn perkkiset iteraatiopisteet.

9.10 Moduulissa mritellyt globaalit muuttujat


Moduulia nollakohta voi muuttaa siten, ett ratkaisun toleranssi on mahdollista asettaa halutuksi. Tm onnistuu korvaamalla moduulissa mritelty nimetty vakio toleranssi julkisella muuttujalla:
REAL, SAVE :: toleranssi = 1E3*EPSILON(1.0)
PUBLIC :: puolitushaku, toleranssi

Huomaa, ett muuttujalle toleranssi annettiin mre SAVE. Koska muuttuja toleranssi alustetaan mrittelylauseessa, saa se automaattisesti SAVEmreen, joten mreen voisi jtt poiskin.
Jos SAVE-mrett ei ole annettu eik muuttujaa alusteta mrittelylauseessa, silyy muuttujan arvo moduulia kyttvien proseduurien kutsukertojen
vlill vain siin tapauksessa, ett moduuli otetaan kyttn mys pohjelmassa.
Moduulia nollakohta voisi nyt kytt seuraavasti:
PROGRAM ratkaisu
USE yhtalo, ONLY : fun
USE nollakohta, ONLY : puolitushaku, toleranssi
IMPLICIT NONE
REAL :: x
toleranssi = 1E-5
x = puolitushaku(fun, 0.0, 5.0)
WRITE (*,*) Nollakohta:, x, fun(x)
END PROGRAM ratkaisu

Tss muutettiin toleranssin oletusarvoa ennen nollakohdan hakua. Toleranssin voisi tietysti mritell mys rutiinin puolitushaku valinnaiseksi
argumentiksi, jolloin ei tarvita erillist julkista muuttujaa.

138

Fortran 95/2003

9.11

Esimerkki: normaalijakautuneet
satunnaisluvut

Fortran 95:n standardialiohjelma RANDOM_NUMBER tuottaa tasajakautuneita


(pseudo)satunnaislukuja. (0, 1)-normaalijakautuneita satunnaislukuja voidaan tuottaa seuraavalla menetelmll:
1. tuotetaan tasajakautuneet satunnaisluvut x1 ja x2 (1, 1)
2. jos piste (x1 , x2 ) on yksikkympyrn sisll ja nollasta poikkeava, jatketaan seuraavaan vaiheeseen; muussa tapauksessa tuotetaan uudet satunnaisluvut


 2 ln(x12 + x22 )
3. lasketaan suure t = 
x12 + x22
4. luvut y1 = tx1 ja y2 = tx2 ovat normaalijakautuneita satunnaislukuja.
Menetelm on havainnollistettu kuvassa 9.5.

1


 2 ln(x12 + x22 )
t=
x12 + x22

x2

y1 = tx1 ja y2 = tx2
1
1

x1

Kuva 9.5: Normaalijakautuneiden satunnaislukujen tuottaminen.

Seuraavan moduulin funktio normaali tuottaa normaalijakautuneen satunnaisluvun edell kuvatulla menetelmll. Koska algoritmi tuottaa kerralla
kaksi satunnaislukua, toinen niist otetaan talteen moduulin yksityiseen
muuttujaan talletettu_arvo.
MODULE satunnaisluvut
USE tarkkuus, ONLY : reaaliluku
IMPLICIT NONE
PRIVATE
REAL (KIND=reaaliluku), SAVE :: talletettu_arvo
LOGICAL, SAVE :: talletettu = .FALSE.
PUBLIC :: normaali
CONTAINS
FUNCTION normaali() RESULT(arvo)
IMPLICIT NONE
REAL (KIND=reaaliluku) :: arvo
REAL (KIND=reaaliluku), DIMENSION(2) :: x
REAL (KIND=reaaliluku) :: x_nelio, apu

9. Moduulit ja operaattorit

139

IF (.NOT. talletettu) THEN


silmukka: DO
CALL RANDOM_NUMBER(x)
x(:) = 2.0*x(:) - 1.0
x_nelio = x(1)**2 + x(2)**2
IF (x_nelio < 1.0 .AND. x_nelio /= 0.0) THEN
EXIT silmukka
END IF
END DO silmukka
apu = SQRT(-2.0*LOG(x_nelio)/x_nelio)
talletettu_arvo = apu*x(1)
talletettu = .TRUE.
arvo = apu*x(2)
ELSE
talletettu = .FALSE.
arvo = talletettu_arvo
END IF
END FUNCTION normaali
END MODULE satunnaisluvut

Aliohjelma RANDOM_NUMBER tuottaa satunnaislukuja vlilt [0, 1), joten ohjelmassa skaalataan luvut vlille [1, 1). Algoritmissa tarvitaan satunnaislukuja vlilt (1, 1), mutta algoritmi hylk ne pisteet, joissa toinen koordinaateista saa arvon 1. Tmn vuoksi tt erikoistapausta ei tarvitse ksitell erikseen.
Moduulin ainoa julkinen olio on funktio normaali. Yksityinen looginen
muuttuja talletettu kertoo, onko edellisell funktion kutsukerralla luotu satunnaisluku kytettviss.
Seuraavassa kyttesimerkiss pyydetn sytteen normaalijakautuneiden
satunnaislukujen lukumr. Ohjelma tuottaa ja tulostaa satunnaisluvut
kutsumalla funktiota normaali.
PROGRAM satunnaistesti
USE satunnaisluvut, ONLY : normaali
IMPLICIT NONE
INTEGER :: i, n
WRITE (*,(A),ADVANCE=NO) Anna n:
READ (*,*) n
DO i = 1, n
WRITE (*,(F10.6)) normaali()
END DO
END PROGRAM satunnaistesti

Ohjelman tulostus voisi nytt tlt:


Anna n: 6
-0.201469
0.970363
2.726932
-0.146014
1.872161
0.495932

Ohjelmassa satunnaistesti otetaan kyttn satunnaisluvut-moduuli.


Tm moduuli ottaa puolestaan kyttn kappaleessa 9.5 (sivu 131) mri-

140

Fortran 95/2003

tellyn moduulin tarkkuus. Kytettyjen moduulien hierarkkista jrjestyst


on havainnollistettu kuvassa 9.6.

pohjelma satunnaistesti

USE satunnaisluvut

USE tarkkuus
Kuva 9.6: Ohjelmayksikiden kutsukaavio.

9.12

Geneeriset proseduurit

Geneerinen proseduuri voi toimia eri tavalla sen mukaan, mink tyyppisi
argumentteja sille annetaan. Monet Fortranin standardifunktioista ovat geneerisi: esimerkiksi trigonometriselle funktiolle SIN voi antaa argumentiksi vaikkapa reaaliluvun, kompleksiluvun tai taulukkotyyppisen argumentin.
Geneerisyytt kutsutaan joskus mys termill ylilataaminen (overloading)
eli operaation mrittelyalueen laajentaminen.
Kappaleissa 9.3, 9.4 ja 9.8 mrittelimme funktion muunna_r, jota voitiin
kytt seuraavasti:
muunna_r(3.0, tuuma, cm)

Jos annamme funktiolle argumentiksi kokonaisluvun, saamme aikaan knnsaikaisen virheilmoituksen:


muunna_r(3, tuuma, cm)

Voimme kuitenkin mritell uuden funktion muunna_i, joka kutsuu aiemmin mritelty muunna_r-funktiota:
REAL FUNCTION muunna_i(x, alkup, kohde) RESULT(tulos)
IMPLICIT NONE
INTEGER, INTENT(IN) :: x
CHARACTER(LEN=*), INTENT(IN) :: alkup, kohde
tulos = muunna_r(REAL(x), alkup, kohde)
END FUNCTION muunna_i

Jos funktio muunna_i sijoitetaan samaan moduuliin aiemmin mritellyn


muunna_r-funktion kanssa, voimme ottaa kyttn muunnosfunktiolle geneerisen nimen muunna. Tllin voimme kytt funktiokutsuja
muunna(3.0, tuuma, cm)
muunna(3, tuuma, cm)

Geneerinen nimi muunna tytyy mritell INTERFACE-lohkossa ennen moduulin CONTAINS-lausetta. Geneerist nime vastaavat moduulin proseduu-

9. Moduulit ja operaattorit

141

rit voi luetella lauseessa MODULE PROCEDURE. Kaikkien tietty geneerist nime vastaavien proseduurien tytyy olla joko funktioita tai aliohjelmia.
MODULE yksikot
IMPLICIT NONE
PRIVATE
Edell esitetty vakiotaulukko matkat
INTERFACE muunna
MODULE PROCEDURE muunna_i, muunna_r
END INTERFACE
PUBLIC :: muunna
CONTAINS
REAL FUNCTION muunna_i(x, alkup, kohde) RESULT(tulos)
IMPLICIT NONE
INTEGER, INTENT(IN) :: x
CHARACTER(LEN=*), INTENT(IN) :: alkup, kohde
REAL :: tulos
tulos = muunna(REAL(x), alkup, kohde)
END FUNCTION muunna_i
RECURSIVE FUNCTION muunna_r(x, alkup, kohde) RESULT(tulos)
Edell esitetty mrittely
END FUNCTION muunna_r
END MODULE yksikot

Moduulissa annettiin funktioille muunna_i ja muunna_r yhteinen geneerinen


nimi muunna. Lauseessa MODULE PROCEDURE ei esitell proseduurien kutsumuotoa, sill kntj lyt tarvittavat tiedot automaattisesti moduulin
proseduureista.
Funktiossa muunna_i kutsutaan muunnosfunktiota muunna reaalilukuargumenttia kytten. Tllin kynnistyy funktio muunna_r, joka tekee varsinaisen tyn.
Moduulin ainoa julkinen tunnus on geneerinen nimi muunna. Emme siis voi
viitata muunnosfunktion eri versioihin moduulin ulkopuolelta.
Kntj valitsee knnsaikana argumenttien tyypin perusteella oikean version geneerist nime vastaavista erityisnimist. Tmn vuoksi geneerist
tunnusta vastaavien proseduurien tulee olla erotettavissa toisistaan argumenttiensa tyypin, lajiparametrin tai ulottuvuuksien lukumrn perusteella.
Jos muodolliset argumentit eroavat ainoastaan jrjestyksens perusteella,
kyseess on geneerisen tunnuksen kannalta sama proseduuri. Seuraavassa
on virheellinen mrittely:
INTERFACE sub
SUBROUTINE sub1(x,i)
IMPLICIT NONE
REAL, INTENT(INOUT) :: x

142

Fortran 95/2003

INTEGER, INTENT(INOUT) :: i
END SUBROUTINE sub1
SUBROUTINE sub2(i,x)
IMPLICIT NONE
INTEGER, INTENT(INOUT) :: i
REAL, INTENT(INOUT) :: x
END SUBROUTINE sub2
END INTERFACE

Mrittelyss on virhe, sill aliohjelmaa sub voitaisiin kutsua avainsanaargumentteja kytten:


CALL sub(x=1.0, i=2)

Tllin ei en voida erottaa, kumpi versio aliohjelmista sub1 ja sub2 pitisi


valita.
Mys ulkoisille proseduureille voi antaa geneerisen nimen INTERFACE-lohkossa. Seuraavassa on tst esimerkki:
INTERFACE f
FUNCTION f_single(x)
IMPLICIT NONE
INTEGER, PARAMETER :: sp = KIND(1.0)
REAL(KIND=sp) :: x
END FUNCTION f_single
FUNCTION f_double(x)
IMPLICIT NONE
INTEGER, PARAMETER :: dp = &
SELECTED_REAL_KIND(2*PRECISION(1.0))
REAL(KIND=dp) :: x
END FUNCTION f_double
END INTERFACE

Riippuen geneerisen funktion f reaalilukuargumentin x lajista valitaan suoritettavaksi joko funktio f_single tai funktio f_double. Kaksoistarkkuuden lajiparametri on mritelty vaatimalla tyypilt tarkuutta kaksi kertaa
normaalitarkkuuden verran. Tmn pitisi olla melko koneriippumaton tapa mritell FORTRAN 77:n tyyppi DOUBLE PRECISION vastaava reaalilukulaji.

9.13

Operaattoreiden mrittely

Moduulien avulla on mahdollista mritell uusia operaattoreita ja antaa


vanhoille operaattoreille uusia merkityksi eri tietotyyppien yhteydess. Tt kutsutaan operaattorin ylilataamiseksi (operator overloading).
Ylilataamista voi kytt numeeristen standardioperaattoreiden (**, *, /, +
ja -) sek loogisten ja vertailuoperaattoreiden yhteydess. Mys sijoitusoperaatiolle voi mritell uusia merkityksi.
Valmiiksi mritelty operaattoria ei voi kuitenkaan mritell uudestaan alkuperisille tietotyypeille. Tten laskutoimitusten merkityst ei voi muuttaa

9. Moduulit ja operaattorit

143

esimerkiksi seuraavassa lausekkeessa:


2*3 + 5.0/6

Voimme mys mritell uusia kyttjn mrittelemi operaattoreita, joiden nimi alkaa pisteell ja pttyy pisteeseen. Esittelemme myhemmin tss kappaleessa operaattorin .fmt. mrittelyn ja kytn.
Kyttjn mrittelemt yksiargumenttiset operaattorit ovat suoritusjrjestykseltn vahvemmat kuin muut Fortranin lausekkeiden termit. Siten esimerkiksi lausekkeiden
(A, // (.fmt. 10) // )
(A, // .fmt. 10 // )

suoritusjrjestys on sama. Kyttjn mrittelemt kaksiargumenttiset operaattorit ovat puolestaan suoritusjrjestykseltn heikoimmat, joten tllaisia operaattoreita sisltvien lausekkeiden yhteydess voivat sulut olla tarpeen.
Luomme seuraavassa moduulin muotoilu, jossa mrittelemme uusia operaattoreita merkkijonoille. Ainoa merkkijonoille mritelty operaatio on (vertailuoperaatioiden lisksi) liitosoperaatio (//). Kytettvissmme on siten
operaattorit **, *, /, + ja -.

9.13.1 Kaksiargumenttiset operaattorit


Voimme mritell operaattorille * merkkijonojen yhteydess seuraavan uuden kytttavan:
n*merkkijono
merkkijono*n

Tss n on kokonaislukutyyppi. Niden lausekkeiden tuloksena olkoon


merkkijono, jossa alkuperist merkkijonoa on toistettu n kertaa. Seuraavassa on thn tarvittavat mrittelyt:
MODULE muotoilu
IMPLICIT NONE
INTERFACE OPERATOR(*)
MODULE PROCEDURE toista_mjono, toista_mjono_2
END INTERFACE
CONTAINS
FUNCTION toista_mjono(kertaa, mjono) RESULT(uusi_mjono)
IMPLICIT NONE
INTEGER, INTENT(IN) :: kertaa
CHARACTER(LEN=*), INTENT(IN) :: mjono
CHARACTER(LEN=kertaa*LEN(mjono)) :: uusi_mjono
uusi_mjono = REPEAT(mjono,kertaa)
END FUNCTION toista_mjono
FUNCTION toista_mjono_2(mjono,kertaa) RESULT(uusi_mjono)
IMPLICIT NONE
INTEGER, INTENT(IN) :: kertaa

144

Fortran 95/2003

CHARACTER(LEN=*), INTENT(IN) :: mjono


CHARACTER(LEN=kertaa*LEN(mjono)) :: uusi_mjono
uusi_mjono = REPEAT(mjono,kertaa)
END FUNCTION toista_mjono_2
END MODULE muotoilu

Standardifunktio REPEAT toistaa merkkijonoa niin monta kertaa kuin haluamme. Moduulissa on tarpeen mritell erillinen funktio kummallekin
toisto-operaattorin kytttavalle (toistokerrat annetaan joko ennen merkkijonoa tai sen jlkeen).
Seuraavassa on esimerkki moduulin kytst:
PROGRAM muotoilutesti
USE muotoilu
WRITE (*,(A)) 4*Hurraa!
WRITE (*,(A)) 3*Hei! *2
END PROGRAM muotoilutesti

Tm ohjelma tulostaa seuraavaa:


Hurraa! Hurraa! Hurraa! Hurraa!
Hei! Hei! Hei! Hei! Hei! Hei!

Jlkimmisess tapauksessa toistettiin merkkijonoa ensin kolme kertaa ja


saatua tulosta viel kaksi kertaa. Saimme siis aikaan haluamamme tuloksen.
Edell mritellyt funktiot toista_mjono ja toista_mjono_2 eroavat toisistaan vain argumenttiensa jrjestyksen suhteen. Kntj osaa valita oikean
version vertaamalla operaattorin * argumenttien jrjestyst niden funktioiden argumenttien jrjestykseen. Seuraava INTERFACE-mrittely olisi kuitenkin laiton:
INTERFACE toista
MODULE PROCEDURE toista_mjono, toista_mjono_2
END INTERFACE toista

Selit, miksi mrittelyss on virhe! (Vihje: voit antaa funktiolle avainsanaargumentteja.)

9.13.2 Yksiargumenttiset operaattorit


Normaali kokonaislukujen I-muotoilukoodi sijoittaa luvun kiinten kokoiseen kenttn. Kokonaislukuja tulostettaessa olisi kuitenkin hyv olla kytss muotoilukoodi, joka sijoittaisi kokonaisluvun juuri tarpeeksi leven
kenttn. (Huomaa, ett Fortran 95 -standardiin kuuluu minimileveysformaatti, esimerkki tst on muotoilukoodi I0.)
Seuraavassa mrittelemme operaattorin .fmt., joka tuottaa tarvittavan minimileveyden muotoilukoodin kokonaislukumuuttujalle. Siten lausekkeen
.fmt. 101

tuloksena on muotoilukoodi I3 ja lausekkeen .fmt. -1 tuloksena on


muotoilukoodi I2. Kokonaislukujen muotoilukoodeista kerrotaan enemmn kappaleessa 12.5.1 sivulla 198.

9. Moduulit ja operaattorit

145

Seuraavat mrittelyt voi list edell kytettyyn muotoilu-moduuliin. Emme kyt Fortran 95:n standardioperaattoreita vaan luomme uuden nimetyn
operaattorin .fmt. (nimen tytyy alkaa pisteell ja ptty pisteeseen).
INTERFACE OPERATOR(.fmt.)
MODULE PROCEDURE minimileveyskoodi
END INTERFACE

CONTAINS-lauseen jlkeen tulee funktion mrittely:


FUNCTION minimileveyskoodi(n) RESULT(mjono)
IMPLICIT NONE
INTEGER, INTENT(IN) :: n
CHARACTER(LEN=3) :: mjono
INTEGER :: pituus
pituus = desimaalit(n)
IF (n < 0) pituus = pituus + 1
WRITE (mjono, (I3)) pituus
mjono = I // TRIM(ADJUSTL(mjono))
CONTAINS
FUNCTION desimaalit(i) RESULT(desim)
IMPLICIT NONE
INTEGER, INTENT(IN) :: i
INTEGER :: desim
INTEGER :: apu
INTRINSIC ABS
desim = 1
apu = ABS(i)
DO WHILE (apu >= 10)
apu = apu/10
desim = desim + 1
END DO
END FUNCTION desimaalit
END FUNCTION minimileveyskoodi

Funktio palauttaa muotoilukoodin kolmen merkin pituisessa merkkijonossa. Kokonaisluvun vaatiman kentn leveys (ilman etumerkki) saadaan selville sisisell funktiolla desimaalit. Tmn jlkeen tulostetaan tarvittavan
kentn pituus merkkijonoon mjono ja listn muotoilukoodin alkuun viel
merkki I.
Edell mritelty operaattoria .fmt. voi nyt kytt seuraavasti:
PROGRAM muotoilutesti
USE muotoilu
IMPLICIT NONE
INTEGER :: m = 1996
CHARACTER(LEN=10) :: formaatti
formaatti = (A, // .fmt. m // ,A)
WRITE (*,(A)) Muotoilukoodi: // formaatti
WRITE (*, formaatti) Luku on , m, .
m = -100000*m
formaatti = (A, // .fmt. m // ,A)

146

Fortran 95/2003

WRITE (*,(A)) Muotoilukoodi: // formaatti


WRITE (*, formaatti) Luku on , m, .
END PROGRAM muotoilutesti

Tmn ohjelman tulostus on seuraavaa:


Muotoilukoodi: (A,I4 ,A)
Luku on 1996.
Muotoilukoodi: (A,I10,A)
Luku on -199600000.

9.13.3 Sijoitusoperaation mrittely uudelleen


Mys sijoitusoperaation merkitys on mahdollista mritell uusille tyypeille. Fortranissa ei ole mritelty kokonaisluvun sijoittamista merkkijonoon,
joten voimme mritell tmn operaation merkityksen itse. Voisimme kytt sijoitusoperaatiota hyvksi esimerkiksi seuraavasti:
PROGRAM muotoilutesti
USE muotoilu
IMPLICIT NONE
INTEGER :: m = 1996
CHARACTER(LEN=10) :: tulostus
tulostus = 1996
WRITE (*,(A)) Vuosi on // TRIM(tulostus) // .
END PROGRAM muotoilutesti

Ohjelman tulostus voisi olla seuraavaa:


Vuosi on 1996.

Tarvittavat lisykset moduuliin muotoilu ovat seuraavat:


MODULE muotoilu
IMPLICIT NONE
INTERFACE ASSIGNMENT(=)
MODULE PROCEDURE mjono_kokonaisluku
END INTERFACE
CONTAINS
SUBROUTINE mjono_kokonaisluku(mjono, n)
IMPLICIT NONE
CHARACTER(LEN=*), INTENT(OUT) :: mjono
INTEGER, INTENT(IN) :: n
CHARACTER(LEN=RANGE(n)+2) :: apu
WRITE (apu,( // .fmt. n // )) n
mjono = ADJUSTL(apu)
END SUBROUTINE mjono_kokonaisluku
END MODULE muotoilu

Aliohjelmassa kokonaisluku tulostetaan kokonaisluku n tarvittavan pituiseen merkkijonomuuttujaan apu kytten sisist tiedostoa (sivu 209). Tulostamiseen kytetn operaattorin .fmt. tuottamaa muotoilukoodia. Merkkijonoargumentti mjono on mritelty seuraavasti:
CHARACTER(LEN=*), INTENT(OUT) :: mjono

9. Moduulit ja operaattorit

147

Tllin sijoituslauseen vasemmalla puolella voi olla mink pituinen merkkijono tahansa.

9.14

Yhteenveto operaattoreiden mrittelyst

Operaattorit mritelln INTERFACE-lohkossa seuraavasti:


INTERFACE OPERATOR(operaattorin_tunnus)
[MODULE PROCEDURE proseduurin_tunnus &
[, proseduurin_tunnus]...]...
END INTERFACE

Kun mrittelemme yksiargumenttisen operaattorin, tulee INTERFACElohkossa esiintyvien proseduurien olla yksiargumenttisia funktioita, joiden muodollisella argumentilla on mre INTENT(IN). Jos luettelemme
INTERFACE-lohkossa useamman funktion nimet, tytyy niden argumenttien tyypin poiketa toisistaan. Tm sen vuoksi, ett kntj pystyy knnsaikana valitsemaan, mik funktiokutsu tulee sijoittaa operaattorin paikalle.
Kaksiargumenttista operaattoria mriteltess tulee INTERFACE-lohkossa
esiintyvien proseduurien olla kaksiargumenttisia funktioita, joiden argumenteilla on mre INTENT(IN). Ensimminen muodollinen argumentti vastaa operaattorin vasenta argumenttia ja toinen oikeaa argumenttia.
Sijoitusoperaattorille mritelln uusi merkitys seuraavasti:
INTERFACE ASSIGNMENT(=)
[MODULE PROCEDURE proseduurin_tunnus &
[, proseduurin_tunnus]...]...
END INTERFACE

Lohkossa esiintyvien proseduurien tulee olla aliohjelmia, joiden ensimminen argumentti on tyyppi INTENT(OUT) ja toinen argumentti puolestaan tyyppi INTENT(IN). Nm muodolliset argumentit vastaavat sijoituslauseen vasenta ja oikeaa puolta. Muodollisten argumenttien tyyppien tulee
poiketa valmiiksi mritellyist sijoituslauseen merkityksist sek muista
kyseisess INTERFACE-lohkossa esiintyvien aliohjelmien tyypeist.

9.15

Funktioiden ja operaattorien kytttavat

Edell mriteltiin standardioperaattorille * uusi merkitys merkkijonojen


ksittelyss. Useimmiten on kuitenkin selkemp kytt operaattorinotaation sijaan funktiokutsuja, varsinkin jos standardioperaation merkitys ei
ole itsestn selv.
Esimerkiksi merkinnn
2*Hurraa!

148

Fortran 95/2003

sijaan on selkemp kytt standardifunktiota REPEAT:


REPEAT(Hurraa! ,2)

Harjoitustehtvn on edell mriteltyj merkkijono-operaatioita vastaavien funktioiden mrittely. Nit merkkijonofunktioita voisi kytt vaikkapa seuraavasti:
PROGRAM muotoilutesti
USE muotoilu
IMPLICIT NONE
INTEGER :: m = 1996
CHARACTER(LEN=10) :: tulostus, formaatti
tulostus = mjono(m)
WRITE (*,(A)) Vuosi on // TRIM(tulostus) // .
formaatti = (A, // fmt(m) // ,A)
WRITE (*,(A)) Muotoilukoodi: // formaatti
WRITE (*, formaatti) Luku on , m, .
END PROGRAM muotoilutesti

Funktio mjono muuttaa kokonaisluvun merkkijonoksi. Funktio fmt tuottaa


kokonaisluvun tulostamiseen tarvittavan muotoilukoodin.
Operaattoreita kannattaa kytt lhinn silloin, kun operaation merkitys
on itsestn selv. Luvussa 10 (sivu 152) luodaan kolmiulotteisia vektoreita
vastaava tietotyyppi sek mritelln tlle tyypille yhteen- ja vhennyslaskuoperaatiot jne. Tss tapauksessa merkint v = v1 + v2 on luonnollinen
tapa esitt vektorien yhteenlasku.

9.16

Muita moduulien kyttmahdollisuuksia

Moduuleissa voi mritell uusia (rakenteisia) muuttujatyyppej ja niihin


liittyvi operaatioita. Tst kerrotaan tarkemmin luvussa 10 (sivu 152).
Moduuliin voi sijoittaa ulkoisiin funktioihin liittyvi INTERFACE-lohkoja, jolloin kytettess vaikkapa aliohjelmakirjastoa voidaan proseduurien kutsumuodot tarkistaa knnsaikana.
Moduuleihin voi sijoittaa NAMELIST-ryhmien mrittelyj, jolloin tiedoston
syttmuodon muuttamiseksi riitt muuttaa moduulissa tehtvi mrittelyj. NAMELIST-ryhmist kerrotaan sivulla 210.

9.17

Yhteenveto

Moduuleita voi kytt tietotyyppien, datan ja nihin liittyvien operaatioiden


mrittelemiseen yhten kokonaisuutena. Kyttmll PRIVATE-mrett ja
-lausetta on mahdollista piilottaa toteutuksen yksityiskohdat moduulin ulkopuolelta. Tllin voidaan olla varmoja siit, ett toteutuksen muuttamisen
ei tarvitse johtaa koko ohjelmiston uudelleen kirjoittamiseen.

9. Moduulit ja operaattorit

149

Moduuli mritelln seuraavasti:


MODULE tunnus
[mrittelylauseet]
[CONTAINS
moduulin_proseduuri
[moduulin_proseduuri]...]
END [MODULE [tunnus]]

Moduuli otetaan kyttn USE-lauseella:


USE moduuli[, paikallinen_nimi => nimi_moduulissa]...
USE moduuli, ONLY : [[paikallinen_nimi =>] nimi_moduulissa &
[, [paikallinen_nimi =>] nimi_moduulissa]...]

Operaattori ja sijoituslause mritelln INTERFACE-lohkossa:


INTERFACE OPERATOR(operaattori)
[kutsumuodon_mrittelylohko]...
[MODULE PROCEDURE tunnus[, tunnus]...]...
END INTERFACE
INTERFACE ASSIGNMENT(=)
[kutsumuodon_mrittelylohko]...
[MODULE PROCEDURE tunnus[, tunnus]...]...
END INTERFACE

Tss luvussa kytiin nopeassa tahdissa lpi moduulien kyttmahdollisuuksia. Lis esimerkkej lytyy luvusta 14 (sivu 234). Koska moduuliajattelun oppii ainoastaan ohjelmoimalla, kannattaa seuraaviin harjoitustehtviin perehty kunnolla! Voit mys toteuttaa jonkin oman ohjelmointitehtvsi kytten moduuleita.

Harjoitustehtvi
1. Lis merimailin ja valovuoden pituusyksikt kappaleessa 9.12 sivulla
140 esitettyyn muunnosohjelmaan.
2. Mrittele geneerinen aliohjelma vaihda, jonka avulla voi vaihtaa argumenttien arvot keskenn:
INTEGER :: i, j
REAL :: x, y
CHARACTER(LEN=10) :: rivi1, rivi2
...
CALL vaihda(i,j)
CALL vaihda(x,y)
CALL vaihda(rivi1,rivi2)

3. Laajenna edell kyttmsi vaihda-aliohjelman toiminta-alueeksi mys


yksi- ja kaksiulotteiset taulukot. Niden alkiot vaihdetaan keskenn.
4. Muuta kappaleen 9.9 (sivu 135) nollakohdan hakurutiinia siten, ett saat
mys selville haussa tarvittujen vlin puolitusten lukumrn.

150

Fortran 95/2003

5. Muuta kappaleen 9.9 (sivu 135) nollakohdan hakurutiinia siten, ett menetelmn kytetn Newtonin menetelm (katso mallia kappaleesta
7.4 sivulta 90).
6. Mrittele merkkijonojen muunnosmoduuli, jonka sisltmt funktiot
isot_merkit ja pienet_merkit muuttavat argumenttinaan saamansa
merkkijonot isoiksi tai pieniksi kirjaimiksi. Ota huomioon mys skandinaaviset merkit!
7. Mrittele funktio fmt, joka palauttaa merkkijonona tss luvussa mritelty operaattoria .fmt. vastaavan kokonaisluvun muotoilukoodin
seuraavasti:
formaatti = (A, // fmt(m) // ,A)

8. Mrittele funktio mjono, joka muuntaa kokonaisluvun merkkijonoksi


seuraavaan tapaan:
tulostus = mjono(m)

9. Mrittele moduulissa satunnaislukuja tuottava funktio rnd, joka palauttaa annetun mrn tasajakautuneita satunnaislukuja. Funktiolla on
kaksi valinnaista argumenttia, joilla voi mrt, mille vlille [a, b) satunnaisluvut skaalataan. Seuraavassa on funktion kyttesimerkkej:
INTEGER, PARAMETER :: n = 10
REAL, DIMENSION(n) :: x, y
x = rnd(n)
y = rnd(n,-1.0,1.0)

10. Sarjassa tai rinnakkain sijoitettujen vastusten resistanssi R lasketaan


kaavoilla

R = R + R ,
resistanssit sarjassa,
1
2
1/R = 1/R1 + 1/R2 , resistanssit rinnakkain.
Mrittele reaalilukumuuttujia ksittelevt operaattorit, joita voidaan
kytt seuraavasti:
REAL :: r, r1, r2
r = r1 .sarjassa. r2
r = r1 .rinnakkain. r2

11. Laajenna edell mriteltyjen operaattorien kyttaluetta siten, ett niit voi kytt mys yksiargumenttisina operaattoreina seuraavasti:
REAL, DIMENSION(5) :: r0
REAL :: r
r = .sarjassa. r0
r = .rinnakkain. r0

Tss taulukkoon r0 on sijoitettu viiden vastuksen resistanssien arvot.


12. Mrittele yksiargumenttiset operaattorit .karvo. ja .khaj., jotka laskevat yksiulotteisen reaalilukutaulukon alkioiden keskiarvon ja keskihajonnan.

9. Moduulit ja operaattorit

151

13. Laajenna edell mriteltyj operaattoreita siten, ett voit antaa operaattorin vasemmanpuoleisena argumenttina loogisen taulukon, joka
kertoo mitk alkiot tulee ottaa mukaan keskiarvon tai keskihajonnan
laskemiseen:
keskiarvo = (x >= -1 .AND. x <= 1) .karvo. x

14. Mrittele yksiargumenttiset operaattorit + ja -, jotka muuttavat merkkijonon isoiksi tai pieniksi kirjaimiksi:
WRITE (*,*) +Isoiksi kirjaimiksi!
WRITE (*,*) -PIENET KIRJAIMET!

15. Mrittele kaksiargumenttinen operaattori -, joka poistaa merkkijonosta annetut merkit ja lis merkkijonon loppuun vastaavan mrn vlilyntej:
WRITE (*,*) Teksti - k
WRITE (*,*) Teksti - ts

Tss tulostuvien merkkijonojen tulisi olla Testi ja Teki

16. Mrittele operaattori /, joka liitt yhteen etu- ja sukunimen sisltvt merkkijonot ja muuttaa kummankin ensimmisen kirjaimen isoksi
ja loput pieniksi. Nimien vliin tulee yksi vlilynti. Seuraavassa on esimerkki operaattorin kytst:
mjono = etu

SUKUA

Tuloksen pitisi olla merkkijono Etu Sukua.


17. Kirjoita moduuli fibomod, jossa mritelty funktio fibo laskee ns. Fibonaccin lukuja:

x = 1, x = 1,
1
2
xn = xn1 + xn2 , n > 2.
Funktion tulisi muistaa aiemmin lasketut luvut annettuun maksimiarvoon asti. Seuraavassa on moduulin kyttesimerkki:
PROGRAM fibotest
USE fibomod, ONLY : fibo, fibo_max
fibo_max = 100
WRITE (*,*) fibo(10): , fibo(10)
WRITE (*,*) fibo(12): , fibo(12)
END PROGRAM fibotest

Kyt luvussa 11 esiteltyj dynaamisia taulukoita aiemmin laskettujen


arvojen tallettamiseen. Voit mys yksinkertaistaa tehtv kyttmll
vakiokokoista taulukkoa.
18. Toteuta kappaleessa 9.10 ehdotettu muutos nollakohta-moduuliin: toleranssi voidaan antaa valinnaisena argumenttina puolitushaku-rutiinille. Jos valinnaista argumenttia ei ole annettu, kyt moduulissa mritelty oletusarvoa.

152

Fortran 95/2003

10

Rakenteiset tietotyypit

Esittelemme tss luvussa rakenteisten tietotyyppien mrittelyn ja annamme kyttesimerkkej rakenteisista tyypeist. Kerromme mys operaattoreiden mrittelyst ja kytst sek abstraktien tietotyyppien luomisesta.

10.1

Rakenteisten tyyppien mrittely

Aiemmista FORTRAN-versioista poiketen Fortran 95 sislt mahdollisuuden kytt rakenteisia tietotyyppej (derived type) taulukoiden ja skalaarien lisksi. Rakenteisten tietotyyppien kytll voi selkeytt ohjelmaa kokoamalla yhteen muuttujaan toisiinsa liittyvi tietoja.
Yhdistmll moduulit ja rakenteiset tietotyypit voimme mritell tietorakenteillemme halutunlaisia operaatioita. Toisin sanoen voimme rakentaa
abstrakteja tietotyyppej, jotka koostuvat datasta sek datalle mritellyist operaatioista. Tllaisesta lhestymistavasta on esimerkkej myhemmin
tss luvussa.
Oman tietotyypin mrittely nytt esimerkiksi tlt:
TYPE vektorityyppi
REAL :: x
REAL :: y
REAL :: z
END TYPE vektorityyppi

Tm tietotyyppi koostuu kolmesta reaalilukutyyppisest alkiosta. Kun tietotyyppi on mritelty, voidaan uutta tyyppi oleva muuttuja mritell ohjelmassa esimerkiksi seuraavilla lausekkeilla:
TYPE(vektorityyppi) :: v
TYPE(vektorityyppi), DIMENSION (100) :: solmut

Rakenteista tietotyyppi olevan muuttujan alkioihin voidaan viitata prosenttimerkin (%) avulla seuraavasti:
v % y = 1.1
solmut(1:100) % x = 0.0
solmut(1:100) % y = 1.0

10. Rakenteiset tietotyypit

153

Tyyppimrittely luo mys tyyppirakenteen alustimen, joka palauttaa arvonaan tmn tyyppisen tieto-olion. Tyyppialustimen nimi on sama kuin tyypinkin.
Seuraavassa sijoitamme rakenteiseen muuttujaan v luvut (1.0, 2.0, 3.0), jotka WRITE-lause tulostaa.
v = vektorityyppi( 1.0,2.0,3.0 )
WRITE(*,*) v % x, v % y, v % z

Alustuksen voisi tehd mys muuttujan mrittelyn yhteydess:


TYPE(vektorityyppi) :: v = vektorityyppi( 1.0,2.0,3.0 )

Rakenteista tyyppi voi kytt proseduurin muodollisena parametrina, jos


tyyppi on tunnettu proseduurille. Proseduuri voi tuntea tyypin monella tavalla: proseduuri on rakenteisen tyypin mrittelevn ohjelmayksikn sisinen proseduuri, proseduurissa otetaan kyttn rakenteisen tyypin mrittelev moduuli tai proseduuri mrittelee tyypin itse. Viimeisin tapa ei ole
suositeltava, koska mys kutsuvan proseduurin on ollut pakko mritell
tyyppi, jolloin on helppo saada aikaan ristiriitaisia mrittelyj.
Olkoon esimerkiksi mriteltyn ulkoinen aliohjelma huono:
SUBROUTINE huono(a)
IMPLICIT NONE
TYPE konflikti
REAL :: a
END TYPE konflikti
TYPE(konflikti) :: a
jne.
END SUBROUTINE huono

Seuraavassa pohjelmassa kutsutaan tt aliohjelmaa, mutta mritelln


tyyppi konflikti uudestaan:
PROGRAM tyyppikonflikti
IMPLICIT NONE
TYPE konflikti
INTEGER :: a
END TYPE konflikti
TYPE(konflikti) :: a
CALL huono(a)
END PROGRAM tyyppikonflikti

Pohjelman ja aliohjelman kyttm mrittely rakenteelle konflikti on


tss esimerkiss erilainen. Proseduurin muodollisena parametrina olevan
muuttujan tyypin mrittely proseduurin sisll on perusteltua ainoastaan,
jos mrittely ei ole saatavilla mistn muualta, eli esimerkiksi jos kutsuva
proseduuri on kirjoitettu jollain toisella ohjelmointikielell. Tllinkin hyv
ohjelmointityyli olisi koota vastintyyppimrittelyt moduuleihin, etenkin
jos tyyppimrittelyj kytetn useammassa proseduurissa.
Rakenteisen tyypin komponentti voi olla mys toinen rakenteinen tyyppi.
Kolmioiden ksittelyyn luomme rakenteisen tyypin kolmiotyyppi:

154

Fortran 95/2003

TYPE kolmiotyyppi
REAL :: pinta_ala
TYPE(vektorityyppi) :: normaali
TYPE(vektorityyppi), DIMENSION(3) :: karkipisteet
END TYPE kolmiotyyppi

Tyypin kolmiotyyppi komponentille normaali voi asettaa arvon seuraavasti:


TYPE(kolmiotyyppi) :: kolmio
kolmio % normaali % x = 0.0
kolmio % normaali % y = 0.0
kolmio % normaali % z = 1.0

tai vaihtoehtoisesti tyypin alustimen avulla:


kolmio % normaali = vektorityyppi( 0.0,0.0,1.0 )

Alustuksen voi hoitaa mys mrittelylauseessa. Seuraavassa on esimerkki


tst:
PROGRAM kolmio_testi
IMPLICIT NONE
TYPE vektorityyppi
REAL :: x
REAL :: y
REAL :: z
END TYPE vektorityyppi
TYPE kolmiotyyppi
REAL :: pinta_ala
TYPE(vektorityyppi) :: normaali
TYPE(vektorityyppi), DIMENSION(3) :: karkipisteet
END TYPE kolmiotyyppi
TYPE(kolmiotyyppi) :: &
kolmio = kolmiotyyppi(1.0, &
vektorityyppi( 0.0,0.0,1.0 ), &
(/ vektorityyppi( 0.0,0.0,0.0 ), &
vektorityyppi( 1.0,-1.0,0.0 ), &
vektorityyppi( 1.0,1.0,0.0 ) /) )
WRITE (*,*) kolmio % pinta_ala
WRITE (*,*) kolmio % normaali
WRITE (*,*) kolmio % karkipisteet(1)
WRITE (*,*) kolmio % karkipisteet(2)
WRITE (*,*) kolmio % karkipisteet(3)
END PROGRAM kolmio_testi

Ohjelmassa kytettiin taulukkoalustinta (/ ... /) muodostamaan kolmialkioinen taulukko tyypin vektorityyppi alkioista.
Fortran-standardi ei mrittele oletusarvoista rakenteen alkioiden talletusjrjestyst muistissa (kuten ei taulukon alkioidenkaan), vaan tm on toteutuskohtaista. Rakenteen alkiojrjestyksen voi kuitenkin pakottaa vastaa-

10. Rakenteiset tietotyypit

155

maan mrittelyjrjestyst antamalla tyyppimrittelyn sisll SEQUENCEmreen:


TYPE vektorityyppi
SEQUENCE
INTEGER :: n
REAL :: x
REAL :: y
REAL :: z
END TYPE vektorityyppi

Mrett SEQUENCE tarvitaan kytnnss vain silloin, kun rakenteisen tyypin muuttujia halutaan vlitt eri ohjelmointikielten vlill.
Rakenteisen tyypin alkioille voi antaa mys oletusarvot suoraan tyypin mrittelyss:
PROGRAM kolmio_testi
IMPLICIT NONE
INTEGER, PARAMETER :: rkd = SELECTED_REAL_KIND(6,30)
REAL, PARAMETER :: alkuarvo = HUGE(1.0_rkd)
TYPE vektorityyppi
REAL(KIND=rkd) :: x = alkuarvo
REAL(KIND=rkd) :: y = alkuarvo
REAL(KIND=rkd) :: z = alkuarvo
END TYPE vektorityyppi
TYPE kolmiotyyppi
REAL(KIND=rkd) :: pinta_ala = alkuarvo
TYPE(vektorityyppi) :: normaali
TYPE(vektorityyppi), DIMENSION(3) :: karkipisteet
END TYPE kolmiotyyppi
TYPE(kolmiotyyppi) :: kolmio
WRITE (*,*) kolmio % normaali
END PROGRAM kolmio_testi

Tm ohjelma voisi tulostaa seuraavaa:


3.4028235E+38

3.4028235E+38

3.4028235E+38

Kolmion normaali alustettiin suurimmalla esitettviss olevalla reaaliluvulla


tyypin vektorityyppi mrittelyn perusteella.

10.2 Esimerkki: vektorilaskentamoduuli


Mrittelemme seuraavassa avaruusvektorityypin ja thn tyyppiin kohdistuvia operaatioita. Kokoamme tyyppimrittelyn ja tarvittavat vektorioperaatiot moduuliin vektorilaskenta.
Vektori koostuu komponenteista (x, y, z). Vektoreille on mritelty moduu-

156

Fortran 95/2003

lissa seuraavat operaatiot:


Arvon sijoitus vektorille. Tmn voi tehd tyyppirakenteen alustimen
avulla, esimerkiksi
v = vektorityyppi( 0.0,1.0,0.0 )

tai sijoittamalla arvot suoraan tyypin alkioihin:


v % x = 0.0
v % y = 0.0
v % z = 0.0

Jljempn kerrotaan, miten sijoitusoperaatio toteutetaan, jos vektorin


alkiot mritelln yksityisiksi, jolloin rakenteen alustinta tai suoraa
sijoitusta ei voi kytt.
v3 = v1+v2: Kahden vektorin summavektori.
v3 = v1-v2: Kahden vektorin erotusvektori.
pistetulo(v1,v2): Kahden vektorin komponenttien tulojen summa.
ristitulo(v1,v2): Kahden vektorin ristitulo.
ABS(v): Vektorin euklidinen normi eli pituus.
Seuraavassa on esitetty moduulin mrittely. Luomme ensin rakenteisen tyypin sek mrittelemme operaattoreiden + ja - ja standardifunktion ABS ylilatauksen:
MODULE vektorilaskenta
IMPLICIT NONE
TYPE vektorityyppi
REAL :: x
REAL :: y
REAL :: z
END TYPE vektorityyppi
INTERFACE OPERATOR(+)
MODULE PROCEDURE vektorisumma
END INTERFACE
INTERFACE OPERATOR(-)
MODULE PROCEDURE vektorierotus
END INTERFACE
INTERFACE ABS
MODULE PROCEDURE vektorinormi
END INTERFACE
CONTAINS
Seuraavassa esiteltvien funktioiden mrittelyt.
END MODULE vektorilaskenta

Voimme mritell moduuliin vektorilaskenta funktioita, jotka ksittelevt tyypin vektorityyppi tieto-olioita. Aluksi mrittelemme funktiot,

10. Rakenteiset tietotyypit

157

jotka laskevat kahden vektorin summa- ja erotusvektorit. Nm mriteltiin


yll operaattoreiden + ja - ylilatauksella kutsuttaviksi. Huomaa, ett mre
INTENT(IN) on pakollinen operaattorin mrittelevn funktion muodollisille argumenteille.
FUNCTION vektorisumma(v1, v2) RESULT(summa_vektori)
! Funktio palauttaa arvonaan vektoreiden
! v1 ja v2 summavektorin
IMPLICIT NONE
TYPE(vektorityyppi) :: summa_vektori
TYPE(vektorityyppi), INTENT(IN) :: v1, v2
summa_vektori % x = v1 % x + v2 % x
summa_vektori % y = v1 % y + v2 % y
summa_vektori % z = v1 % z + v2 % z
END FUNCTION vektorisumma
FUNCTION vektorierotus(v1, v2) RESULT(erotus_vektori)
! Funktio palauttaa arvonaan vektoreiden
! v1 ja v2 erotusvektorin
IMPLICIT NONE
TYPE(vektorityyppi) :: erotus_vektori
TYPE(vektorityyppi), INTENT(IN) :: v1, v2
erotus_vektori % x = v1 % x - v2 % x
erotus_vektori % y = v1 % y - v2 % y
erotus_vektori % z = v1 % z - v2 % z
END FUNCTION vektorierotus

Lopuksi teemme funktiot, jotka mrittelevt loput vektorityyppiin kohdistuvista operaatioista: pistetulo, vektorin pituus ja vektoriristitulo.
FUNCTION pistetulo(v1, v2) RESULT(tulos)
! Funktio palauttaa arvonaan vektoreiden
! v1 ja v2 pistetulon
IMPLICIT NONE
REAL :: tulos
TYPE(vektorityyppi), INTENT(IN) :: v1, v2
tulos = v1 % x * v2 % x
tulos = tulos + v1 % y * v2 % y
tulos = tulos + v1 % z * v2 % z
END FUNCTION pistetulo
FUNCTION vektorinormi( v ) RESULT(pituus)
! Funktio palauttaa arvonaan vektorin v pituuden
IMPLICIT NONE
REAL :: pituus
TYPE(vektorityyppi), INTENT(IN) :: v
pituus = SQRT(pistetulo(v,v))
END FUNCTION vektorinormi

158

Fortran 95/2003

FUNCTION ristitulo(v1, v2) RESULT(tulos)


! Funktio palauttaa arvonaan vektoreiden
! v1 ja v2 ristitulon
IMPLICIT NONE
TYPE(vektorityyppi) :: tulos
TYPE(vektorityyppi), INTENT(IN) :: v1, v2
tulos % x = v1 % y * v2 % z - v1 % z * v2 % y
tulos % y = v1 % z * v2 % x - v1 % x * v2 % z
tulos % z = v1 % x * v2 % y - v1 % y * v2 % x
END FUNCTION ristitulo

Alla on pieni testiohjelma vektorimoduulin kokeiluun:


PROGRAM vektoritesti
USE vektorilaskenta
IMPLICIT NONE
TYPE(vektorityyppi) :: u, v, w
REAL :: k, pituus
CHARACTER(LEN=*), PARAMETER :: form = (A,3F7.3)
u = vektorityyppi( 1.0, 0.0, 0.0)
v = vektorityyppi(-1.0, 1.0, 1.0)
w = ristitulo(u,v)
k = pistetulo(u,v)
pituus = ABS(u)
WRITE(*,form)
WRITE(*,form)
WRITE(*,form)
WRITE(*,form)
WRITE(*,form)
END PROGRAM

Vektori u: , u
Vektori v: , v
Vektorin u pituus: , pituus
Vektoreiden u ja v ristitulo: , w
Vektoreiden u ja v pistetulo: , k

Ohjelman tulostus nytt tlt:


Vektori u:
1.000 0.000 0.000
Vektori v: -1.000 1.000 1.000
Vektorin u pituus:
1.000
Vektoreiden u ja v ristitulo:
0.000 -1.000
Vektoreiden u ja v pistetulo: -1.000

10.3

1.000

NULL-funktio

Fortran 95:ess (mutta ei Fortran 90:ss) osoitintaulukot voi alustaa kohteesta irrotetuiksi (disassociated) NULL-funktion avulla:
REAL, DIMENSION(:), POINTER

Mys rakenteiset tyypit voi alustaa:

:: t => NULL()

10. Rakenteiset tietotyypit

159

TYPE :: pinotyyppi
REAL, DIMENSION(:), POINTER :: arvot => NULL()
INTEGER :: huippu = -1
END TYPE pinotyyppi
TYPE(pinotyyppi) :: &
pino, & ! Oletusalustus
pino2 = pinotyyppi(NULL(),0)

Tss alustimme osoitintaulukot kohteesta irrotetuiksi NULL-funktion avulla.

10.4 Julkisuus ja yksityisyys


Vektorityypin mrittelyss ei edell kerrottu, voiko moduulin kyttj viitata suoraan itse vektorin alkioihin, vai voiko vektorityypin muuttujaa ksitell vain moduulissa mriteltyjen operaatioiden kautta. Oletusarvoisesti,
jos tyypin mrittelyss ei muuta sanota, tyypin alkiot ovat julkisia eli alkiot
nkyvt kyttjlle suoraan.
Voisimme mritell vektorityypin alkiot mys yksityisiksi:
TYPE vektorityyppi
PRIVATE
REAL :: x
REAL :: y
REAL :: z
END TYPE vektorityyppi

Tllin moduulia kyttv ohjelma ei voi viitata alkioihin suoraan. Tst seuraa mys se, ett tarvittaessa voimme vaihtaa tyypin mrittelyn ja moduulin toteutuksen toiseksi ilman, ett moduulia kyttvi ohjelmia tarvitsisi
muuttaa. Tyypin alkioista osa ei voi olla julkisia ja osa yksityisi, vaan joko
kaikki ovat julkisia tai kaikki yksityisi.

10.5 Esimerkki: vektorimoduulin jatkokehittely


Jos mrittelemme vektorityypin alkiot yksityisiksi, tarvitsemme moduuliin
vektorin alustavan funktion (seuraavassa vektori). Tyyppirakenteen alustinta ei voi kytt thn tarkoitukseen, koska emme ne vektorityypin sisist toteutusta.
FUNCTION vektori(x, y, z) RESULT(uusi)
! Funktio palauttaa arvonaan vektorin, jonka
! komponenttien arvot on annettu argumentteina
IMPLICIT NONE
REAL :: x, y, z
TYPE(vektorityyppi) :: uusi

160

Fortran 95/2003

uusi % x =
uusi % y =
uusi % z =
END FUNCTION

x
y
z
vektori

Lisksi tarvitsemme vektorin komponentit palauttavan funktion. Funktio


voisi sijoittaa komponenttien arvot taulukkoon ja palauttaa tmn taulukon arvonaan:
FUNCTION komponentit(vektori) RESULT(tulostaulukko)
! Funktio palauttaa vektorin komponenttien
! arvot tulostaulukossa
IMPLICIT NONE
TYPE(vektorityyppi) :: vektori
REAL, DIMENSION(3) :: tulostaulukko
tulostaulukko(1) = vektori % x
tulostaulukko(2) = vektori % y
tulostaulukko(3) = vektori % z
END FUNCTION komponentit

Moduulia vektorilaskenta voisi kytt nyt seuraavaan tapaan:


PROGRAM vektoritesti
USE vektorilaskenta
IMPLICIT NONE
TYPE(vektorityyppi) :: u, v, w
REAL :: k, pituus
CHARACTER(LEN=*), PARAMETER :: form = (A,3F7.3)
u = vektori(1.0,0.0,0.0)
v = vektori(-1.0,1.0,1.0)
w = ristitulo(u,v)
k = pistetulo(u,v)
pituus = ABS(u)
WRITE(*,form) Vektori u: , komponentit(u)
WRITE(*,form) Vektori v: , komponentit(v)
WRITE(*,form) Vektorin u pituus: , pituus
WRITE(*,form) Vektoreiden u ja v ristitulo: , &
komponentit(w)
WRITE(*,form) Vektoreiden u ja v pistetulo: , k
END PROGRAM

Vektorin arvo mrtn kyttmll funktiota vektori ja vektorin komponentit saadaan selville funktiolla komponentit.
Voisimme laajentaa vektorityypin mrittely viel esimerkiksi siten, ett
se sislt tiedon koordinaattisysteemist, jossa vektorin komponentit on
esitetty:
TYPE vektorityyppi
PRIVATE
REAL :: x, y, z
TYPE(koordinaatisto) :: nykykoordinaatisto
END TYPE vektorityyppi

10. Rakenteiset tietotyypit

161

Voisimme mys laajentaa vektorityypin ksittelemn n-ulotteisen avaruuden vektoreita, jolloin vektorin komponentit kannattaisi alunperinkin esitt
taulukkona:
TYPE vektorityyppi
PRIVATE
INTEGER :: n
REAL DIMENSION(:), POINTER :: komponentit
TYPE(koordinaatisto) :: nykykoordinaatisto
END TYPE vektorityyppi

Vektorin alustavan proseduurin tehtviin kuuluu nyt mys varata komponenteille tarpeellinen mr tilaa. Dynaamisesta tilanvarauksesta kerrotaan
kappaleessa 11.11 (sivu 178) ja POINTER-mreell luoduista osoitinmuuttujista kappaleessa 11.13 (sivu 183).

10.6 Yhteenveto
Voit huomattavasti selkeytt ohjelmaa tehtvn sopivalla tietotyypin valinnalla. Tm samalla helpottaa ratkaisualgoritmin toteutusta. Yhdistmll rakenteiset tietotyypit ja moduulit voit luoda abstrakteja tietotyyppej
sek piilottaa toteutuksen yksityiskohdat kyttjlt, jonka ei nit yleens
tarvitse tiet.
Tyyppimrittely tehdn seuraavasti:
TYPE [, julkisuus ::] nimi
muuttujamrittelyt
END TYPE nimi

Rakenteista tietotyyppi kytetn seuraavasti:


TYPE(nimi) :: muuttuja

Tyypin alkioihin viitataan prosenttimerkin avulla:


nimi % komponentti

Tyyppimrittely luo rakenteen alustimen, jonka nimi on sama kuin tyypinkin. Alustinta kytetn antamaan rakenteisen tietotyypin muuttujalle
arvo:
muuttuja = tyyppi(arvo1,...,arvon)

Harjoitustehtvi
1. Laske vektorimoduulilla pistetulo vektoreista, joista toinen on kahden
vektorin ristitulo ja toinen jompi kumpi alkuperisist vektoreista. Mit
saat tulokseksi? Miksi?
Kirjoita vektorikolmitulon laskeva funktio, jonka otsikkorivi on

162

Fortran 95/2003

FUNCTION kolmitulo(v1, v2, v3) RESULT(a)

2. Kyt hyvksi jo mriteltyj operaatioita vektorimoduulissa ja lis


moduuliin tietotyyppi
TYPE kolmiotyyppi
TYPE(vektorityyppi), DIMENSION(3) :: karkipisteet
END TYPE kolmiotyyppi

Mrittele funktio normaali(k), joka laskee kolmion k yksikknormaalin. Vihje: muodosta vektorit v1 = t2-t1 ja v2 = t3-t1, miss suureet
t1,t2 ja t3 ovat kolmion krkipisteiden paikkavektorit ja kyt hyvksesi edellisen harjoitustehtvn tulosta. Onko normaali mielestsi yksiksitteinen?
Mrittele mys kolmion pinta-alan laskeva funktio pinta_ala(k).
3. Lis vektorimoduuliin operaatio, joka tarkistaa, ovatko kaksi vektoria
likimain samat. Kyt tss operaattorin == ylilatausta. (Likimrinen
yhtsuuruus mritelln tehtvss 9 sivulla 65.)
4. Laajenna kolmiotyypin mrittely ksittmn yleinen tasomonikulmio.
5. Lis vektorimoduuliin operaatiot, joka muuntavat vektorin komponentit karteesisesta koordinaatistosta pallokoordinaatistoon ja pinvastoin.
Niden funktioiden otsikkolauseet ovat
FUNCTION vektori_pallo(r, theta, phi) RESULT(vektori)

ja
FUNCTION komponentit_pallo(vektori) RESULT(tulostaulukko)

Koska moduulin sisinen esitysmuoto on karteesinen, funktioiden tytyy tehd tarpeelliset muunnokset. Muunnos karteesiseen koordinaatistoon tapahtuu kaavoilla

x = r sin cos ,
y = r sin sin ,

z = r cos .
6. Mrittele aliohjelma vaihda, joka vaihtaa kahden vektorin arvot keskenn:
CALL vaihda(v1,v2)

7. Mrittele rakenteinen tyyppi, joka voi tallettaa syntymajan seuraavassa muodossa:


21 01 1990

Siis rakenteinen tyyppi sislt kolme kokonaislukua, joilla on eri KINDarvot: SELECTED_INT_KIND(2) ja SELECTED_INT_KIND(4).
8. Lis edellisen harjoituksen rakenteiseen tyyppiin kentt nime varten.
9. Kirjoita funktio, joka palauttaa merkkijonossa nimen ja pivmrn
seuraavassa muodossa:
Jaska Jokunen (01.01.1999)

10. Rakenteiset tietotyypit

163

Kyt edellisess tehtvss luotua rakenteista tyyppi.


10. Mrittele operaattori .d., joka laskee kahden vektorin ptepisteiden
etisyyden toisistaan:
dist = u .d. v

Kyt mrittelyss kaavaa ABS(u-v). Huomaa, ett ABS-operaation toteuttava funktio vektorinormi (sivu 157) tuottaa ylivuodon, jos vektorin komponentit ovat lhell ylivuototasoa, vaikka tulos olisikin esitettviss liukulukuna. Esimerkin ongelmasta tarjoaa lauseke
ABS(vektori(a,a,a))

kun a = HUGE(1.0)/10. Korjaa funktion mrittely.


11. Mrittele pistetulo-operaattori .dot., jota kytetn seuraavasti:
s = u .dot. v

Mrittele mys ristitulo-operaattori .cross..


12. Mrittele funktio, joka laskee kahden vektorin vlisen kulman kaavalla


v w
= arccos
.
|v||w|
13. Mrittele kertolaskuoperaatio v = n*u miss n on skalaarityyppi REAL
tai INTEGER, ja muuttujat u ja v ovat vektorityyppi. Operaatiossa skaalataan vektorin alkiot annetulla kertoimella.
14. Esittelimme kappaleessa 3.12 (sivu 33) oppilaiden nimien ja arvosanojen tallentamiseen sopivan rakenteisen tyypin kurssilainen. Mrittele moduuli, jossa on tmn rakenteisen tyypin ksittelyyn tarvittavia
operaatioita. Mrittele oppilastietojen luku- ja tulostusoperaatiot sek
arvosanojen keskiarvon laskemiseen soveltuva proseduuri.

164

Fortran 95/2003

11

Taulukot ja
osoitinmuuttujat

Esittelemme tss luvussa Fortran 95 -kielen taulukko- ja osoitinmuuttujien


mrittelyn ja kyttmahdollisuudet. Lisksi kerromme ajonaikaisesta eli
dynaamisesta muistitilan varaamisesta.

11.1

Taulukoiden mrittely ja kytt

Taulukko (array) mritelln kertomalla kntjlle taulukon alkioiden


tyyppi, taulukon ulottuvuuksien mr (rank) sek mahdollisesti ulottuvuuksien koot eli taulukon muoto (shape). Lisksi mrittelyss voi antaa
joukon erilaisia mreit, kuten onko taulukko dynaamisesti varattavissa
tai voiko se olla kohteena osoitinmuuttujalle.
Tavallisesti taulukkomuuttuja mritelln joko antamalla tyyppimrittelyn jlkeen mre DIMENSION tai kirjoittamalla taulukon koko taulukon nimen pern. Esimerkiksi lausekkeet
REAL, DIMENSION(0:100,-50:50,10:110) :: x
REAL :: y(101,101,101)

mrittelevt molemmat kolmiulotteisen taulukon. Alkioita on kummassakin taulukossa 101 101 101. Tllainen taulukkomrittely on Fortran 95
-standardin kielell tunnetun muotoinen taulukko (explicit-shape array). Mrittelyst voi halutessaan jtt indeksien alarajat pois, jolloin nm oletetaan ykksiksi. Esimerkiksi reaalilukutaulukon mrittely on siis yleisemmin
muotoa
REAL, DIMENSION(ala1:yla1,ala2:yla2,...,alaN :ylaN ) :: x

tai muotoa
REAL :: x(ala1:yla1,ala2:yla2,...,alaN :ylaN )

Suurin taulukoiden indeksien eli ulottuvuuksien mr Fortran 95 -standardissa on seitsemn.


Seuraavassa ohjelmassa taulukoimme funktion sin x arvot vlilt [0, ] taulukkoon x:

11. Taulukot ja osoitinmuuttujat

165

PROGRAM taulukointi
IMPLICIT NONE
INTEGER, PARAMETER :: n = 100
INTEGER :: i
REAL, DIMENSION(n) :: x
REAL :: pi
pi = 2*ACOS(0.0)
DO i = 1, n
x(i) = SIN(pi*(i-1.0)/(n-1.0))
END DO
WRITE(*,(A)) sin(x) =
WRITE(*,(5(F11.7))) x
END PROGRAM taulukointi

Ohjelma tulostaa seuraavaa:


sin(x) =
0.0000000
0.1580014
...
0.1265922

0.0317279
0.1892512

0.0634239
0.2203106

0.0950560
0.2511480

0.1265925
0.2817326

0.0950560

0.0634239

0.0317279 -0.0000001

Dynaamisesti varattavan taulukon (dynamic array) kokoa ei kerrota mrittelyvaiheessa, vaan sek ala- ett ylrajat jtetn auki. Mrittelyss kytetn joko mrett ALLOCATABLE tai myhemmin esiteltv POINTERmrett. Esimerkiksi lause
REAL, DIMENSION(:,:,:), ALLOCATABLE :: x

mrittelee kolmiulotteisen taulukon, jonka kokoa ei ole mritelty. Mrittelyss kerrotaan, ett taulukko on kolmiulotteinen sek dynaamisesti
varattavissa. Tllaista taulukkoa kutsutaan mukautuvan muotoiseksi taulukoksi (deferred-shape array). Dynaamisesta muistin varauksesta kerrotaan
enemmn kappaleessa 11.11 (sivu 178).
Proseduureissa voi taulukoille varata tilaa mys suoraan muuttujamrittelyss automaattisten taulukoiden (automatic array) avulla:
SUBROUTINE ali(n)
IMPLICIT NONE
INTEGER :: n
REAL, DIMENSION(n) :: x
jne.
END SUBROUTINE ali

Voit antaa taulukoille mys PARAMETER-mreen eli mritell vakiotaulukon:


INTEGER, DIMENSION(3), PARAMETER :: luvut = (/ 1,2,3 /)

166

Fortran 95/2003

11.2

Taulukon alkioiden ksittely

Taulukon alkioihin viitataan antamalla taulukon nimen perss sulkeissa


taulukon alkion indeksi(t). Seuraavassa ohjelmassa sijoitetaan ensin taulukon x kymmenenteen alkioon luku 0.0. Tmn jlkeen alustetaan taulukon
y alkioiden arvoksi 1.0.
PROGRAM silmukka
IMPLICIT NONE
INTEGER, PARAMETER :: n = 10
REAL, DIMENSION(n) :: x
REAL, DIMENSION(n,n,n) :: y
INTEGER :: i, j, k
x(n) = 0.0
DO i = 1, n
DO j = 1, n
DO k = 1, n
y(i,j,k) = 1.0
END DO
END DO
END DO
END PROGRAM silmukka

Jlkimmisen operaation voisi tehd helpommin taulukko-operaatioiden avulla eli yksinkertaisesti asettamalla:
y = 1.0

Taulukko-operaatioista kerrotaan kappaleessa 11.8 (sivu 174).


Taulukosta voi valita osan antamalla taulukon indeksit muodossa
x(alku:loppu:lisys)

Seuraava esimerkki vaihtaa taulukon x ensimmisen kymmenen alkion jrjestyksen keskenn:


REAL, DIMENSION(10) :: x
x(1:10) = x(10:1:-1)

Lausekkeen oikea puoli suoritetaan ensin, jolloin kntj tarvittaessa varaa


tilapisen muistitilan tulokselle, mink jlkeen tulos sijoitetaan vasemmalla puolella annettuun muuttujaan. Indeksien alku- tai loppurajat voi jtt
syntaksista pois, jolloin rajat otetaan taulukon mrittelyst. Edellinen taulukkosyntaksi ei siis ole sama asia kuin silmukka
DO i = 1, 10
x(i) = x(11-i)
END DO

Selit miksi!
Seuraava esimerkki lis kahden vlein taulukon x alkiot yhdest kymmeneen taulukon y alkioihin:
REAL, DIMENSION(5) :: y

11. Taulukot ja osoitinmuuttujat

167

y = y + x(::2)

Samaan tapaan voidaan valita osa useampiulotteisesta taulukosta. Mrittelemme aluksi taulukon
REAL, DIMENSION(5,8) :: a

Tllin lausekkeen
a(2:5:2, 4:6)

arvo on taulukko, jonka koko on 2 3 ja joka koostuu taulukon a riveist 2


ja 4 sek sarakkeista 4, 5 ja 6. Seuraavassa on merkill esitetty taulukosta
a valitut alkiot:
1

1
2
3
4
5

Taulukkoa voi mys indeksoida kokonaislukuvektorilla eli yksiulotteisella


taulukolla. Seuraavassa sijoitamme taulukkoon y taulukon x alkiot 1, 1, 2, 5
ja 9.
INTEGER, DIMENSION(5) :: i
REAL, DIMENSION(10) :: x, y
i = (/1,1,2,5,9/)
y(1:5) = x(i)

Sijoitusoperaatiota on havainnollistettu kuvassa 11.1.

x(1) x(2) x(3) . . . x(9) x(10)

1 1 2 5 9

x(i)

x(1) x(1) x(2) x(5) x(9)

y(1) y(2) y(3) y(4) y(5)

Kuva 11.1: Taulukon indeksointi kokonaislukuvektorilla.

Huomaa, ett taulukon x ensimminen alkio sijoitetaan sek taulukon y ensimmiseen ett toiseen alkioon. Fortran 95 -standardi kielt sijoituksen
toisin pin: samassa lausekkeessa ei voi sijoittaa kahteen kertaan arvoa samalle indeksille. Toisin sanoen lauseke
y(i) = x(1:5)

168

Fortran 95/2003

on kielletty yll annetuille vektorin i arvoille. Jos samoja arvoja ei esiinny


indeksivektorissa, taulukkoa voi kyll indeksoida vektorilla mys sijoituslausekkeen vasemmalla puolella.

11.3

Taulukkoalustin

Kytimme edell taulukkoalustinta (array constructor), jonka muotoja ovat


esimerkiksi
x(1:3) = (/ 1.0, 2.5, 3.0 /)
x(1:n) = (/ (SIN(pi*i/(n-1.0)), i = 0, n-1) /)

Taulukon alustin siis mritelln sulkeissa (/ . . . /). Jlkimmisen sijoituslauseen oikealla puolella on niin sanottu toistolista (implied do list),
joka toimii kuten tavallinenkin DO-silmukka. Toistolista on sulkujen sisll, ja indeksimuuttujan mrittely tulee silmukan sislln jlkeen pilkulla
erotettuna:
(SIN(pi*i/(n-1.0)), i = 0, n-1)

Toistolista on taulukon alustuksessa samassa asemassa kuin muutkin alustuslausekkeen osat. Esimerkiksi lauseke
(/ 0, (i, i = 1,5), 1 /)

tuottaa taulukon, jonka alkiot ovat


0 1 2 3 4 5 1

Rakenteen
(/ 0, (1, i = 1,4), (2, i = 1,3) /)

tuottamat arvot ovat puolestaan


0 1 1 1 1 2 2 2

Taulukkoalustimen tulos on aina yksiulotteinen taulukko eli vektori. Jos


haluat sijoittaa arvot useampiulotteiseen taulukkoon alustimen avulla, on
kytettv RESHAPE-funktiota. Alla olevassa esimerkiss sijoitamme kaksiulotteisen taulukon x alkioihin (1:2,1:3) arvot yhdest kuuteen taulukkoalustimen avulla:
x(1:2,1:3) = RESHAPE( (/ 1, 2, 3, 4, 5, 6 /), (/ 2, 3 /) )

RESHAPE-funktio palauttaa taulukon, jonka arvot on annettu ensimmisess


argumentissa. Tmn argumentin tulee olla yksiulotteinen taulukko. Palautettavan taulukon muoto mrytyy funktion toisen argumentin mukaan.
Edellisess esimerkiss RESHAPE-funktio tuottaa 2 3 -matriisin. Sijoituslauseen toimintaa on havainnollistettu kuvassa 11.2.

11. Taulukot ja osoitinmuuttujat

169

x(1,1) = 1

x(1,2) = 3

x(1,3) = 5

x(2,1) = 2

x(2,2) = 4

x(2,3) = 6

Kuva 11.2: Taulukon alkiojrjestys.

11.4

Taulukon alkioiden jrjestys

Fortran-standardi ei mr taulukon alkioiden talletusjrjestyst muistissa, vaan tm on jtetty kntjn toteuttajan tehtvksi. Taulukko-operaatioille on kyllkin mritelty taulukoiden alkiojrjestys (array element order),
joka on sama kuin FORTRAN 77 -standardissakin. Alkiojrjestys on mrtty siten, ett taulukon ensimmist indeksi vastaavat alkiot ovat jrjestyksess ensin, toista indeksi vastaavat alkiot tmn jlkeen, sitten kolmatta
jne. Matriisille alkioiden ksittelyjrjestys on siten sarakkeittain kuten
matematiikassa yleisesti ja pinvastoin kuin C-kieless.
Edellisen esimerkin alkiot tulivat siis talletetuksi taulukkoon x seuraavassa
alkiojrjestyksess:
x(1,1)
x(2,1)
x(1,2)
x(2,2)
x(1,3)
x(2,3)

11.5

=
=
=
=
=
=

1
2
3
4
5
6

Esimerkki: funktion arvojen talletus


taulukkoon

Taulukoidaan jlleen funktio sin x. Tll kertaa kysytn kyttjlt taulukoitavien pisteiden mr vlilt [0, ]. Taulukolle varataan dynaamisesti
muistia, ja funktion arvot tulostetaan ptteelle.
PROGRAM trigo
IMPLICIT NONE
INTEGER, PARAMETER :: reaali = SELECTED_REAL_KIND(10)
REAL(KIND=reaali), DIMENSION(:), ALLOCATABLE :: x
REAL(KIND=reaali), PARAMETER :: &
pi = 3.141592653589793_reaali
INTEGER :: i, n, allocstat
WRITE(*,(A),ADVANCE=no) Anna n:

170

Fortran 95/2003

READ(*,*) n
IF (n > 0) THEN
ALLOCATE(x(n), STAT = allocstat)
IF ( allocstat /= 0 ) STOP
ELSE
STOP n <= 0!
END IF
x = (/ (i, i = 0, n-1) /)
x = SIN(pi*x/(n-1))
WRITE(*,(A)) sin(x) =
WRITE(*,(5(F10.7))) x
END PROGRAM trigo

Ohjelman kytt nytt tlt:


Anna n: 12
sin(x) =
0.0000000 0.2817326 0.5406408 0.7557496 0.9096320
0.9898214 0.9898214 0.9096320 0.7557496 0.5406408
0.2817326 0.0000000

11.6

Taulukoiden vlitys aliohjelmiin


Seuraava ohjelma laskee funktion f (x, y) = sin x 2 + y 2 kuvaajan taulukkoon z. Funktion arvot lasketaan aliohjelmassa f. Argumenttina vlitetn
pisteet, joissa funktio evaluoidaan, sek tulostaulukko. Aliohjelma tarkistaa lisksi, ett taulukot ovat todella samanmuotoisia, jotta operaatio olisi
mritelty.
PROGRAM taulukko
IMPLICIT NONE
INTEGER,PARAMETER :: n = 5
REAL, DIMENSION(n) :: t
REAL, DIMENSION(n,n) :: x, y, z
INTEGER :: i
t = (/ (10.0*i/(n-1)-5.0, i = 0, n-1) /)
DO i = 1,n
x(i,1:n) = t
y(1:n,i) = t
END DO
CALL f(x,y,z)
WRITE(*,*) z
CONTAINS
SUBROUTINE f(x,y,z)
IMPLICIT NONE
REAL, DIMENSION(:,:) :: x, y, z
IF ( ANY(SHAPE(x) /= SHAPE(y)) .OR. &
ANY(SHAPE(x) /= SHAPE(z)) ) THEN

11. Taulukot ja osoitinmuuttujat

171

WRITE(*,*) Argumentit eivt ole samanmuotoisia


STOP
ELSE
z = SIN(SQRT(x**2 + y**2))
END IF
END SUBROUTINE f
END PROGRAM taulukko

Funktion f kyttm standardifunktio SHAPE palauttaa arvonaan muuttujan muodon. Kaksiulotteisen taulukon muoto on rivien ja sarakkeiden lukumr. Standardifunktio ANY palauttaa tiedon siit, onko sille argumenttina
annetun loogisen taulukon arvoista mikn tosi.
Aliohjelman mrittelyss voi muodollisena argumenttina olla kolme erityyppist taulukkomrittely. Ensimminen nist on oletetun muotoinen
taulukko (assumed-shape array):
REAL, DIMENSION(ala1:,ala2:,ala3:) :: x

Taulukon varsinainen muoto mrytyy aliohjelmaa kutsuttaessa todellisen argumentin mukaan. Alarajat voi halutessaan jtt pois mrittelyst, jolloin ne oletetaan ykksiksi. Tss taulukkomrittelyss ei voi kytt
ALLOCATABLE-mrett. Oletetun muotoista taulukkomrittely kytettess proseduurin kutsumuodon on oltava tunnettu sek muodollisen ja todellisen parametrin tyyppien, lajiparametrien ja taulukkojen indeksien mrien
tytyy kaikkien olla samat.
Toinen taulukkoargumenttien kytttapa on tunnetun muotoinen taulukko
(explicit-shape array) eli taulukkomrittely, jossa taulukon muoto on kokonaan mrtty. Toisin kuin oletetun muotoiselle taulukolle, muodollisen ja
todellisen argumentin dimensioiden mrn ei tarvitse olla samat. Seuraavassa esimerkiss pohjelmassa mritelty kaksiulotteista taulukko ksitelln sek yksiulotteisena aliohjelmassa ali_1 ett kolmiulotteisena aliohjelmassa ali_3.
PROGRAM tunnettu_muoto
IMPLICIT NONE
INTEGER, PARAMETER :: n = 5
REAL, DIMENSION(n,n) :: taul = 0.0
CALL ali_1(taul,n)
WRITE (*,(5(F8.3))) taul(3,:)
CALL ali_3(taul,n)
WRITE (*,(5(F8.3))) taul(3,:)
CONTAINS
SUBROUTINE ali_1(taul,n)
IMPLICIT NONE
INTEGER :: n
REAL, DIMENSION(n*n) :: taul
taul(8:20) = -1.0
END SUBROUTINE ali_1
SUBROUTINE ali_3(taul,n)
IMPLICIT NONE
INTEGER :: n
REAL, DIMENSION(n,n/2,2) :: taul

172

Fortran 95/2003

taul(3,:,:) = 1.0
END SUBROUTINE ali_3
END PROGRAM tunnettu_muoto

Kun knnmme ja ajamme ohjelman, tulostus nytt seuraavalta:


0.000
1.000

-1.000
1.000

-1.000
1.000

-1.000
1.000

0.000
0.000

Tss sijoitimme arvon 1 yksiulotteisen taulukon alkioihin 8, 9, . . . , 20. Kun


tulostimme vastaavan kaksiulotteisen taulukon kolmannen rivin, huomasimme alkioiden 2, 3 ja 4 muuttaneen arvoaan. Kun sijoitimme kolmiulotteiseen taulukkoon arvoja, muuttuivat tietenkin mys vastaavat alkuperisen
taulukon arvot.
Tunnetun muotoisten taulukkojen kyttminen on riskialtista, ja kaikki tarkistukset ovat ohjelmoijan vastuulla. Muista siis varmistaa, ett koodi tekee,
mit haluat sen tekevn.
Jos taulukon osia vlitetn proseduurille, jonka muodollinen argumentti
on mritelty tunnetun muotoisena taulukkona ja jonka dimensiot mahdollisesti eivt ole samat kuin taulukon mrittelyss kutsuvassa ohjelmassa,
osa taulukosta saatetaan joutua kopioimaan vliaikaiseen muuttujaan proseduuriin mentess ja takaisin alkuperiseen taulukkoon proseduurista palattaessa. Kopiointia ei tarvita, jos muodollinen argumentti on mritelty
oletetun muotoiseksi taulukoksi. Tllin ohjelma on luultavasti tehokkaampi.
Eriulotteisten taulukkojen alkioiden vastaavuudet mrytyvt Fortranin alkiojrjestyksen perusteella. Esimerkiksi kaksiulotteisen taulukon alkiot ovat
muistissa perkkin sarakkeittain (kuva 11.2 sivulla 169). Muistisntn voi
kytt lausetta ensimminen indeksi muuttuu nopeimmin.
Tunnetun muotoiselle taulukolle ei voi tietenkn kytt mreit POINTER
tai ALLOCATABLE, eik niille siis voi varata tilaa aliohjelmassa, vaan tilanvaraus on tytynyt tehd aikaisemmin.
Kolmas muodollisena argumenttina annetun taulukon mrittely on oletetun
kokoinen taulukko (assumed-size array):
REAL, DIMENSION(n1,n2,*), :: x

Tss taulukon koko jtetn ilmoittamatta viimeisen indeksin osalta. Tm


muoto on jnne FORTRAN 77 -standardista eik ole suositeltava.
Proseduuriin voi vlitt mys taulukon osan:
PROGRAM taulukko_pala
IMPLICIT NONE
REAL, DIMENSION(10) :: a = 1.0, b = -1.0
INTRINSIC SIN
b(2:9) = SIN(a(2:9))
END PROGRAM taulukko_pala

Taulukon osan voi vlitt mys ulkoiseen proseduuriin, kunhan samalla


vlitt taulukon koon:

11. Taulukot ja osoitinmuuttujat

173

PROGRAM taulukko_pala_2
IMPLICIT NONE
INTEGER, PARAMETER :: n = 6
REAL, DIMENSION(n) :: a = 1.0, b = -1.0
EXTERNAL ali
CALL ali(a(1:n:2), n/2, b(1:n/2), n/2)
WRITE (*,(6(F8.3))) a, b
END PROGRAM taulukko_pala_2
SUBROUTINE ali(t1, m, t2, n)
IMPLICIT NONE
INTEGER :: m, n
REAL :: t1(m), t2(n)
t1 = 2.0
t2 = 4.0
END SUBROUTINE ali

Tss vlitimme ulkoiseen funktioon ali joka toisen alkion taulukosta a ja


kolme ensimmist alkiota taulukosta b. Ohjelma tulostaa seuraavaa:
2.000
4.000

11.7

1.000
4.000

2.000
4.000

1.000
-1.000

2.000
-1.000

1.000
-1.000

Taulukkoarvoiset funktiot

Funktiot voivat olla mys taulukkoarvoisia. Tm saadaan aikaiseksi mrittelemll RESULT-lausekkeessa annettu muuttuja taulukoksi (jonka tytyy
olla tunnetun muotoinen). Seuraavassa on tst esimerkki:
PROGRAM taulukkofunktio
IMPLICIT NONE
INTEGER, PARAMETER :: n = 5
REAL, DIMENSION(n,n) :: x, y
REAL, DIMENSION(n) :: t
INTEGER :: i
t = (/ (10.0*i/(n-1)-5.0, i = 0, n-1) /)
DO i = 1,n
x(i,1:n) = t
y(1:n,i) = t
END DO
WRITE(*,*) f(x,y)
CONTAINS
FUNCTION f(x,y) RESULT(z)
IMPLICIT NONE
REAL, DIMENSION(:,:) :: x,y
REAL, DIMENSION(SIZE(x,1),SIZE(x,2)) :: z
IF ( ANY(SHAPE(x) /= SHAPE(y)) ) THEN
WRITE(*,*) Argumentit eivt ole samanmuotoisia
STOP

174

Fortran 95/2003

ELSE
z = SIN(SQRT(x**2 + y**2))
END IF
END FUNCTION f
END PROGRAM taulukkofunktio

Funktio f palauttaa arvonaan kaksiulotteisen taulukon z, jonka koko saadaan selville standardifunktiolla SIZE. Funktio tarkastaa, ett taulukot x ja
y ovat saman kokoisia.
Palautettavan taulukon koko pit pysty laskemaan funktion argumenteista. Jos tm ei ole mahdollista, tulostaulukko pit palauttaa osoitinmuuttujia kytten, kuten kappaleen 11.11 funktiossa varaa (sivu 180).

11.8

Taulukkojen ksittely kokonaisuuksina

Fortran 95 -standardissa on paljon taulukko-operaatioita, joiden avulla taulukoita voidaan ksitell kokonaisuuksina. Tavalliset operaattorit (esimerkiksi +, , , sek loogiset operaattorit) suoritetaan taulukoille alkioittain. Mys useat Fortranin standardifunktiot (intrinsic function) ovat alkioittaisia, kuten esimerkiksi SIN, COS, SQRT jne. Lisksi kieless on taulukoiden
ksittelyyn tarkoitettuja sisisi apufunktioita, kuten matriisitulo (MATMUL)
sek matriisin alkioiden summan (SUM) tai tulon lasku (PROD).
Alla oleva aliohjelma laskee taulukon arvot sisltmien lukujen keskiarvon
ja keskihajonnan kytten taulukkofunktioita SIZE, SUM ja SQRT.
SUBROUTINE tilasto(arvot, keskiarvo, keskihajonta)
IMPLICIT NONE
REAL, DIMENSION(:), INTENT(IN) :: arvot
REAL, INTENT(OUT) :: keskiarvo, keskihajonta
REAL :: varianssi
INTEGER :: n
n = SIZE(arvot)
keskiarvo = SUM(arvot)/n
varianssi = SUM((arvot-keskiarvo)**2)/(n-1)
keskihajonta = SQRT(varianssi)
END SUBROUTINE tilasto

11.9

Taulukkojen ksittely standardifunktioilla

Taulukoiden ksittelyyn on Fortran 95 -standardissa useita standardifunktioita (katso mys lukua 13):
TRANSPOSE(a): Matriisin a transpoosi.
MATMUL(a,b): Matriisien a ja b matriisitulo.

11. Taulukot ja osoitinmuuttujat

175

DOT_PRODUCT(v1,v2): Vektoreiden v1 ja v2 sistulo.


SUM(array,dim,mask): Taulukon array alkioiden summa. Kuten edell mainittiin, niin argumentit dim ja mask ovat valinnaisia. Jos dim
on annettu, lasketaan vain tt indeksi vastaavat summat. Jos maskargumentti on annettu, kytetn taulukosta array summan laskemiseen vain alkioita, joiden indekseill taulukon mask vastaava arvo on
tosi.
PRODUCT(array,dim,mask): Taulukon alkioiden tulo.
MAXVAL(array,dim,mask), MINVAL(array,dim,mask): Taulukon alkioiden suurin ja pienin arvo.
MAXLOC(array,mask), MINLOC(array,mask): Suurimman ja pienimmn arvon paikat taulukossa.
COUNT(larray,dim): Tosien arvojen mr loogisessa taulukossa.
ANY(larray,dim): Tosi, jos jokin loogisen taulukon larray arvoista
on tosi.
ALL(larray,dim): Tosi, jos kaikki loogisen taulukon larray arvoista
ovat tosia.
LBOUND(array,dim): Taulukon indeksien alarajat.
UBOUND(array,dim): Taulukon indeksien ylrajat.
SHAPE(array): Taulukon muoto.
RESHAPE(vector,shape): Funktio palauttaa arvonaan taulukon, jonka
muoto on argumentin shape mrm. Taulukon arvot ovat perisin
argumentista vector.
SIZE(array,dim): Taulukon alkioiden lukumr.
ALLOCATED(array): Funktion arvo on tosi, jos dynaamisesti varattavalle taulukolle on varattu tilaa.
Seuraavassa esimerkiss alustamme taulukon matrix kyttmll funktiota
RESHAPE. Tmn jlkeen tulostamme matriisin alkioiden summan, matriisin
sarakesummat sek matriisin rivisummat.
PROGRAM matriisi
IMPLICIT NONE
INTEGER, PARAMETER :: n = 5
CHARACTER(LEN=*), PARAMETER :: form = (A,5F7.2)
REAL, DIMENSION(n,n) :: matrix
INTEGER :: i, j
matrix = RESHAPE( (/ ( (i-j, i = 1, n), j = 1, n) /), &
(/ n, n /) )
WRITE(*,form) Matriisin alkioiden summa on, SUM(matrix)
WRITE(*,form) Matriisin sarakesummat ovat, SUM(matrix,1)
WRITE(*,form) Matriisin rivisummat ovat, SUM(matrix,2)
END PROGRAM matriisi

Seuraavan esimerkin ensimminen suoritettava lauseke sijoittaa muuttujaan


n taulukon x positiivisten arvojen mrn. Toinen ja kolmas lauseke sijoittavat loogiseen muuttujaan l tiedon siit, onko joku taulukon alkioista negatiivinen.

176

Fortran 95/2003

REAL, DIMENSION(10,10) :: x
INTEGER :: n
LOGICAL :: l
n = COUNT( x > 0.0 )
l = ANY( x < 0.0 )
l = ALL( x >= 0.0 )

Rakenne WHERE(ehto) antaa mahdollisuuden valita taulukon alkioista ne,


joille halutaan tehd jokin operaatio. Seuraava ohjelmanosa asettaa taulukon x negatiiviset alkiot nolliksi.
WHERE( x < 0 )
x = 0
END WHERE

Saman asian voi tehd hiukan vhemmll kirjoittamisella kyttmll lausetta WHERE:
WHERE( x < 0 ) x = 0

WHERE-rakenteeseen voidaan liitt ELSEWHERE-haara:


WHERE( x /= 0.0 )
y = y / x
ELSEWHERE
y = 0.0
END WHERE

Fortran 95 -standardissa (toisin kuin Fortran 90:ss) useita WHERE-lauseita


voi olla siskkin:
WHERE (a >= 0)
WHERE (a /= 0)
a = 1.0/a
ELSEWHERE
a = HUGE(a)
ENDWHERE
ELSEWHERE
a = -HUGE(a)
ENDWHERE

Fortran sislt mys muutaman standardifunktion, joilla taulukoista voidaan eri tavoin valita haluttu joukko arvoja:
MERGE(truearray,falsearray,mask): Funktio palauttaa taulukon,
jonka alkiot valitaan argumentin mask mukaan siten, ett jos taulukon
mask vastinalkion arvo on tosi, alkio valitaan taulukosta truearray, ja
vastaavasti jos taulukon mask vastinalkion arvo on eptosi, alkio valitaan taulukosta falsearray.
SPREAD(array,dim,ncopies): Kopioidaan argumenttina annettua taulukkoa ncopies kertaa uuteen taulukkoon.
PACK(array,mask,vector): Tulostaulukkoon haetaan ne array-taulukon alkiot, joita vastaavat taulukon mask alkiot ovat tosia. Tulostaulukko on vektori (yksiulotteinen taulukko). Jos valinnainen argumentti

11. Taulukot ja osoitinmuuttujat

177

vector on annettu, tulostaulukon loppuun listn arvoja tst taulukosta, kunnes tulostaulukon koko on sama kuin taulukon vector.
UNPACK(vector,mask,array): Taulukon vector arvot sijoitetaan taulukon array paikalle siell, miss taulukon mask arvot ovat tosia.
CSHIFT(array,shift,dim): Siirretn taulukon alkioita argumentin
shift kertoma mr argumentin dim antamaan suuntaan. Taulukon
reunalta ulos menevt alkiot tulevat taulukon toiselta laidalta sisn.
EOSHIFT(array,shift,boundary,dim): Funktio toimii muuten samalla tavalla kuin funktio CSHIFT, paitsi ett taulukon rajalta ulos joutuvat
alkiot eivt kierr toiselle laidalle, vaan nm arvot otetaan valinnaisesta argumentista boundary (jos tt ei ole annettu, arvot asetetaan
nolliksi).

11.10

FORALL-rakenne

Fortran 95:een (mutta ei Fortran 90:een) sisltyy FORALL-lause ja -rakenne,


jotka laskevat joukon laskutoimituksia rinnakkain annetuilla indeksien arvoilla:
INTEGER, PARAMETER :: n = 100
INTEGER :: i, j
REAL, DIMENSION(n, n) :: a
...
FORALL (i = 1:n:2, j = 1:n:2, a(i,j) > 0)
a(i,j) = i + j
END FORALL
FORALL (i = 1:n) a(i,i) = i**2

Seuraavassa on esimerkki siskkisist DO-silmukoista:


DO i = 1, n
DO j = 1, m
A(i,j) = i + j
END DO
END DO

Voimme esitt saman FORALL-lauseena ja -rakenteena:


FORALL (i = 1:n, j = 1:m) a(i,j) = i + j
FORALL (i = 1:n, j = 1:m)
a(i,j) = i + j
END FORALL

FORALL mahdollistaa mys vinojen taulukkolohkojen ksittelyn:


FORALL (i = 1:n) a(i,i) = b(i)
FORALL (i = 2:n) a(i,i-1) = c(i)

178

Fortran 95/2003

Tss sijoitimme taulukon a lvistjlle vektorin yksiulotteisen taulukon b


alkioita ja lvistjn alle vektorin c alkioita. Jos n = 5, edellisten FORALLlauseiden tulos nytt seuraavalta:

b(1)

c(2) b(2)

c(3) b(3)

.
a(1:n,1:n) =

c(4) b(4)

c(5) b(5)
Normaali taulukkosyntaksi poimii taulukoista suorakulmaisen lohkon. Esimerkkin mrittelemme taulukon
REAL, DIMENSION(5,8) :: y

Tllin lauseke y(2:5:2, 4:6) poimii taulukosta seuraavan 23 -kokoisen


lohkon:

1
2
3
4
5

FORALL-rakenteessa voi olla useampi lause:


FORALL (i = 2:n-1, j = 2:n-1)
a(i,j) = 0.25*(a(i+1,j) + a(i-1,j) + a(i,j+1) + a(i,j-1))
b(i,j) = 1.0/(a(i+1,j+1)
END FORALL

Kytettviss on mys ehdollinen FORALL-rakenne:


FORALL (i = 1:n, j = 1:m, a(i) < 9.0 .AND. b(j) < 9.0)
c(i,j) = a(i) + b(j)
END FORALL

11.11

Dynaaminen muistinvaraus

Taulukoille voidaan varata tilaa staattisen varauksen lisksi ohjelman suorituksen aikana, jolloin taulukon koko voidaan mrt esimerkiksi ohjelman
sytteiden mukaan. Dynaamisesti varattava taulukko tytyy ilmoittaa kntjlle mreell ALLOCATABLE tai vaihtoehtoisesti mreell POINTER. Mre POINTER mrittelee osoitinmuuttujan, joista kerrotaan enemmn kappaleessa 11.13 (sivu 183). Mys proseduurien automaattiset taulukot, joista on
kerrottu kappaleessa 11.1, ovat dynaamisesti varattavia taulukoita.
Joissain tilanteissa, kuten esimerkiksi omien tietotyyppien mrittelyss,
ALLOCATABLE-mrett ei voi kytt, vaan on kytettv POINTER-mrett.
Seuraavassa on esimerkki mreiden kytst:

11. Taulukot ja osoitinmuuttujat

179

REAL, DIMENSION(:,:), ALLOCATABLE :: x


REAL, DIMENSION(:), POINTER :: y

Taulukon koot siis jtetn kokonaan mrittelemtt, vain taulukon dimensioiden mr annetaan mrittelyss kyttmll symbolia :. Ajonaikainen
tilanvaraus tehdn ALLOCATE-lauseella seuraavasti:
INTEGER :: allocstat
ALLOCATE(x(-n:n,0:m), y(n), STAT=allocstat)
IF (allocstat /= 0) STOP

Varattavan taulukon muoto annetaan muuttujan nimen perss sulkeissa.


STAT-osassa mrtty kokonaislukumuuttuja allocstat saa arvon nolla,
jos varaus onnistuu, ja nollasta poikkeavan arvon, jos varaus eponnistuu.
Jos valinnaista STAT-osaa ei ole annettu, ohjelman suoritus pttyy muistin
varauksen syyst tai toisesta eponnistuessa.
DEALLOCATE-lauseella voidaan vapauttaa ALLOCATE-lauseessa taulukolle varattu tila. DEALLOCATE-lauseen syntaksi on
DEALLOCATE(x, STAT=deallocstat)

STAT-lausekkeessa mainittuun muuttujaan ptevt samat kommentit kuin


ALLOCATE-lauseen yhteydesskin.
Voimme selvitt ALLOCATED(x)-funktiolla, onko muuttujalle x varattu tilaa.
Voimme siis testata ohjelmassamme, onko taulukolle x jo varattu tilaa, ja
jos ei, niin tehd varauksen seuraavaan tapaan:
IF (.NOT. ALLOCATED(x)) ALLOCATE(...)

Proseduurin sisll paikalliseen muuttujaan varattu tila on yleens vapautettava ennen proseduurista poistumista: paikalliset muuttujat voivat, toteutuksesta riippuen, menett tilansa proseduurista poistuttaessa. Nm
muuttujat varataan yleens joka kerta uudestaan ohjelman yllpitmst
pinomuistista.
Tarvittaessa muuttujan saa kyllkin silyttmn arvonsa proseduurikutsujen vlill kyttmll SAVE-mrett. Fortran 95 -kieless, Fortran 90:st
poiketen, paikallisiin muuttujiin ALLOCATE-lauseella varattu tila vapautetaan
automaattisesti, jos nille ei ole annettu SAVE-mrett. Hyv tapa kuitenkin
on kytt eksplisiittisesti DEALLOCATE-lausetta.
Jos proseduuriin vlitetn POINTER-tyyppinen taulukko, joka varataan dynaamisesti, alkaa taulukon indeksointi samasta arvosta kuin vlitettvss
POINTER-taulukossa. Vertaamme POINTER- ja ALLOCATABLE-taulukkojen vlittmist eri tavalla mriteltyihin funktioihin:
PROGRAM ptr_testi
IMPLICIT NONE
REAL, DIMENSION(:), POINTER :: a
REAL, DIMENSION(:), ALLOCATABLE :: b
ALLOCATE (a(-1:1), b(-1:1))
WRITE (*,(A,5(I4))) Alarajat: , lb_1(a), lb_1(b), &
lb_2(a), lb_3(a), lb_3(b)

180

Fortran 95/2003

CONTAINS
INTEGER FUNCTION lb_1(x) RESULT(lb)
IMPLICIT NONE
REAL, DIMENSION(:) :: x
lb = LBOUND(x,1)
END FUNCTION lb_1
INTEGER FUNCTION lb_2(x) RESULT(lb)
IMPLICIT NONE
REAL, DIMENSION(:), POINTER :: x
lb = LBOUND(x,1)
END FUNCTION lb_2
INTEGER FUNCTION lb_3(x) RESULT(lb)
IMPLICIT NONE
REAL, DIMENSION(2:) :: x
lb = LBOUND(x,1)
END FUNCTION lb_3
END PROGRAM ptr_testi

Funktio LBOUND palauttaa indeksin alarajan. Ohjelma tulostaa seuraavaa:


Alarajat:

-1

Funktiossa lb_1 alaraja on oletusarvo eli 1. Funktioon lb_2 vlitetn osoitintaulukko, ja indeksointi alkaa esimerkiss 1:st. Funktiossa lb_3 on
puolestaan mritelty, ett indeksointi aloitetaan aina arvosta 2.
Jos proseduuri varaa muistia osoitinmuuttujaan (POINTER), varattu muistitila voidaan palauttaa kutsuvalle proseduurille, jolloin sen tehtvksi j
hoitaa mys muistitilan vapauttaminen.
Seuraavassa ohjelmassa funktio varaa palauttaa osoitinmuuttujan, jonka
voi sijoittaa toiseen osoitinmuuttujaan komennolla => (kappale 11.13 sivulla 183):
PROGRAM varaus
IMPLICIT NONE
REAL, DIMENSION(:), POINTER :: a
a => varaa(1000)
WRITE(*,*) Varatun taulukon koko=, SIZE(a)
DEALLOCATE(a)
CONTAINS
FUNCTION varaa(n) RESULT(p)
IMPLICIT NONE
INTEGER :: n
REAL, DIMENSION(:), POINTER :: p
INTEGER :: allocstat
ALLOCATE(p(n), STAT=allocstat)
IF (allocstat > 0) CALL varausvirhe(allocstat)
CONTAINS
SUBROUTINE varausvirhe
WRITE (*,*) Muistin varaus eponnistui,&
& paluukoodi:, allocstat
STOP
END SUBROUTINE varausvirhe

11. Taulukot ja osoitinmuuttujat

181

END FUNCTION varaa


END PROGRAM varaus

Seuraavassa on esimerkki dynaamisen muistinvarauksen kytst:


PROGRAM pisteparit_n
! Lasketaan odotusarvo n-ulotteisesta yksikkkuutiosta
! satunnaisesti valittujen pisteiden etisyydelle.
IMPLICIT NONE
INTEGER, PARAMETER :: reaali = SELECTED_REAL_KIND(12)
REAL (KIND=reaali), DIMENSION(:,:), ALLOCATABLE :: a, b
REAL (KIND=reaali) :: t = 0.0, keskiarvo
INTEGER :: m, n, allocstat
WRITE (*,*) Anna dimensio ja pisteparien lkm:
READ (*,*) m, n
WRITE (*,*) Dimensio:, m, pistepareja:, n
IF (m > 0 .AND. n > 0) THEN
ALLOCATE(a(m,n), b(m,n), STAT=allocstat)
IF (allocstat > 0) CALL varausvirhe
CALL RANDOM_NUMBER(a)
CALL RANDOM_NUMBER(b)
t = SUM(SQRT(SUM((a-b)**2, DIM=1)))
keskiarvo = t/n
WRITE (*,*) Etisyyden keskiarvo:, keskiarvo
ELSE
WRITE (*,*) Lukumr tai dimensio negatiivinen!
END IF
CONTAINS
SUBROUTINE varausvirhe
WRITE (*,*) Muistin varaus eponnistui,&
& paluukoodi:, allocstat
STOP
END SUBROUTINE varausvirhe
END PROGRAM pisteparit_n

Seuraavassa on ohjelman kyttesimerkki:


Anna dimensio ja pisteparien lkm:
2 100000
Dimensio: 2 pistepareja: 100000
Etisyyden keskiarvo:
0.5211873481557983

Jos tehtvn dimensio tai pisteparien lukumr valitaan suureksi, tarvitaan edellisess ohjelmassa hyvin paljon keskusmuistia. Jos dimensio on
esimerkiksi 5 ja pistepareja on 100 000, varataan ohjelmassa muistitilaa
2 5 100 000 = 106 reaaliluvun verran. Jos reaaliluku vie tilaa esimerkiksi kahdeksan tavua, varataan ohjelmassa tss tapauksessa keskusmuistia
8 megatavua.
Fortran 95 vapauttaa automaattisesti proseduureissa varatut paikalliset dynaamiset taulukot:
SUBROUTINE testi(n)
IMPLICIT NONE
INTEGER :: n

182

Fortran 95/2003

REAL, ALLOCATABLE :: x(:)


ALLOCATE(x(n))
...
END SUBROUTINE testi

Siis lauseen ALLOCATE jlkeen ei en vlttmtt tarvita DEALLOCATE-ksky.

11.12

Esimerkki: Erastotheneen seula

Erastotheneen seula on klassinen algoritmi alkulukujen lytmiseksi. Vaikka nykyisin tunnetaan paljon tehokkaampiakin menetelmi, pystytn tll
menetelmll etsimn esimerkiksi miljoonaa pienemmt alkuluvut kohtalaisen nopeasti. Seula perustuu yksinkertaiseen ideaan: poistetaan lukutaulukosta ensin kahdella jaolliset luvut, sitten kolmella jaolliset luvut, sitten
viidell jaolliset luvut jne.
Kytmme Erastotheneen seulan toteuttamiseen taulukkosyntaksia ja dynaamista muistinvarausta. Ohjelma kysyy alkulukujen ylrajaa ja seuloo tmn jlkeen alkuluvut taulukkoon numerot.
PROGRAM Erastotheneen_seula
IMPLICIT NONE
INTEGER, PARAMETER :: iso = SELECTED_INT_KIND(9)
INTEGER(iso) :: max_nro
INTEGER(iso), DIMENSION(:), ALLOCATABLE :: numerot
INTEGER(iso) :: i, alku_lkm
INTEGER :: ok
WRITE (*,*) Syt alkulukujen ylraja:
READ (*,*) max_nro
IF (max_nro > 2) THEN
ALLOCATE (numerot(max_nro), STAT=ok)
IF (ok /= 0) THEN
WRITE (*,*) Muistinvaraus eponnistui!
STOP
END IF
! Taulukon alustus luvuilla 0, 2, 3, 4, ...
numerot = (/ 0, (i, i = 2, max_nro) /)
DO i = 2, FLOOR(SQRT(REAL(max_nro)))
IF (numerot(i) /= 0) THEN
numerot(2*i:max_nro:i) = 0
ENDIF
END DO
! Lasketaan alkulukujen lkm
alku_lkm = COUNT(numerot /= 0)
! Kertn alkuluvut taulukon alkuun
numerot(1:alku_lkm) = PACK(numerot, numerot /= 0)
! Tulostus

11. Taulukot ja osoitinmuuttujat

183

WRITE (*,(A,I10,A,I10)) Lysin , alku_lkm, &


kpl alkulukuja, jotka olivat <=, max_nro
WRITE (*,(5I10)) numerot(1:alku_lkm)
ELSE
WRITE (*,*) Ylrajan tytyy olla > 2!
END IF
END PROGRAM Erastotheneen_seula

Silmukassa
DO i = 2, FLOOR(SQRT(REAL(max_nro)))

ei tarvitse kyd lpi kaikkia lukuja maksimiarvoon max_nro asti, sill mak
simiarvon nelijuurta max_nro suuremmat luvut tulisivat muuten kyty
lpi kahteen kertaan.
Taulukko-operaatio COUNT laskee .TRUE.-arvojen lukumrn ja operaatio
PACK pakkaa taulukosta halutut alkiot.
Ohjelma tulostaa kaikki lydetyt alkuluvut, joten maksimiarvoksi ei kannata
sytt kovin suurta lukua:
% f90 seula.f90
% ./a.out
Syt alkulukujen ylraja:
100
Lysin
25 kpl alkulukuja, jotka olivat <=
2
3
5
7
11
13
17
19
23
29
31
37
41
43
47
53
59
61
67
71
73
79
83
89
97

100

Ohjelma tulostaa vain alkulukujen lukumrn, jos poistat lauseen (tai muutat sen huutomerkill kommentiksi)
WRITE (*,(5I10)) numerot(1:alku_lkm)

Seuraavassa kyttesimerkki:
Syt alkulukujen ylraja:
1000000
Lysin
78498 kpl alkulukuja, jotka olivat <=

1000000

Laskentaan menee aikaa korkeintaan muutama sekunti.

11.13

Osoitinmuuttujat

Osoitinmuuttujalla voidaan antaa jollekin muuttujalle, taulukon osalle tai


rakenteisen tietotyypin osalle toinen nimi eli alias. Toisaalta osoitinmuuttuja voidaan kohdistaa uuteen muistialueeseen, jolloin sit kytetn kuten
luvussa 11.11 ksiteltyj dynaamisesti varattavia taulukkoja. Osoitinmuuttuja mritelln antamalla mre POINTER.

184

Fortran 95/2003

Osoitinmuuttujien trkeimpi kytttarkoituksia ovat dynaamiset tietorakenteet kuten lista- ja puurakenteet sek muuttuvan mittaiset taulukot omissa tietotyypeiss.
Jotta osoitinmuuttujaa voitaisiin kytt, tytyy se ensin saada osoittamaan
johonkin muistitilaan. Uuden muistialueen varaaminen ja thn osoittaminen tehdn siis ALLOCATE-lauseella, mink jlkeen osoittimen kytt on
oleellisesti samanlaista kuin tavallisten muuttujienkin.
REAL, DIMENSION(:,:), POINTER ::
ALLOCATE (x(100,100))
x(3:6, 3:6) = 0.0

Toisaalta osoitinmuuttujan voi laittaa osoittamaan jo varattuun muistitilaan


osoitinsijoituksella (merkint =>).
REAL, POINTER :: osoitin
REAL, DIMENSION(10), TARGET :: taulukko
osoitin => taulukko(4)

Osoitinmuuttujan osoittamalle kohteelle tytyy muuttujamrittelyss antaa mre TARGET (tai kohteen tytyy olla toinen osoitin). Tmn lisksi
osoittimen ja kohteen alkioiden tyyppien tulee olla lajiparametri mukaan
lukien samat. Taulukoiden yhteydess taulukoiden ulottuvuuksien mrien
on oltava samat.
Kun osoitinmuuttuja on tavalla tai toisella saatu osoittamaan johonkin varattuun muistitilaan, se kyttytyy kuten tavallinenkin muuttuja. Arvon sijoittamiseksi osoitinmuuttujan kohteeseen kytetn tavallista sijoituslausetta.
Jos osoitinmuuttuja osoitin on alustettu kuten edell, niin lauseke
osoitin = 5

muuttaa taulukon taulukko neljnnen alkion arvoksi luvun 5.


Taulukkoon viittaavan osoitinmuuttujan mrittelyss ei voi kytt mrett ALLOCATABLE, vaan tm sisltyy mreeseen POINTER. Muista osoitintaulukoiden ja ALLOCATABLE-mreell mriteltyjen taulukoiden eroista
mainittakoon:
Omien tietotyyppien mrittelyiss ALLOCATABLE-mrett ei voi kytt, vaan tllin on kytettv POINTER mrett.
Funktio ei voi palauttaa arvonaan ALLOCATABLE-mreell mritelty
taulukkoa. Funktio voi palauttaa arvonaan taulukon, jolle on varattu
tilaa automaattisesti funktion mrittelyss. Esimerkiksi funktio
FUNCTION n_yksi(n) RESULT(a)
IMPLICIT NONE
INTEGER :: n
REAL, DIMENSION(n) :: a
a(1:n) = 1
END FUNCTION n_yksi

palauttaa arvonaan taulukon, jonka arvot on alustettu ykksiksi.


Funktio voi palauttaa mys POINTER-mreen avulla luotuja dynaamisia
taulukoita.

11. Taulukot ja osoitinmuuttujat

185

ALLOCATABLE-mrett ei voi kytt proseduurin muodolliselle parametrille, mutta POINTER-mrett voi kytt.
Seuraavassa esimerkiss osoitinmuuttujia kytetn eri tavoin. Aluksi mrittelemme muuttujat ja alustamme taulukon a:
REAL, DIMENSION(5), TARGET :: a
REAL, POINTER :: b, d
REAL, POINTER, DIMENSION(:) :: c
a = (/ (i, i = 1, 5) /)

Sijoitamme osoittimeen b taulukon a toisen alkion osoitteen:


b => a(2)
WRITE(*,*) b

WRITE-lauseen tulostus on
2.000000

Seuraavan lausekkeen jlkeen osoitinmuuttuja c on alias taulukon a alkioille


3 ja 5.
c => a(3:5:2)
WRITE(*,*) c

WRITE-lauseen tulostus on
3.000000

5.000000

Edellisi osoitinmuuttujien b ja c kyttesimerkkej on havainnollistettu


kuvassa 11.3.

1 2 3 4 5
a
b
c
1 2
Kuva 11.3: Osoitinmuuttujien kyttesimerkki.

Kun sijoitamme osoitinmuuttujaan b jonkin arvon, muutamme osoittimen


kohteen arvoa:
b = 1
WRITE(*,*) a(2)

WRITE-lauseen tulostus on
1.000000

POINTER-mreell mriteltyyn osoitinmuuttujaan voi varata muistitilaa


dynaamisesti:
ALLOCATE(c(5))

186

Fortran 95/2003

d => c(2)
d = 5
WRITE(*,*) c(2)

Lause d
Lause d
si luvun
tulostus

=> c(2) saa osoitinmuuttujan d osoittamaan muuttujaan c(2).


= 5 puolestaan asettaa muuttujan d osoittaman muuttujan arvok5, joten tss muutetaan alkion c(2) arvoa. Siten WRITE-lauseen
on
5.000000

Sijoitusoperaatioiden => ja = eron ymmrtminen on trke osoitinmuuttujia kytettess. Vr sijoituslause johtaa vaikeasti lydettviin virhetilanteisiin. Muistitilan varausta ja osoittimen d kytt on havainnollistettu
kuvassa 11.4.

1 2 3 4 5
c
d
Kuva 11.4: Osoitinmuuttujaan c on varattu tilaa dynaamisesti. Osoitinmuuttuja d
osoittaa taulukon c toiseen alkioon.

Taulukko-osoittimen alkioille ei voi yksitellen kytt osoitinsijoitusta, toisin sanoen taulukko-osoitinta ei voi osoituksessa indeksoida. Tmn rajoituksen voi kiert mrittelemll taulukon, jonka alkiot ovat itse mritelty toissijaista tietotyyppi. Tm toissijainen tietotyyppi koostuu puolestaan yhdest komponentista, joka on osoitinmuuttuja ensisijaiseen rakenteen sisltvn tietotyyppiin. Osoitinmuuttujien tllaista kytt selvent
luvun 14.7 sukupuuesimerkki sivulla 261.
Funktio ASSOCIATED(osoitin) palauttaa arvonaan tiedon siit, onko osoittimelle annettu jokin kohde. Tlle funktiolle ei kuitenkaan voi antaa argumenttina osoitinta, jota ei ole alustettu tavalla tai toisella. Ennen osoittimen kohdistamista se on epmrisess tilassa. Sit ei siis voi vlitt ASSOCIATEDlausekkeelle, jotta voisimme tarkistaa onko se kohdistettu. Tst ongelmasta
psemme lauseella NULLIFY(osoitin), joka alustaa osoittimen nollattuun
tilaan, muttei kohdista osoitinta mihinkn kohteeseen.
Fortran 95 sislt NULLIFY-lauseen lisksi funktion NULL(), jolla osoitinmuuttuja voidaan mys alustaa:
osoitin => NULL()

Funktiota NULL voi kytt muuttujamrittelyiss alustimena sek rakenteiden oletusarvojen mrityksiss, toisin kuin NULLIFY-lausetta.
Funktiokutsu ASSOCIATED(a,b) kertoo, osoittaako osoitinmuuttuja a kohteeseen b. Jos sek a ett b ovat osoitintyyppi, kertoo funktiokutsu, osoittavatko ne samaan kohteeseen.

11. Taulukot ja osoitinmuuttujat

11.14

187

Esimerkki: listarakenne

Tss luvussa teemme ohjelman, joka ksittelee linkitetty listaa. Listan alkiot ovat tss esimerkiss kaksi kokonaislukua: avain ja lkm. Mrittelemme listan siten, ett listtess listaan alkioita ne tallentuvat avaimen
mukaan suuruusjrjestyksess. Lisksi listassa on tieto siit, kuinka monta
kertaa kukin luku on talletettu listaan.
MODULE listamoduuli
IMPLICIT NONE
TYPE alkiotyyppi
INTEGER :: avain, lkm
END TYPE alkiotyyppi
TYPE listatyyppi
TYPE(alkiotyyppi) :: alkio
TYPE(listatyyppi), POINTER :: seuraava
END TYPE listatyyppi
CONTAINS
Aliohjelman lisaa mrittely.
END MODULE listamoduuli

Vaikka tyyppimrittelyss listatyyppi kytetn tyyppi itsen, on tm rakenne luvallinen: rakenteinen tyyppi voi sislt osoittimen omaan
tyyppiins.
Aliohjelma, joka lis alkion listaan, voitaisiin mritell seuraavasti:
SUBROUTINE lisaa(lista, luku)
! Listn kokonaisluku listaan, siten ett alkiot pysyvt
! avaimen mukaan kasvavassa jrjestyksess.
IMPLICIT NONE
TYPE(listatyyppi), POINTER :: lista
INTEGER :: luku
TYPE(listatyyppi), POINTER :: edellinen, uusi, nykyinen
nykyinen => lista
NULLIFY(edellinen)
DO WHILE( ASSOCIATED(nykyinen) )
IF ( luku > nykyinen % alkio % avain ) THEN
! Jos listtv luku on suurempi kuin listan
! nykyalkion avain mennn listassa eteenpin
edellinen => nykyinen
nykyinen => nykyinen % seuraava
ELSE IF ( nykyinen % alkio % avain == luku ) THEN
! Listassa on jo tm arvo
nykyinen % alkio % lkm = nykyinen % alkio % lkm + 1
EXIT
ELSE
! Oikea paikka lytyi. Listn listaan uusi alkio
ALLOCATE( uusi )
IF ( ASSOCIATED(edellinen) ) THEN
edellinen % seuraava => uusi

188

Fortran 95/2003

ELSE
lista => uusi
END IF
uusi % alkio % avain = luku
uusi % alkio % lkm = 1
uusi % seuraava => nykyinen
EXIT
END IF
END DO
! Jos lista tuli kyty kokonaisuudessaan lpi, uusi
! arvo kuuluu listan loppuun. Jos listaa ei alunperin
! ollut olemassa, luodaan se.
IF ( .NOT. ASSOCIATED(nykyinen) ) THEN
IF ( ASSOCIATED(edellinen) ) THEN
ALLOCATE(uusi)
edellinen % seuraava => uusi
ELSE
ALLOCATE( lista )
uusi => lista
END IF
uusi % alkio % avain = luku
uusi % alkio % lkm = 1
NULLIFY( uusi % seuraava )
END IF
END SUBROUTINE lisaa

Lista pttyy siis osoittimeen, jolle ASSOCIATED-funktio palauttaa arvon eptosi. Kun listaan luodaan uusi alkio, sen seuraaja tytyy alustaa NULLIFYfunktiolla
NULLIFY( uusi % seuraava )

Aliohjelmaa lisaa tytyy ensimmisell kerralla kutsua muuttujalla, joka


on alustettu NULLIFY-funktiolla. Kun lista on luotu, sen lpikynti jrjestyksess tuottaa luvut suuruusjrjestyksess pienimmst suurimpaan:
PROGRAM listatesti
USE listamoduuli
IMPLICIT NONE
TYPE(listatyyppi), POINTER :: lista
INTEGER :: i,n
REAL :: x
NULLIFY(lista)
DO i = 1,5
CALL RANDOM_NUMBER(x)
n = 10 * x
WRITE( *,* ) Lis: , n
CALL lisaa(lista,n)
END DO
WRITE(*,*) Listan sislt:
DO WHILE( ASSOCIATED(lista) )
WRITE(*,*) Alkio: ,lista % alkio % avain, &
Lukumr: ,lista % alkio % lkm

11. Taulukot ja osoitinmuuttujat

189

lista => lista % seuraava


END DO
END PROGRAM listatesti

Seuraavassa on ohjelman listatesti tuottama tulostus:


Lis:
Lis:
Lis:
Lis:
Lis:

1
9
8
4
4

Listan sislt:
Alkio:
Alkio:
Alkio:
Alkio:

1
4
8
9

Lukumr:
Lukumr:
Lukumr:
Lukumr:

1
2
1
1

Luvussa 14 (sivu 234) on esimerkkein osoitinmuuttujien kytst esitetty


binripuun ja sukupuun ksittely.

11.15

Yhteenveto

Taulukon mrittelyss kytetn mrett DIMENSION:


tyyppi, DIMENSION(indeksien rajat) :: muuttujien_nimet

Nin mritelty taulukko on tunnetun muotoinen taulukko.


Aliohjelmaan taulukoita vlitettess on (yksiulotteisen) taulukon mrittely
seuraavan nkinen:
tyyppi, DIMENSION([alaraja]:[ylraja]) :: muuttujan_nimi

Tm mrittely tuottaa ns. oletetun muotoisen taulukon.


Taulukoille ja osoitinmuuttujille voi varata tilaa dynaamisesti ohjelman suorituksen aikana. Dynaamisesti varattavan taulukon mrittely on seuraavaa
muotoa:
tyyppi, DIMENSION(:,[:,...,:]), ALLOCATABLE :: muuttujan_nimi

Tm on ns. mukautuvan muotoinen taulukko. Dynaaminen muistin varaus


tehdn funktiolla
ALLOCATE(muuttujan_nimi([alaraja:]ylraja] &
[, STAT=muuttujan_nimi])

Varatun muistitilan voi vapauttaa funktiolla


DEALLOCATE(muuttujan_nimi[, STAT=muuttujan_nimi])

Nollan kokoinen taulukko on luvallinen Fortranissa. Esimerkiksi sijoitus


x(1:0) = 3

190

Fortran 95/2003

ei tee mitn. Toisaalta taulukot x(1:0,1:2) ja x(1:0,1:3) eivt ole samanmuotoiset (conformable), koska ensimmisen taulukon koko on 0 2 ja
toisen 0 3.
Taulukkoa voi indeksoida kokonaislukuvakiolla, skalaarimuuttujalla tai kokonaislukuvektorilla sek rakenteella [alku][:loppu][:lisys].
Osoitinmuuttuja luodaan POINTER-mreell:
tyyppi, POINTER :: muuttujan nimi

Osoitinmuuttujan kohteena olevalle muuttujalle tulee antaa mre TARGET:


tyyppi, TARGET :: muuttujan nimi

Tmn lisksi osoittimen ja sen kohteen on oltava samaa tyyppi, sek taulukoiden osalta saman muotoisia.
Trke osoitinmuuttujien kyttkohde ovat dynaamiset tietorakenteet kuten listat (kappale 11.14 sivulla 187) ja puurakenteet. Binripuun toteutusta ksittelee kappale 14.6 sivulla 256 ja sukupuun toteutusta kappale 14.7
sivulla 261.

Harjoitustehtvi
1. Onko seuraava mrittely oikein?
REAL DIMENSION(1:3,2:3) :: aa

2. Mrittele kokonaislukutaulukko taulu, jossa on 3 rivi ja 4 saraketta.


3. Muunna seuraava ohjelma siten, ett siin kytetn DO-silmukoiden
sijaan taulukkosyntaksia.
PROGRAM taulukko
IMPLICIT NONE
INTEGER, PARAMETER :: n = 10
REAL :: a
REAL, DIMENSION(n) :: b
REAL, DIMENSION(n,n) :: c, d
INTEGER :: i, j
CALL RANDOM_NUMBER(b)
CALL RANDOM_NUMBER(c)
WRITE (*,*) b
DO i = 1,n
DO j = 1,n
d(j,i) = 1.0 - c(j,i)
END DO
END DO
DO j = 2, n
a = b(j) - b(j-1)
DO i = 2, n

11. Taulukot ja osoitinmuuttujat

191

c(i,j) = a*c(i-1,j) + d(i,j)


END DO
END DO
WRITE (*,*) Vastaus:, c(n,n)
END PROGRAM taulukko

4. Mrittele kaksi osoitinmuuttujaa. Aseta toinen osoittamaan yksiulotteiseen reaalilukutaulukkoon


REAL, DIMENSION(10) :: a

ja toinen saman taulukon kuudenteen alkioon.


5. Kyt osoitinmuuttujia sijoittamaan edellisen tehtvn taulukon a jrjestysluvultaan parillisiin alkioihin arvo 1.0 ja parittomiin alkioihin
arvo 1.0. Taulukon arvojen tulee siten olla seuraavan listan mukaisia:
(1.0, 1.0, 1.0, 1.0, . . . ).
6. Tee aliohjelma, joka palauttaa osoitinmuuttujassa halutun kokoisen
taulukon. Anna taulukon koko aliohjelman argumenttina. Voit kytt
aliohjelmaa esimerkiksi seuraavasti:
CALL varaa(p, n)

7. Lis tss luvussa mriteltyyn listojen ksittelyyn aliohjelma, joka


poistaa listasta alkion, jonka avain annetaan:
CALL poista(lista,luku)

8. Alkulukuteoreeman mukaan
lim

(n)
= 1,
n/ ln n

miss (n) on kokonaislukua n pienempien alkulukujen lukumr.


Tutki osamrn (n)/(n/ ln n) kyttytymist, kun n on k 105 , k =
1, 2, . . . , 20.
9. Kirjoita siskkiset DO-silmukat, joilla lasketaan matriisitulo
cjk =

aji bik , j = 1, . . . , l, k = 1, . . . n,

i=1

miss matriisi A on kooltaan lm ja B on kooltaan mn. Testaa koodisi


nopeutta MATMUL-standardialiohjelmaan verrattuna. Onko silmukoiden
jrjestyksell merkityst laskentanopeuteen? Voit lisksi testata BLASkirjaston matriisitulorutiinin SGEMM nopeutta [Haa98, HHL+ 02].
10. Laske matriisin transpoosi aliohjelmalla TRANSPOSE ja vertaa nopeutta
itse kirjoittamaasi koodiin. Onko silmukoiden jrjestyksell merkityst
laskentanopeuteen?

192

Fortran 95/2003

12

Tiedon sytt ja tulostus

Fortran 95:ss on monipuoliset mahdollisuudet tiedon syttn ja tulostamiseen. Aluksi esittelemme yksinkertaiset sytt- ja tulostuslauseet, jotka
muotoilevat tulostuksen automaattisesti. Sen jlkeen ksittelemme syttja tulostuslauseiden yleist rakennetta ja muotoilukoodeja. Kerromme mys
tiedostojen ksittelyst, binrisest sytst ja tulostuksesta sek suorasaantitiedostoista. Lopuksi kerromme muun muassa merkkijonoihin kirjoittamisesta sek virhetilanteiden ksittelyst.

12.1

Sytt ja tulostus

Sytt ja tulostus on olennainen osa jokaista tietokoneohjelmaa. Ohjelma


voi kysy lhttietoja nppimistlt ja palauttaa laskentatuloksia nytlle.
Ohjelma voi mys lukea tiedostoista ja kirjoittaa tiedostoihin, tai ohjelma
voi vuorovaikuttaa ympristns kanssa muulla tavoin (esimerkkin prosessinohjaus).
Tietokoneohjelmat lukevat sytttietonsa syttlauseiden avulla. Tulostuslauseilla voidaan kirjoittaa laskentatuloksia kuvaruudulle tai tiedostoihin.
Ohjelman sisll voidaan avata tiedostoja lukemista tai kirjoittamista varten. Kuvassa 12.1 havainnollistetaan tiedonsiirtoa.
Kun siirrt ohjelmaa toiseen tietokonearkkitehtuuriin, voit joutua muuttamaan sytt- ja tulostuslauseita. Ohjelman selkeyden vuoksi nm lauseet
on syyt kirjoittaa selvsti erilleen muusta ohjelmakoodista, jolloin niiden
lytminen ja muuttaminen on helppoa. Oman itsenisen sytt- ja tulostusmoduulin tekeminen saattaa olla paras tapa tehd helposti siirrettv
koodia.
Kirjoittaessasi ohjelmaa voit aluksi kytt yksinkertaisia, tiedot automaattisesti muotoilevia sytt- ja tulostuslauseita. Ohjelman toiminnan varmistamiseksi voit kytt tilapisi tulostuslauseita, joilla varmistutaan vlitulosten oikeellisuudesta. Poista nm lopullisesta ohjelmaversiosta ja kiinnit lisksi huomiota tulostuksen muotoiluun. Hyvn ohjelmointityyliin kuuluu lisksi tarkistaa sytttietojen oikeellisuus sek varautua sytn ja tulostuksen virhetilanteisiin. Ohjelman virheit voi paikantaa mys erityisten
virheenjljitysohjelmien (debugger) avulla.

12. Tiedon sytt ja tulostus

Nppimist
Tiedosto levyll
jne.

Nyttpte
Paperitulostin
Tiedosto levyll
jne.

193

Sytt

INTEGER
REAL
COMPLEX
LOGICAL
CHARACTER

Skalaarit
Taulukot
Rakenteiset tyypit

Tulostus

INTEGER
REAL
COMPLEX
LOGICAL
CHARACTER

Skalaarit
Taulukot
Rakenteiset tyypit

Kuva 12.1: Tiedon siirtyminen sytss ja tulostuksessa.

12.2

Yksinkertaiset sytt- ja tulostuslauseet

Fortranin sytt- ja tulostuslauseet READ ja PRINT tulostavat ja lukevat oletusarvoisesti ptteelt. Lauseiden kytt on esitelty seuraavassa esimerkiss:
PROGRAM kulmikas
IMPLICIT NONE
REAL :: alpha
PRINT *, Anna kulma alpha
READ *, alpha
PRINT *, Kulman sini ja kosini ovat , &
SIN(alpha), COS(alpha)
END PROGRAM kulmikas

Ohjelman ajo voisi nytt esimerkiksi tlt:


Anna kulma alpha
1.5152
Kulman sini ja kosini ovat 0.9984549284 0.5556767061E-01

Ohjelmassa symboli * ilmaisee, ett kntj tulkitsee syttvirtaa automaattisesti syttmuuttujien tyyppien mukaan ja muotoilee tulostuksen automaattisesti. Esimerkiss annettiin PRINT-lauseelle argumentiksi lista erityyppisi muuttujia. Mys sytss ohjelma voi lukea useille muuttujille
arvot samalla READ-lauseella. Perkkiset sytettvt arvot erotetaan vlilynnill tai pilkulla, tai ne voi mys kirjoittaa eri riveille.
Edell esitettyjen yksinkertaisten READ- ja PRINT-lauseiden sijaan kannattaa
useimmiten kytt lauseita READ ja WRITE seuraavaan tapaan:
PROGRAM kulmikas
IMPLICIT NONE
REAL :: alpha
WRITE (*,*) Anna kulma alpha

194

Fortran 95/2003

READ (*,*) alpha


WRITE (*,*) Kulman sini ja kosini ovat , &
SIN(alpha), COS(alpha)
END PROGRAM kulmikas

Tm ohjelma toimii tsmlleen samoin kuin edellinen. WRITE- ja READlauseissa ensimminen symboli * ilmaisee, ett kytetn oletusarvoisia
sytt- ja tulostuskanavia ja toinen symboli * viittaa oletusmuotoiluun.

12.3

Listan ohjaama muotoilu

Edell esitellyt sytt- ja tulostuslauseet olivat esimerkkej oletusmuotoilusta eli listan ohjaamasta muotoilusta (list-directed formatting), josta kerromme tss hieman tarkemmin. Myhemmin kerromme sarakesidonnaisesta
sytst ja tulostuksesta, jota kytetn muotoilukoodien avulla.

12.3.1 Listan ohjaama sytt


Listan ohjaamassa sytss (symboli * syttlauseissa) tietokone lukee ptteelt tietoa niin kauan, kunnes READ-lauseessa esitetyt muuttujat ovat saaneet arvonsa tai kunnes tulee virhetilanne. Yksi READ-lause voi tten lukea
arvoja usealta syterivilt. Toisaalta READ-lause lukee aina kokonaisen rivin,
joten viimeisten sytttietojen jlkeen voi laittaa vapaamuotoisia tekstikommentteja.
Perkkiset sytteet erotetaan joko vlilynneill tai pilkuilla. Perkkisten
pilkkujen vliss ajatellaan olevan tyhj arvo, joka ei muuta vastaavan muuttujan arvoa. Sytteell / lopetetaan READ-lauseen suoritus, jolloin syttlistassa jljell olevien muuttujien arvoja ei muuteta.
Listan ohjaamassa sytss kokonais- ja reaaliluvut annetaan sellaisenaan,
kompleksiluvut suluissa olevina lukupareina, merkkijonot heittomerkkien
() tai lainausmerkkien (") sisll ja loogiset muuttujat arvoina T (tosi) tai F
(eptosi). Useita perkkisi numeerisia arvoja voi sytt mys tyyliin r*v ,
miss r on toistokerroin ja v on arvo.
Eriss tapauksissa voi merkkijonolausekkeet sytt sellaisenaan ilman
heittomerkkej. Ehtona tlle on, ett
merkkijonolauseke ei sisll vlilyntej, pilkkuja tai vinoviivaa /
merkkijonolauseke ei jatku usealle riville
lausekkeen ensimminen merkki ei ole heittomerkki tai lainausmerkki "
merkkijonon alussa ei ole numeroa, jonka perss on asteriski *.
Seuraavassa ohjelmanosassa luetaan samalla READ-lauseella useita eri tyyppisi muuttujia:

12. Tiedon sytt ja tulostus

195

INTEGER :: i
REAL :: x
CHARACTER(LEN=10) :: nimi
COMPLEX :: z
LOGICAL :: totuus
READ (*,*) i, x, nimi, z, totuus

Esimerkkisytteit ohjelmalle voivat olla esimerkiksi


2 3.0 "Aku Ankka" (1.0, 0.0) F Roskaa
2, 3, Aku, (0, 1), T
2, 3 Iines (0.0, 3.14), T
3 1.41
Tupu
(3,5) T

Ensimmisess sytteess rivin lopussa oli kommentti Roskaa. Viimeinen


syte on jaettu vapaasti kolmelle eri riville.
Seuraavassa ohjelmanosassa on kolmelle kokonaislukumuuttujalle annettu
alkuarvo ja niille luetaan arvot READ-lauseella.
INTEGER :: a = 3, b = 4, c = 5
READ (*,*) a, b, c

Alla olevassa taulukossa on esitetty ohjelmalle annettuja syterivej ja muuttujien arvoja syterivin lukemisen jlkeen.
Sytetty rivi
2 -1 -3
1, 3, 2
2, , 0
3*0 Nollaus
1 / loppuu

Muuttujien arvot lukemisen jlkeen


a
b
c
2
1
2
0
1

1
3
4
0
4

3
2
0
0
5

Huomaa erityisesti kolmannella rivill pilkkujen vliss annettu tyhj arvo,


joka ei muuta muuttujien arvoja. Neljnnell rivill sytmme kolme nollaarvoa, ja tmn jlkeen rivill on kommentti. Viimeisell rivill lopetamme
lukemisen merkill /, mink jlkeen rivill on kommentti.

12.3.2 Listan ohjaama tulostus


Listan ohjaamassa tulostuksessa (tulostuslauseissa symboli *) tietokone ptt itse sopivan esitysmuodon tulostettaville muuttujille. Muuttujien vliin
tulostetaan vlilynti tai pilkku, ja tulostus jaetaan automaattisesti eri riveille. Tulostuksen alkuun tulee vhintn yksi vlilynti. Kntj voi mys
tulostaa perkkiset samat arvot esimerkiksi muodossa 3*1.0.

196

Fortran 95/2003

Oletusarvoisesti merkkijonot tulostetaan sellaisenaan ja perkkisten merkkijonolausekkeiden vliin ei tule erotinta. Kaikkien muiden muuttujatyyppien osalta listan ohjaamaa tulostusta voidaan suoraan kytt listan ohjaamassa sytss. OPEN-lauseen DELIM-tarkentimella voidaan listan ohjaamaa
tulostusta muuttaa siten, ett merkkijonojen ymprille tulostetaan lainausmerkki tai heittomerkki. Oletus on DELIM=NONE. Muita vaihtoehtoja ovat
APOSTROPHE eli heittomerkki () ja QUOTE eli lainausmerkki (").
Seuraava ohjelmanptk tulostaa eri tyyppisi muuttujia listan ohjaamalla
tulostuksella:
INTEGER :: i = 3
REAL :: x = 3.14
CHARACTER(LEN=*), PARAMETER :: kehote = "Avot"
WRITE (*,*) i, kehote, x

Ohjelman tulostus voisi nytt esimerkiksi seuraavalta:


3Avot

3.140000

Huomaa, ett esimerkkitulostuksessa kokonaisluku 3 ja sit seuraava merkkijonolauseke kirjoitettiin suoraan perkkin.

12.4

Sytt- ja tulostuslauseet

Tss esittelemme sytt- ja tulostuslauseiden tydelliset muodot, joissa


listan ohjaaman muotoilun symboli * voidaan korvata muotoilukoodilla ja
joille voidaan antaa kanavanumero tiedostojen ksittely varten.
Lauseet WRITE ja PRINT tulostavat tietoa, ja lause READ lukee tietoa. READlauseella on kaksi muotoa, ilman kanavanumeroa ja kanavanumeron kanssa.
Seuraavat syttesimerkit kyttvt oletuskanavaa ja listan ohjaamaa muotoilua ja ovat toiminnaltaan identtisi:
READ *, n, x, y
READ (*,*) n, x, y
READ (UNIT=*,FMT=*) n, x, y

Samoin seuraavat tulostuslauseet ovat toiminnaltaan identtisi:


PRINT *, Alkio (, i, ,, j, ) =, a(i,j)
WRITE (*,*) Alkio (, i, ,, j, ) =, a(i,j)
WRITE (UNIT=*,FMT=*) Alkio (, i, ,, j, ) =, a(i,j)

Fortranin sytt- ja tulostuslauseet ovat yleisess muodossaan seuraavat:


PRINT fmt, iolist
WRITE (unit, fmt) iolist
READ fmt, iolist
READ (unit, fmt) iolist

Tydellisess muodossaan sytt- ja tulostuslauseet ottavat argumenteikseen kanavanumeron (unit) sek muotoilukoodin (fmt). Lisksi tarvitaan tu-

12. Tiedon sytt ja tulostus

197

lostuslista (iolist) eli lista luettavista tai tulostettavista muuttujista tai lausekkeista.
Kanavanumero mahdollistaa lukemisen ja kirjoittamisen ulkoisesta tiedostosta. Oletusarvoista sytt- ja tulostuskanavaa merkitn symbolilla *. Tiedostojen avaamista ja kanavanumeroita ksitelln sivulla 204.

12.5

Muotoilukoodit

Muotoilukoodi mr, miten tieto pit sytn tai tulostuksen yhteydess muotoilla. Muotoilukoodi siis ilmaisee, kuinka leven kenttn ja miten
muotoiltuna kukin muuttuja tulisi kirjoittaa. Muotoilukoodi voidaan antaa
eri tavoin: merkkijonovakiona, merkkijonolausekkeena, merkkijonomuuttujana tai FORMAT-lauseen lausenumerona. Aikaisemmissa esimerkeiss muotoilukoodi korvattiin symbolilla *, jolloin valittiin listan ohjaama muotoilu.
Seuraavassa esimerkiss on muotoilukoodi I5 ilmaistu kolmella eri tavalla.
Tm koodi tulostaa kokonaisluvun n viisi merkki leven kenttn.
PROGRAM tulosta
IMPLICIT NONE
INTEGER :: n = 34
CHARACTER(LEN=*), PARAMETER :: form = (I5)
WRITE (*, (I5)) n
WRITE (*, form) n
WRITE (*, 30) n
30 FORMAT (I5)
END PROGRAM tulosta

! Merkkivakio muotoilukoodina
! Merkkijonomuuttuja (tss vakio)
! FORMAT-lauseen kytt

FORMAT-lauseen kytt tulisi vltt. FORMAT-lauseen ja rivinumeroiden


kytt heikent ohjelman luettavuutta, koska samaan toimintoon liittyv
koodia on eri paikoissa ohjelmatiedostoa. Saman asian voi hoitaa selkemmin kyttmll merkkijonomuuttujaa tai nimetty merkkijonovakiota.
Fortranissa kannattaa kytt listan ohjaamaa sytt useimmissa tapauksissa. Sytn muotoilukoodit ovat vahvasti sarakesidonnaisia: kyttjn tytyy kirjoittaa sytettv tieto tsmlleen oikeisiin sarakkeisiin kullakin rivill. Fortranissa ei ole muotoilukoodia, joka lukisi tuntemattoman mittaisen
kokonaisluvun tai reaaliluvun.
Tulostuksessa muotoilukoodien kytt selvent tulostusta verrattuna listan ohjaamaan tulostukseen. Toisaalta tulostuksessakin tytyy aina ilmoittaa kentn leveys. Automaattinen kentn leveyden valinta toimii vain listan
ohjaaman tulostuksen yhteydess.
Seuraavassa esittelemme trkeimmt kokonaislukujen, reaalilukujen ja
merkkijonojen ksittelyyn tarkoitetut muotoilukoodit. Yhteenveto muotoilukoodeista on annettu taulukossa 12.1.
Muotoilukoodeihin liittyy kentn leveys. Sytss luetaan kentn leveyden
mukainen mr merkkej ja yritetn tulkita ne kyseisen tietotyyppin.

198

Fortran 95/2003

Tulostuksessa kokonais- ja reaaliluvut tulevat kentn oikeaan laitaan. Jos


tulostettavassa luvussa on enemmn desimaaleja kuin kentss on tilaa,
tytetn kentt merkeill *.
Taulukko 12.1: Yhteenveto eri tietotyypeille sopivista muotoilukoodeista. Merkint
w tarkoittaa kentn leveytt, d desimaaliosan pituutta ja e eksponentin pituutta.

Tietotyyppi

Muotoilukoodi Esimerkki

Kokonaisluvut
Kokonaisluvut binriesityksen
Kokonaisluvut oktaaliesityksen
Kokonaisluvut heksadesimaaliesityksen

Iw
Bw
Ow
Zw

I5
B8
O5
Z4

Reaaliluvut desimaaliesityksen

Fw .d

F7.4

Reaaliluvut eksponenttiesityksen
Tekniikan esitysmuoto
Tieteellinen esitysmuoto

Ew .d
Ew .dEe
ENw .d
ESw .d

E12.3
E12.3E4
EN12.3
ES12.3

Loogiset muuttujat

Lw

L7

Merkkijonot

A
Aw

A
A20

Yleinen esitystapa

Gw .d

G9.3

12.5.1 Kokonaisluvut
Kokonaislukujen muotoilukoodi on I. Koodin jlkeen ilmoitetaan kentn
leveys, esimerkiksi I5. Valinnaisesti voi kentn leveyden jlkeen ilmoittaa
tulostettavien numeroiden vhimmismrn (esim. I5.3), jolloin pienten
lukujen edelle tulostetaan nollia.
Seuraavassa on kokonaislukuja tulostava esimerkkiohjelma:
WRITE (*,(I5)) 10
WRITE (*,(I5.3)) 10
WRITE (*,(I5)) 100000

Ohjelman tulostus nytt tlt:


10
010
*****

Viimeisell rivill luvun esitys oli liian leve viiden merkin kenttn, jolloin
tulostettiin *-merkkej.
Kokonaisluvut voi mys tulostaa binri-, oktaali- tai heksadesimaaliesityksen muotoilukoodeilla B, O tai Z. Nit kytetn samoin kuin I-muotoilukoodia.
Kokonaislukujen sytss luetaan aina kentn leveyden mittainen mr

12. Tiedon sytt ja tulostus

199

merkkej tai rivin loppuun asti. Lukujen vliss olevat vlilynnit eivt ole
merkitsevi. Muotoilukoodien kytt voi helposti johtaa virhetilanteisiin, jos
luvut eivt ole tsmlleen oikeissa sarakkeissa.
Alla olevassa taulukossa on esitetty sytettyj merkkej ja niist luettuja
kokonaislukuja, kun luvut luetaan seuraavilla lauseilla:
INTEGER :: i, j
READ (*, (2I5)) i, j

Syte
1234567890
123
45
123 45
1 3 4 5 6 4

Luetut luvut
12345
123
12
134

67890
45
345
56

12.5.2 Reaaliluvut
Reaaliluvut voidaan tulostaa desimaaliesityksen (koodi F) tai eksponenttiesityksen (koodi E). Kumpaankin koodiin liittyy kentn leveys ja desimaalien lukumr, esimerkiksi F9.3 tai E12.3. Desimaalipiste sek eksponentin esitys sisltyvt sallittuun kokonaispituuteen. E-koodille voi antaa mys
eksponenttiosan pituuden, esimerkiksi E15.3E4.
Seuraavat esimerkit valaisevat F- ja E-koodien kytt:
WRITE
WRITE
WRITE
WRITE
WRITE
WRITE

(*,
(*,
(*,
(*,
(*,
(*,

(F7.2)) 12.3443
(F9.4)) 12.3443
(F9.4)) 37283845.3872
(E12.3)) 12.3443
(E12.5)) 1.243E-5
(E15.3E4)) 1.243E-5

Esimerkkien tulostus nytt tlt:


12.34
12.3443
*********
0.123E+02
0.12430E-04
0.124E-0004

Muotoilukoodista E on olemassa tekniseen esitykseen tarkoitettu muunnos


EN, jolle eksponentti on kolmella jaollinen. Lisksi on kytettviss tieteelliseen esitykseen tarkoitettu muunnos ES, jolle desimaalipisteen vasemmalla
puolella oleva luku on yhden ja kymmenen vlill.
Seuraavassa on esimerkkej reaalilukujen tulostamisesta:
WRITE
WRITE
WRITE
WRITE

(*,(F9.3)) 0.0345
(*,(E12.3)) 0.0345
(*,(EN12.3)) 0.0345
(*,(ES12.3)) 0.0345

200

Fortran 95/2003

Tulostukseksi saadaan:
0.034
0.345E-01
34.500E-03
3.450E-02

Tiedon sytss reaalilukujen muotoilukoodit Fw .d ja Ew .d toimivat seuraavasti: ohjelma lukee joka tapauksessa kentn leveyden w ilmoittaman
mrn merkkej tai kunnes rivi loppuu. Numeroiden vliss olevat vlilynnit eivt oletusarvoisesti ole merkitsevi.
Jos sytetyss luvussa on desimaalipiste, mr se desimaaliosan alun.
Tllin muotoilukoodin (esimerkiksi Fw .d) desimaalien mrll d ei ole
merkityst. Jos luvussa ei ole desimaalipistett, tulkitaan oikeanpuoleiset d
merkki desimaalipisteen oikealla puolella olevaksi desimaaliosaksi.
Luvut voi sytt mys eksponenttimuodossa, vaikkapa esitysmuodossa
2.1E5, 2.1E+5 tai 2.1+5. Esimerkiksi jos luetaan reaalilukuja lauseella
READ (*,(F7.2)) x

muuntuvat luvut taulukon 12.2 mukaisesti. Taulukon toiselta rivilt huomataan, ett vlilyntej ei otettu huomioon. Kolmas rivi osoittaa, ett luvusta kytettiin kaikki nelj desimaalia, vaikka muotoilukoodissa oli ilmoitettu
kentn leveydeksi kaksi. Kentn leveytt kytetn vasta silloin, kun sytetyss luvussa ei ole desimaalipistett. Esimerkeiss ilman desimaalipistett
sytetyt luvut tulkitaan sadasosina eli kaksi oikeanpuolimmaista lukua tulkitaan desimaaliosaksi. Lisksi taulukossa on esimerkkej eksponenttimuotoisesta sytst, jossa eksponenttia osoittavan e-kirjaimen voi halutessaan
jtt pois.
Taulukko 12.2: Tiedon syttminen muotoilukoodin (F7.2) avulla.

Syte

Luettu reaaliluku

2.1
2 . 1
-2.3142
21
2
1324
1.2e1
1.2e-1
-1.2-2

2.1
2.1
-2.3142
0.21
0.02
13.24
12
0.12
-0.012

12.5.3 Kompleksiluvut
Kompleksilukuja varten tulee antaa kaksi muotoilukoodia, reaali- ja imaginriosalle erikseen. Seuraavassa on esimerkki tst:
COMPLEX :: z = (0.3234, 3.423)

12. Tiedon sytt ja tulostus

201

WRITE (*,(F6.3, E9.2)) z

Tulostuksena on 0.323 0.34E+01. Samoin sytss luetaan reaali- ja imaginriosa erikseen omilla muotoilukoodeilla. Kytettess listan ohjaamaa
muotoilua (symboli *) luetaan ja tulostetaan kompleksiluvut sulkujen sisll
olevina lukupareina. Jos kompleksiluku tulostettaisiin kyttmll esimerkiksi muotoilukoodia (F6.3), tulostuisi ensin reaaliosa, tmn jlkeen
rivinvaihto ja lopuksi luvun imaginriosa omalle rivilleen.

12.5.4 Merkkijonot
Merkkijonoja muotoillaan koodilla A, jolle annetaan parametriksi kentn leveys. Jos merkkijono on lyhyempi kuin kentn leveys, sijoitetaan merkkijono
kentn oikeaan laitaan. Jos merkkijono on pidempi kuin kentt, katkaistaan
merkkej merkkijonon lopusta. Merkkijonojen muotoilukoodia voi kytt
mys ilman kentn leveytt, jolloin koko merkkijonolauseke tulostetaan tai
luetaan.
Seuraavassa on esimerkkej merkkijonojen tulostamisesta:
WRITE (*,(A4)) Kuk
WRITE (*,(A4)) Kukkuluuruu
WRITE (*,(A)) Kukkuluuruu

Tm ohjelman tulostus nytt seuraavalta:


Kuk
Kukk
Kukkuluuruu

12.5.5 Yleinen muotoilukoodi


Yleinen muotoilukoodi Gw .d toimii reaaliluvuille luvun suuruuden mukaan
kuten muotoilukoodit Fw .d tai Ew .d. F-koodin desimaaliosaa kavennetaan
kuitenkin siten, ett kentn kokonaisleveys on sama kuin E-koodille poislukien eksponenttiosa. Kokonaisluvuille muotoilukoodi G toimii kuten koodi
Iw ja merkkijonoille kuten koodi Aw .
Taulukossa 12.3 on esimerkkej muotoilukoodilla G12.5 tulostetuista reaaliluvuista.
Taulukko 12.3: Muotoilukoodi G12.5 reaalilukujen tulostuksessa.

Reaaliluku

Tulostus

0.0000234
0.1832
12.328
1343.43
3628372.843

.23400E-04
.18320
12.328
1343.4
.36284E+07

202

Fortran 95/2003

12.5.6 Ohjauskoodit
Trkeimmt sytn ja tulostuksen ohjauskoodit on esitetty taulukossa 12.4.
Koodi TR toimii tsmlleen samoin kuin koodi X.
Taulukko 12.4: Sytn ja tulostuksen ohjauskoodeja.

Tehtv

Koodi Esimerkki

Rivinvaihto
Absoluuttinen sarkaus
Suhteellinen sarkaus oikealle
Tulosta vlilyntej
Suhteellinen sarkaus vasemmalle
Vlilynnit eivt vaikuta sytss (oletus)
Vlilynnit tulkitaan nolliksi sytss
Tulosta plusmerkki luvun eteen
l tulosta plusmerkki luvun eteen

/
Tn
TRn
nX
TLn
BN
BZ
SP
SS

12.6

(I5,/,I8)
(I5,T20,I5)
(I5,TR5,I5)
(I5,5X,I5)
(I5,TL3,I5)
(BN,I5)
(BZ,I5)
(SP,I5)
(SS,I5)

Muotoilukoodien yhdistminen ja
tulostuslistat

Usein tulostuslistassa on monta lauseketta, ja muotoilukoodi sislt muotoiluohjeet nille eri tietotyypeille. Muotoilukoodi voi sislt yll esiteltyj
eri tietotyyppien koodeja, ohjauskoodeja, merkkijonovakioita sek toistorakenteita.
Esimerkiksi viisi perkkist kokonaislukua voidaan tulostaa muotoilukoodilla (5I8) ja kolme kokonaisluku-reaalilukuparia muotoilukoodilla
(3(I5,F8.3))

Jos muotoilukoodi loppuu ennen kuin kaikki tulostuslistan alkiot on ksitelty, tulostetaan rivinvaihto ja aloitetaan koodin kyttminen alusta.
Seuraava ohjelma havainnollistaa tulostuslistoja:
PROGRAM tulosta
IMPLICIT NONE
REAL, DIMENSION(5) :: a = 1
WRITE (*,("Taulukko a", 5G9.3)) a
WRITE (*,("Rivi ", 3G9.3)) a
END PROGRAM tulosta

Ohjelman tulostus nytt tlt:


Taulukko a 1.00
1.00
1.00
Rivi 1.00
1.00
1.00
Rivi 1.00
1.00

1.00

1.00

12. Tiedon sytt ja tulostus

203

Muotoilukoodin toistoon voi vaikuttaa kyttmll sulkuja. Esimerkiksi muotoilukoodi (A,/,(5F7.3)) tulostaa aluksi merkkijonon ja rivinvaihdon
sek lopuksi reaalilukuja, viisi lukua riville. Siis muotoilukoodin toistaminen
aloitetaan lopussa olevien sulkujen sislt. Seuraavassa on tst esimerkki:

PROGRAM toisto
IMPLICIT NONE
INTEGER :: i
WRITE (*,(A,/,(5F7.3))) Lukujono:, &
(SIN(i/10.0), i = 1, 12)
END PROGRAM toisto

Ohjelma tulostaa seuraavaa:


Lukujono:
0.100 0.199
0.565 0.644
0.891 0.932

0.296
0.717

0.389
0.783

0.479
0.841

Tulostuslista voi koostua toistolausekkeesta, jonka tosin voi usein korvata


taulukkosyntaksilla:
PROGRAM lue
IMPLICIT NONE
REAL, DIMENSION(5) :: a
INTEGER :: i
READ (*,*) (a(i), i=1,5)
READ (*,*) a
READ (*,*) a(1:5)
END PROGRAM lue

Tss kaikki READ-lauseet toimivat samoin.


Koko taulukkoa voi siten sytss tai tulostuksessa ksitell yhten kokonaisuutena. Samoin rakenteisen tietotyypin voi tulostaa yhdell lauseella,
jos tietotyyppi ei sisll osoitinmuuttujia.
Fortran 95 (mutta ei Fortran 90) mahdollistaa tulostuksen muotoilun minimilevyiseen kenttn. Muotoilukoodeille I, B, O, Z ja F voi antaa kentn leveydeksi arvon nolla, ja tllin tulostetaan mahdollisimman pieneen tilaan:
PROGRAM minimi_tulos
WRITE (*,(3(I0,:,"; "))) 12, -130, 14000
WRITE (*,(3(F0.4,:,"; "))) 1.23e2, -12.3, 1.23e5
END PROGRAM minimi_tulos

Ohjelma tulostaa seuraavaa:


12; -130; 14000
123.0000; -12.3000; 123000.0000

Luvut tulostuivat puolipisteell ja vlilynnill erotettuina. Kytimme merkki : lopettamaan muotoilukoodin kytn, kun viimeinen tulostuslistan luku
on tulostettu.

204

Fortran 95/2003

12.7

Tietueen osan tulostaminen tai lukeminen

Normaalisti sytt- tai tulostuslausekkeen suorituksen jlkeen siirrytn


seuraavalle riville. Rivinvaihdon voi est READ- tai WRITE-lauseen mreell
ADVANCE=NO. Tt tekniikkaa ei voi kytt, jos lauseessa kytetn listan
ohjaamaa muotoilua eli *-muotoilukoodia.
Seuraava ohjelma lukee samalta rivilt ensin taulukon dimension, sitten varaa tilan ja sen jlkeen lukee taulukon alkiot.
PROGRAM lue_taulukko
IMPLICIT NONE
REAL, ALLOCATABLE, DIMENSION(:) :: a
INTEGER :: i, n
READ (*, (I2), ADVANCE=NO) n
ALLOCATE (a(n))
READ (*,*) (a(i), i=1,n)
END PROGRAM lue_taulukko

Ohjelmalle voisi antaa esimerkiksi syterivin


3 3.14 2.71 1.4142

Seuraavat lauseet kirjoittavat ensin rivin alkuun kehotteen ja lukevat samalta


rivilt kokonaisluvun.
WRITE (*,(A), ADVANCE=NO) >
READ (*,*) n

12.8

Tiedostojen ksittely

Seuraavissa kappaleissa kerrotaan tiedostojen ksittelyst ja niiden liittmisest sytt- ja tulostuskanaviin. OPEN-lauseella avataan tiedosto ja liitetn se annettuun kanavanumeroon, INQUIRE-lauseella kyselln tiedoston
tai kanavan ominaisuuksia ja CLOSE-lauseella suljetaan tiedosto. Auki oleva
tiedosto voidaan kelata alkuun lauseella REWIND ja rivin verran taaksepin
lauseella BACKSPACE. Mys edell esitetyt READ- ja WRITE-lauseet ottavat parametrikseen kanavanumeron.

12.8.1 Tiedoston avaaminen


Yleisin OPEN-lauseen muoto on seuraava:
OPEN ([UNIT=]kanava, FILE=tiedosto)

Tiedosto data.txt avataan esimerkiksi seuraavasti:


OPEN (10, FILE=data.txt)

Tmn jlkeen kanavalle 10 tehtv sytt ja tulostus ohjautuu tiedostoon


data.txt. Tiedosto suljetaan CLOSE-lauseella.

12. Tiedon sytt ja tulostus

205

Yleens kanava 5 on syttkanava (standard input), ja kanava 6 on tulostuskanava (standard output). Lisksi useissa Unix-jrjestelmiss kanava 0
on varattu virheilmoituksille (standard error). Nm kanavat on valmiiksi
avattu, joten nit kanavanumeroita voi suoraan kytt sytt- ja tulostuslauseissa. Kuten tmn luvun alussa esitettiin, sytt- ja tulostuslauseissa
kanavanumero voidaan korvata merkill *, jolloin luetaan ja kirjoitetaan
ptteelt.
Monissa tietokoneissa kyttjrjestelm avaa uuden tiedoston, jos kirjoitetaan kanavanumerolle, jota ei ole liitetty mihinkn tiedostoon. Esimerkiksi
kanavalle 7 kirjoittaminen saattaa tuottaa tiedoston fort.7. Toisissa tietokoneissa avaamattomalle kanavalle kirjoittaminen tuottaa virheilmoituksen.
Seuraavassa esimerkiss luetaan tiedostosta data.a taulukon koko, varataan taulukko ja luetaan taulukon alkiot.
PROGRAM lue_taulukko
IMPLICIT NONE
REAL, ALLOCATABLE, DIMENSION(:) :: a
INTEGER :: n
OPEN (10, FILE=data.a)
READ (10, *) n
ALLOCATE (a(n))
READ (10, *) a
CLOSE (10)
END PROGRAM lue_taulukko

OPEN-lauseelle voi antaa useita tarkentimia. Tiedoston olemassaoloa ohjataan tarkentimella STATUS=st, miss st on merkkijonolauseke, jonka arvo
on OLD, NEW, REPLACE, SCRATCH tai UNKNOWN.
Arvo OLD edellytt, ett tiedosto on olemassa, ja arvo NEW puolestaan,
ett tiedosto ei saa olla olemassa. Tarkentimen arvolla REPLACE avattu
vanha tiedosto tuhotaan, muuten avataan uusi tiedosto. Arvo SCRATCH
luo tilapistiedoston, joka hvitetn CLOSE-lauseen suorituksen yhteydess. Tss tapauksessa tiedostolle ei saa antaa nime.
Seuraavassa esimerkiss avataan tilapistiedosto kanavalle 10:
OPEN (10, STATUS=SCRATCH)

Tarkentimella POSITION mrtn, mihin kohtaan tiedostoa aloitetaan kirjoittaa. Yleisimmin kytetty arvo tlle tarkenteelle on APPEND, jolloin kirjoitetaan tiedoston pern:
OPEN(20, FILE=data.a, POSITION=APPEND)

Tarkennin ACTION est lukeminsen tai kirjoittamisen. Arvo READ est


WRITE-lauseen kytn ja arvo WRITE est READ-lauseen kytn. Arvo
READWRITE sallii kummatkin. Tarkentimen ACTION oletusarvo vaihtelee
tietokoneesta toiseen.

206

Fortran 95/2003

12.8.2 Binritiedostot
Fortranin oletusarvoinen tiedostotyyppi on tekstitiedosto, joka sislt luettavassa muodossa tallennettuja merkkijonoja ja lukuja. Niden lukeminen
ja kirjoittaminen on esimerkki formatoidusta tiedonsiirrosta.
Kuitenkin suurten tiedostojen ksittelyss on edullisempaa kytt binritiedostoja, joissa luvut ja merkkijonot on ilmaistu tietokoneen sisisess esitysmuodossa. Binritiedostojen kytt on formatoimatonta tiedonsiirtoa.
Fortranin binritiedostoilla on oma, kyseisest Fortran-toteutuksesta riippuva sisinen esitysmuoto. Siten esimerkiksi C-kielell kirjoitettu binritiedosto ei sellaisenaan ole yleens luettavissa Fortran-ohjelmalla.
Binritiedostot voivat sst huomattavasti tilaa, ne ovat nopeampia kytt, ja lisksi lukujen tarkkuus ei heikkene luku- ja kirjoitusoperaatioiden
takia. Binritiedostojen huono puoli on, ett ihminen ei saa niiden sisllst
selv, eik tiedostoja voi yleens siirt eri tietokoneiden vlill.
Binritiedosto avataan kytten OPEN-lauseessa tarkenninta
FORM=UNFORMATTED

Binritiedostojen luku- ja kirjoituslauseissa ei kytet muotoilukoodeja.


Alla on annettu esimerkki tilapisen binritiedoston ksittelyst:
PROGRAM binaari
IMPLICIT NONE
REAL, DIMENSION(100) :: a
OPEN (10, STATUS=SCRATCH, FORM=UNFORMATTED)
WRITE (10) a
REWIND 10
...
READ (10) a
CLOSE (10)
END PROGRAM binaari

Tss siis tilapinen binritiedosto avattiin OPEN-lauseella kanavalle 10.


Tiedostoon kirjoitettiin taulukko a. Huomaa, ett WRITE-lauseessa ei kytetty minknlaisia muotoilukoodeja. Tiedosto kelattiin alkuun REWIND-lauseella. Myhemmin ohjelmassa taulukko luettiin uudelleen sisn READ-lauseella
jlleen ilman muotoilukoodeja. Kun tm tilapistiedosto suljetaan CLOSElauseella, se hvitetn automaattisesti.
Tilapistiedostojen kytt oli aikaisemmin yleist, koska koneiden keskusmuistit olivat pieni. Nyt niit tarvitaan vain todella isoille taulukoille, joita
ei voida pit koneen keskusmuistissa. Tilapistiedostojen kytt on huomattavasti hitaampaa kuin esimerkiksi saman datan ksittely taulukoilla.

12.8.3 Suorasaantitiedostot
Fortranin tiedostot ovat oletusarvoisesti perkkissaantitiedostoja, joita luetaan alusta alkaen tietue eli rivi kerrallaan. Fortranissa voi mys avata suo-

12. Tiedon sytt ja tulostus

207

rasaantitiedostoja, jolloin tiedostosta voidaan lukea rivej halutussa jrjestyksess. Suorasaantitiedostot ovat oletusarvoisesti binrisi.
Suorasaantitiedostot ovat hydyllisi esimerkiksi monissa tietokantasovelluksissa. Tietueita ei tarvitse kirjoittaa jrjestyksess, vaan tiedostossa voi
olla reiki eli tyhji kirjoittamattomia tietueita.
Suorasaantitiedostot avataan kytten OPEN-lauseen tarkenninta
ACCESS=DIRECT

Oletusarvoisesti tiedostot ovat binrisi, joten tekstimuotoisen suorasaantitiedoston avaukseen tulee liitt tarkennin FORM=FORMATTED. Tietueen
pituus on ilmoitettava avauksessa tarkentimella RECL=pituus. Tietueen pituus ilmaistaan tekstitiedostoille merkkein ja binritiedostoille yleens tavuina. Luku- ja kirjoituslauseissa tulee valita tietue tarkentimella REC=tietue,
miss tietue tarkoittaa tietueen jrjestysnumeroa.
Alla olevassa esimerkiss avataan kaksi suorasaantitiedostoa, toinen binrisen ja toinen tekstimuotoisena.
INTEGER, DIMENSION(10) :: r
OPEN (10, FILE=dir.bin, ACCESS=DIRECT, RECL=40)
OPEN (20, FILE=dir.txt, ACCESS=DIRECT, &
FORM=FORMATTED, RECL=10)
WRITE (10, REC=13) r
WRITE (20, (F10.3), REC=3) r

Binritiedostoon kirjoitetaan tietue numero 13, joka sislt kymmenalkioisen kokonaislukutaulukon. Tss oletetaan, ett kokonaislukumuuttuja vie nelj tavua keskusmuistia ja vaatii vastaavan mrn tilaa binritiedostossa. Tekstitiedostoon kirjoitetaan kokonaislukutaulukon alkiot kukin
omaksi tietueekseen tietueesta 3 alkaen.

12.8.4 Tiedoston tai kanavan ominaisuuksien kysely


Tiedoston tai kanavan ominaisuuksia voi kysell INQUIRE-lauseella, joka on
muotoa
INQUIRE (UNIT=kanava, lista)

tai muotoa
INQUIRE (FILE=tiedosto, lista)

Argumentti lista sislt joukon lismreit ja niihin liittyvi muuttujia.


INQUIRE-lauseen suorituksen jlkeen kukin muuttuja sislt tiedoston tai
kanavan ominaisuuksia kuvaavan arvon. Trkeimmt INQUIRE-lauseen mreet ja niihin liittyvien muuttujien tyypit on annettu taulukossa 12.5.
Seuraava esimerkki kuvaa INQUIRE-lauseen kytt:
PROGRAM kysely
IMPLICIT NONE
LOGICAL :: olemassa

208

Fortran 95/2003

Taulukko 12.5: INQUIRE-lauseeseen liittyvi mreit ja niiden paluumuuttujien


tyypit.

Mre

Selitys

Muuttujatyyppi

EXIST
OPENED
NAME
ACCESS
FORM
ACTION
NEXTREC
IOLENGTH

Onko tiedosto olemassa


Onko tiedosto tai kanava avattu
Kanavalle avatun tiedoston nimi
Saantitapa
FORMATTED tai UNFORMATTED
READ, WRITE tai READWRITE
Seuraavan tietueen numero
Tulostettavan datan viem tila

Looginen
Looginen
Merkkijono
Merkkijono
Merkkijono
Merkkijono
Kokonaisluku
Kokonaisluku

INTEGER :: n
REAL :: x
CHARACTER(LEN=12) :: saanti, muoto
INQUIRE (FILE=data.bin, EXIST=olemassa)
IF (.NOT. olemassa) &
STOP Tiedostoa data.bin ei ole olemassa!
OPEN(UNIT=10, FILE=data.bin, ACCESS=DIRECT, RECL=4)
READ(10, REC=3) x
INQUIRE (UNIT=10, ACCESS=saanti, FORM=muoto, NEXTREC=n)
WRITE (*,*) saanti, muoto, n, x
END PROGRAM kysely

Ohjelmassa testataan tiedoston data.bin olemassaoloa ja lopetetaan, jos


tiedostoa ei lydy. Jos tiedosto on olemassa, se avataan binrisen suorasaantitiedostona, jonka tietueen pituus on nelj tavua. Tiedostosta luetaan
reaaliluku tietueesta 3. Sen jlkeen kysytn INQUIRE-lauseella, mik on tiedoston saantitapa, onko kyseess binrinen vai tekstitiedosto sek seuraavaksi ksiteltvn tietueen numeroa. INQUIRE-lauseen suorituksen jlkeen
nm tiedot ovat muuttujissa saanti, muoto ja n, jotka tulostetaan ptteelle.
Ohjelman tulostus nytt tlt:
DIRECT

UNFORMATTED

4 23.00000000

INQUIRE-lauseella saa mys selville tulostettavan muuttujalistan esityspituuden tulostuslaitteella. Tll tavalla voi esimerkiksi saada selville muuttujien binrisen talletustavan viemn tilan (yleens sama kuin lukujen viem
keskusmuistitila):
INTEGER :: len
INTEGER :: i
INQUIRE (IOLENGTH=len) i
WRITE (*,*) Kokonaisluvut, len, tavua

12. Tiedon sytt ja tulostus

12.9

209

Luku merkkijonoista ja kirjoitus


merkkijonoihin

READ- ja WRITE-lauseissa voi kanavanumeron korvata merkkijonomuuttujan nimell, jolloin sytt- ja tulostusoperaatiot kohdistuvat thn merkkijonoon (ns. sisinen tiedosto). Tt voidaan kytt hyvksi esimerkiksi
luotaessa tilapistiedostojen nimi tai analysoitaessa ja muunnettaessa syterivej.
Seuraavassa esimerkiss muodostetaan tilapistiedoston nimi kirjoittamalla
se merkkijonomuuttujaan (esimerkitiedoston nimi on temp.007).
CHARACTER(LEN=20) :: tnimi
INTEGER :: n = 7
WRITE (tnimi, ("temp.", I3.3)) n
PRINT *, tnimi
OPEN(10, FILE=tnimi)

Seuraavassa tiedoston nimi muodostetaan merkkijonon, vuosiluvun ja ptteen yhdistelmst:


IMPLICIT NONE
INTEGER :: vuosi CHARACTER(LEN=2) :: hiukkaskoko
CHARACTER(LEN=10) :: tnimi
hiukkaskoko = pm
vuosi = 2007
WRITE (tnimi, (A,I4.4,".dat")) hiukkaskoko, vuosi
WRITE (*,(A)) tnimi
OPEN(10, FILE=tnimi)

Tss tiedoston nimeksi tulee pm2007.dat.


Seuraava esimerkki lukee ptteelt reaaliluvun, joka sislt desimaalipilkun. Rivi luetaan ensin merkkijonomuuttujaan. Pilkku muutetaan desimaalipisteeksi. Lopuksi luku luetaan merkkijonomuuttujasta reaalilukumuuttujaan.
CHARACTER(LEN=80) :: rivi
REAL :: numero
INTEGER :: pilkku
READ (*,(A)) rivi
pilkku = INDEX(rivi, ,)
rivi(pilkku:pilkku) = .
READ (rivi, *) numero

Standardifunktio INDEX hakee rivin ensimmisen pilkun sarakeindeksin. Ohjelma olettaa, ett rivill on tsmlleen yksi pilkku.

210

Fortran 95/2003

12.10

NAMELIST-rakenne

NAMELIST-rakenne mahdollistaa muuttujaryhmn ksittelemisen yhten kokonaisuutena sytss ja tulostuksessa. Muuttujille voidaan antaa arvot niiden nimien mukaan vapaassa jrjestyksess. Erityisen hydyllinen NAMELISTrakenne on silloin, kun muuttujille on annettu alkuarvot ja osalle muuttujista luetaan uudet arvot tiedostosta.
NAMELIST-rakenteen kytt havainnollistaa seuraava esimerkki:
PROGRAM leivo
IMPLICIT NONE
INTEGER :: jauhot = 1, sokeri = 1, munat = 1
NAMELIST /kakku/ jauhot, sokeri, munat
READ (*, NML=kakku)
WRITE (*, NML=kakku)
END PROGRAM leivo

NAMELIST-ryhmn nimi tytyy siis antaa tarkentimella NML.


Sytss NAMELIST-ryhmn ksittely aloitetaan &-merkill, jonka perss on
ryhmn nimi. Tmn jlkeen tulee joukko sijoituslauseita ryhmn muuttujille ja sytt lopetetaan merkill /. Yll olevalle ohjelmalle sytetn haluttujen muuttujien arvot ptteelt esimerkiksi seuraavasti:
&kakku munat=3, jauhot=4/

Tllin tulostus voisi nytt seuraavalta:


&KAKKU
JAUHOT=4, SOKERI=1, MUNAT=3
/

NAMELIST-rakenne on ktev esimerkiksi pienten sytttiedostojen muotoilussa, jolloin sytttiedostosta tulee itsedokumentoiva.

12.11

Virhetilanteiden ksittely

Tiedostojen ksittelyss tulisi aina varautua tilanteisiin, jossa esimerkiksi


avattavaa tiedostoa ei ole olemassa tai tiedosto loppuu kesken lukuoperaation aikana. Fortran tarjoaa tilamuuttujia virhetilanteista selvimiseen.
OPEN-lauseeseen voi list seuraavan lismreen:
IOSTAT=iovar

Tss iovar on kokonaislukumuuttuja, joka saa arvon 0, jos tiedoston avaus


onnistui, ja positiivisen arvon virhetilanteessa.
WRITE- ja READ-lauseisiin voi liitt saman lismreen:
IOSTAT=iovar

Luettaessa tiedostoa READ-lauseella saa tilamuuttuja iovar negatiivisen arvon, jos tiedosto loppuu, positiivisen arvon virhetilanteessa ja arvon nolla,

12. Tiedon sytt ja tulostus

211

jos luku onnistuu. IOSTAT-mreen palauttamat virhekoodit ovat laitteistoriippuvia.


Jos IOSTAT-lismrett ei ole annettu, ohjelman suoritus voi pyshty tiedostovirheeseen. Jos tm lismre annetaan, virhetilanteista toipuminen
on ohjelmoijan vastuulla.
Seuraava esimerkki havainnollistaa virhetilanteiden ksittely:
PROGRAM io
IMPLICIT NONE
INTEGER :: avaus, luku
INTEGER, PARAMETER :: kanava = 10
CHARACTER(LEN=*), PARAMETER :: tiedosto = data.txt
REAL :: x
OPEN(kanava, FILE=tiedosto, STATUS=old, IOSTAT=avaus)
IF (avaus /= 0) THEN
WRITE (*,*) Virhe tiedoston , tiedosto,&
avaamisessa, virhekoodi:, avaus
STOP
END IF
DO
READ (kanava, *, IOSTAT=luku) x
IF (luku < 0) THEN
WRITE (*,*) Tiedoston loppu, ohjelman suoritus &
&jatkuu:, luku
EXIT
ELSE IF (luku > 0) then
WRITE (*,*) Virhe lukemisessa, virhekoodi:, luku
STOP
ELSE
WRITE (*,*) Luettu arvo:, x
END IF
END DO
WRITE (*,*) Lopullinen arvo:, x
END PROGRAM io

Sytt- ja tulostuslauseisiin voi mys liitt lausenumeroita, joihin hyptn


virhetilanteissa. Lausenumeroiden kytt ei kuitenkaan ole suositeltavaa,
koska ne vhentvt ohjelman selkeytt ja hmrtvt ohjelman loogista
rakennetta.

12.12

Yhteenveto

Tss luvussa on esitetty trkeimmt Fortranin sytt- ja tulostusoperaatiot. Monista piirteist, erityisesti OPEN-lauseen lismreist, on ksitelty
vain oleellisimmat toiminnot. Seuraavissa luetteloissa on esitelty trkeimpien sytt- ja tulostuslauseiden syntaksi. Valinnaiset osat on kirjoitettu
hakasulkuihin.

212

Fortran 95/2003

READ fmt [,iolist]


READ ([UNIT=]kanava, [FMT=]fmt [, IOSTAT=iostat]
[, ERR=err-lause] [,END=end-lause] [, ADVANCE=NO]) iolist
PRINT fmt [,iolist]
WRITE ([UNIT=]kanava, [FMT=]fmt [, IOSTAT=iostat]
[, ERR=err-lause] [, ADVANCE=NO]) [iolist]
BACKSPACE kanava
BACKSPACE ([UNIT=]kanava[, IOSTAT=iostat] [, ERR=err-lause])
REWIND kanava
REWIND ([UNIT=]kanava[, IOSTAT=iostat][, ERR=err-lause])
OPEN ([UNIT=]kanava, olist)
CLOSE ([UNIT=]kanava[, IOSTAT=iostat][, ERR=err-lause])
INQUIRE ([UNIT=]kanava, ilist)
INQUIRE (FILE=tiedosto, ilist)
INQUIRE (IOLENGTH=pituus) iolist

OPEN-lauseen lismreit (listassa olist) ovat muun muassa:


FILE=tiedosto
IOSTAT=iostat
ERR=err-lause
STATUS=st
ACCESS=acc
FORM=fm
RECL=rl
POSITION=pos
ACTION=act
DELIM=dlm

INQUIRE-lauseen lismreit on esitelty taulukossa 12.5 (sivu 208).


Tiedon esitysmuotoa sytss ja tulostuksessa ohjataan muotoilukoodeilla.
Eri tietotyyppien muotoilukoodeja on esitelty taulukossa 12.1 (sivu 198) ja
ohjauskoodeja taulukossa 12.4 (sivu 202).

Harjoitustehtvi
1. Mit eroa on WRITE- ja PRINT-lauseilla?
2. Seuraavassa esimerkiss on mritelty eri tavoilla Fortranin muotoilulausekkeita. Mit ohjelma tekee? Mitk mrittelyt ovat mielestsi toimivimpia?
PROGRAM muoto
IMPLICIT NONE
REAL :: x
CHARACTER(LEN=11) :: form1
CHARACTER(LEN=*), PARAMETER :: form2 = (F12.3,A)
x = 12.0
form1 = (F12.3,A)
WRITE (*, form1) x, hello
WRITE (*, form2) 2*x, hi
WRITE (*, (F12.3,A)) 3*x, hi hi
END PROGRAM muoto

12. Tiedon sytt ja tulostus

213

3. Kirjoita tulostuslauseet seuraaville taulukoille:


REAL, DIMENSION(6,10) :: a
INTEGER, DIMENSION(24) :: h
CHARACTER(LEN=10), DIMENSION(6) :: mtaulu
LOGICAL, DIMENSION(2) :: ehto
COMPLEX, DIMENSION(20) :: z

4. Kirjoita listan ohjaamat syttrutiinit, jotka lukevat arvot edellisen tehtvn taulukoille. Anna esimerkki ohjelmasi sytttiedostosta.
5. Tiedostossa on formatoitua dataa seuraavan taulukon mukaisessa sarakesidonnaisessa esitysmuodossa:
Tieto

Sarakkeet Koodaus

etunimi
sukunimi
sukupuoli
siviilisty

115
2145
50
52

ik

5457

merkkijono
merkkijono
N=nainen, M=mies
0=naimaton, 1=naimisissa,
2=avoliitossa, 3=muu tai tuntematon
vuosina

Tiedosto loppuu, kun sarakkeissa 115 on teksti


Tiedoston loppu

Tiedostossa on korkeintaan 500 tietuetta eli rivi.


Kirjoita ohjelma, joka lukee tiedot edell esitetyn muotoisesta tiedostosta, laskee keskiarvon ille sek tulostaa ikjakauman ja siviilisdyn
jakauman.
6. Mit seuraava ohjelma tulostaa, kun sytteen annetaan merkkijono
0123456789?
PROGRAM tulostesti
IMPLICIT NONE
CHARACTER(LEN=7) :: mj1, mj2, mj3
READ (*, (A8,T3,A5,TR2,A)) mj1, mj2, mj3
WRITE(*, (3X,A8,3X,A5,3X,A)) mj1, mj2, mj3
WRITE(*, (3(3X,A))) mj1, mj2, mj3
END PROGRAM tulostesti

7. Laske ja trigonometristen funktioiden sin x, cos x ja tan x arvot, kun


kulma x ky 0:sta 90:een asteeseen yhden asteen vlein. Kyt sopivia
muotoilukoodeja.
8. Lue tiedostosta 20 reaalilukua, yksi rivi kohti. Tulosta luvut toiseen
tiedostoon knteisess jrjestyksess. Varaudu mys lukemisessa tuleviin virhetilanteisiin.
Muunna ohjelmaasi siten, ett luettavien reaalilukujen mr on mielivaltainen. Ohjelma tiet, ett tiedosto on loppunut, kun IOSTAT on
negatiivinen. Lue kanavasta * ja kirjoita kanavalle *. Tllin ohjelmaa
voi kytt Unixissa seuraavasti:

214

Fortran 95/2003

% ohjelma < sytetiedosto > tulostustiedosto

9. Kirjoita ohjelma, joka tulostaa funktion f (x) = (sin x)/(x + cos x)2
kuvaajan tekstiptteelle esimerkiksi seuraavasti:
Syt ala- ja ylraja: 2 5
Syt laskentapisteiden lukumr
ja kuvaajan leveys vaakatasossa: 15 32
x:
y:
:.........:.........:.........:.
2.000
0.362
+
2.214
0.307
+
2.429
0.234
+
2.643
0.154
+
2.857
0.078
+
3.071
0.016
+
3.286 -0.027
+
3.500 -0.053 +
3.714 -0.066 +
3.929 -0.068 +
4.143 -0.065 +
4.357 -0.058 +
4.571 -0.050 +
4.786 -0.042
+
5.000 -0.034
+

10. Kirjoita ohjelma, joka tulostaa kertotaulun luvuille 1, . . . , 16.


11. Kirjoita ohjelma, joka kertoo kuinka monta kertaa annettu sana esiintyy
tiedostossa. Tiedoston nimi luetaan ptteelt. Varaudu mys tiedoston
avaamisessa tuleviin virhetilanteisiin.
12. Kirjoita ohjelma, joka tulostaa kuukauden kalenterin, kun sille kerrotaan vuosi ja kuukausi. Tietyn pivmrn viikonpivn saat selville
kaavasta
w = MOD(k + FLOOR(2.6*m - 0.2) - 2*c + y &
+ FLOOR(y/4) + FLOOR(c/4), 7)

Tss muuttuja k on kuukauden piv (1, 2, . . . , 31). Muuttuja m on kuukausi: 1 = maaliskuu, 2 = huhtikuu, . . . , 10 = joulukuu, 11 = tammikuu,
12 = helmikuu (ksittele tammikuu ja helmikuu edellisen vuoden kuukausina). Muuttuja c on vuosisata, esimerkiksi vuonna 1987 on c = 19.
Muuttuja y on vuosi eli vuonna 1987 on y = 87, paitsi tammi- ja helmikuussa y = 86. Muuttuja w on viikonpiv: 0 = sunnuntai, . . . , 6 =
lauantai.
13. Kirjoita ohjelma, joka laskee vektorin euklidisen normin. Vertaa ohjelmasi tehokkuutta BLAS-rutiiniin SNRM2, kun vektorin alkioiden lukumr on suuri.

13. Standardiproseduurit

13

215

Standardiproseduurit

Kymme lpi Fortran 95 -kielen standardiproseduurit (intrinsic procedure),


jotka ovat joko funktioita tai aliohjelmia. Standardifunktioista kytetn
toisinaan mys nime varusfunktiot.

13.1

Standardiproseduurien perusksitteit

Fortran 95 -standardi sislt 115 standardialiohjelmaa ja -funktiota. Useimmat standardiproseduurit ovat geneerisi, joten samaa funktio- tai aliohjelmakutsua voi kytt eri tyyppisille argumenteille. Tten funktiokutsua
SIN(x) voi kytt eri lajien reaali- ja kompleksilukuargumenteille.
Osa standardiproseduureista on alkioittaisia (elemental), mik tarkoittaa, ett proseduurikutsu kohdistuu kuhunkin argumentin alkioon erikseen. Esimerkiksi ohjelmakoodi
REAL, DIMENSION(100) :: x, y
INTRINSIC SIN
x = SIN(y)

laskee sinifunktion arvot kaikille taulukon y alkioiden arvoille.


Kyselyfunktioiden (inquiry functions) avulla voi selvitt argumenttien ominaisuuksia. Esimerkiksi reaalimuuttujan x numeerisen tarkkuuden saa selville funktiokutsulla PRECISION(x).
Muunnosfunktioiden (transformational functions) avulla muunnetaan (yleens) taulukkotyyppisi argumentteja toiseen muotoon. Esimerkiksi muunnosfunktio SUM laskee argumenttitaulukon alkioiden summan.
Standardiproseduurit on hyv esitell INTRINSIC-lauseessa ohjelmayksikn
alussa ennen suoritettavia lauseita, jolloin ohjelmakoodin lukija tiet, mit
standardiproseduureja ohjelmassa kytetn. Mrittely
INTRINSIC SIN

kertoo, ett kytmme standardifunktiota SIN. INTRINSIC-lauseen kytt


on pakollista, jos standardifunktio vlitetn proseduurin todellisena argumenttina.

216

Fortran 95/2003

13.2

Standardiproseduurien kuvauksia

Standardiproseduurien kuvaukset on koottu tmn luvun seuraavassa lueteltuihin taulukoihin:


Taulukon sislt

Taulukko Sivu

Sekalaisia varusfunktioita
Kellonaika ja pivmr
Satunnaislukujen tuottaminen
Trigonometriset sek logaritmi- ja eksponenttifunktiot
Numeerinen tarkkuus ja konversiot
Numeerisia ja matemaattisia funktiota
Merkkijonojen ksittely
Taulukko-operaatioita
Lis taulukko-operaatioita
Proseduureja bittien ksittelyyn

13.1
13.2
13.3
13.4
13.5
13.6
13.7
13.8
13.9
13.10

217
217
218
220
221
222
223
224
225
226

Taulukoissa on kerrottu kunkin proseduurin kutsusyntaksi sek argumenttien mahdolliset tyypit. Lisksi on kerrottu argumenttien INTENT-arvot (IN,
OUT tai INOUT) sek ilmoitettu, onko argumentti valinnainen (OPTIONAL). Proseduuri voi palauttaa tulokset OUT- tai INOUT-tyyppisiss argumenteissa ja
lukee sytteen IN- tai INOUT-tyyppisist argumenteista.
Funktion palauttaman arvon tyyppi on esitetty seuraavaan tapaan:
L = ALLOCATED(array) tutki, onko taulukolle varattu tilaa

Kirjain L tarkoittaa, ett funktion arvo on tyyppi LOGICAL. Muita mahdollisuuksia ovat: I eli INTEGER, R eli REAL, D eli DOUBLE PRECISION, C eli
CHARACTER ja Z eli COMPLEX.
Kaksoistarkkuuden tyyppi DOUBLE PRECISION esiintyy taulukoissa historiallisista syist. Tmn tyypin kytn voi korvata parametrisoidulla reaalilukutyypill.
Jos funktion arvo riippuu argumenttien tyypist, on tm merkitty seuraavasti:
* = MAXVAL(array,dim,mask) maksimiarvo

Monissa taulukkoja ksitteleviss proseduureissa kytetn argumenttina


mask-muuttujaa. Argumentti mask on looginen (yleens taulukkotyyppinen)
muuttuja, joka ohjaa proseduurin toimintaa. Funktion MAXVAL argumentin
mask tulee olla saman muotoinen looginen taulukko kuin argumentin array.
Taulukosta array ksitelln vain ne alkiot, joita vastaava taulukon mask
arvo on tosi.
Aliohjelmakutsut on merkitty taulukoihin seuraavasti:
CALL NIMI(argumentit)

Jos useammalla proseduurilla on sama argumenttilista, on nm proseduurit lueteltu taulukossa perkkin ja niiden alapuolella on yhteinen argumenttien kuvaus.

13. Standardiproseduurit

217

Huomaa, ett jos funktiokutsun argumentti on kompleksilukuvakio, tytyy


aliohjelmakutsussa kytt kaksinkertaisia sulkuja:
z = SQRT((1.0,-1.0))

Fortranin standardiproseduurien kytt esitelln mys oppaan muissa luvuissa. Kerromme Fortranin lausekkeista luvussa 6 (sivu 67). Havainnollistamme taulukkojen ksittely luvussa 11 (sivu 164).

Taulukko 13.1: Sekalaisia standardifunktioita.


Argumentti

Argumentin tyyppi

Suunta Valinnaisuus

L = ALLOCATED(array) tutki, onko taulukolle varattu tilaa


array

ALLOCATABLE

IN

L = ASSOCIATED(pointer,target) osoittaako osoitinmuuttuja


kohteeseen
pointer
POINTER
IN
target
POINTER tai TARGET
IN
OPTIONAL
L = PRESENT(a) onko argumentti annettu proseduurin kutsussa
a

mik tahansa tyyppi

IN

* = NULL(mold) palauttaa kohteesta irrotetun osoittimen


mold

mik tahansa osoitin

IN

OPTIONAL

Taulukko 13.2: Kellonaika, pivmr ja cpu-aika.


Argumentti

Argumentin tyyppi

Suunta Valinnaisuus

CALL DATE_AND_TIME(date,time,zone,values) pivmr ja


kellonaika
date
CHARACTER(LEN=8)
OUT
OPTIONAL
time
CHARACTER(LEN=10)
OUT
OPTIONAL
zone
CHARACTER(LEN=5)
OUT
OPTIONAL
values
INTEGER, DIMENSION(8)
OUT
OPTIONAL
CALL SYSTEM_CLOCK(count,count_rate,count_max) tietokoneen
seinkelloaika
count
INTEGER
OUT
OPTIONAL
count_rate INTEGER
OUT
OPTIONAL
count_max INTEGER
OUT
OPTIONAL
CALL CPU_TIME(time) prosessorin CPU-aika sekunneissa
time

REAL

OUT

218

Fortran 95/2003

Taulukko 13.3: Satunnaislukujen tuottaminen.


Argumentti

Argumentin tyyppi

Suunta Valinnaisuus

CALL RANDOM_NUMBER(harvest) satunnaislukujen tuottaminen


harvest
REAL
OUT
CALL RANDOM_SEED(size,put,get) siemenlukujen asetus ja haku
size
put
get

13.3

INTEGER
INTEGER, DIMENSION(m)
INTEGER, DIMENSION(m)

OUT
IN
OUT

OPTIONAL
OPTIONAL
OPTIONAL

Esimerkki: kellonaika ja pivmr

Taulukossa 13.2 esitetyill rutiineilla saat selville kellonajan ja pivmrn:


PROGRAM ajanhetki
IMPLICIT NONE
INTEGER, PARAMETER:: &
r8 = SELECTED_REAL_KIND(12,100)
CHARACTER(LEN=8) :: pvm
CHARACTER(LEN=10) :: aika
CALL DATE_AND_TIME(date = pvm)
CALL DATE_AND_TIME(time = aika)
WRITE (*,(A,A8)) Pvm: , pvm
WRITE (*,(A,A10)) Aika: , aika
WRITE (*,(A,F10.3)) Sekunnit: , sekunnit()
CONTAINS
FUNCTION sekunnit() RESULT (aika)
IMPLICIT NONE
REAL(r8) :: aika
INTEGER, DIMENSION(8) :: t
CALL DATE_AND_TIME(values = t)
aika = t(5)*3600.0_r8+t(6)*60.0_r8+t(7)+t(8)/1000.0_r8
END FUNCTION sekunnit
END PROGRAM ajanhetki

Ohjelma tulostaa seuraavaa:


Pvm: 19980728
Aika: 110039.849
Sekunnit: 39639.851

Kyseess on siis vuoden 1998 heinkuun 28. piv, ja kello on hiukan yli
11:00:39. Ohjelman tulostamat sekunnit puolestaan vastaavat kuluvan vuorokauden kellonaikaa sekunteina.

13. Standardiproseduurit

13.4

219

Esimerkki: satunnaisluvut ja kellonaika

Taulukossa 13.3 on esitelty satunnaislukujen tuottamiseen tarvittavat rutiinit. Satunnaisluvut riippuvat satunnaislukugeneraattorin alustamiseen kytetyist siemenluvuista. Samoilla siemenluvuilla saadaan aina sama satunnaislukusarja. Jos siemenlukua ei aseteta, ohjelma voi tuottaa eri ajokerroilla
samat satunnaisluvut.
Seuraavassa ohjelmassa alustamme satunnaislukugeneraattorin kellonajan
perusteella laskettavalla siemenluvulla ja tuotamme halutun mrn tasajakautuneita satunnaislukuja vlilt [a, b).
PROGRAM satunnainen
IMPLICIT NONE
CALL alusta
CALL tulosta
CONTAINS
SUBROUTINE alusta
IMPLICIT NONE
INTEGER :: koko, tila
INTEGER, DIMENSION(8) :: t
INTEGER, DIMENSION(:), ALLOCATABLE :: siemen
CALL RANDOM_SEED(size=koko)
ALLOCATE(siemen(koko), stat=tila)
IF (tila > 0) STOP Tilanvaraus eponnistui!
CALL DATE_AND_TIME(values = t)
siemen = 100*t(7) + t(8)/10
CALL RANDOM_SEED(put=siemen)
END SUBROUTINE alusta
SUBROUTINE tulosta
IMPLICIT NONE
INTEGER :: n, tila
REAL :: a, b
REAL, DIMENSION(:), ALLOCATABLE :: luvut
write (*,(A),ADVANCE=NO) Anna ala- ja ylraja:
READ (*,*) a, b
WRITE (*,(A),ADVANCE=NO) &
Anna satunnaislukujen lukumr:
READ (*,*) n
ALLOCATE(luvut(n), stat=tila)
IF (tila > 0) STOP Tilanvaraus eponnistui!
CALL RANDOM_NUMBER(luvut)
WRITE (*,(A)) Satunnaisluvut:
WRITE (*,(6(F10.6))) a + (b-a)*luvut
END SUBROUTINE tulosta
END PROGRAM satunnainen

Satunnaislukugeneraattori alustettiin aliohjelmassa alusta ja satunnaislukujen tuottaminen tehtiin aliohjelmassa tulosta. Satunnaislukugeneraattorin vaatiman siemenlukutaulukon koko selvitettiin aliohjelmakutsulla
CALL RANDOM_SEED(size=koko)

220

Fortran 95/2003

Siemenlukutaulukon sisllksi asetettiin rutiinin DATE_AND_TIME palauttamista sekunneista ja millisekunneista laskettu kokonaisluku, joka on vlilt
[0, 29999]:
CALL DATE_AND_TIME(values = t)
siemen = 100*t(7) + t(8)/10
CALL RANDOM_SEED(put=siemen)

Satunnaisluvut tuotettiin reaalilukutaulukkoon luvut aliohjelmakutsulla


CALL RANDOM_NUMBER(luvut)

Tuloksena on tasajakautuneita satunnaislukuja vlilt [0, 1). Tulostuslausekkeessa a + (b-a)*luvut luvut skaalataan vlille [a, b).
Seuraavassa on ohjelman kyttesimerkki:
Anna ala- ja ylraja: -1 1
Anna satunnaislukujen lukumr: 10
Satunnaisluvut:
-0.253170 0.654368 -0.700821 -0.983755 -0.877386
0.135530 -0.885027 -0.704163 -0.781958

0.948415

Taulukko 13.4: Trigonometriset sek logaritmi- ja eksponenttifunktiot.


Argumentti

Argumentin tyyppi

R = ACOS(x) arkuskosini
R = ASIN(x) arkussini
x
REAL, |x| 1

Suunta Valinnaisuus

IN

R = ATAN(x) arkustangentti
R = TAN(x) tangentti
x

REAL

* = COS(x) kosini
* = SIN(x) sini
x
REAL tai COMPLEX

IN

IN

R = ATAN2(y,x) arkustangentti arvolle Y/X


y
REAL
x
REAL
R = COSH(x) hyperbolinen kosini

IN
IN

R = SINH(x) hyperbolinen sini


R = TANH(x) hyperbolinen tangentti
x

REAL

IN

* = EXP(x) eksponenttifunktio
* = LOG(x) luonnollinen logaritmi
x

REAL tai COMPLEX

IN

R = LOG10(x) kymmenkantainen logaritmi


x

REAL, x > 0

IN

13. Standardiproseduurit

221

Taulukko 13.5: Numeerinen tarkkuus ja konversiot.


Argumentti

Argumentin tyyppi

I = DIGITS(x) merkitsevien numeroiden mr


I = RADIX(x) aritmetiikan kantaluku
x
INTEGER tai REAL
I = RANGE(x) eksponentin lukualue

Suunta Valinnaisuus

IN

x
numeerinen
IN
* = HUGE(x) suurin luku annetun muuttujan tyyppi
x

INTEGER tai REAL

IN

R = EPSILON(x) muuttujan x tyyppi vastaava kone-epsilon


I = EXPONENT(x) luvun x eksponenttiosa
I = MAXEXPONENT(x) suurin eksponentti annetun muuttujan tyyppi
I = MINEXPONENT(x) pienin eksponentti annetun muuttujan tyyppi
I = PRECISION(x) muuttujan tyypin tarkkuus desimaaleina
R = RRSPACING(x) suhteellisen tarkkuuden knteisarvo
R = SPACING(x) luvun absoluuttinen tarkkuus
R = TINY(x) muuttujan x tyyppi vastaava pienin positiivinen luku
x

REAL

IN

Z = CMPLX(x,y,kind) muunnos kompleksiluvuksi


x
numeerinen
IN
y
INTEGER tai REAL
IN
OPTIONAL
kind
INTEGER
IN
OPTIONAL
D = DBLE(a) muunnos kaksoistarkkuuteen
a
numeerinen
IN
I = INT(a,kind) muunnos kokonaisluvuksi (katkaisu nollaa kohti)
a
numeerinen
IN
kind
INTEGER
IN
OPTIONAL
L = LOGICAL(l,kind) muunnos loogisen tyypin eri lajien vlill
l
LOGICAL
kind
INTEGER
R = REAL(a,kind) muunnos reaaliluvuksi
a
numeerinen
kind
INTEGER
I = KIND(x) selvitetn muuttujan tyyppi

IN
IN

OPTIONAL

IN
IN

OPTIONAL

x
mik tahansa perustyyppi
IN
I = SELECTED_INT_KIND(r) halutun tarkkuuden kokonaislukutyyppi
r
INTEGER
IN
I = SELECTED_REAL_KIND(p,r) halutun tarkkuuden reaalilukutyyppi
p
INTEGER
r
INTEGER
R = SCALE(x,i) skaalaa reaaliluku: x = x*(b**i)
x
REAL
i
INTEGER
R = SET_EXPONENT(x,i) aseta luvun x eksponentti
x
i

REAL
INTEGER

IN
IN
IN
IN
IN
IN

OPTIONAL
OPTIONAL

222

Fortran 95/2003

Taulukko 13.6: Numeerisia ja matemaattisia funktioita.


Argumentti

Argumentin tyyppi

* = ABS(a) luvun itseisarvo


a
numeerinen
R = AIMAG(z) kompleksiluvun imaginaariosa

Suunta Valinnaisuus

IN

z
COMPLEX
IN
R = AINT(a,kind) katkaisu kokonaislukuun nollan suuntaan
R = ANINT(a,kind) lhin kokonaisluku
a
REAL
IN
kind
INTEGER
IN
I = CEILING(a,kind) pyristys ylspin
a
REAL
kind
INTEGER
Z = CONJG(z) kompleksikonjugaatti

IN
IN

z
COMPLEX
* = DIM(x,y) erotus x y tai nolla jos x y < 0

IN

x
INTEGER tai REAL
y
sama kuin x
D = DPROD(x,y) kaksoistarkkuuden tulo: x y

IN
IN

x
oletustarkkuuden REAL
y
oletustarkkuuden REAL
I = FLOOR(a,kind) katkaisu alaspin

IN
IN

a
REAL
kind
INTEGER
R = FRACTION(x) luvun murto-osa
x
REAL
* = MAX(a1,a2,a3, . . . ) lukujen maksimiarvo

IN
IN

OPTIONAL

OPTIONAL

OPTIONAL

IN

* = MIN(a1,a2,a3, . . . ) lukujen minimiarvo


a1
a2
a3
* = MOD(a,p)

INTEGER tai REAL


sama kuin a1
sama kuin a1
arvo a - p*INT(a/p)

IN
IN
IN

OPTIONAL

* = MODULO(a,p) arvo a modulo p


a
INTEGER tai REAL
IN
p
sama kuin a
IN
R = NEAREST(x,s) lhin reaaliluku annettuun suuntaan
x
REAL
IN
s
REAL
IN
I = NINT(a,kind) lhin kokonaisluku
a
REAL
kind
INTEGER
* = SIGN(a,b) a:n itseisarvo samanmerkkisen kuin b
a
INTEGER tai REAL
b
sama kuin a

* = SQRT(x) nelijuuri: x
x

REAL tai COMPLEX

IN
IN
IN
IN
IN

OPTIONAL

13. Standardiproseduurit

223

Taulukko 13.7: Merkkijonojen ksittely.


Argumentti

Argumentin tyyppi

Suunta Valinnaisuus

C = ACHAR(i) paikassa i oleva ASCII-merkki


i
INTEGER
IN
C = ADJUSTL(string) siirr vlilynnit merkkijonon alusta loppuun
C = ADJUSTR(string) siirr vlilynnit merkkijonon lopusta alkuun
string
CHARACTER
C = CHAR(i,kind) paikassa i oleva merkki

IN

i
INTEGER
kind
INTEGER
I = IACHAR(c) ASCII-merkin jrjestysnumero

IN
IN

OPTIONAL

I = ICHAR(c) merkin jrjestysnumero


c
CHARACTER(LEN=1)
IN
I = INDEX(string,substring,back) osamerkkijonon alkukohta
string
CHARACTER
substring CHARACTER
back
LOGICAL
I = LEN(string) merkkijonon pituus

IN
IN
IN

OPTIONAL

I = LEN_TRIM(string) pituus ilman lopussa olevia vlilyntej


string
CHARACTER
IN
L = LGE(string_a,string_b) ASCII-jrjestyksen a b vertailu
L = LGT(string_a,string_b) ASCII-jrjestyksen a > b vertailu
L = LLE(string_a,string_b) ASCII-jrjestyksen a b vertailu
L = LLT(string_a,string_b) ASCII-jrjestyksen a < b vertailu
string_a
CHARACTER
string_b
CHARACTER
C = REPEAT(string,ncopies) toista merkkijonoa

IN
IN

string
CHARACTER
IN
ncopies
INTEGER
IN
C = SCAN(string,set,back) hae merkkej merkkijonosta
string
CHARACTER
set
CHARACTER
back
LOGICAL
C = TRIM(string) poista lopussa olevat vlilynnit

IN
IN
IN

OPTIONAL

string
CHARACTER
IN
I = VERIFY(string,set,back) tarkista, koostuuko merkkijono
annetuista merkeist
string
CHARACTER
IN
set
CHARACTER
IN
back
LOGICAL
IN
OPTIONAL

224

Fortran 95/2003

Taulukko 13.8: Taulukko-operaatioita.


Argumentti

Argumentin tyyppi

Suunta Valinnaisuus

L = ALL(mask,dim) tarkista mask-taulukon totuusarvoja


L = ANY(mask,dim) tarkista mask-taulukon totuusarvoja
I = COUNT(mask,dim) laske .true.-arvojen mrt
mask
LOGICAL, DIMENSION(n, . . . )
IN
dim
INTEGER
IN
OPTIONAL
* = CSHIFT(array,shift,dim) siirr matriisin arvoja kehss
array
taulukko
IN
shift
INTEGER
IN
dim
INTEGER
IN
OPTIONAL
* = EOSHIFT(array,shift,boundary,dim) siirr matriisin arvoja
array
taulukko
IN
shift
INTEGER
IN
boundary
sama tyyppi kuin array
IN
OPTIONAL
dim
INTEGER
IN
OPTIONAL
* = MERGE(tsource,fsource,mask) poimi arvoja kahdesta muuttujasta
tsource
mik tahansa
IN
fsource
sama kuin tsource
IN
mask
LOGICAL
IN
* = PACK(array,mask,vector) pakkaa halutut taulukon alkiot
array
mik tahansa taulukko
IN
mask
LOGICAL, DIMENSION(n, . . . )
IN
vector
samaa tyyppi kuin array
IN
* = UNPACK(vector,mask,field) pura vektorin alkiot
vector
taulukko, DIMENSION(n)
IN
mask
LOGICAL, DIMENSION(n, . . . )
IN
field
samaa tyyppi kuin vector
IN
* = RESHAPE(source,shape,pad,order) muunna taulukko
haluttuun muotoon
source
mik tahansa taulukko
IN
shape
INTEGER, DIMENSION(m)
IN
pad
taulukko tyyppi source
IN
order
sama kuin shape
IN
* = SPREAD(source,dim,ncopies) kopioidaan alkioita
source
mik tahansa taulukko
dim
INTEGER
ncopies
INTEGER
* = TRANSFER(source,mold,size) siirr dataa
source
mold
size

mik tahansa taulukko tai skalaari


mik tahansa taulukko tai skalaari
INTEGER

OPTIONAL

OPTIONAL
OPTIONAL

IN
IN
IN
IN
IN
IN

OPTIONAL

13. Standardiproseduurit

225

Taulukko 13.9: Lis taulukko-operaatioita.


Argumentti

Argumentin tyyppi

Suunta Valinnaisuus

I(:) = MAXLOC(array,mask) maksimiarvon sijainti


I(:) = MAXLOC(array,dim,mask)
I(:) = MINLOC(array,mask) minimiarvon sijainti
I(:) = MINLOC(array,dim,mask)
array

INTEGER, DIMENSION(n, . . . ) tai


REAL, DIMENSION(n, . . . )
dim
INTEGER
mask
LOGICAL, DIMENSION(n, . . . )
* = MAXVAL(array,dim,mask) maksimiarvo

IN
IN
IN

OPTIONAL

* = MINVAL(array,dim,mask) minimiarvo
array

INTEGER, DIMENSION(n, . . . ) tai


IN
REAL, DIMENSION(n, . . . )
dim
INTEGER
IN
mask
LOGICAL, DIMENSION(n, . . . )
IN
I = LBOUND(array,dim) taulukkoindeksien alaraja(t)

OPTIONAL
OPTIONAL

I = UBOUND(array,dim) taulukkoindeksien ylraja(t)


array
taulukko
IN
dim
INTEGER
IN
I = SHAPE(source) argumentin muoto (ulottuvuuksien koot)
source
mik tahansa taulukko tai skalaari
I = SIZE(array,dim) taulukon alkioiden lukumr

OPTIONAL

IN

array
mik tahansa taulukko
IN
dim
INTEGER
IN
* = PRODUCT(array,dim,mask) taulukon alkioiden tulo

OPTIONAL

* = SUM(array,dim,mask) taulukon alkioiden summa


array
numeerinen taulukko
IN
dim
INTEGER
IN
OPTIONAL
mask
LOGICAL, DIMENSION(n, . . . )
IN
OPTIONAL
* = DOT_PRODUCT(vector_a,vector_b) vektorien pistetulo
vector_a
numeerinen tai looginen
vector_b
sama kuin vector_a
* = MATMUL(matrix_a,matrix_b) matriisitulo

IN
IN

matrix_a
numeerinen tai looginen matriisi
matrix_b
numeerinen tai looginen matriisi
* = TRANSPOSE(matrix) matriisin transpoosi

IN
IN

matrix

taulukko, DIMENSION(m,n)

IN

226

Fortran 95/2003

Taulukko 13.10: Proseduureja bittien ksittelyyn.


Argumentti

Argumentin tyyppi

I = BIT_SIZE(i) kokonaisluvun bittien mr


i
INTEGER
L = BTEST(i,pos) tutki halutun bitin tilaa

Suunta Valinnaisuus

IN

I = IBCLR(i,pos) aseta haluttu bitti nollaksi


i
INTEGER
IN
pos
INTEGER
IN
I = IBITS(i,pos,len) hae kokonaisluvusta jono bittej
i
INTEGER
pos
INTEGER
len
INTEGER
I = IBSET(i,pos) aseta haluttu bitti ykkseksi

IN
IN
IN

i
INTEGER
IN
pos
INTEGER
IN
I = IAND(i,j) kokonaislukujen looginen and-operaatio
I = IEOR(i,j) biteittinen xor-operaatio (exclusive or)
I = IOR(i,j) biteittinen or-operaatio
i
INTEGER
j
sama kuin i
I = NOT(i) biteittinen looginen komplementti
i

INTEGER

IN
IN
IN

I = ISHFT(i,shift) siirr bittej oikealle tai vasemmalle


i
INTEGER
shift
INTEGER
I = ISHFTC(i,shift,size) siirr bittej kehss

IN
IN

i
INTEGER
IN
shift
INTEGER
IN
size
INTEGER
IN
OPTIONAL
CALL MVBITS(from,frompos,len,to,topos) kopioi bittej muuttujasta
toiseen
from
INTEGER
IN
frompos
INTEGER
IN
len
INTEGER
IN
to
INTEGER
INOUT
topos
INTEGER
IN

13.5

Esimerkki: bittienksittelyoperaatiot

Fortran 95:n bittienksittelyproseduurit perustuvat USA:n armeijan standardiin (MIL-STD 1753). Useimmat bittioperaatiot ovat alkioittaisia, joten niille
voi antaa mys taulukkoargumentteja.
Bittioperaatiot ksittelevt kokonaislukuja, joissa on jonossa s bitti wk ,
k = 0, 1, . . . , s 1. Bittien jrjestys on oikealta vasemmalle, ja kokonaisluvun

13. Standardiproseduurit

227

ei-negatiivinen arvo on
s1

wk 2k .

k=0

Tm kokonaislukujen esitysmalli ptee vain bittioperaatioiden yhteydess.


Bittien ksittelyproseduurit on esitelty taulukossa 13.10 sivulla 226. Seuraavassa on muutamia esimerkkej proseduurien kytst.
Kokonaislukumuuttujan i lajiin sisltyvien bittien lukumrn (edell s) saa
selville funktiokutsulla
BIT_SIZE(i)

Tietyn bitin arvon saa selville funktiokutsulla BTEST(i,pos), miss kokonaislukumuuttujan pos arvo on vlilt 0 pos < BIT_SIZE(i). Funktio
palauttaa loogisen arvon .TRUE. tai .FALSE..
Funktiokutsu IAND(i,j) palauttaa kokonaislukujen i ja j vastaaville biteille tehdyn and-operaation tuloksen. Seuraavassa on esimerkki funktion
toiminnasta:
i
j
IAND(i,j)

0
0
0

0
1
0

1
0
0

1
1
1

Kokonaislukujen i ja j tytyy olla samaa lajia. Funktio IAND palauttaa arvonaan tt lajia olevan kokonaisluvun.
Funktio ISHFTC(i,shift,size) palauttaa kokonaisluvun, jossa size oikeanpuoleista bitti on siirretty kehss |shift| paikkaa vasemmalle (tai
oikealle, jos shift on negatiivinen).
Aliohjelmakutsu
CALL MVBITS(from,frompos,len,to,topos)

kopioi kokonaisluvun from bittej kokonaislukuun to. Kopiointi alkaa paikassa frompos olevasta bitist. Kopioitavan bittijonon pituus on len. Bitit
kopioidaan paikasta topos alkaen. Argumenttien tytyy tytt seuraavat
ehdot: len 0, frompos 0, frompos+len BIT_SIZE(from), topos 0
ja topos+len BIT_SIZE(to).

13.6

Esimerkki: data pakkaaminen

Pieni kokonaislukuja voi pakata suuren tarkkuuden kokonaislukuun, koska


posa biteist on kyttmttmi. Seuraava ohjelma lukee ptteelt nelj pient kokonaislukua ja pakkaa nm kokonaisluvun sisllksi. Funktiota
bitit kytetn bittijonojen tulostamiseen ptteelle.

228

Fortran 95/2003

PROGRAM bittioperaatiot
IMPLICIT NONE
INTEGER, PARAMETER :: &
kok_luku = SELECTED_INT_KIND(9), bit_len = 8
INTEGER(KIND=kok_luku), DIMENSION(4) :: luvut
INTEGER :: i
WRITE (*,(A,I3)) Syt nelj kokonaislukua vlilt &
&0 ... , 2**bit_len-1
READ (*,*) luvut
DO i = 1, SIZE(luvut)
WRITE (*,(A,I1,":",7X,A)) Data , i, bitit(luvut(i))
END DO
WRITE (*,(A,A)) Pakattu data: , bitit(pakkaa(luvut))
CONTAINS
FUNCTION bitit(n)
IMPLICIT NONE
INTEGER(KIND=kok_luku), INTENT(IN) :: n
CHARACTER(LEN=4*bit_len) :: bitit
INTEGER :: i, lkm
bitit =
lkm = MIN(LEN(bitit), BIT_SIZE(n))
DO i = 0, lkm-1
IF(BTEST(n,i)) THEN
bitit(lkm-i:lkm-i) = 1
ELSE
bitit(lkm-i:lkm-i) = 0
END IF
END DO
END FUNCTION bitit
FUNCTION pakkaa(taulu)
IMPLICIT NONE
INTEGER, DIMENSION(:), INTENT(IN) :: taulu
INTEGER(KIND=kok_luku) :: pakkaa
INTEGER :: i, paikka
pakkaa = 0
DO i = 1, SIZE(taulu)
paikka = bit_len*(i-1)
IF ((paikka + bit_len) > BIT_SIZE(pakkaa)) RETURN
CALL MVBITS(taulu(i), 0, bit_len, pakkaa, paikka)
END DO
END FUNCTION pakkaa
END PROGRAM bittioperaatiot

Sisinen funktio bitit muuntaa kokonaisluvun merkkijonoksi, jossa esiintyy kokonaisluvun bittej vastaavat merkit 0 ja 1. Sisinen funktio
pakkaa siirt kokonaislukutaulukon taulu merkitsevt bitit funktion arvona palautettavan kokonaisluvun sislle.
Ohjelman kytt voisi nytt tlt:
Syt nelj kokonaislukua vlilt 0 ... 255
100 127 254 209
Luvut ovat: 100, 127, 254, 209
Data 1:
00000000000000000000000001100100
Data 2:
00000000000000000000000001111111

13. Standardiproseduurit

229

Data 3:
00000000000000000000000011111110
Data 4:
00000000000000000000000011010001
Pakattu data: 11010001111111100111111101100100

Datan purkaminen pakatusta muodosta on harjoitustehtvn.

13.7

Standardifunktioiden erityisnimet

Taulukoissa 13.1 . . . 13.10 on esitetty standardiproseduurien geneeriset


nimet. Kntj valitsee knnsaikana argumenttien tyypin perusteella oikean version standardiproseduurista.
Standardifunktioita voi kytt mys proseduurien todellisina argumentteina, mutta tllin on kytettv geneeristen nimien sijasta standardifunktioiden erityisnimi (specic name). Tm johtuu siit, ett vlitettess standardifunktio proseduuriin kntj ei osaa sijoittaa todellisen argumentin
paikalle standardifunktion oikeaa versiota. Standardifunktioiden erityisnimi on lueteltu taulukoissa 13.11 ja 13.12.
Taulukossa 13.11 on lueteltu ne standardifunktioiden erityisnimet, joita saa
kytt proseduurien todellisina argumentteina. Fortranin standardialiohjelmia ei saa kytt proseduurien todellisina argumentteina. Lisksi taulukossa 13.12 esitettyjen standardifunktioiden erityisnimi ei saa kytt
todellisina argumentteina.
Tyyppi I tarkoittaa kokonaislukua, R reaalilukua, D kaksoistarkkuuden reaalilukua, Z kompleksilukua ja C merkkityyppi. Erityisnimi vastaavien argumenttien ja tulosten tyypit on lueteltu vastaavassa jrjestyksess.
Seuraavassa on esimerkki erityisnimien kytst:
PROGRAM erityis
IMPLICIT NONE
INTRINSIC SIN, CSIN
REAL :: x = 1.0, y = 0.0
WRITE (*,*) Arvot: , SIN(x), SIN(CMPLX(x,y))
WRITE (*,*) Erotus: , testi(SIN, CSIN, x, y)
CONTAINS
REAL FUNCTION testi(f1, f2, x, y)
IMPLICIT NONE
REAL, EXTERNAL :: f1
COMPLEX, EXTERNAL :: f2
REAL, INTENT(IN) :: x, y
testi = ABS(f1(x) - f2(CMPLX(x,y)))
END FUNCTION testi
END PROGRAM erityis

230

Fortran 95/2003

Pohjelmassa tulostettiin sinifunktion arvot kytten geneerist nime SIN.


Lisksi sisiseen funktioon testi vlitettiin sinifunktion reaali- ja kompleksilukuargumentteja vastaavat erityisnimet SIN ja CSIN.
Ohjelman tulostus voisi nytt tlt:
Arvot:
Erotus:

0.8414710
(0.8414710,0.0000000E+00)
0.0000000E+00

Taulukko 13.11: Standardifunktioiden erityisnimi. Nit erityisnimi saa kytt


todellisina argumentteina.

Geneerinen nimi

Erityisnimet

Arg. tyyppi Tulos

AINT(a)
ANINT(a)
NINT(a)
ABS(a)
MOD(a,p)
SQRT(x)
EXP(x)
LOG(x)
LOG10(x)
SIN(x)
COS(x)
TAN(x)
ASIN(x)
ACOS(x)
ATAN(x)
ATAN2(y,x)
SINH(x)
COSH(x)
TANH(x)
AIMAG(z)
CONJG(z)
LEN(s)
INDEX(s,t)
SIGN(a,b)
DIM(x,y)

AINT, DINT
ANINT, DNINT
NINT, IDNINT
IABS, ABS, DABS, CABS
MOD, AMOD, DMOD
SQRT, DSQRT, CSQRT
EXP, DEXP, CEXP
ALOG, DLOG, CLOG
ALOG10, DLOG10
SIN, DSIN, CSIN
COS, DCOS, CCOS
TAN, DTAN
ASIN, DASIN
ACOS, DACOS
ATAN, DATAN
ATAN2, DATAN2
SINH, DSINH
COSH, DCOSH
TANH, DTANH
AIMAG
CONJG
LEN
INDEX
ISIGN, SIGN, DSIGN
IDIM, DIM, DDIM
DPROD(x,y)

R, D
R, D
R, D
I, R,
I, R,
R, D,
R, D,
R, D,
R, D
R, D,
R, D,
R, D
R, D
R, D
R, D
R, D
R, D
R, D
R, D
Z
Z
C
C
I, R,
I, R,
R

13.8

D, Z
D
Z
Z
Z
Z
Z

D
D

R,
R,
I
I,
I,
R,
R,
R,
R,
R,
R,
R,
R,
R,
R,
R,
R,
R,
R,
R
Z
I
I
I,
I,
D

D
D
R, D, Z
R, D
D, Z
D, Z
D, Z
D
D, Z
D, Z
D
D
D
D
D
D
D
D

R, D
R, D

Listietoja

Tss esitetty laajempi Fortranin standardiproseduurien kuvaus lytyy ksikirjoista Fortran 90 Handbook [ABM+ 92] ja Fortran 95 Handbook [ABM+ 97]
sek Fortran-kntjien ksikirjoista. Mys teos Fortran 95 Language Guide [Geh96] soveltuu ksikirjaksi.

13. Standardiproseduurit

231

Taulukko 13.12: Standardifunktioiden erityisnimi. Niden standardifunktioiden


erityisnimi ei saa kytt todellisina argumentteina.

13.9

Geneerinen nimi

Erityisnimi Arg. tyyppi Tulos

INT(a)

INT
IFIX
IDINT

R
R
D

I
I
I

REAL(a)

REAL
FLOAT
SNGL

I
I
D

R
R
R

MAX(a1,a2,...)

MAX0
AMAX1
DMAX1
AMAX0
MAX1

I
R
D
I
R

I
R
D
R
I

MIN(a1,a2,...)

MIN0
AMIN1
DMIN1
AMIN0
MIN1

I
R
D
I
R

I
R
D
R
I

Yhteenveto

Fortran 95:een sisltyy yli sata standardiproseduuria, joita kannattaa kytt hyvksi omassa ohjelmointityss. Nm proseduurit ovat tehokas ja
luotettava tapa toteuttaa ohjelmoinnin perusoperaatioita.

Harjoitustehtvi
1. Ratkaise yhtln x n = 1 kaikki kompleksiset juuret (vihje: e2 k i = 1,
k = 0, 1, . . . ).
2. Muuta kappaleen 13.4 satunnaislukuja tuottavaa ohjelmaa siten, ett
kyttj pyydetn syttmn siemenluku.
3. Kirjoita aliohjelma, joka muuntaa napakoordinaattiesityksen (r , ) karteesisiksi koordinaateiksi (x, y). Kirjoita mys pinvastaisen muunnoksen tekev aliohjelma. Kyt hyvksi mritelmi
sin = y/r ,

cos = x/r ,


r = x2 + y 2 .

4. Laske viisialkioisten reaalilukutyypin vektoreiden sistulo standardifunktiolla DOT_PRODUCT.

232

Fortran 95/2003

5. Laske 5 4 ja 4 3 -kokoisten matriisien A ja B tulo C = AB standardifunktiolla MATMUL. Laske mys tulo C T = B T AT , miss B T on matriisin
B transpoosi jne.
6. Etsi kyttjn syttmst korkeintaan 80 merkin pituisesta merkkijonosta ensimmisen numeromerkin sijainti kytten SCAN-funktiota.
7. Kirjoita looginen funktio numerot, joka ilmoittaa, koostuuko argumenttina annettu merkkijono pelkstn numeromerkeist. Kyt apuna standardifunktiota VERIFY.
8. Mrittele aliohjelma suorakulmio, joka tulostaa halutun kokoisia suorakulmioita. Aliohjelmakutsun CALL suorakulmio(3,5) tulostus voisi nytt tlt:
+---+
!
!
+---+

Kyt apunasi REPEAT-funktiota.


9. Lis edellisen tehtvn aliohjelmaan valinnainen argumentti, joka kertoo kuinka monta kertaa vierekkin suorakulmio tulostetaan. Aliohjelmakutsun CALL suorakulmio(3,5,6) tulostus voisi nytt seuraavalta:
+---+ +---+ +---+ +---+ +---+ +---+
!
! !
! !
! !
! !
! !
!
+---+ +---+ +---+ +---+ +---+ +---+

10. Lis kappaleen 13.6 ohjelmaan funktio, joka purkaa kokonaislukumuuttujaan pakatut pienet positiiviset kokonaisluvut. Funktion tulee
palauttaa arvonaan neljn alkion mittainen kokonaislukutaulukko.
11. Tee salasanageneraattori, joka muodostaa ja tulostaa 8 merkist koostuvan satunnaisen salasanan, joka koostuu isoista ja pienist kirjaimista,
numeroista sek erikoismerkeist
!"#%&/()=?+*;:,._-<>

12. Kirjoita rutiini, joka palauttaa n alkiota vlilt [a, b] tasavlein:


a(1:n) = interval_real(a, b, n)

13. Kirjoita funktiot, jotka palauttavat arvoinaan yksiulotteisen reaalilukutaulukon, jonka alkiot muodostavat
(a) aritmeettisen jonon, jonka alkuarvo ja askel on annettu
(b) geometrisen jonon, jonka alkuarvo ja kerroin on annettu.
Funktioiden kutsut voisivat nytt seuraavilta:
a(1:n) = seq_real(start, inc, n)
b(1:n) = geomseq_real(start, coeff, n)

14. Kirjoita funktiot, jotka palauttavat yksiulotteisen reaalilukutaulukon


maksimi- ja minimialkioiden indeksin kokonaislukumuuttujaan. Funktiolle voi antaa mys valinnaisen argumentin, jolloin vain osa taulukon
alkioista otetaan mukaan maksimiarvon etsintn:

13. Standardiproseduurit

233

i = maxloci_real(vector,mask)
i = miniloci_real(vector,mask)

15. Kirjoita edellist tehtv vastaavat funktiot kokonaislukuargumenteille. Sijoita funktiot moduuliin ja mrittele niille geneeriset nimet, esimerkiksi maxloci ja miniloci.

234

Fortran 95/2003

14

Esimerkkiohjelmia

Tss luvussa kertaamme Fortran 95 -kielen mahdollisuuksia aikaisemmin


esitettyj asioita valaisevien esimerkkien avulla:

14.1

Aihe

Kappale Sivu

Nelilaskin
Datan lajittelu
Sokkelon tekeminen
Yksiulotteinen soluautomaatti
Life-soluautomaatti
Binripuu
Sukupuu
Optimointi: evoluutiostrategiat
Lineaaristen yhtlryhmien ratkaisu

14.1
14.2
14.3
14.4
14.5
14.6
14.7
14.8
14.9

234
237
243
247
253
256
261
263
269

Nelilaskin

Knteist puolalaista logiikkaa toteuttava nelilaskin toimii seuraavasti. Olkoon sytteen


5 Return 2.2 Return * Return

Tuloksena on luku 11 eli kertolaskun 5 2.2 tulos. Vastaavasti syte


10 Return 3 Return / Return

tuottaa tulokseksi luvun 3.333 . . . eli jakolaskun 10/3 tuloksen. Seuraavassa


on Fortran-ohjelma, joka simuloi tllaista laskinta. Laskin tuntee operaatiot
+, -, * ja / sek komennot Q (lopeta) ja P (tulosta luku).
PROGRAM nelilaskin
! Yksinkertainen rpn-laskin
! Pinon ksittelyoperaatiot:
USE pinomoduuli, ONLY : push, pop
IMPLICIT NONE
CHARACTER (LEN=*), PARAMETER :: kehote = rpn>
CHARACTER (LEN=80) :: rivi

14. Esimerkkiohjelmia

235

REAL :: a, b
silmukka: DO
WRITE(UNIT=*,FMT=(A),ADVANCE=no) kehote
READ (*,(A)) rivi
rivi = ADJUSTL(rivi)
IF (LEN_TRIM(rivi) /= 1) THEN
CALL lue_syote(rivi)
ELSE
SELECT CASE (rivi(1:1))
CASE (+)
a = pop(); b = pop(); CALL push(b+a)
CASE (-)
a = pop(); b = pop(); CALL push(b-a)
CASE (*)
a = pop(); b = pop(); CALL push(b*a)
CASE (/)
a = pop(); b = pop(); CALL push(b/a)
CASE (P, p)
a = pop(); WRITE (*,*) a
CASE (Q, q)
STOP
CASE DEFAULT
CALL lue_syote(rivi)
END SELECT
END IF
END DO silmukka
CONTAINS
SUBROUTINE lue_syote(rivi)
IMPLICIT NONE
CHARACTER (LEN=*), INTENT(IN) :: rivi
INTEGER :: tila
REAL :: luku
tila = 0
READ (rivi,*,IOSTAT=tila) luku
IF (tila == 0) THEN
CALL push(luku)
ELSE
WRITE (*,*) Syntaksivirhe - viimeisin &
&sytttietorivi hyltty!
END IF
END SUBROUTINE lue_syote
END PROGRAM nelilaskin

Mrittelemme moduulin pinomoduuli myhemmin tss kappaleessa. Tst moduulista ovat perisin pinon ksittelyoperaatiot pop ja push.
Ohjelma lukee kyttjn antaman syterivin 80 merkin pituiseen merkkijonomuuttujaan rivi. Alussa siirretn vlilynnit sytteen alusta loppuun.
Jos sytteess on enemmn kuin yksi vlilynnist poikkeava merkki, kyseess on luvun sisltv syte tai virhetilanne. Jos sytteen pituus on yksi
vlilynnist poikkeava merkki, on kyseess komento, yksittinen numeromerkki tai virhetilanne.
Seuraavassa on ohjelman kyttesimerkki:
rpn> 5

236

Fortran 95/2003

rpn> 2.2
rpn> *
rpn> p
11.00000000
rpn> 10
rpn> 3
rpn> /
rpn> p
3.333333254
rpn> q

Moduulin pinomoduuli voi toteuttaa esimerkiksi seuraavasti:


MODULE pinomoduuli
! Tm moduuli toteuttaa yksinkertaisen pinon
IMPLICIT NONE
PRIVATE
INTEGER, PARAMETER :: maksimikoko = 100
TYPE :: pinotyyppi
REAL, DIMENSION(maksimikoko) :: arvot
INTEGER :: huippu
END TYPE pinotyyppi
TYPE(pinotyyppi) :: pino = pinotyyppi(0.0, 1)
PUBLIC :: pop, push
CONTAINS
SUBROUTINE push(arvo)
! Laitetaan pinoon reaaliluku
IMPLICIT NONE
REAL, INTENT(IN) :: arvo
IF (pino%huippu > maksimikoko) THEN
WRITE (*,*) "Pino on tysi"
ELSE
pino%arvot(pino%huippu) = arvo
pino%huippu = pino%huippu + 1
END IF
END SUBROUTINE push
REAL FUNCTION pop()
! Pinon pllimminen luku palautetaan
IMPLICIT NONE
IF (pino%huippu <= 1) THEN
WRITE (*,*) "Pino on tyhj"
pop = 0
ELSE
pino%huippu = pino%huippu - 1
pop = pino%arvot(pino%huippu)
END IF
END FUNCTION pop
END MODULE pinomoduuli

Moduulissa kytetty tyyppi pinotyyppi on yksityinen, sill moduulin sisltmt tieto-oliot on piilotettu kyttmll PRIVATE-lausetta. Tten voimme muuttaa pinon toteutuksen yksityiskohtia moduulin toiminnallisuuden
muuttumatta.

14. Esimerkkiohjelmia

14.2

237

Datan lajittelu

Tss esimerkiss lajittelemme yksiulotteisia taulukkoja suuruusjrjestykseen. Ensimmisen lajittelualgoritmina kytmme sijoituslajittelua (insertion sort), joka on yksinkertainen ja riittv pienelle datamrlle (parikymment alkiota). Tmn jlkeen toteutamme Shellsort-lajittelualgoritmin
nousuindeksin laskemista varten.

14.2.1 Sijoituslajittelu
Voimme mritell reaalilukutaulukon lajittelevan aliohjelman lajittele_r
seuraavasti:
MODULE jarjestys
IMPLICIT NONE
CONTAINS
SUBROUTINE lajittele_r(taulu)
IMPLICIT NONE
REAL, INTENT(INOUT), DIMENSION(:) :: taulu
REAL :: apu
INTEGER :: i, j, n
n = SIZE(taulu)
DO i = 2, n
apu = taulu(i)
DO j = i, 2, -1
IF (taulu(j-1) <= apu) EXIT
taulu(j) = taulu(j-1)
END DO
taulu(j) = apu
END DO
END SUBROUTINE lajittele_r
END MODULE jarjestys

Aliohjelmaan lajittelu vlitetn lajiteltava taulukko taulu. Aliohjelma


palauttaa taulukon alkiot lajiteltuna kasvavaan jrjestykseen. Lajiteltavan
taulukon koko saadaan selville aliohjelmakutsulla SIZE.
Voimme kytt lajittelumoduulia seuraavasti. Aliohjelma lajittele_r otetaan kyttn USE-lauseessa nimell lajittele.
PROGRAM lajittelu_testi
USE jarjestys, ONLY : lajittele => lajittele_r
IMPLICIT NONE
REAL, DIMENSION(:), ALLOCATABLE :: d
INTEGER :: n, i, tila
WRITE (*,(A),ADVANCE=NO) Anna n>1:
READ (*,*) n
IF (n <= 1) THEN
STOP n <= 1
ELSE
ALLOCATE(d(n), STAT=tila)
IF (tila /= 0) THEN

238

Fortran 95/2003

STOP Muistinvaraus eponnistui!


ELSE
CALL RANDOM_NUMBER(d)
WRITE (*,(A,/,8(F9.5))) Data:, d
CALL lajittele(d)
WRITE (*,(A,/,8(F9.5))) Lajiteltu data:, d
END IF
END IF
END PROGRAM lajittelu_testi

Ohjelma lukee ptteelt lajiteltavan reaalilukutaulukon koon. Taulukolle d


varataan tila ALLOCATE-lauseessa ja tmn jlkeen taulukko tytetn satunnaisluvuilla. Lajittelu tehdn aliohjelmakutsulla
CALL lajittele(d)

14.2.2 Geneerinen lajitteluproseduuri


Moduulia jarjestys voi laajentaa ksittelemn mys kokonaisluku- ja
merkkijonotaulukoita. Kytmme geneerist nime lajittele eri lajitteluproseduureille:
MODULE jarjestys
IMPLICIT NONE
PRIVATE
INTERFACE lajittele
MODULE PROCEDURE lajittele_r, lajittele_i, &
lajittele_c
END INTERFACE
PUBLIC :: lajittele
CONTAINS
Lajitteluproseduurien mrittelyt
END MODULE jarjestys

Aliohjelma lajittele_i on lhestulkoon edell esitellyn lajittele_r-aliohjelman kopio, ainoa muutos tulee riveille:
SUBROUTINE lajittele_i(taulu)
IMPLICIT NONE
INTEGER, DIMENSION(:), INTENT(INOUT) :: taulu
INTEGER :: apu
jne.
END SUBROUTINE lajittele_i

Merkkijonotaulukkojen lajitteluun sopii seuraava aliohjelma:


SUBROUTINE lajittele_c(taulu)
IMPLICIT NONE
CHARACTER(LEN=*), DIMENSION(:), INTENT(INOUT) :: taulu
CHARACTER(LEN=LEN(taulu(1))) :: apu
jne.
END SUBROUTINE lajittele_c

14. Esimerkkiohjelmia

239

Tmkin aliohjelma on muilta osin reaalilukuja lajittelevan aliohjelman suora kopio, sill mys merkkijonojen jrjestyksen vertailuun voi kytt operaattoria <=.
Voimme kytt geneerisen lajitteluproseduurin lajittele sisltv moduulia seuraavasti:
PROGRAM lajittelu_testi
USE jarjestys, ONLY : lajittele
IMPLICIT NONE
REAL, DIMENSION(:), ALLOCATABLE :: dr
INTEGER, DIMENSION(:), ALLOCATABLE :: di
CHARACTER(LEN=8), DIMENSION(:), ALLOCATABLE :: dc
INTEGER :: n, i, tila
WRITE (*,(A),ADVANCE=NO) Anna n>1:
READ (*,*) n
IF (n <= 1) THEN
STOP n <= 1
ELSE
ALLOCATE(dr(n), di(n), dc(n), STAT=tila)
IF (tila /= 0) THEN
STOP Muistinvaraus eponnistui!
ELSE
CALL RANDOM_NUMBER(dr)
di = 1e6*dr
dc = teksti-
dc(:)(8:8) = CHAR(IACHAR( ) + &
MOD(di,1+IACHAR(z)-IACHAR( )))
WRITE (*,(A,/,6(F9.5))) Data:, dr
CALL lajittele(dr)
WRITE (*,(A,/,6(F9.5))) Lajiteltu data:, dr
WRITE (*,(A,/,6(I9))) Data:, di
CALL lajittele(di)
WRITE (*,(A,/,6(I9))) Lajiteltu data:, di
WRITE (*,(A,/,6(A9))) Data:, dc
CALL lajittele(dc)
WRITE (*,(A,/,6(A9))) Lajiteltu data:, dc
END IF
END IF
END PROGRAM lajittelu_testi

Pohjelmassa tytetn reaalilukutaulukko dr satunnaisluvuilla reaalilukuvlilt [0, 1). Kokonaislukutaulukon di arvot saadaan kertomalla nm
reaaliluvut miljoonalla. Merkkitaulukon dc arvoiksi asetetaan merkkijonot
teksti-c, miss c on satunnainen ASCII-merkki vlilynnin ja z-kirjaimen vlilt (katso liitett D sivulla 324).
Ohjelman tulostus nytt tlt:
Anna n>1: 11
Data:
0.11639 0.96485

0.88297

0.42049

0.49586

0.57739

240

Fortran 95/2003

0.94234
Lajiteltu
0.11639
0.57739
Data:
116391
942340
Lajiteltu
116391
577386
Data:
teksti-"
teksti-C
Lajiteltu
teksti-"
teksti-b

0.24316
data:
0.24316
0.68923

0.55013

0.68923

0.47643

0.42049
0.88297

0.47643
0.94234

0.49586
0.96485

0.55013

964846
243162
data:
243162
689230

882970
550126

420486
689230

495856
476426

577386

420486
882970

476426
942340

495856
964846

550126

teksti-
teksti-*
data:
teksti-*
teksti-r

teksti-x teksti-b teksti-x teksti-r


teksti-? teksti-w teksti-I
teksti-? teksti-C teksti-I teksti-
teksti-w teksti-x teksti-x

14.2.3 Nousuindeksin laskeminen


Nousuindeksi on kokonaislukutaulukko, jonka avulla taulukon alkiot saadaan indeksoitua nousevaan jrjestykseen. Nousuindeksi voidaan laskea
Shellsort-algoritmia kytten (katso esimerkiksi teoksia [CLR90, Sed84]):
MODULE nousu
IMPLICIT NONE
PRIVATE
PUBLIC :: nousuindeksi
CONTAINS
FUNCTION nousuindeksi(taulu) RESULT(idx)
! Shellsort-algoritmi
IMPLICIT NONE
REAL, DIMENSION(:), INTENT(IN) :: taulu
INTEGER, DIMENSION(SIZE(taulu)) :: idx
INTEGER :: n, askel, i, j, apuidx
n = SIZE(taulu)
idx = (/ (i, i = 1, n) /)
askel = 1
DO WHILE (askel <= n)
askel = 3*askel + 1
END DO
DO
askel = askel/3
DO i = askel+1, n
apuidx = idx(i)
j = i
DO WHILE (j > askel)
IF (taulu(idx(j-askel)) <= taulu(apuidx)) EXIT
idx(j) = idx(j-askel)
j = j - askel
END DO
idx(j) = apuidx
END DO

14. Esimerkkiohjelmia

241

IF (askel == 1) EXIT
END DO
END FUNCTION nousuindeksi
END MODULE nousu

Funktio nousuindeksi palauttaa tuloksen taulukossa idx. Shellsort-algoritmi on kohtalaisen nopea, vaikkakin isoille taulukoille (tuhansia alkioita) olisi
Quicksort-algoritmi luultavasti nopeampi.
Ohjelmakoodissa on kytetty Fortran 95:n ohjausrakenteita DO ... END DO
ja DO WHILE ... END DO sek EXIT-lausetta. Indeksitaulu idx alustetaan
kytten Fortranin taulukkoalustinta (/ ... /).
Olkoon lajiteltavana reaalilukutaulukko tiedot, jolloin nousuindeksin laskeminen ja lajittelu voitaisiin tehd seuraavasti:
PROGRAM lajittelu_testi
! Otetaan kyttn lajittelufunktio:
USE nousu, ONLY : nousuindeksi
IMPLICIT NONE
REAL, DIMENSION(:), ALLOCATABLE :: tiedot
INTEGER, DIMENSION(:), ALLOCATABLE :: indeksit
INTEGER :: n, tila
WRITE (*,(A),ADVANCE=NO) Anna n>1:
READ (*,*) n
IF (n <= 1) THEN
STOP n <= 1
ELSE
ALLOCATE(tiedot(n), indeksit(n), STAT=tila)
IF (tila /= 0) THEN
STOP Virhe muistinvarauksessa!
END IF
CALL RANDOM_NUMBER(tiedot)
WRITE (*,(A,/,8(F9.5))) Data:, tiedot
indeksit = nousuindeksi(tiedot)
WRITE (*,(A,/,8(F9.5))) Lajiteltu data:, &
tiedot(indeksit)
END IF
END PROGRAM lajittelu_testi

Nousuindeksi lasketaan kokonaislukutaulukkoon indeksit:


indeksit = nousuindeksi(tiedot)

Lajiteltu taulukko voidaan tulostaa indeksoimalla taulukkoa tiedot indeksitaulukolla indeksit.


Koska nousuindeksi-funktiossa on kytetty epsuoraa indeksointia, on koodi tehottomampi kuin suoraviivainen Shellsort-algoritmin toteutus. Nousuindeksi voidaan kuitenkin kytt useamman taulukon lajitteluun yhteisen
lajittelukriteerin perusteella.

242

Fortran 95/2003

14.2.4 Monen taulukon lajittelu


Voimme kytt nousuindeksi lajittelemaan useampi taulukko yhteisen lajittelukriteerin mukaan. Seuraavassa on esimerkki vaikkapa moduuliin nousu
listtvst lajittelualiohjelmasta:
SUBROUTINE lajittele(taulu, lisataulu1, lisataulu2)
! Taulukkojen lajittelu indeksitaulun avulla
IMPLICIT NONE
REAL, DIMENSION(:), INTENT(INOUT) :: taulu
REAL, DIMENSION(:), INTENT(INOUT), OPTIONAL :: &
lisataulu1, lisataulu2
INTEGER, DIMENSION(SIZE(taulu)) :: indeksit
indeksit = nousuindeksi(taulu)
taulu = taulu(indeksit)
IF (PRESENT(lisataulu1)) THEN
IF (SIZE(taulu) /= SIZE(lisataulu1)) THEN
STOP Taulukot eri kokoiset!
ELSE
lisataulu1 = lisataulu1(indeksit)
END IF
END IF
IF (PRESENT(lisataulu2)) THEN
IF (SIZE(taulu) /= SIZE(lisataulu2)) THEN
STOP Taulukot eri kokoiset!
ELSE
lisataulu2 = lisataulu2(indeksit)
END IF
END IF
END SUBROUTINE lajittele

Aliohjelmassa lajittele lasketaan nousuindeksi taulukolle taulu, ja tmn jlkeen argumentti taulu lajitellaan nousuindeksin avulla. Jos aliohjelmalle lajittele on annettu valinnaiset (OPTIONAL) argumentit lisataulu1
tai lisataulu2, lajitellaan nm saman nousuindeksin avulla. Valinnaisten argumenttien antaminen aliohjelmakutsussa voidaan selvitt standardifunktion PRESENT avulla. Funktio SIZE palauttaa taulukon koon.
Voimme kytt tt lajittelualiohjelmaa esimerkiksi seuraavasti:
PROGRAM nousukas
USE nousu, ONLY : lajittele
IMPLICIT NONE
REAL, DIMENSION(100) :: d1, d2, d3
CALL RANDOM_NUMBER(d1)
d2 = 10.0 - d1
d3 = 2.0*(d1 - 0.5)
CALL lajittele(d1, d2, d3)
END PROGRAM nousukas

14. Esimerkkiohjelmia

243

Tss lajittelemme taulukot d1, d2 ja d3 kytten lajittelujrjestyksen taulukkoa d1.

14.3

Sokkelon tekeminen

Tehtvn on luoda sokkeloruudukko, jossa on tsmlleen yksi reitti sokkelon lpi vasemmasta ylkulmasta oikeaan alakulmaan. Tehtv voidaan
jakaa seuraaviin osavaiheisiin:
1. kysytn halutun sokkelon kokoa ja alustetaan laskentatehtv
2. luodaan sokkelo
3. tulostetaan muodostettu sokkelo.
Sokkelo voidaan luoda ehk helpoiten kaksiulotteiseen kokonaislukutaulukkoon. Sokkelon tekemisess tarvittava tieto vapaista ja kussakin vaiheessa
kytetyist ruuduista sijoitetaan thn taulukkoon.
Seuraavassa esitetn runko sokkelon tekevlle ohjelmalle:
PROGRAM sokkelo
IMPLICIT NONE
INTEGER, DIMENSION(:,:), ALLOCATABLE :: taulu
INTEGER :: leveys, korkeus
Alustetaan tehtv
Luodaan sokkelo
Tulostetaan sokkelo
END PROGRAM sokkelo

Pohjelmassa mriteltiin globaalit nimet taulu, leveys ja korkeus.


Sokkelon alustamisen voi tehd seuraavalla aliohjelmalla:
SUBROUTINE alusta
IMPLICIT NONE
INTEGER :: tila
WRITE (*,*) Anna sokkelon leveys ja korkeus:
READ (*,*) leveys, korkeus
IF (leveys <= 1 .OR. korkeus <= 1) &
STOP Liian pieni sokkelo!
ALLOCATE(taulu(0:korkeus+1,0:leveys+1), STAT=tila)
IF (tila /= 0) STOP Muistinvaraus eponnistui!
taulu(1:korkeus,1:leveys) = tyhja
taulu(0,:) = reuna
taulu(korkeus+1,:) = reuna
taulu(:,0) = reuna
taulu(:,leveys+1) = reuna
END SUBROUTINE alusta

Aliohjelmassa alusta kytetn pohjelman alussa mriteltvi globaaleja vakioita reuna ja tyhj, joiden arvot mrmme myhemmin.

244

Fortran 95/2003

Varsinaisen sokkelon luontiin ky seuraava itsen kutsuva aliohjelma:


RECURSIVE SUBROUTINE sijoita(k, l, n)
IMPLICIT NONE
INTEGER, INTENT(IN) :: k, l, n
taulu(k,l) = n
DO WHILE (.NOT. umpikuja(k,l))
SELECT CASE (arvaa(k,l))
CASE(alas)
CALL sijoita(k,l+1,n+1)
CASE(ylos)
CALL sijoita(k,l-1,n+1)
CASE(oikealle)
CALL sijoita(k+1,l,n+1)
CASE(vasemmalle)
CALL sijoita(k-1,l,n+1)
END SELECT
END DO
END SUBROUTINE sijoita

Aliohjelmassa tutkitaan, pstnk pisteest (k, l) kulkemaan eteenpin.


Tmn tarkistaa funktio umpikuja. Jos vapaita ruutuja lytyy taulukosta
taulu, valitsee funktio arvaa jonkin suunnan. Tt varten tytyy mritell
vaikkapa seuraavat arvot eri suuntia tarkoittaville vakioille ohjelmayksikn
alussa:
INTEGER, PARAMETER :: ylos = 1, alas = 2, &
oikealle = 3, vasemmalle = 4

Ohjelma siis simuloi diskreetti satunnaiskulkua (random walk) suorakulmaisessa hilassa. Umpikujaan joutumisen voi tarkistaa seuraavalla funktiolla:
LOGICAL FUNCTION umpikuja(k, l)
IMPLICIT NONE
INTEGER, INTENT(IN) :: k, l
umpikuja = taulu(k,l+1) /= tyhja .AND. &
taulu(k,l-1) /= tyhja .AND. &
taulu(k+1,l) /= tyhja .AND. &
taulu(k-1,l) /= tyhja
END FUNCTION umpikuja

Funktio umpikuja tarkistaa, onko taulukossa taulu vapaita viereisi ruutuja


vaaka- ja pystysuunnassa. Uuden suunnan voi tmn jlkeen valita seuraavasti:
INTEGER FUNCTION arvaa(k,l)
IMPLICIT NONE
INTEGER, INTENT(IN) :: k, l
INTEGER, DIMENSION(3) :: vapaat
REAL :: t
SELECT CASE (suuntia(vapaat,k,l))
CASE(1)
arvaa = vapaat(1)
CASE(2)
CALL RANDOM_NUMBER(t)
arvaa = vapaat(FLOOR(2*t+1))

14. Esimerkkiohjelmia

245

CASE(3)
CALL RANDOM_NUMBER(t)
arvaa = vapaat(FLOOR(3*t+1))
END SELECT
END FUNCTION arvaa

Riippuen vapaiden suuntien mrst valitaan satunnaisesti niist jokin.


Tarvittavien satunnaislukujen tuottamiseen kytetn standardialiohjelmaa
RANDOM_NUMBER. Funktiossa arvaa kytetn apufunktiota suuntia vapaina
olevien suuntien lytmiseksi:
FUNCTION suuntia(vapaat,k,l) RESULT(i)
IMPLICIT NONE
INTEGER, DIMENSION(:), INTENT(INOUT) :: vapaat
INTEGER, INTENT(IN) :: k,l
INTEGER :: i
i = 0
IF (taulu(k,l+1) == tyhja) THEN
i = i + 1
vapaat(i) = alas
END IF
IF (taulu(k,l-1) == tyhja) THEN
i = i + 1
vapaat(i) = ylos
END IF
IF (taulu(k+1,l) == tyhja) THEN
i = i + 1
vapaat(i) = oikealle
END IF
IF (taulu(k-1,l) == tyhja) THEN
i = i + 1
vapaat(i) = vasemmalle
END IF
END FUNCTION suuntia

Nyt meill onkin mriteltyn valmis sokkelonteko-ohjelma lukuunottamatta tulostusproseduuria. Voimme simuloida tulostusta aluksi seuraavalla yksinkertaisella aliohjelmalla:
SUBROUTINE tulosta
IMPLICIT NONE
INTEGER :: k
DO k = 1, korkeus
WRITE (*,(24(I3))) taulu(k,1:leveys)
END DO
END SUBROUTINE tulosta

Pohjelma tytyy kirjoittaa vaikkapa seuraavan nkiseksi:


PROGRAM sokkelo
IMPLICIT NONE
INTEGER, PARAMETER :: alku = 1, tyhja = alku - 1, &
reuna = tyhja - 1
INTEGER, PARAMETER :: ylos = 1, alas = 2, &
oikealle = 3, vasemmalle = 4
INTEGER, DIMENSION(:,:), ALLOCATABLE :: taulu
INTEGER :: leveys, korkeus

246

Fortran 95/2003

CALL alusta
CALL sijoita(1,1,alku)
CALL tulosta
CONTAINS
Proseduurit alusta, sijoita, umpikuja, arvaa,
suuntia ja tulosta
END PROGRAM sokkelo

Aliohjelmakutsu
CALL sijoita(1,1,alku)

aloittaa sokkelon luomisen kokonaislukutaulukon taulu vasemmasta ylkulmasta. Ohjelman tulostus voisi olla seuraavaa:
Anna sokkelon leveys
10 5
1 4 5 34 33 32 31
2 3 6 19 20 33 34
9 8 7 18 21 26 27
10 13 14 17 22 25 42
11 12 15 16 23 24 43

ja korkeus:
30
29
28
41
40

31
34
35
38
39

32
33
36
37
40

Sokkelon ratkaisureitti kulkee vasemmasta ylkulmasta oikeaan alakulmaan


seuraamalla kasvavaa lukujonoa 1, 2, 3, . . . , 39, 40.
Haluamme tulostaa sokkelon hiukan visuaalisemmassa muodossa. Tmn
voi tehd seuraavan aliohjelman avulla:
SUBROUTINE tulosta
IMPLICIT NONE
CHARACTER, PARAMETER :: pystyaita = !, &
vaaka_aita = -, kulmaus = +, vali =
CHARACTER(LEN=1), DIMENSION(:,:), ALLOCATABLE :: &
merkkitaulu
INTEGER :: tila, k, l, mk, ml, i, j
k = korkeus
l = leveys
mk = 2*k
ml = 2*l
ALLOCATE(merkkitaulu(0:mk,0:ml), STAT=tila)
IF (tila /= 0) STOP Muistinvaraus eponnistui!
merkkitaulu(1:mk-1:2,1:ml-1:2) = vali
merkkitaulu(::2,::2) = kulmaus
merkkitaulu(1:mk-1:2,::2) = pystyaita
merkkitaulu(::2,1:ml-1:2) = vaaka_aita
WHERE (ABS(taulu(1:k-1,1:l)-taulu(2:k,1:l)) == 1)
merkkitaulu(2:mk-2:2,1:ml-1:2) = vali
END WHERE
WHERE (ABS(taulu(1:k,1:l-1)-taulu(1:k,2:l)) == 1)
merkkitaulu(1:mk-1:2,2:ml-2:2) = vali
END WHERE
merkkitaulu(0,1) = vali
merkkitaulu(mk,ml-1) = vali
DO i = 0, mk
DO j = 0, ml-1

14. Esimerkkiohjelmia

247

WRITE (*,(A),ADVANCE=NO) merkkitaulu(i,j)


END DO
WRITE (*,(A)) merkkitaulu(i,ml)
END DO
DEALLOCATE(merkkitaulu)
END SUBROUTINE tulosta

Tulostusproseduurissa luodaan sokkelon mrittelev merkkityypin taulukko merkkitaulu kyttmll taulukkonotaatiota. Tulostus tehdn riveittin.
Seuraavassa ohjelman kyttesimerkki:
Anna sokkelon leveys ja korkeus:
10 5
+ +-+-+-+-+-+-+-+-+-+
! !
!
!
+ + + +-+-+ +-+ +-+ +
!
! !
!
! !
!
+-+-+ + + +-+-+ + +-+
!
! ! !
!
!
+ +-+-+ + + +-+-+-+ +
! !
! ! ! !
!
!
+ + + + + + + + + +-+
!
!
!
! !
!
+-+-+-+-+-+-+-+-+-+ +

Tss tulostettiin sama sokkelo, joka esitettiin aiemmin kokonaislukutaulukkona. Ohjelmaa on kytetty mys tulostamaan tmn kirjan kansikuva.

14.4

Yksiulotteinen soluautomaatti

Soluautomaatit (cellular automata) ovat diskreettej dynaamisia systeemej,


jotka vaihtavat tilaansa synkronisesti. Useimmat soluautomaatit ovat lhes
tydellisen rinnakkaisia algoritmeja. Niiden toteuttaminen Fortranin taulukko-operaatioilla onnistuu helposti.

14.4.1 Soluautomaattien mrittely


Yksiulotteinen soluautomaatti koostuu jonosta soluja x(i), joista kullakin
on vasemman- ja oikeanpuoleinen naapurisolu x(i 1) ja x(i + 1). Olkoon
solujen lukumr n. Soluautomaattimme on periodinen, jos solun x(n)
oikea naapuri on solu x(1) ja solun x(1) vasen naapuri on solu x(n).
Edellisess tapauksessa kullakin solulla oli vain yksi naapuri kummallakin
puolellaan. Tllin sanotaan, ett naapuriston sde r on yksi. Yleisemmss
tapauksessa voimme ajatella, ett kunkin solun tilaan vaikuttaa useampi
kuin yksi naapuri, jolloin sde r on suurempi kuin yksi.
Solu x(i) saa arvoja diskreetist joukosta {0, 1, 2, . . . , k 1}. Arvojen lukumr k voi olla esimerkiksi 2, jolloin soluautomaatti on binrinen eli

248

Fortran 95/2003

tilajoukko on {0, 1}.


Kaikki soluautomaatin solut muuttavat tilaansa x(i) synkronisesti. Soluautomaatin (r = 1, k = 2) muutossnt voisi olla esimerkiksi seuraava: jos
solujen {x(i 1), x(i), x(i + 1)} (eli naapuriston) arvojen summa on 1, arvo
seuraavalla ajanhetkell on 1. Muussa tapauksessa arvo on 0.
Edell kuvattua soluautomaattia sanotaan totalistiseksi. Tllaisen soluautomaatin snt riippuu vain naapurisolujen arvojen summasta. Totalistisen
(r , k)-soluautomaatin soluympristn arvojen summa voi saada yhteens
(k 1)(2r + 1) + 1 eri arvoa. Tapauksessa (r = 1, k = 2) arvoja on nelj: 0,
1, 2 ja 3.
Soluautomaatin tilamuutosta kuvaava funktio voidaan totalistisen soluautomaatin tapauksessa kuvata taulukkona, jossa on (k 1)(2r + 1) + 1 alkiota.
Erilaisten funktioiden lukumr on k(k1)(2r +1)+1 .
Esimerkiksi totalistisia (r = 1, k = 2)-soluautomaatteja on 24 = 16 erilaista. Kukin tllainen soluautomaatti voidaan kuvata neljn merkin mittaisena
binrilukuna. Esimerkiksi soluautomaatti 0010 tuottaa arvon yksi silloin,
kun tilojen summa on yksi. Nm binriluvut voidaan muuntaa desimaalijrjestelmn, jolloin soluautomaatit saadaan numeroitua (numerot 0 . . . 15).
Esimerkkinmme olleen soluautomaatin jrjestysnumero on siis 2.

14.4.2 Soluautomaatin toteutus


Toteutamme seuraavassa moduulin ca1d, jossa on julkiset (PUBLIC) proseduurit ca_init ja ca_table soluautomaattihilan alustamiseen ja soluautomaatin simuloimiseen. Moduulin sisiset muuttujat ja tyypinmrittelyt
eivt ny ulkopuolelle.
Moduulin runko nytt seuraavalta:
MODULE ca1d
IMPLICIT NONE
PUBLIC :: ca_init, ca_table
PRIVATE
INTEGER, PARAMETER :: short = SELECTED_INT_KIND(2), &
long = SELECTED_INT_KIND(9)
TYPE ca_rule_type
INTEGER(KIND=short) :: k, r
INTEGER(KIND=long) :: rule_nr
INTEGER(KIND=short), DIMENSION(:), POINTER :: table
END TYPE ca_rule_type
CONTAINS
Funktioiden ca_init ja ca_table mrittely
END MODULE ca1d

Mrittelimme moduulin sisiseen kyttn tietotyypin ca_rule_type, jonka taulukkoon table voimme sijoittaa soluautomaatin snttaulukon. Soluautomaatin alustamisen voisi tehd seuraavasti:
FUNCTION ca_init(n, k) RESULT(res)
IMPLICIT NONE

14. Esimerkkiohjelmia

249

INTEGER, INTENT(IN) :: n, k
INTEGER, DIMENSION(n) :: res
REAL, DIMENSION(n) :: t
CALL RANDOM_NUMBER(t)
res = k*t
END FUNCTION ca_init

Tmn funktion palauttama arvo on yksiulotteinen taulukko, jonka pituus


on n ja jonka arvot ovat kokonaislukuja vlilt [0, k 1]. Arvot lasketaan
standardialiohjelman RANDOM_NUMBER avulla.
Soluautomaatin simuloimisen voisimme toteuttaa seuraavasti:
FUNCTION ca_table(config, k, r, nr, iter) RESULT(res)
IMPLICIT NONE
INTEGER, DIMENSION(:), INTENT(IN) :: config
INTEGER, INTENT(IN) :: k, r, nr, iter
INTEGER, DIMENSION(SIZE(config),0:iter) :: res
TYPE(ca_rule_type) :: rule
INTEGER :: i
rule = ca_rule(k, r, nr)
res(:,0) = config
DO i = 1, iter
res(:,i) = ca_value(res(:,i-1))
END DO
CONTAINS
Funktion ca_value mrittely
END FUNCTION ca_table

Funktiossa ca_table kytettiin funktiota ca_rule laskemaan soluautomaatin snt taulukkoon rule, joka on tyyppi ca_rule_type. Sisisell funktiolla ca_value puolestaan lasketaan soluautomaattihilan arvo seuraavalla
ajanhetkell. Tmn voisi toteuttaa seuraavasti:
FUNCTION ca_value(config) RESULT(res)
IMPLICIT NONE
INTEGER, DIMENSION(:), INTENT(IN) :: config
INTEGER, DIMENSION(SIZE(config)) :: res
INTEGER :: r, k, n, i
r = rule % r
k = rule % k
n = SIZE(config)
res = 0
DO i = -r, r
res = res + CSHIFT(config,i)
END DO
res = rule % table(res+1)
END FUNCTION ca_value

Funktiossa kytetn isntproseduurista ca_table perisin olevan rulemuuttujan arvoa. Rakenteisen muuttujan komponentteihin viitataan prosenttimerkin avulla: rule%r, rule%k ja rule%table.
Funktiossa ca_value lasketaan rinnakkain soluautomaattihilan uudet arvot. Muuttuja res on taulukko, ja lauseessa res = 0 asetetaan taulukon
kaikki alkiot nolliksi. Kaikkien naapuristojen alkioiden summat lasketaan

250

Fortran 95/2003

kyttmll apuna CSHIFT-funktiota, joka siirt taulukkoa config halutun


mrn askeleita. Lauseessa
res = res + CSHIFT(config,i)

listn siten res-taulukon alkioihin config-taulukon alkiot siirrettyin periodisesti i askelta. Lopulliset soluautomaattihilan arvot saadaan indeksoimalla taulukkoa rule%table seuraavaan tapaan:
res(i) = rule % table(res(i)+1)

Tss lauseessa tehdn sijoitus res-taulukon yhdelle alkiolle. Funktiossa


ca_value kytetn taulukkosyntaksia ja tehdn sijoitus kaikille alkioille
yht aikaa:
res = rule % table(res+1)

En tarvitsee toteuttaa snttaulukon laskeva funktio ca_rule:


FUNCTION ca_rule(k, r, nr) RESULT(res)
IMPLICIT NONE
INTEGER, INTENT(IN) :: k, r, nr
TYPE(ca_rule_type) :: res
res % k = k
res % r = r
res % rule_nr = nr
CALL totalistic_rule_table
CONTAINS
SUBROUTINE totalistic_rule_table
IMPLICIT NONE
INTEGER :: allocstat, n, i
n = (k-1)*(2*r+1) + 1
ALLOCATE(res % table(n), STAT=allocstat)
IF (allocstat /= 0) STOP allocation failed!
res % table = (/ (rule_value(i,nr,k), i = 1, n) /)
END SUBROUTINE totalistic_rule_table
RECURSIVE FUNCTION rule_value(b, r, k) RESULT(value)
IMPLICIT NONE
INTEGER, INTENT(IN) :: b, r
INTEGER, INTENT(IN) :: k
INTEGER :: value
IF (b == 1) THEN
value = MOD(r,k)
ELSE
value = rule_value(b-1, r/k, k)
END IF
END FUNCTION rule_value
END FUNCTION ca_rule

Funktio palauttaa tyyppi ca_rule_type olevan tuloksen muuttujassa res.


Soluautomaattisnnn taulukolle varataan tilaa ALLOCATE-kskyll, jonka
jlkeen taulukon res%table kuhunkin alkioon lasketaan soluautomaattisnt vastaava arvo. Apuna kytetn rekursiivista funktiota rule_value.
Tm funktio tekee itse asiassa muunnoksia desimaalijrjestelmst k-kantaiseen lukujrjestelmn.

14. Esimerkkiohjelmia

251

14.4.3 Kyttesimerkki
Kytmme moduulia ca1d soluautomaattien havainnollistukseen:
PROGRAM ca1dtest
USE ca1d, ONLY : ca_init, ca_table
IMPLICIT NONE
INTEGER, DIMENSION(:,:), ALLOCATABLE :: table
INTEGER, DIMENSION(:), ALLOCATABLE :: init
INTEGER :: nr, k, r, t, n, iter, i
WRITE (*,*) Anna nr, k, r:
READ (*,*) nr, k, r
WRITE (*,*) Pituus ja iter. lkm:
READ (*,*) n, iter
ALLOCATE(table(n,0:iter), init(n))
init = ca_init(n, k)
table = ca_table(init, k, r, nr, iter)
DO i = 0, iter
WRITE (*,(35I2))) table(:,i)
END DO
END PROGRAM ca1dtest

Ohjelman toiminta nytt seuraavalta:


Anna nr, k, r:
2 2 1
Pituus ja iter.
24 6
0 0 0 1 1 1 0 1
1 0 1 0 0 0 0 0
...
0 1 1 1 0 1 0 0

lkm:
1 1 0 1 0 1 1 1 0 0 1 0 0 0 1 1
0 0 0 1 0 0 0 0 1 1 1 1 0 1 0 0
1 0 0 1 0 0 1 1 0 0 1 0 0 1 1 1

Tulostimme soluautomaatin (k = 2, r = 1) (sntnumero 2) tilojen kehityksen riveittin, ylimpn on satunnainen alkutila.

14.4.4 Tulostus PGM-muodossa


Edell kytetty tekstimuotoinen tulostus ei ole kovin havainnollista. Voimme tulostaa datan harmaasvykuvana kyttmll PGM-formaattia (portable
graymap).
Seuraavassa on thn soveltuva aliohjelma:
SUBROUTINE write_pgm(filename, table)
IMPLICIT NONE
CHARACTER(LEN=*), INTENT(IN) :: filename
INTEGER, DIMENSION(:,:), INTENT(IN) :: table
INTEGER :: m, n, minim
INTEGER, PARAMETER :: io_unit = 1
OPEN (io_unit, file=filename)
minim = MINVAL(table)
WRITE (io_unit, (A)) P2

252

Fortran 95/2003

WRITE (io_unit, (A)) # // filename


m = SIZE(table,1)
n = SIZE(table,2)
WRITE (io_unit, (2I6,/,I4)) m, n, MAXVAL(table)-minim
WRITE (io_unit, (16I4)) table-minim
END SUBROUTINE write_pgm

PGM-tiedoston kaksi ensimmist merkki ovat P2. Tmn jlkeen tulee kuvan dimensiot sek datassa esiintyv maksimiarvo. Tulostusksky voisi nytt seuraavalta:
CALL write_pgm(ca1d.pgm, table)

Esimerkiksi edellisess tapauksessa tulostus voisi olla seuraavan nkist:


P2
# ca1d.pgm
24
7
1
0
0
0
0
0
1

1
0

1
0

1
0

0
1

1
1

1
1

1
0

0 ...
1 ...

Tss kuvan koko on 24 7 pikseli ja maksimiarvo on 1. Merkill # alkava


rivi tulkitaan kommentiksi.
PGM-tiedostoja voi lukea Unix-ympristss esimerkiksi xv-ohjelmistolla,
jolla ne voi mys muuntaa esimerkiksi PostScript-muotoon. Tulos voisi nytt seuraavalta:

Tss hilan pituus oli 240 ja soluautomaattia iteroitiin 100 kertaa. Tilat on
esitetty riveittin, ylimpn on alkutila.
PGM-formaatin sijaan voi kytt PPM-formaattia (portable pixmap), jossa
voi esitt vrillist dataa.

14.4.5 Taulukkosyntaksin mahdollisuuksia


Edell laskimme soluautomaattihilan tilan seuraavalla ajanhetkell kytten
funktiota ca_value. Arvo laskettiin DO-silmukassa, jota toistettiin 2r + 1
kertaa. Tmn funktion voisi toteuttaa toisellakin tavalla:
FUNCTION ca_value(config) RESULT(res)
IMPLICIT NONE
INTEGER, DIMENSION(:), INTENT(IN) :: config
INTEGER, DIMENSION(SIZE(config)) :: res
INTEGER :: r, k, n, i

14. Esimerkkiohjelmia

253

r = rule % r
k = rule % k
n = SIZE(config)
res = SUM( CSHIFT( SPREAD(config, DIM=2, ncopies=2*r+1), &
shift=(/(i, i = -r, r)/), DIM=1), DIM=2)
res = rule % table(res+1)
END FUNCTION ca_value

Tss selvimme taulukkosyntaksilla, yhtn silmukkaa ei tarvita. Ongelmaksi voisi muodostua muistinkytt, sill funktio SPREAD tuottaa taulukon,
jonka koko on n(2r + 1). Toisaalta useimmiten sde r on paljon pienempi
kuin hilan pituus n, joten tm ei ole ongelma. Tmn koodin luettavuus ei
kuitenkaan ole yht hyv kuin aiemman silmukkaversion.

14.4.6 Listietoa
Soluautomaateista kerrotaan mm. teoksissa Theory and Applications of Cellular Automata (Stephen Wolfram, World Scientic, 1986) ja A New Kind of
Science (Stephen Wolfram, Wolfram Media, 2002). CSC on julkaissut teoksen
Workshop on Cellular Automata, Proceedings (toim. Juha Haataja, 1991).

14.5

Life-soluautomaatti

Life-soluautomaatti eli Life-peli koostuu kaksiulotteiseen suorakulmaiseen


hilaan sijoitetuista soluista. Kunkin solun naapureiksi lasketaan kahdeksan lhint solua. Solulla on kaksi tilaa: elossa ja kuollut. Kaikki solut
muuttavat tilojaan yht aikaa seuraavia sntj noudattamalla:
jos tyhjn solun naapureista on kolme elossa, naapuriston keskelle syntyy uusi solu
jos naapureista on kaksi tai kolme elossa, vanha solu silyy hengiss
muussa tapauksessa elossa oleva solu kuolee joko yksinisyyteen tai
liikakansoitukseen.
Seuraavassa pohjelmassa on mritelty Life-soluautomaattia simuloivan
ohjelman rakenne. Solujen tilat talletetaan kokonaislukutaulukoihin lauta_1
ja lauta_2.
PROGRAM Life
IMPLICIT NONE
CHARACTER (LEN=*), PARAMETER :: tiedosto = life.dat
INTEGER, PARAMETER :: maksimikoko = 200
INTEGER, DIMENSION(:,:), POINTER :: lauta_1, lauta_2
INTEGER :: leveys = 20, korkeus = 20, toistot = 1000, iter
CALL lue_lauta
CALL pyyhi

254

Fortran 95/2003

DO iter = 1, toistot/2
CALL piirra(lauta_1,lauta_2)
CALL laske(lauta_1,lauta_2)
CALL piirra(lauta_2,lauta_1)
CALL laske(lauta_2,lauta_1)
END DO
IF (MOD(toistot,2) == 1) THEN
CALL piirra(lauta_1,lauta_2)
CALL laske(lauta_1,lauta_2)
END IF
CALL piirra(lauta_2,lauta_1)
CALL pyyhi
CONTAINS
Aliohjelmat lue_lauta, pyyhi, piirra ja laske
END PROGRAM Life

Aliohjelma lue_lauta lukee toistokertojen eli sukupolvien lukumrn,


pelilaudan koon ja alkukonguraation tiedostosta. Ohjelmassa kytetn
taulukkoja lauta_1 ja lauta_2 Life-pelin nykyisen ja seuraavan tilan tallentamiseen. Lautoja kytetn vuorotellen, joten DO-silmukassa tehdn
yhdell kierroksella kaksi Life-pelin iteraatiota.
Pelilaudan sislt voidaan lukea tiedostosta seuraavasti:
SUBROUTINE lue_lauta
IMPLICIT NONE
INTEGER :: i, j
CHARACTER(LEN=maksimikoko) :: rivi
NAMELIST / lista / toistot, leveys, korkeus
OPEN (1, FILE=tiedosto, STATUS=OLD, ACTION=READ, &
FORM=FORMATTED)
READ (1, NML=lista)
IF (toistot < 1) STOP Iteraatioiden maksimimr &
&nolla tai negatiivinen!
IF (leveys < 1 .OR. korkeus < 1 .OR. &
leveys > maksimikoko) THEN
STOP Liian pieni tai iso lauta!
END IF
ALLOCATE (lauta_1(korkeus,leveys))
lauta_1 = 0
DO i = 1, korkeus
READ(1,(A),END=99) rivi
DO j = 1, leveys
IF (rivi(j:j) /= ) lauta_1(i,j) = 1
END DO
END DO
99 CLOSE(1)
ALLOCATE (lauta_2(SIZE(lauta_1,1),SIZE(lauta_1,2)))
lauta_2(:,:) = 0
END SUBROUTINE lue_lauta

Toistokertojen lukumrn sek laudan leveyden ja korkeuden lukemiseen


kytetn NAMELIST-ryhm lista.
Aliohjelma pyyhi voisi olla seuraava:

14. Esimerkkiohjelmia

255

SUBROUTINE pyyhi
WRITE (*,(A)) CHAR(27) // [2J
END SUBROUTINE pyyhi

Tss kytetn VT100-tekstiptteen ohjauskoodeja, ja tulostettu koodi


tyhjent ruudun sislln (CHAR(27) on Esc -merkki).
Life-pelin seuraava sukupolvi voidaan laskea kyttmll taulukkosyntaksia. Tss kytetn periodisia reunaehtoja, jolloin taulukon vasen reuna on
oikean reunan naapuri ja ylreuna on alareunan naapuri.
SUBROUTINE laske(lauta_1,lauta_2)
IMPLICIT NONE
INTEGER, DIMENSION(:,:), POINTER :: lauta_1, lauta_2
INTEGER, DIMENSION(SIZE(lauta_1,1),SIZE(lauta_1,2)) :: &
summa
summa = CSHIFT (lauta_1, shift= 1, DIM=1) + &
CSHIFT (lauta_1, shift=-1, DIM=1) + &
CSHIFT (lauta_1, shift= 1, DIM=2) + &
CSHIFT (lauta_1, shift=-1, DIM=2) + &
CSHIFT (CSHIFT (lauta_1, shift= 1, DIM=2),
shift= 1, DIM=1) + &
CSHIFT (CSHIFT (lauta_1, shift= 1, DIM=2),
shift=-1, DIM=1) + &
CSHIFT (CSHIFT (lauta_1, shift=-1, DIM=2),
shift= 1, DIM=1) + &
CSHIFT (CSHIFT (lauta_1, shift=-1, DIM=2),
shift=-1, DIM=1)
WHERE (summa < 2 .OR. summa > 3)
lauta_2 = 0
ELSEWHERE
lauta_2 = lauta_1
END WHERE
WHERE (summa == 3) lauta_2 = 1
END SUBROUTINE laske

&
&
&
&

Peliss tapahtuneet muutokset voidaan piirt kytten VT100-ptteen ohjauskoodeja. Aliohjelmassa piirra kytetn kappaleessa 9.13.2 sivulla 144
esitelty muotoiluoperaattoria .fmt., jolloin tulostettavan merkin rivi ja
saraketta kuvaavat luvut saadaan tulostettua tsmlleen oikean kokoisiin
kenttiin. Aliohjelman muodollinen argumentti lauta_1 on Life-pelin uusi
tilanne ja lauta_2 vanha tilanne.
SUBROUTINE piirra(lauta_1,lauta_2)
USE muotoilu ! Kytetn operaattoria ".fmt."
IMPLICIT NONE
CHARACTER(LEN=*), PARAMETER :: escape = CHAR(27) // [
INTEGER, DIMENSION(:,:), POINTER :: lauta_1, lauta_2
INTEGER :: i, j
DO i = 1, SIZE(lauta_1,1)
DO j = 1, SIZE(lauta_1,2)
IF (lauta_1(i,j) /= lauta_2(i,j)) THEN
WRITE (*,(A),ADVANCE=NO) escape
WRITE (*,( // .fmt. i // ,A), &

256

Fortran 95/2003

ADVANCE=NO) i, ;
WRITE (*,( // .fmt. (j-1) // ,A), &
ADVANCE=NO) j-1, H
IF (lauta_1(i,j) /= 0) THEN
WRITE (*,(A),ADVANCE=NO) *
ELSE
WRITE (*,(A),ADVANCE=NO)
END IF
END IF
END DO
END DO
IF (lauta_1(1,1) /= 0) THEN
WRITE (*,(A)) escape // 1;0H*
ELSE
WRITE (*,(A)) escape // 1;0H
END IF
END SUBROUTINE piirra

Aliohjelmassa piirretn vain muuttuneet solut, joten ohjelman tulostus on


nopeaa. Tulostuksen lopussa siirretn osoitin ruudun ylkulmaan.
Seuraavassa on esimerkki ohjelman sytttiedostosta (tiedoston alussa sytetn NAMELIST-ryhm lista):
&lista toistot = 300, leveys = 40, korkeus = 20 /
*
* ***
*
*
***
*

Tuloksena lasketaan 300 toistokertaa 40 20 -kokoisella laudalla. Seuraavassa on kuuden ensimmisen iteraatioaskeleen tilanteet (vain laudan vasemman ylkulman tilanne on esitetty).
*
**
**

*
***
**
*

**
***
* *
* **
**

14.6

*
* *
*

**
* *
* *
**

**
* *
***

*
**
*** * *
*
* * *
* **
*
***

**
**
* **
* *
**

***
***
*
*

**
****
*

*
* *
*

**
*
*

*
* *
*

Binripuu

Binripuu on yksinkertainen mutta tehokas dynaaminen tietorakenne, jota


voidaan kytt esimerkiksi tiedon jrjestmiseen puumaiseksi rakenteeksi.

14. Esimerkkiohjelmia

257

Tst rakenteesta tieto on yleens haettavissa nopeammin kuin esimerkiksi


yksinkertaisesta listasta tai taulukosta.
Seuraavassa mriteltvn binripuun solmuun puu sisltyy hakuavain, siihen liittyv tieto sek osoittimet vasempaan ja oikeaan alipuuhun:
TYPE puu
CHARACTER(LEN=avain_pituus) :: avain
CHARACTER(LEN=tiedon_pituus) :: tieto
TYPE(puu), POINTER :: vasen, oikea
END TYPE puu

Hakuavain on tss merkkijono (esimerkiksi puhelinnumero), jonka mukaan


tieto (esimerkiksi puhelimen omistajan nimi) on puussa jrjestetty ja jonka
mukaan tietoa etsitn.
Puun vasempaan haaraan laitetaan kaikki ne avaimet, jotka tulevat lajittelujrjestyksess ennen kyseisen solmun avainta. Vastaavasti oikeassa haarassa ovat lajittelujrjestyksess myhemmt avaimet.
Puuta luotaessa tehdn aluksi juurisolmu, jossa on aakkosten alussa oleva
merkkijono. Tllin siis hakualgoritmissa siirrytn aina oikeaan alipuuhun.
Aluksi oikeassa alipuussa on vain loppumerkki loppu, jonka alipuut viittaavat solmuun itseens.
Kun puuhun sytetn uusia avain-tieto -pareja, avainkentt verrataan nykyisen solmun avainkenttn ja siirrytn joko vasempaan tai oikeaan alipuuhun. Nin jatketaan kunnes trmtn solmuun loppu, jota ennen luodaan uusi solmu.
Etsint varten loppusolmun avainkentksi laitetaan etsittv avain ja loppusolmun tietokentksi teksti Ei lytynyt. Siten etsinnss lytyy aina
jokin solmu, jonka avain vastaa etsittv avainta. Jos avainta ei lytynyt
puusta, palautetaan loppusolmun tietokentt merkkin virheest.
Jos binripuussa on n solmua, alkio lytyy parhaassa tapauksessa log2 n
askeleella. Tm edellytt, ett puu on tasapainoitettu, eli ett syttvaiheessa avaimet ovat olleet satunnaisessa jrjestyksess, jolloin solmuissa
kytetn yht paljon vasempia ja oikeita alipuita. Jos sen sijaan avaimet
sytetn valmiiksi aakkosjrjestyksess, kytetn pelkstn oikeapuoleisia alipuita ja haku kest n askelta.
Kyttmmme binripuun toteuttava algoritmi on perisin kirjasta Algorithms [Sed84]. Moduuli noudattaa tietoabstraktion periaatteita, joiden mukaan tietotyyppi ja sit ksittelevt proseduurit on liitetty yhteen, eik niiden
toteutus ny kutsuvalle pohjelmalle.
MODULE bpuu
! Moduulissa toteutetaan lisys ja etsint binripuussa
IMPLICIT NONE
PRIVATE
INTEGER, PARAMETER :: avain_pituus = 4, &
tiedon_pituus = 30
TYPE puu
CHARACTER(LEN=avain_pituus) :: avain
CHARACTER(LEN=tiedon_pituus) :: tieto

258

Fortran 95/2003

TYPE(puu), POINTER :: vasen, oikea


END TYPE puu
TYPE(puu), POINTER :: juuri, loppu
CHARACTER(LEN=avain_pituus), PARAMETER :: pienin_avain =
CHARACTER(LEN=*), PARAMETER :: puu_ei_loydy = Ei lytynyt
PUBLIC :: avain_pituus, tiedon_pituus, etsi, syota, &
alusta_puu, tulosta_puu
CONTAINS
SUBROUTINE alusta_puu()
! Tm aliohjelma alustaa binripuun juuren sek
! loppumerkin.
! Juuressa avain on pienin_avain, loppumerkin
! osoittimet osoittavat loppumerkkiin.
ALLOCATE(juuri, loppu)
juuri%avain = pienin_avain
juuri%oikea => loppu
loppu%tieto = puu_ei_loydy
loppu%vasen => loppu
loppu%oikea => loppu
END SUBROUTINE alusta_puu
FUNCTION etsi (avain) RESULT(tieto)
! Tm aliohjelma etsii avainta taulukosta ja palauttaa
! tieto-alkion.
IMPLICIT NONE
CHARACTER(LEN=avain_pituus), INTENT(IN) :: avain
CHARACTER(LEN=tiedon_pituus) :: tieto
TYPE(puu), POINTER :: mina
mina => juuri%oikea
loppu%avain = avain
DO WHILE (avain /= mina%avain)
IF (avain < mina%avain) THEN
mina => mina%vasen
ELSE
mina => mina%oikea
END IF
END DO
tieto = mina%tieto
END FUNCTION etsi
SUBROUTINE syota(avain, tieto)
! Tm aliohjelma tekee puuhun uuden solmun, jonka
! tunnukset ovat avain ja tieto.
IMPLICIT NONE
CHARACTER(LEN=*), INTENT(IN) :: avain
CHARACTER(LEN=*), INTENT(IN) :: tieto
TYPE(puu), POINTER :: isa, mina
isa => juuri
mina => juuri%oikea
DO WHILE (.NOT. ASSOCIATED(mina, loppu))
isa => mina
IF (avain < mina%avain) THEN
mina => mina%vasen

14. Esimerkkiohjelmia

259

ELSE
mina => mina%oikea
END IF
END DO
ALLOCATE(mina)
mina%avain = avain
mina%tieto = tieto
mina%vasen => loppu
mina%oikea => loppu
IF (avain < isa%avain) THEN
isa%vasen => mina
ELSE
isa%oikea => mina
END IF
END SUBROUTINE syota
SUBROUTINE tulosta_puu()
! Tm on pllystakki tulostusaliohjelmalle
WRITE (*,*) Listaus tiedoista puurakenteen &
&mukaan sisennettyin:
CALL tulosta_sisainen(juuri%oikea, 1)
END SUBROUTINE tulosta_puu
RECURSIVE SUBROUTINE tulosta_sisainen(mina, taso)
! Tm aliohjelma tulostaa rekursiivisesti puun
IMPLICIT NONE
TYPE(puu), POINTER :: mina
INTEGER, INTENT(IN) :: taso
IF (.NOT. ASSOCIATED(mina, loppu)) THEN
CALL tulosta_sisainen(mina%vasen, taso+1)
WRITE (*,*) REPEAT( ,2*taso), mina%avain, mina%tieto
CALL tulosta_sisainen(mina%oikea, taso+1)
END IF
END SUBROUTINE tulosta_sisainen
END MODULE bpuu

Seuraava pohjelma alustaa binripuun, lukee ptteelt avaimia ja tietoalkioita ja lis ne puuhun. Sen jlkeen tulostetaan puu. Viimeinen silmukka kysyy kyttjlt avainkentti ja hakee puusta vastaavan tietokentn. Jos avainta ei lydy puusta, algoritmi palauttaa loppu-solmussa olevan
virheilmoituksen.
PROGRAM puut
USE bpuu
IMPLICIT NONE
CHARACTER(LEN=avain_pituus) :: avain
CHARACTER(LEN=tiedon_pituus) :: tieto
CALL alusta_puu()
tiedon_luku: DO
WRITE (*, (A), ADVANCE = NO) Syt avain &
&(tyhj lopettaa):
READ(*, (A)) avain

260

Fortran 95/2003

IF (avain == ) EXIT tiedon_luku


WRITE (*, (A), ADVANCE = NO) Syt tieto:
READ (*, (A)) tieto
CALL syota(avain, tieto)
END DO tiedon_luku
CALL tulosta_puu
tiedon_haku: DO
WRITE (*, (A), advance = no) Syt avain &
&(tyhj lopettaa):
READ (*, (A)) avain
IF (avain == ) EXIT tiedon_haku
WRITE (*, *) Tieto on: , etsi(avain)
END DO tiedon_haku
END PROGRAM puut

Seuraavassa ohjelman esimerkkiajossa kytetn sytteen teleliikennealueiden suuntanumeroita ja nimi:


Syt
Syt
Syt
Syt
Syt
Syt
Syt
Syt
Syt
Syt
Syt
Syt
Syt
Syt
Syt
Syt
Syt
Syt
Syt
Syt
Syt
Syt
Syt
Syt
Syt
Syt
Syt

avain (tyhj lopettaa):


tieto: Ahvenanmaa
avain (tyhj lopettaa):
tieto: Keski-Suomi
avain (tyhj lopettaa):
tieto: Lappi
avain (tyhj lopettaa):
tieto: Pohjois-Karjala
avain (tyhj lopettaa):
tieto: Uusimaa II
avain (tyhj lopettaa):
tieto: Hme
avain (tyhj lopettaa):
tieto: Kuopio
avain (tyhj lopettaa):
tieto: Mikkeli
avain (tyhj lopettaa):
tieto: Turku ja Pori
avain (tyhj lopettaa):
tieto: Vaasa
avain (tyhj lopettaa):
tieto: Kymi
avain (tyhj lopettaa):
tieto: Oulu
avain (tyhj lopettaa):
tieto: Uusimaa I
avain (tyhj lopettaa):

018
014
016
013
019
03
017
015
02
06
05
08
09

Listaus tiedoista puurakenteen mukaan sisennettyin:


013 Pohjois-Karjala
014 Keski-Suomi
015 Mikkeli
016 Lappi
017 Kuopio
018 Ahvenanmaa
019 Uusimaa II

14. Esimerkkiohjelmia

261

02

03

Turku ja Pori
Hme
05 Kymi
06 Vaasa
08 Oulu
09 Uusimaa I

Syt avain (tyhj lopettaa): 019


Tieto on: Uusimaa II
Syt avain (tyhj lopettaa): 09
Tieto on: Uusimaa I
Syt avain (tyhj lopettaa): 920
Tieto on: Ei lytynyt
Syt avain (tyhj lopettaa):

14.7

Sukupuu

Sukupuun esittmiseen ei edell kytetty binripuu sovi, koska vanhemmilla voi olla nolla, yksi tai useampi jlkelist. Tarvitaan siis uudenlainen listarakenne. Seuraava moduuli toteuttaa sukupuutyypin sek proseduurit sukutiedon lukemiseen ja tulostamiseen. Moduulissa on mritelty tietotyyppi
perhe, joka sislt vanhempien nimet ja syntymajat, lasten lukumrn
sek osoittimet lapsiin.
Lapsien esittmist varten tarvitaan taulukko lapset, jonka alkiot sisltvt osoitinmuuttujan perhe-tietotyyppiin. Ohjelmassa viitataan muuttujan
mina lapseen numero i rakenteella
mina%lapset(i)%p

Tss p on tietotyypin perheosoitin ainoa komponentti, joka osoittaa lapsen perhe-tietorakenteeseen.


MODULE sukupuu
IMPLICIT NONE
TYPE perheosoitin
TYPE (perhe), POINTER :: p
END TYPE perheosoitin
TYPE perhe
CHARACTER (LEN=30) :: nimi
CHARACTER (LEN=10) :: synt_aika
CHARACTER (LEN=30) :: puoliso
CHARACTER (LEN=10) :: puol_synt_aika
INTEGER :: lapsia
TYPE (perheosoitin), POINTER :: lapset(:)
END TYPE perhe
CONTAINS
RECURSIVE FUNCTION lue() RESULT (mina)
! Tm funktio lukee perheen tiedot ja kutsuu
! lukufunktiota rekursiivisesti kaikille lapsille.
! Funktio palauttaa osoittimen perheeseen.
IMPLICIT NONE

262

Fortran 95/2003

TYPE(perhe), POINTER :: mina


INTEGER :: i
ALLOCATE(mina)
WRITE (*,(A),ADVANCE=NO) Anna nimi:
READ (*,(A)) mina%nimi
WRITE (*,(A),ADVANCE=NO) Anna syntymaika:
READ (*,(A)) mina%synt_aika
WRITE (*,(A),ADVANCE=NO) Anna puolison nimi:
READ (*,(A)) mina%puoliso
WRITE (*,(A),ADVANCE=NO) Anna puolison syntymaika:
READ (*,(A)) mina%puol_synt_aika
WRITE (*,(A),ADVANCE=NO) Anna lasten lukumr:
READ (*,*) mina%lapsia
ALLOCATE (mina%lapset(mina%lapsia))
DO i = 1, mina%lapsia
WRITE (*,*) mina%nimi, : lapsi numero , i
mina%lapset(i)%p => lue()
END DO
END FUNCTION lue
RECURSIVE SUBROUTINE tulosta(mina, polvi)
! Tm aliohjelma tulostaa sisennetyn sukupuun
IMPLICIT NONE
TYPE(perhe), POINTER :: mina
INTEGER, INTENT(IN) :: polvi ! Sukupolven numero
INTEGER :: i
WRITE(*,(5A),advance=no) REPEAT( ,4*polvi), &
TRIM(mina%nimi), (, TRIM(mina%synt_aika) ,)
IF (LEN_TRIM(mina%puoliso) > 0) THEN
WRITE (*,(5A)) & , TRIM(mina%puoliso), (, &
TRIM(mina%puol_synt_aika), )
ELSE
WRITE (*,*)
END IF
DO i = 1, mina%lapsia
CALL tulosta(mina%lapset(i)%p, polvi+1)
END DO
END SUBROUTINE tulosta
RECURSIVE SUBROUTINE etsi(mina, nimi)
! Tm aliohjelma etsii merkkijonoa nimi sukupuusta
! ja tulostaa lydetyt henkilt
IMPLICIT NONE
TYPE(perhe), POINTER :: mina
CHARACTER(LEN=*), INTENT(IN) :: nimi
INTEGER :: i
IF (INDEX(mina%nimi, nimi) > 0) THEN
WRITE (*,(4A)) TRIM(mina%nimi), &
(, TRIM(mina%synt_aika) ,)
END IF
IF (INDEX(mina%puoliso, nimi) > 0) THEN
WRITE (*,(4A)) TRIM(mina%puoliso), &

14. Esimerkkiohjelmia

263

(, TRIM(mina%puol_synt_aika) ,)
END IF
DO i = 1, mina%lapsia
CALL etsi(mina%lapset(i)%p, nimi)
END DO
END SUBROUTINE etsi
END MODULE sukupuu

Seuraavassa on pohjelma, joka lukee sukupuun sisn ptteelt, tulostaa


sen ja etsii kaikki Juhani-nimiset henkilt sukupuusta.
PROGRAM suku
USE sukupuu
IMPLICIT NONE
TYPE(perhe), POINTER :: juuri
juuri => lue() ! Luetaan sukupuu sisn
CALL tulosta(juuri, 1) ! Tulostetaan sukupuu
WRITE (*,*)
CALL etsi(juuri, Juhani) ! Etsitn kaikki Juhanit
END PROGRAM suku

Ohjelman tulostus hypoteettisella sukupuuaineistolla voisi olla seuraavanlainen:


Matti Virtanen (1.3.1922) & Maija Virtanen (27.8.1924)
Juhani Virtanen (28.3.1945) & Sisko Virtanen (14.4.1943)
Tupu Virtanen (10.10.1970) & Iines Virtanen (4.3.1971)
Hupu Virtanen (30.11.1993)
Aku Virtanen (6.5.1972)
Lupu Virtanen (16.12.1976)
Seija Mkinen (17.7.1946) & Heikki Mkinen (23.8.1942)
Aila Jrvinen (6.9.1950) & Heikki Jrvinen (14.8.1945)
Unelias Jrvinen (6.1.1969) & Lumikki Jrvinen (19.9.1970)
Jr Jrvinen (14.5.1972)
Vilkas Jrvinen (8.6.1975)
Viisas Jrvinen (27.12.1976)
Venla Lahtinen (1.4.1950) & Juhani Lahtinen (30.10.1951)
Eero Lahtinen (5.5.1976)
Elviira Lahtinen (28.7.1981)
Tapani Lahtinen (4.12.1986)
Soile Nieminen (1.3.1958) & Juhani Nieminen (4.7.1960)
Katri Nieminen (19.4.1985)
Mikki Nieminen (13.11.1990)
Minni Nieminen (20.10.1992)
Juhani Virtanen (28.3.1945)
Juhani Lahtinen (30.10.1951)
Juhani Nieminen (4.7.1960)

14.8

Optimointitehtvien ratkaiseminen:
evoluutiostrategiat

Evoluutiostrategiat ovat noin 30 vuotta sitten kehitetty optimointitehtvien ratkaisumenetelm. Dierentiaalievoluutio (DE) on yksinkertainen ver-

264

Fortran 95/2003

sio evoluutiostrategioista. Tm menetelm on osoittautunut kohtalaisen


tehokkaaksi monissa vaikeissa optimointitehtviss.
Koska DE-algoritmi perustuu posin taulukko-operaatioiden kyttn, Fortran mahdollistaa menetelmn toteuttamisen selkesti ja tiiviisti ilman ylimrisi silmukkarakenteita.

14.8.1 Globaali optimointi


Tarkastelemme rajoitteetonta optimointitehtv
minimi f (x), kun x kuuluu joukkoon Rn .
x

Globaalissa optimoinnissa etsitn kohdefunktion f paikallisista minimikohdista arvoltaan pienint, mik on usein erittin vaikeaa. Onneksi usein
riitt likimrisen minimin lytminen.
Dierentiaalievoluutio eli DE soveltuu vaikeiden optimointitehtvien ratkaisumenetelmksi, mutta DE:ss on omat hankaluutensa. Onneksi menetelmss ei kuitenkaan ole yht paljon viriteltvi parametreja kuin esimerkiksi monissa geneettisiss algoritmeissa. DE-menetelm nyttisi mys olevan
melko riippumaton algoritmissa kytetyist parametreista.

14.8.2 Dierentiaalievoluution perusteet


Dierentiaalievoluutiossa ksittelemme ratkaisuvektoreista koostuvaa populaatiota. Populaatiota kytetn satunnaisuuden lhteen muutettaessa
ratkaisuvektoreita parempien ratkaisujen toivossa.
DE-menetelmn populaatioon kuuluu p kpl n-ulotteisia reaalilukuvektoreita xi . Usein populaation kooksi valitaan p = 5n. Jokaisella iteraatiokierroksella jrjestetn p turnausta, joissa verrataan populaation i:nnen jsenen
paremmuutta uuteen ratkaisuvektoriin xt nhden. Tm uusi vektori saadaan yhdistmll vektoriin xi kohinavektori x , joka tuotetaan valitsemalla
satunnaisesti kolme vektoria {xa , xb , xc } vanhasta populaatiosta:
x = xc + c(xa xb ).
Tss kerroin c on menetelmn parametri vlilt (0, 1.2]. Yleens c kannattaa
valita vlilt [0.4, 1.0].
Vektorien xi ja x yhdistminen tapahtuu geneettisist algoritmeista tutulla
rekombinaatiolla (risteytys), jolloin vektorin xt komponentit valitaan risteytymistodennkisyyden r perusteella vektoreista xi ja x . Vakio r on vlill
[0, 1]. Usein r = 0.1 on hyv valinta. Yleens varmistetaan, ett ainakin yksi
vektorin xt komponenteista saadaan kohinavektorista x .

14.8.3 Evoluutiostrategioiden toteutus


Mrittelemme aluksi ohjelmistossa kytetyn laskentatarkkuuden:

14. Esimerkkiohjelmia

265

MODULE de_kind
IMPLICIT NONE
INTEGER, PARAMETER, PUBLIC :: &
prec = SELECTED_REAL_KIND(12,100)
END MODULE de_kind

Varsinainen optimointialgoritmi mritelln moduulissa de_opt:


MODULE de_opt
USE de_kind, ONLY : prec ! Laskentatarkkuus
IMPLICIT NONE
PRIVATE
INTEGER, PARAMETER :: step = 10
PUBLIC :: initialize, optimize, prec
CONTAINS
Moduulin proseduurien mrittelyt . . .
END MODULE de_opt

Moduulissa on siis kolme julkista tunnusta: proseduurit initialize ja


optimize sek vakio prec. Kaikki muu on mritelty yksityiseksi eik ny
moduulin ulkopuolelle.
Populaation alustaminen tehdn seuraavasti:
SUBROUTINE initialize(f, pop, cost, low, up)
IMPLICIT NONE
INTERFACE
FUNCTION f(x) RESULT(res)
USE de_kind, ONLY : prec
REAL(KIND=prec), DIMENSION(:), INTENT(IN) :: x
REAL(KIND=prec) :: res
END FUNCTION f
END INTERFACE
REAL(KIND=prec), DIMENSION(:,:), INTENT(OUT) :: pop
REAL(KIND=prec), DIMENSION(:), INTENT(OUT) :: cost
REAL(KIND=prec), INTENT(IN) :: low, up
INTEGER :: pop_size, i
pop_size = SIZE(pop,2)
IF (SIZE(cost) /= pop_size) &
STOP Kustannusvektori vrn kokoinen!
CALL RANDOM_NUMBER(pop)
pop = low + (up - low)*pop
DO i = 1, pop_size
cost(i) = f(pop(:,i))
END DO
END SUBROUTINE initialize

Aliohjelmaan initialize vlitetn kohdefunktio f, jotta voimme laskea


funktion arvot (cost) alkupopulaatiossa pop. Aliohjelmalle annetaan argumenttina mys ala- ja ylrajat (low ja up) vektorien alkioille.
Varsinainen optimointialiohjelma voidaan mritell seuraavasti:

266

Fortran 95/2003

SUBROUTINE optimize(f, pop, cost, max_iter, c, r)


IMPLICIT NONE
INTERFACE
FUNCTION f(x) RESULT(res)
USE de_kind, ONLY : prec
REAL(KIND=prec), DIMENSION(:), INTENT(IN) :: x
REAL(KIND=prec) :: res
END FUNCTION f
END INTERFACE
REAL(KIND=prec), DIMENSION(:,:), INTENT(INOUT) :: pop
REAL(KIND=prec), DIMENSION(:), INTENT(INOUT) :: cost
INTEGER, INTENT(IN) :: max_iter
REAL(KIND=prec), INTENT(IN) :: c, r
INTEGER :: n, pop_size, allocstat, iter, i, idx1, idx2, idx3
REAL(KIND=prec), DIMENSION(:,:), ALLOCATABLE :: new_pop
REAL(KIND=prec), DIMENSION(SIZE(pop,1)) :: trial
REAL(KIND=prec) :: score
LOGICAL, DIMENSION(SIZE(pop,1)) :: mask
n = SIZE(pop,1)
pop_size = SIZE(pop,2)
WRITE (*,(A,I2,/,A,I4,/,A,F5.3,/,A,F5.3)) &
Dimensio: , n, &
Pop. koko: , pop_size, &
Kerroin: , c, &
Risteytystodenn: , r
IF (SIZE(cost) /= pop_size) &
STOP Vektori cost vr kokoa!
ALLOCATE(new_pop(n,pop_size), STAT=allocstat)
IF (allocstat /= 0) STOP Virhe muistivarauksessa!
WRITE (*,(A,I4,G14.6)) &
Iteraatio, minimi: , 0, MINVAL(cost)
DO iter = 1, max_iter
DO i = 1, pop_size
CALL triple(i, pop_size, idx1, idx2, idx3)
mask = rnd(n) < r
mask(idx(n)) = .TRUE.
WHERE (mask)
trial = pop(:,idx3) + c*(pop(:,idx1)-pop(:,idx2))
ELSEWHERE
trial = pop(:,i)
END WHERE
score = f(trial)
IF (score < cost(i)) THEN
new_pop(:,i) = trial
cost(i) = score
ELSE
new_pop(:,i) = pop(:,i)
END IF
END DO
pop = new_pop

14. Esimerkkiohjelmia

267

IF (MOD(iter,step) == 0) THEN
WRITE (*,(A,I4,G14.6)) Iteraatio, minimi: , &
iter, MINVAL(cost)
END IF
END DO
END SUBROUTINE optimize

Aliohjelmassa kytetn hyvksi aliohjelmaa triple sek funktioita rnd


ja idx. Varsinainen laskenta tehtn sisimmss DO-silmukassa. Aliohjelma
triple palauttaa kolmen eri alkion jrjestysnumerot. Lauseissa
mask = rnd(n) < r
mask(idx(n)) = .TRUE.

lasketaan rekombinaatiota ohjaava looginen vektori mask, jota kytetn


WHERE-rakenteessa. Funktio rnd palauttaa n alkion mittaisen satunnaislukuvektorin, ja funktio idx palauttaa kokonaisluvun vlilt 1, 2, . . . , n. Mikli uusi vektori trial on parempi ratkaisu kuin populaation i:nnes alkio
pop(:,i), valitaan se uuteen populaatioon.
Vektorikolmikon {xa , xb , xc } indeksit voidaan laskea seuraavasti:
SUBROUTINE triple(i, n, i1, i2, i3)
IMPLICIT NONE
INTEGER, INTENT(IN) :: i, n
INTEGER, INTENT(OUT) :: i1, i2, i3
DO
i1 = idx(n)
IF (i1 /= i) EXIT
END DO
DO
i2 = idx(n)
IF (i2 /= i .AND. i2 /= i1) EXIT
END DO
DO
i3 = idx(n)
IF (ALL(i3 /= (/i,i1,i2/))) EXIT
END DO
END SUBROUTINE triple

Funktio rnd palauttaa satunnaisvektorin, jossa on n alkiota vlilt [0, 1):


FUNCTION rnd(n)
IMPLICIT NONE
INTEGER, INTENT(IN) :: n
REAL(KIND=prec), DIMENSION(n) :: rnd
CALL RANDOM_NUMBER(rnd)
END FUNCTION rnd

Funktio idx palauttaa satunnaisen kokonaisluvun vlilt 1, 2, . . . , n:


INTEGER FUNCTION idx(n)
IMPLICIT NONE
INTEGER, INTENT(IN) :: n
REAL(KIND=prec) :: x
CALL RANDOM_NUMBER(x)
idx = n*x + 1
END FUNCTION idx

268

Fortran 95/2003

14.8.4 DE:n kyttesimerkki


Olkoon minimoitavana funktio



f (x) = 1 + (xi 100)2 /4000
cos(xi 100)/ i.
i

Tmn funktion globaali minimi on x = (100, 100, . . . , 100) ja minimiarvo


f (x ) = 0. Voimme mritell kohdefunktion f seuraavasti:
MODULE fun
USE de_opt, ONLY : prec
CONTAINS
REAL(KIND=prec) FUNCTION f(x)
IMPLICIT NONE
REAL(KIND=prec), DIMENSION(:), INTENT(IN) :: x
REAL(KIND=prec), PARAMETER :: d = 4000.0
INTEGER :: i, n
REAL(KIND=prec), DIMENSION(SIZE(x)) :: ival
n = SIZE(x)
ival = (/ (i, i = 1, n) /)
f = SUM((x - 100.0)**2)/d
f = f - PRODUCT(COS((x - 100.0)/SQRT(ival))) + 1.0
END FUNCTION f
END MODULE fun

Seuraavassa pohjelmassa kytmme moduulia de_opt moduulista fun


lytyvn kohdefunktion f minimointiin:
PROGRAM de_test
USE fun, ONLY : f ! Kohdefunktio
USE de_opt, ONLY : initialize, optimize, prec
IMPLICIT NONE
INTEGER, PARAMETER :: n = 5, p = 24, max_iter = 500
REAL(KIND=prec) :: low = -600, up = 600, &
r = 0.1, c = 0.7
REAL(KIND=prec), DIMENSION(n,p) :: popul
REAL(KIND=prec), DIMENSION(p) :: values
CALL initialize(f, popul, values, low, up)
WRITE (*,(A,/,(3G14.6))) Arvot alussa: , values
CALL optimize(f, popul, values, max_iter, c, r)
WRITE (*,(A,/,(3G14.6))) Arvot lopussa: , values
WRITE (*,(A,/,G14.6)) Minimiarvo: , MINVAL(values)
WRITE (*,(A,/,(3G14.6))) Minimikohta: , &
popul(:,MINLOC(values))
END PROGRAM de_test

Tehtvn dimensioksi asetettiin n = 5. Ohjelmassa valitiiin populaation


kooksi p = 24, iteraatioiden mrksi 500, risteytystodennkisyydeksi
r = 0.1 ja kertoimeksi c = 0.7. Alkupopulaation alkioiden komponentit
xi ovat vlilt [600, 600].
Jos edell esitellyt moduulit ja pohjelma ovat nimin vastaavissa tiedostoissa (de_kind.f90, de_opt.f90 jne.), voidaan ohjelma knt, linkitt

14. Esimerkkiohjelmia

269

ja ajaa seuraavasti:
%
%
%
%
%

f90 -c de_kind.f90
f90 -c de_opt.f90
f90 -c fun.f90
f90 de_test.f90 de_kind.o de_opt.o fun.o
./a.out

Seuraavassa on esimerkki ohjelman tulostuksesta:


Arvot alussa:
308.759
124.886
176.973
...
162.530
150.726
151.268
Dimensio: 5
Pop. koko:
24
Kerroin: 0.700
Risteytystodenn: 0.100
Iteraatio, minimi:
0
22.6019
Iteraatio, minimi:
10
5.10033
Iteraatio, minimi:
20
2.17014
Iteraatio, minimi:
30
1.47248
...
Iteraatio, minimi: 470 0.187714E-04
Iteraatio, minimi: 480 0.647771E-05
Iteraatio, minimi: 490 0.647771E-05
Iteraatio, minimi: 500 0.617226E-05
Arvot lopussa:
0.685460E-04 0.148735E-01 0.525957E-04
...
0.149787E-01 0.148037E-01 0.617226E-05
Minimiarvo:
0.617226E-05
Minimikohta:
99.9968
99.9993
100.002
100.000
99.9983

Ohjelma siis lysi arvion tehtvn globaalille minimikohdalle.

14.8.5 Listietoa
DE-menetelm on esitelty lehdess Dr. Dobbs Journal (April 1997, sivut 1824). Optimointimenetelmi on esitelty CSC:n oppaissa Optimointitehtvien
ratkaiseminen [Haa04] ja Numeeriset menetelmt kytnnss [HHL+ 02].

14.9

Lineaaristen yhtlryhmien ratkaisu

Liittogradienttimenetelm (conjugate gradient method) on usein tehokas tapa ratkaista lineaarisia yhtlryhmi
Ax = b.

270

Fortran 95/2003

Tss vektorit x ja b kuuluvat joukkoon Rn ja matriisi A on symmetrinen


ja positiivisesti deniitti [HHL+ 02]. Oletamme mys, ett matriisi A on tihe
(vhn nollasta poikkeavia alkioita)
Seuraavassa on kuvaus yksinkertaisesta liittogradienttimenetelmst:
r1 = b Ax1
p1 = r 1
k=1
rk , rk 
DO WHILE 1 1 > 
r , r 
k

rk , rk 
pk , Apk 

xk+1 = xk + k pk
rk+1 = rk k Apk
k

rk+1 , rk+1 
rk , rk 

pk+1 = rk+1 + k pk
k
END DO

= k+1

Algoritmin lopetusehdossa vaaditaan, ett residuaalin rk = b Axk normi


rk , rk  on pienentynyt tarpeeksi.
Mrittelemme laskentatarkkuuden moduulissa cg_precision:
MODULE cg_precision
IMPLICIT NONE
! Liukulukujen tarkkuus:
INTEGER, PARAMETER :: &
prec = SELECTED_REAL_KIND(12,50)
END MODULE cg_precision

Toteutamme liittogradienttialgoritmin seuraavasti:


MODULE cg_alg
USE cg_precision, ONLY : prec
CONTAINS
SUBROUTINE cg(n, b, x, itmax, eps, matr_mult)
! Aliohjelma ratkaisee lineaarisen yhtlryhmn Ax=b
! liittogradienttialgoritmilla (CG)
IMPLICIT NONE
INTEGER, INTENT(IN) :: n
! Yhtlryhmn koko
REAL(prec), INTENT(IN) :: b(n)
! Oikean puolen vektori
REAL(prec), INTENT(INOUT) :: x(n) ! Alkuarvaus ja ratkaisu
INTEGER, INTENT(IN) :: itmax
! Iteraatioiden lkm
REAL(prec), INTENT(IN) :: eps
! Suppenemiskriteeri
EXTERNAL matr_mult
! Laskee matriisi-vektoritulon
REAL(prec) :: p(n)
! Etsintsuunta
REAL(prec) :: ap(n)
! A*p
REAL(prec) :: res(n)
! Residuaali
REAL(prec) :: alpha, beta ! Askelpituudet

14. Esimerkkiohjelmia

271

REAL(prec) :: rnorm
! Residuaalin normin neli
REAL(prec) :: rnnorm
! Uuden residuaalin normin neli
REAL(prec) :: rnorm0
! Alkuresiduaalin normin neli
INTEGER :: k
! Iteraatiokierros
CALL matr_mult(n, x,res) ! Laske alkuresiduaali b-Ax
res = b - res
p = res
! Etsintsuunta on alkuresiduaali
k = 1
rnorm = DOT_PRODUCT(res,res)
rnorm0 = rnorm
! Alkuresiduaalin normin neli
DO WHILE(k < itmax .AND. SQRT(rnorm)/SQRT(rnorm0) > eps)
CALL matr_mult(n, p, ap)
alpha = rnorm / DOT_PRODUCT(p,ap)
x = x + alpha * p
! Pivit iteraattia
res = res - alpha * ap ! Pivit residuaalia
rnnorm = DOT_PRODUCT(res,res)
beta = rnnorm / rnorm
p = res + beta * p
! Pivit etsintsuuntaa
rnorm = rnnorm
WRITE(*,*) k, SQRT(rnorm)
k = k + 1
END DO
WRITE (*, (/,A,I3,A,/)) CG suppeni , &
k-1, iteraatiossa
END SUBROUTINE cg
END MODULE cg_alg

Liittogradienttialgoritmissa tarvitaan matriisi-vektoritulon laskeva aliohjelma. Vlitmme aliohjelmaan cg tmn aliohjelman muodollisessa argumentissa matr_mult.
Seuraavassa moduulissa mrittelemme matriisitulon laskevan aliohjelman
cg_matrix_mult. Moduulissa on lisksi aliohjelma cg_matrix_init matriisin alustamiseen. Matriisi talletetaan moduulin yksityiseen muuttujaan
coeffmat.
MODULE cg_matrix
USE cg_precision, ONLY : prec
IMPLICIT NONE
REAL(prec), DIMENSION(:,:), ALLOCATABLE, PRIVATE :: &
coeffmat
CONTAINS
SUBROUTINE cg_matrix_init(n)
! Aliohjelma alustaa kerroinmatriisin
INTEGER, INTENT(IN) :: n
INTEGER :: allocstat, i, j
ALLOCATE(coeffmat(n, n), STAT=allocstat)
IF (allocstat /= 0) STOP muistinvaraus eponistui!
DO j=1, n
DO i=1, n
coeffmat(i,j) = 1/(1.0_prec+ABS(i-j))
END DO
END DO
END SUBROUTINE cg_matrix_init

272

Fortran 95/2003

SUBROUTINE cg_matrix_mult(n, x, y)
! Aliohjelma laskee matriisi-vektoritulon y=A*x
INTEGER, INTENT(IN) :: n
REAL(prec), INTENT(IN) :: x(n)
REAL(prec), INTENT(OUT) :: y(n)
y = MATMUL(coeffmat, x)
END SUBROUTINE cg_matrix_mult
END MODULE cg_matrix

Olemme sijoittaneet matriisitulon laskevan aliohjelman erilliseen moduuliin, jolloin ohjelman muuttaminen myhemmin on helpompaa. Tarvittaessa voisimme muuttaa tt moduulia vaikkapa tallettaen matriisin nauhamatriisina tai harvana matriisina. Mys rinnakkaistetun version tekeminen on
helpompaa [HM01].
Moduulien cg_alg ja cg_matrix avulla voimmekin nyt toteuttaa liittogradienttimenetelmn. Tarvittava pohjelma on esitetty seuraavassa:
PROGRAM cg_iterate
! Ohjelma ratkaisee tihen lineaarisen yhtlryhmn Ax=b
! liittogradienttialgoritmilla
USE cg_precision, ONLY : prec
USE cg_matrix, ONLY : cg_matrix_init, cg_matrix_mult
USE cg_alg, ONLY : cg
IMPLICIT NONE
INTEGER :: n
REAL(prec), DIMENSION(:), ALLOCATABLE :: rhs, x
INTEGER :: allocstat, u, t1, t2
WRITE (*,(A), ADVANCE=no) Anna matriisin koko n:
READ (*,*) n
CALL SYSTEM_CLOCK(count_rate=u)
CALL SYSTEM_CLOCK(count=t1)
ALLOCATE (rhs(n), x(n), STAT=allocstat)
IF (allocstat /= 0) STOP Muistinvaraus eponistui!
CALL cg_matrix_init(n)
rhs = 1
x = 0
CALL cg(n, rhs, x, 100, 1e-5_prec, cg_matrix_mult)
CALL SYSTEM_CLOCK(count=t2)
WRITE (*,(A,I4,A,F6.2)) Koko: , n, &
Aika: , REAL(t2-t1)/u
END PROGRAM cg_iterate

Voimme knt ja linkitt ohjelmamme seuraavasti:


% f90 -c cg_precision.f90
% f90 -c cg_alg.f90 cg_matrix.f90
% f90 cg_iterate.f90 cg_precision.o cg_alg.o cg_matrix.o

Seuraavassa on ohjelman kyttesimerkki:


% ./a.out

14. Esimerkkiohjelmia

273

Anna matriisin koko n: 500


1
1.62835857655826
2 0.495156832021520
3 0.236759072338562
...
15 2.818037136816830E-004
16 1.529607014624823E-004
CG suppeni
Koko:

16 iteraatiossa

500 Aika:

0.23

Siis 500 500 -kokoisen yhtlryhmn ratkaisuun kului aikaa noin 0.2 sekuntia.

14.9.1 Listietoja
Lineaaristen yhtlryhmien ratkaisusta kerrotaan CSC:n oppaassa Numeeriset menetelmt kytnnss [HHL+ 02]. Rinnakkaistettu versio liittogradienttialgoritmista lytyy CSC:n julkaisemasta MPI-oppaasta [HM01].

Harjoitustehtvi
1. Muuta kappaleen 14.1 (sivu 234) pinomoduulia siten, ettei pinolla ole
kiinte maksimikokoa.
2. Lis nelilaskimeen potenssiinkorotusoperaatio ^. Voit kytt komentona mys Fortranin potenssiinkorotusoperaattoria **, mutta tllin
joudut ksittelemn mys kahdesta merkist koostuvia komentoja.
3. Jos lajiteltavana on suuri mr dataa, kannattaa kytt esimerkiksi
Quicksort-algoritmia (katso vaikkapa teoksia [CLR90, Sed84]). Toteuta
kappaleen 14.2.1 lajittelumoduuli kytten Quicksortia.
4. Muuta kappaleen 14.3 sokkelonteko-ohjelman tulostusproseduuria siten, ett tulostuksessa ei kytet taulukko-operaatioita vaan sokkelo
tulostetaan suoraan kytten WRITE-lausetta.
5. Muuta sokkelonteko-ohjelman tulostusproseduuria siten, ett se tulostaa mys sokkelon ratkaisun. Vihje: aloita polun loppupst.
6. Kirjoita ohjelma, joka ratkaisee valmiin sokkelon. Sokkelon voi sytt
ohjelmalle esimerkiksi merkkijonotaulukkona.
7. Tutki kappaleessa 14.4 esiteltyj yksiulotteisia soluautomaatteja. Miten
kyttytyy totalistinen soluautomaatti (k = 2, r = 1), sntnumero
10? Ent (k = 5, r = 1)-soluautomaatti, snt 80092580? (Voit tulostaa
soluautomaatin vreiss kyttmll esim. PPM-tiedostomuotoa.)

274

Fortran 95/2003

8. Muuta Life-soluautomaattia simuloivaa ohjelmaa siten, ett saat talteen


erillisiin tiedostoihin soluautomaatin sukupolvia esittvt tilanteet. Voit
nimet tiedostot nimill tila.001, tila.002 jne.
9. Muuta binripuun toteuttavaa ohjelmaa siten, ett ohjelmaan voi sytt puhelinnumeroita ja niihin liittyvi nimi. Tee hakuja puhelinnumeroiden perusteella.
10. Syt sukupuun toteuttavalle ohjelmalle oma sukupuusi ja tee siit hakuja.
11. Hae maksimi funktiolle
f (x) = 3 (1 x1 )2 exp(x12 (x2 + 1)2 ) 10 (
exp(x12 x22 )

x1
x13 x25 )
5

1
exp((x1 + 1)2 x22 ) + 10.
3

Muuta tehtv minimointitehtvksi ja kyt kappaleessa 14.8 esitelty DE-menetelm. Funktiolla on kolme lokaalia maksimia lytk
menetelm aina globaalin maksimin?
12. Tutki kappaleessa 14.9 esitellyn liittogradienttimenetelmn suppenemisnopeutta. Miten menetelmn tehokkuus riippuu alkuarvauksen hyvyydest?
13. Pohjustus (preconditioning) nopeuttaa liittogradienttimenetelmn suppenemista. Toteuta kappaleen 14.9 liittogradialgoritmiin jokin sopiva
pohjustusmenetelm [HHL+ 02].

15. Siirtyminen Fortran 95 -kieleen

15

275

Siirtyminen Fortran 95
-kieleen

Tm luku on tarkoitettu niille, jotka ovat siirtymss FORTRAN 77:st


Fortran 95:een. Luvusta lienee hyty mys niille, jotka joutuvat perehtymn vanhoihin FORTRAN-ohjelmiin. Kerromme, mit uutta Fortran 95
-standardissa on verrattuna FORTRAN 77:n. Lisksi kerromme Fortran 95:n
vltettvist ja harvoin tarvittavista piirteist. Nm piirteet ovat jnteit
vanhasta FORTRANista, ja ksittelemme niiden korvaamista paremmilla ilmaisukeinoilla.

15.1

Miten Fortran 95:een kannattaa siirty

Trkein ohje uuden standardin kyttn ottamiseksi on:


If it works, dont x it!
Siis jos kytsssi on hyvin toimiva FORTRAN 77 -koodi, ei sit yleens
kannata muuntaa Fortran 95:ksi. Luultavasti muunnetusta koodista tulisi
kuitenkin vanhan FORTRANin nkist.
Fortran 95 -kntji voi kuitenkin kytt FORTRAN 77 -koodien kntmiseen. Fortran 95 -kntjt tarjoavat hyvn diagnostiikan esimerkiksi epstandardien ja vanhentuvien piirteiden lytmiseksi.
Jos olet tekemss kokonaan uutta koodia, kannattaa se kirjoittaa Fortran 95:ll jo standardiin kuuluvan paremman virheentarkistuksen takia. Jos
voit kytt vanhaa koodia aliohjelmakirjaston tapaan, eik uusi koodi edellyt vanhan koodin uudelleen kirjoittamista, voi Fortran 95:n kytt harkita
vanhan ohjelmakoodin rinnalla.
Jos vanhaa FORTRAN-koodia on muutettava huomattavasti (uusien ominaisuuksien lisys jne.), voit samalla koodata ohjelmakoodin uudestaan Fortran 95:ksi. Jos ohjelmassa on paljon vektori- ja taulukko-operaatioita, voi
koodi samalla huomattavasti selkeyty. Mys Fortran 95:een kuuluvia rakenteisia tietotyyppej kannattaa kytt koodin yksinkertaistamiseen.

276

15.2

Fortran 95/2003

Fortran 95:n uudet piirteet

Kymme aluksi lpi Fortran 95:n suurimmat muutokset aikaisempaan FORTRAN 77 -standardiin verrattuna.
Lhdekoodin vapaa syttmuoto tarjoaa luontevan tavan kirjoittaa ohjelmakoodia. Samalla vltyt turhilta sarakesidonnaisuuteen liittyvilt virheilt.
Fortran 95 tarjoaa moduulien ja sisisten proseduurien kyttmahdollisuuden, jolloin proseduurien argumentit voidaan tarkistaa knnsaikana. Lisksi voit kirjoittaa rekursiivisia proseduureja.
Fortran 95:n lajiparametrit mahdollistavat muuttujien lukualueen ja tarkkuuden mrittelyn laitteistosta riippumattomalla tavalla. Tllin koodin
siirrettvyys paranee.
Fortran 95:ss on mahdollisuus mritell omia muuttujatyyppej ja yleist
standardiproseduurit ja -operaattorit kohdistumaan uuden tyyppisiin muuttujiin. Lisksi voit mritell omia operaattoreita. Geneeristen proseduurien
avulla voit ksitell saman nkisell proseduurin kutsulla eri tyyppisi argumentteja.
Taulukkosyntaksi ja osoitinmuuttujat mahdollistavat tehokkaamman ja joustavamman tavan ksitell suuria tietomri. Dynaamisen muistinksittelyn
ansiosta proseduureille ei en tarvitse vlitt argumentteina tytiloja.
SELECT CASE -ohjausrakenne sek DO-toistorakenteen yleisemmt muodot
helpottavat ohjelmakoodin logiikan ilmaisemista.
Sytt- ja tulostusproseduurien ominaisuuksia on laajennettu. Esimerkiksi
tiedonsiirto NAMELIST-ryhmien avulla kuuluu nyt standardiin.
Fortran 95 sislt paljon uusia standardifunktioita ja -aliohjelmia (yhteens
standardiproseduureja on 115). Useimmat nist standardiproseduureista
ovat geneerisi eli niit voi kytt ksittelemn eri tyyppisi argumentteja.
Tmn lisksi Fortran 95 -kntj tarjoaa aikaisempaa tarkemmat ilmoitukset virheelliselle tai epstandardille lhdekoodille. Kntjn on mm. ilmoitettava vanhentuvien Fortranin piirteiden kytst.

15.3

FORTRAN 77:n huonoja puolia

Monet nykyiset ohjelmointikielet tarjoavat FORTRAN 77: kehittyneemmn


ja helpomman tavan ohjelmoida. Seuraavassa kymme lpi trkeimpi vanhan standardin puutteita. Niden lisksi FORTRAN 77:st puuttuivat mys
rekursiiviset proseduurit sek mahdollisuus rinnakkaisuuden esittmiseen.

15. Siirtyminen Fortran 95 -kieleen

277

15.3.1 Vanha sarakesidonnainen syttmuoto


FORTRAN 77:ss kytetty sarakesidonnainen lhdekoodin syttmuoto on
syyt korvata Fortran 95:n vapaalla syttmuodolla. Sarakesidonnaista muotoa voi viel kytt, mutta tm on virhealtista ja tarpeettoman hankalaa vapaamuotoiseen syttmuotoon verrattuna. Sarakesidonnainen syttmuoto
on muinaisjnne reikkorttien aikakaudelta, jolloin se oli jrkev tapa kirjoittaa ohjelmakoodia.
Samassa ohjelmatiedostossa ei voi kytt sek sarakesidonnaista ett vapaamuotoista syttmuotoa. Jos vanhaa ohjelmaa muutetaan vapaamuotoiseen syttmuotoon, muutos tytyy tehd ohjelmatiedosto kerrallaan. Huomaa, ett INCLUDE-lauseella ohjelmayksikn sisn luettu ohjelmakoodi tulkitaan osaksi samaa ohjelmatiedostoa.
Sarakesidonnaisessa muodossa rivin pituus on korkeintaan 72 merkki, mik aiheuttaa helposti ongelmia ja ohjelmointivirheit. Ensimmiset viisi saraketta on varattu lausenumeroille. Kuudennessa sarakkeessa voi olla vain
jatkorivin tunnus (vlilynnist tai nollasta poikkeava merkki).
Fortran 95:n sarakesidonnaisen syttmuodon kommenttirivi alkaa sarakkeessa 1 olevalla vlilynnist tai nollasta poikkeavalla merkill.
FORTRAN 77:ss kommenttirivi voi alkaa vain merkill C tai *. Kommentteja
ei voi sijoittaa koodirivien loppuun sarakesidonnaisessa lhdekoodissa.
Ohjelmakoodissa saa merkkijonojen ja kommenttien ulkopuolella kytt
vain isoja kirjaimia. Muuttujien nimet voivat olla korkeintaan kuuden merkin
mittaisia, eik alaviiva (_) kuulu merkistn.

15.3.2 Vlilynnit eivt olleet merkitsevi


FORTRAN 77 -standardissa eivt vlilynnit olleet merkitsevi muualla kuin
merkkijonovakioiden sisll. Tten jos rivi
DO 15 I = 1,10

muuttuu kirjoitusvirheen takia riviksi


DO 15 I = 1.10

niin lauseen merkitys muuttuu DO-silmukan aloituksesta sijoituslauseeksi


DO15I=1.10

Urbaani legenda kertoo, ett ers avaruusluotain menetettiin tllaisen Fortran-koodissa olleen kirjoitusvirheen vuoksi.
Onneksi nykyisess Fortran-standardissa vlilynnit ovat merkitsevi. Lisksi lause IMPLICIT NONE suojaa mahdollisilta muuttujien nimien kirjoitusvirheilt. Seuraavassa esimerkki:
INTEGER ARV0, ARV1
ARVO = 12
ARV1 = FUN(ARVO)

278

Fortran 95/2003

Huomasitko, ett koodissa oli kirjoitusvirhe? Muuttujan nimen piti olla ARV0
eik ARVO (tai pienill kirjaimilla: arv0 eik arvo). Jos tss tapauksessa olisi
kytss Fortranin tunnusten etumerkkisnt, vlitettisiin funktioon FUN
kokonaislukumuuttujan arv0 sijaan alustamaton reaalilukumuuttuja arvo.
Tuloksena on arvaamattomasti kyttytyv ohjelma.

15.3.3 Numeeristen tyyppien huono siirrettvyys


FORTRAN 77 sislt reaalilukutyypit REAL ja DOUBLE PRECISION, jotka
merkitsevt eri koneilla eri asioita. Tten toisen koneen kaksoistarkkuus
voi vastata toisen koneen yksinkertaista tarkkuutta. Tarkkuuden muunnoksia varten on FORTRAN 77 -kntjiss lukuisia muunnosvalitsimia, joilla saa esimerkiksi REAL-tyypin muuttujat vastaamaan DOUBLE PRECISION
-tyyppi. Tm on kuitenkin virhealtista, erityisesti jos ohjelmakoodissa on
kytetty COMMON-alueita tai EQUIVALENCE-lauseita.
Fortran 95:n lajiparametrit korvaavat kokonaan DOUBLE PRECISION -tyypin
kytttarpeen. Lisksi lajiparametrit mahdollistavat kytetyn koneen numeeristen ominaisuuksien selvittmisen. Kytettviss on mm. standardifunktiot EPSILON ja PRECISION, joita voi kytt vaikkapa algoritmin suppenemiskriteerin laskemiseen.

15.3.4 Dynaamisten ja rakenteisten tietotyyppien puute


FORTRAN 77 tarjosi vain staattisen tilanvarauksen taulukoille. Tten tilapist tytilaa ei voinut luoda tarpeen mukaan, vaan ohjelman kynnistyess oli varattava tytilavektoreita mahdollista myhemp tarvetta varten.
FORTRAN 77 ei myskn tuntenut rakenteisia tietotyyppej tai osoitinmuuttujia. Tten useimmat tietorakenteet oli toteutettava hankalasti skalaarien ja taulukkojen avulla. Algoritmeissa tehtiin usein tarpeetonta datan
kopiointia paikasta toiseen, mink olisi voinut vltt osoitinmuuttujilla.
Fortran 95:ss on kytettviss dynaamiset taulukot (mreet ALLOCATABLE
ja POINTER) sek proseduurien automaattisesti varattavat taulukot.

15.3.5 Vaaralliset riippuvuudet tiedon talletustavasta


FORTRAN 77:ss olivat COMMON-alueet ainoa tapa jakaa globaalia dataa ohjelmayksikkjen kesken, jos ei halunnut vlitt dataa proseduurien parametrilistoissa. COMMON-alueiden kytt koskevat snnt olivat hyvin vljt,
jolloin kyttj pystyi tekemn pahoja ohjelmointivirheit.
Lisksi EQUIVALENCE-lausetta kytettiin mm. taulukkojen aliasten luomiseen, mik teki koodeista usein hyvin koneriippuvaisia. Tm aiheutti suuria
ongelmia koodien siirtmiselle koneesta toiseen.
Ikv kyll FORTRAN 77 -kieless ei ollut vaihtoehtoja COMMON-alueiden
ja EQUIVALENCE-lauseiden kytlle. Fortran 95:ss voi kytt moduuleja

15. Siirtyminen Fortran 95 -kieleen

279

COMMON-alueiden sijasta. EQUIVALENCE-lauseet voi korvata osoitinmuuttujilla tai vaikkapa standardifunktiolla TRANSFER.
Seuraava FORTRAN 77 -koodi saattaa knty ilman varoituksia:
PROGRAM HUH
REAL LUKU, A, B
COMMON /HUPS/ LUKU, A, B
CALL NURJA
WRITE (*,*) LUKU,A,B: , LUKU, A, B
END
SUBROUTINE NURJA
COMMON /HUPS/ LUKU, A, B
LUKU = 3
A = 4
B = 5
END

Ohjelma toimii odottamattomalla tavalla:


LUKU,A,B:

0.0000000E+00

4.000000

5.000000

Vaikka muuttujalle LUKU annettiin arvoksi luku 3, tulosti ohjelma arvon nolla. Tm johtuu siit, ett aliohjelmassa NURJA kytettiin alkukirjainsnt,
ja muuttuja LUKU oletettiin kokonaislukutyyppiseksi! Tllin pohjelmassa
kytettyyn COMMON-alueeseen tallettui tmn kokonaislukumuuttujan arvo.

15.4

FORTRAN 77 -koodien muuntaminen uuteen


muotoon

Seuraavasta muistilistasta on hyty muunnettaessa vanhoja FORTRAN 77


-koodeja uuden Fortran 95 -kielen mukaisiksi.
Jos ohjelmointiprojekti on iso ja siin on mukana monta ohjelmoijaa, on
syyt kytt versionhallintajrjestelm. Tllin pystytn tarvittaessa palaamaan takaisin aikaisempiin ohjelmistoversioihin.
Muodosta testiaineisto koodin oikeellisuuden testaamiseksi, jolloin havaitset virheet varmemmin kuin jlkikteen testaamalla. Testaa ohjelmayksikkj sek erikseen ett yhdess toisten ohjelmayksikkjen kanssa.
l kyt sarakesidonnaista ohjelmakoodin syttmuotoa, sill se huonontaa ohjelmakoodin luettavuutta. Muuta kommenttimerkki C huutomerkiksi
! sek muuta jatkorivit uuden kytnnn mukaisiksi.
Seuraavassa on pieni esimerkki FORTRAN 77 -koodista:
SUBROUTINE ACTSCA
C
C
C

ACTSCA

ACTIVATES THE RESCALING

DOUBLE PRECISION DIST,BIAS,GRAGRA,GRA


COMMON /SCALE/ DIST(10,10,20),BIAS(10,20),

280

Fortran 95/2003

GRA(10,20),NGRA(20),LSCA

C
IF(LSCA.EQ.0)LSCA = 1
C
RETURN
END

Tmn voisi muuntaa aluksi seuraavaan muotoon:


SUBROUTINE ACTSCA
!

ACTSCA

ACTIVATES THE RESCALING

DOUBLE PRECISION DIST,BIAS,GRAGRA,GRA


COMMON /SCALE/ DIST(10,10,20),BIAS(10,20), &
GRA(10,20),NGRA(20),LSCA
IF(LSCA.EQ.0)LSCA = 1
RETURN
END

Tss muutimme kommenttimerkiksi huutomerkin !. Lisksi kytimme Fortran 95:n tapaa ilmaista jatkorivi merkill &. Koodi on nyt vapaan lhdekoodin
muotoista.
Seuraava versio voisi nytt tlt:
SUBROUTINE actsca
! actsca activates the rescaling
USE scale_mod, ONLY : lsca
IMPLICIT NONE
IF (lsca == 0) lsca = 1
END SUBROUTINE actsca

Tss siirsimme COMMON-alueen SCALE tiedot moduuliin scale_mod. Aliohjelmassa otimme tst moduulista kyttn vain tunnuksen lsca. Aliohjelma yksinkertaistui huomattavasti.
l kyt Fortranin implisiittist tyypinmrittely eli alkukirjainsnt.
Kyt sen sijaan lausetta IMPLICIT NONE ohjelmayksikkjen alussa. Tllin
mahdolliset tunnusten kirjoitusvirheet tulevat useimmiten esiin jo knnsaikana.
Selkeyt ja yhtenist muuttujamrittelyt. l kyt erillisi DIMENSION- ja
PARAMETER-lauseita, vaan vastaavia tyyppimreit. Siis mrittelyt
INTEGER N
PARAMETER (N = 100)
REAL X
DIMENSION X(N)

on syyt muuttaa muotoon


INTEGER, PARAMETER :: n = 100
REAL, DIMENSION(n) :: x

Tarkista, ettei koodissa kytet epstandardeja piirteit, kuten tyyppi DOUBLE

15. Siirtyminen Fortran 95 -kieleen

281

COMPLEX, tyyliin REAL*16 mriteltyj muuttujien tarkkuuksia ja NAMELISTdatan epstandardeja versioita.


Poista vanhentuviin piirteisiin kuuluvat ohjausrakenteet. Kyt esimerkiksi
DO ... END DO -rakenteita seuraavan tyyppisten rakenteiden sijaan:
100

DO 100 I = 1, N
PRINT (F10.6), X(I)

Korvaava ja parempi rakenne olisi seuraava:


DO i = 1, n
PRINT (F10.6), x(i)
END DO

Monet silmukat voi mys korvata taulukkosyntaksilla. Edellisen silmukan


voi kirjoittaa seuraavasti yhdeksi lauseeksi:
PRINT (F10.6), x(1:n)

Kyt moduuleita, sisisi aliohjelmia ja INTERFACE-lohkoja proseduurikutsujen virheentarkistukseen.


Korvaa COMMON-alueet ja BLOCK DATA -yksikt moduuleilla. Tllin ohjelman
siirrettvyys paranee ja saat kyttsi Fortran 95:n kehittyneet tyyppitarkistukset.
Vlt EQUIVALENCE-lauseita, sill nm heikentvt ohjelmakoodin siirrettvyytt. EQUIVALENCE-lauseiden tarve on poistunut uuden standardin myt. Kytettviss on korvaavina vaihtoehtoina mm. taulukkojen POINTER-,
TARGET- ja ALLOCATABLE-mreet sek TRANSFER- ja RESHAPE-funktiot.
Kyt dynaamisia taulukkomrittelyj kiinteiden tytilavektoreiden sijaan.
Voit kytt joko taulukoiden ALLOCATABLE-mrett tai osoitinmuuttujia
(POINTER). Dynaamista muistinksittely on esitelty luvussa 11 (sivu 164).
Korvaa lausefunktiot (statement function) sisisill proseduureilla. Nm
tarjoavat paremmat knnsaikaiset tarkistukset ja tuottavat tehokasta koodia. Ulkoisten proseduurien sijaan tulisi kytt joko sisisi proseduureja
tai moduuleja.
Muuta mrittely DOUBLE PRECISION esimerkiksi mrittelyksi
INTEGER, PARAMETER :: tarkka = SELECTED_REAL_KIND(10)
REAL(KIND=tarkka) :: x

Muuttujalla x on tss tapauksessa vhintn 10 desimaalinumeron esitystarkkuus.

15.5

Ohjausrakenteiden kytt

FORTRAN 77:ss ei ollut kytettviss END DO -lausetta silmukan lopettamiseen, mik johti seuraavan kaltaisiin vaikeasti luettaviin silmukoihin:
DOUBLE PRECISION A, X

282

Fortran 95/2003

DIMENSION A(N,N), X(N)

100

DO 100 I=2,N
X(I)=1D30
DO 100 II=1,N
A(II,I)=0D0

Tmn toistorakenteen voi esitt selkemmin seuraavasti:


INTEGER, PARAMETER :: tarkka = SELECTED_REAL_KIND(12)
REAL(KIND=tarkka), DIMENSION(n,n) :: a
REAL(KIND=tarkka), DIMENSION(n) :: x
DO i = 2, n
x(i) = HUGE(x)
DO ii = 1, n
a(ii,i) = 0.0_tarkka
END DO
END DO

Ohjelmakoodissa kytettiin standardifunktiota HUGE suurimman muuttujatyypill x esitettvn luvun tuottamiseen.


Edelliset silmukat saadaan viel selkempn muotoon taulukkosyntaksia
kyttmll:
x(2:n) = HUGE(x)
a(1:n,2:n) = 0.0_tarkka

Monissa vanhoissa Fortran-koodeissa on esimerkiksi seuraavan nkisi rakenteita:


INTEGER I, M, A, B

100
200
500

IF (I .EQ. 1) GOTO 100


IF (I .EQ. 3) GOTO 200
M = M + A
GOTO 500
M = M + B
GOTO 500
M = M - B
CONTINUE

Tmn esimerkin voi kirjoittaa selkemmin SELECT CASE -rakennetta kytten seuraavasti:
INTEGER :: i, m, a, b
SELECT CASE (i)
CASE (1)
m = m + b
CASE (3)
m = m - b
CASE DEFAULT
m = m + a
END SELECT

Ehk selkein mahdollisuus on IF-rakenteen kytt:

15. Siirtyminen Fortran 95 -kieleen

283

INTEGER :: i, m, a, b
IF (i == 1) THEN
m = m + b
ELSE IF (i == 3) THEN
m = m - b
ELSE
m = m + a
END IF

Haarautuva hyppylause (computed goto) johtaa helposti erittin sekavaan


ohjelmalogiikkaan. Seuraavassa on esimerkki:
INTEGER EHTO
REAL X, Y, Z

10
11

GO TO (10, 11, 10) EHTO


Y = Z
X = Y + 2
X = X**2
X = X + 3

Edellinen rakenne voitaisiin esitt yksinkertaisemmin seuraavasti:


INTEGER :: ehto
REAL :: x, y, z
SELECT CASE (ehto)
CASE (1,3)
x = y + 2
x = x**2
x = x + 3
CASE (2)
x = x + 3
CASE DEFAULT
y = z
x = y + 2
x = x**2
x = x + 3
END SELECT

Tss tapauksessa voisi kytt mys IF-rakennetta (tm on harjoitustehtvn).

15.6

COMMON-alueiden korvaus moduuleilla

Seuraavassa on esimerkki tyypillisist FORTRAN 77 -koodissa kytetyist


COMMON-alueista:
COMMON/EQ/N,NTOT
COMMON/FCALLS/NC
COMMON/TOL/ABSTOL,RELTOL

Seuraavassa on nm COMMON-alueet korvaava moduuli:

284

Fortran 95/2003

MODULE globaalit_muuttujat
IMPLICIT NONE
INTEGER, SAVE :: n, ntot, nc
REAL, SAVE :: abstol, reltol
END MODULE globaalit_muuttujat

Muuttujien mre SAVE on tarpeen, jos moduulin arvojen halutaan silyvn mys proseduurikutsujen vlill! Jos SAVE-mrett ei kytet, silyvt
moduulissa mriteltyjen muuttujien arvot ohjelman suorituksen ajan ainoastaan silloin, kun moduuli otetaan kyttn mys pohjelmassa.
Moduulia voi kytt vaikkapa seuraavasti:
USE globaalit_muuttujat
USE globaalit_muuttujat, ONLY : n, ntot

Ensimminen USE-lause otti kyttn kaikki moduulin oliot. Toisessa lauseessa otettiin kyttn vain muuttujat n ja ntot.
Edell sijoitettiin kaikki globaalit muuttujat samaan moduuliin. Tmn sijasta voi tietysti kytt erillisi moduuleja, joiden kytt vastaa alkuperisten
COMMON-alueiden kytt.

15.7

Fortranin vanhentuvat piirteet

Fortran 90 -standardi sislsi vanhentuviksi merkittyj piirteit (obsolescent


feature). Vanhentuvat piirteet siirrettiin standardin seuraavissa versioissa
poistettujen piirteiden (deleted feature) listalle, jolloin ne eivt en kuulu
standardiin.
Fortran 95 -standardissa mriteltiin seuraavat poistetut piirteet:
reaalilukutyyppiset silmukkamuuttujat
hyppy END IF -lauseeseen lohkon ulkopuolelta
PAUSE-lause
ASSIGN-komento, epsuora hyppylause (assigned goto) sek FORMATlausenumeron sijoittaminen kokonaislukumuuttujaan ASSIGN-lauseella
nH-muotoilukoodit (Hollerith-merkkijonovakiot).
Vanhentuvien piirteiden listalle siirrettiin Fortran 95:ss seuraavat piirteet:
aritmeettinen IF-lause
usean DO-silmukan yhteinen lopetuslause
vaihtoehtoinen paluukohta
haarautuva hyppylause (computed goto)

15. Siirtyminen Fortran 95 -kieleen

285

lausefunktiot
suoritettavien lauseiden joukossa olevat DATA-lauseet
oletetun mittaiset merkkijonot (assumed length)
sarakesidonnainen lhdekoodin muoto
CHARACTER*-tyyliset merkkijonojen mrittelyt.
Seuraavassa kuvataan lyhyesti joitakin standardin vanhentuvista ja poistetuista piirteist. Niden kytt tulee vltt.

15.7.1 Aritmeettinen IF-lause


Aritmeettinen IF-lause on kolmisuuntainen hyppylause:
IF (I) 10,20,30

Jos muuttuja I on negatiivinen, hyptn lauseeseen 10; jos I on nolla,


hyptn lauseeseen 20 ja muussa tapauksessa lauseeseen 30.
Aritmeettisen IF-lauseen voi korvata IF-rakenteella.

15.7.2 Hyppy END IF -lauseeseen rakenteen ulkopuolelta


Aikaisemmin seuraava ohjelmakoodi oli sallittua:

100

IF (Y .EQ. 0) GOTO 100


IF (X .GT. 0) THEN
Y = 1.0/X
END IF

Fortran 90 -standardissa tytyy hyppylauseen osoitteena olla END IF -lausetta seuraava lause (esimerkiksi CONTINUE-lause).
Hyppylauseet kannattaa useimmiten korvata kehittyneemmill ohjausrakenteilla.

15.7.3 Reaalilukutyyppiset silmukkamuuttujat


FORTRAN 77 salli DO-rakenteessa reaalilukutyyppiset silmukkamuuttujat.
Tm johtaa helposti laiteriippuvaisuuksiin. Esimerkiksi silmukka
DO x = 0.0, 1.0, 0.1
WRITE (*,*) x
END DO

voidaan suorittaa 10 tai 11 kertaa riippuen reaalilukujen esitystavasta. Reaalilukutyyppisten silmukkamuuttujien sijaan on syyt kytt kokonaislukutyyppisi lausekkeita, koska tllin toistokertojen mr on sama riippumatta liukulukuaritmetiikan tarkkuudesta.

286

Fortran 95/2003

15.7.4 Usean DO-silmukan yhteinen lopetuslause


Vanhoissa koodeissa nkee seuraavan nkisi silmukoita:

100

DO 100 I=2,N
X(I)=1D30
DO 100 II=1,N
A(II,I)=0D0

Tmn sijaan kannattaa kytt END DO -lausetta silmukkojen lopettamiseen. Tt on havainnollistettu kappaleessa 15.5 (sivu 281).
Mys silmukan pttminen muuhun kuin END DO tai CONTINUE-lauseeseen
on mritelty vanhentuvaksi piirteeksi.

15.7.5 ASSIGN-lause ja epsuora hyppylause


Epsuoraa hyppylausetta (assigned goto) on kytetty simuloimaan proseduurikutsua.
Seuraavassa on esimerkki epsuorasta hyppylauseesta:
ASSIGN 100 TO I
GOTO I

Tmn sijaan on syyt kytt joko IF-lausetta tai proseduurin kutsua.


ASSIGN-lausetta on kytetty mys FORMAT-lauseen lausenumeron sijoittamiseen kokonaislukumuuttujaan. Tmn sijaan kannattaa muotoilukoodi sijoittaa merkkityypin muuttujaan tai nimettyyn merkkijonovakioon ja kytt tt tiedonsiirto-operaatioissa.

15.7.6 Vaihtoehtoinen paluukohta


Aliohjelman vaihtoehtoinen paluukohta (alternate return) mahdollisti paluun
proseduurikutsusta erikseen mriteltvn lauseeseen:

100
200

PROGRAM kutsu
...
CALL ali(i, j, *100, *200)
...
CONTINUE
...
CONTINUE
...
END
SUBROUTINE ali(i, j, *, *)
...
RETURN 1
...
RETURN 2
...
END

15. Siirtyminen Fortran 95 -kieleen

287

Vaihtoehtoisen paluukohdan sijaan on syyt kytt kokonaislukutyyppist


paluukoodia ja vaikkapa SELECT CASE -rakennetta:
PROGRAM kutsu
...
CALL ali(i, j, koodi)
SELECT CASE(koodi)
CASE(1)
...
END SELECT
END PROGRAM kutsu

15.7.7 PAUSE-lause
PAUSE-lause keskeytt ohjelman suorituksen, kunnes nppint painamalla
annetaan lupa jatkaa suoritusta. Tmn sijaan kannattaa tulostaa kehote
WRITE- tai PRINT-lauseella ja lukea ptteelt syte READ-lauseella merkiksi
siit, ett ohjelman suoritusta saa jatkaa:
CHARACTER(LEN=80) :: rivi
DO
WRITE (*,*) Jatketaanko suoritusta (K,k)?
READ (*,(A)) rivi
rivi = ADJUSTL(rivi)
IF (rivi(1:1) == K .OR. rivi(1:1) == k) EXIT
END DO

15.7.8 Hollerith-merkkijonovakiot
Hollerith-merkkijonovakioita kytettiin osana FORMAT-lauseden muotoilukoodia. Tmn sijaan voi kytt lainausmerkkeihin sijoitettua merkkijonovakiota.
Seuraavassa on esimerkki vanhentuvasta mrittelyst:
100

PRINT 100, X
FORMAT (5HArvo:,F10.6)

Tmn voi esitt selkemmin seuraavasti:


PRINT ("Arvo:",F10.6), x

15.8

Vltettvi Fortranin piirteit

Seuraavia Fortranin piirteit olisi syyt vltt, vaikka kntjt niit yh


ymmrtvt.

288

Fortran 95/2003

Taulukon alkioiden sijoittelujrjestys muistissa (storage association).


Standardi ei en vaadi, ett taulukkojen alkiot talletetaan jossain tietyss jrjestyksess koneen keskusmuistiin. Tm ei olisi edes mahdollista rinnakkaiskoneissa!
COMMON-alueiden kytt.
EQUIVALENCE-lauseen kytt.
BLOCK DATA -yksikkjen kytt.
Proseduuriin vlitettvn taulukon koon ja dimensioiden lukumrn
vristely. Tt on kytetty esimerkiksi vlittmn kaksiulotteinen
taulukko yksiulotteisena vektorina aliohjelmaan. Koska taulukon alkioiden jrjestyst muistissa ei ole mritelty standardissa, tm voi johtaa
pahoihin virhetilanteisiin!
ENTRY-lause.
Ohjelmakoodin sarakesidonnainen syttmuoto.
DOUBLE PRECISION -tyyppi.
Haarautuva hyppylause (computed goto)
Merkkijonon pituuden mrittely tyyliin
CHARACTER*30

Kyt sen sijaan mrittely CHARACTER(LEN=pituus).


Lausefunktiot (statement function). Nm voi korvata sisisill proseduureilla.
Standardifunktioiden erityisnimet (specic name). Kyt sen sijaan standardifunktioiden geneerisi nimi. Siis SIN eik DSIN jne.
Tyypillinen esimerkki vltettvst ja vaarallisesta FORTRAN-koodista on
seuraava tapa vlitt kaksiulotteinen taulukko yksiulotteisena taulukkona
aliohjelmaan:

100

PROGRAM HULLU
INTEGER N
PARAMETER (N = 10)
REAL A(N,N+1)
CALL ALUSTA(A,N,N+1)
WRITE (*,*) A(1,1), A(2,3)
END
SUBROUTINE ALUSTA(V,N,M)
INTEGER N, M
REAL V(N*M)
INTEGER I
DO 100 I = 1, N*M
V(I) = I
CONTINUE
END

15. Siirtyminen Fortran 95 -kieleen

289

Ohjelma tulostaa seuraavaa:


1.000000

22.00000

Parempi tapa esitt edellinen alustusoperaatio on seuraava:


PROGRAM hullu
IMPLICIT NONE
INTEGER, PARAMETER :: n = 10
REAL, DIMENSION(n,n+1) :: a
CALL alusta(a)
WRITE (*,*) a(1,1), a(2,3)
CONTAINS
SUBROUTINE alusta(taulukko)
IMPLICIT NONE
REAL, DIMENSION(:,:), INTENT(OUT) :: taulukko
INTEGER :: i, n, m
n = SIZE(taulukko,1)
m = SIZE(taulukko,2)
taulukko = RESHAPE( (/ (i, i = 1, n*m) /), &
(/ n, m /) )
END SUBROUTINE alusta
END PROGRAM hullu

Tss kytettiin standardifunktiota RESHAPE taulukon alustamiseen.

Harjoitustehtvi
1. Muunna seuraava sarakesidonnaista FORTRAN 77 -syttmuotoa kyttv ohjelma vapaaseen syttmuotoon. Kyt ohjelman alussa lausetta IMPLICIT NONE ja silmukoissa ohjausrakennetta DO ... END DO. Sisenn ohjelmakoodi selkesti.
PROGRAM SILMUKAT
REAL X(100,100), Y(100), Z(100), A, S
INTEGER I, J, K

3
2
5

DO 1 J = 1, 100
Y(J) = 0.1*J
DO 1 I = 1, 100
X(I,J) = 1.0 + Y(J)
CONTINUE
DO 2 J = 1, 100
S = 0.0
DO 3 I = 1, 100
S = S + X(I,J)
CONTINUE
Z(J) = SQRT(Y(J))/S
CONTINUE
DO 5 J = 1, 100
IF (Z(J) .LT. 0.001) Z(J) = 0.0
A = Z(1)
K = 1

290

Fortran 95/2003

DO 6 I = 2, 100
IF (Z(I) .GT. A) THEN
K = I
A = Z(I)
ENDIF
CONTINUE
WRITE (*,*) A, K
END

2. Muunna edellisen tehtvn ohjelmakoodi taulukkosyntaksiksi jos mahdollista. Kyt Fortran 90 -kielen sisnrakennettuja funktioita SUM,
MAXVAL ja MAXLOC.
3. Esit seuraava haarautuvaa hyppylausetta kyttv ohjelmakoodi IFrakennetta kytten:
INTEGER EHTO
REAL X, Y, Z

10
11

GO TO (10, 11, 10) EHTO


Y = Z
X = Y + 2
X = X**2
X = X + 3

16. Fortran 2003

16

291

Fortran 2003

Nykyinen Fortran-standardi on nimeltn Fortran 2003. Standardi on huomattava laajennus verrattuna aiempiin standardeihin. Esitmme tiiviin katsauksen standardin uusiin piirteisiin.
Uusien piirteiden mrst johtuen standardia tysin noudattavia kntji
ei tt kirjoittaessa (vuoden 2007 kevll) ole saatavilla. Sosittelemme, ett Fortran-ohjelmoinnissa kytetn Fortran 95 -standardin piirteit, jotka
sisltyvt Fortran 2003:een.
Seuraava standardi, tynimeltn Fortran 2008, on jo valmisteilla. Tm
standardi tuo lis uusia piirteit Fortran-kieleen. Nit uusia ominaisuuksia
ymmrtvi kntji saamme odottaa luultavasti useita vuosia.

16.1

Fortran 2003:n trkeimmt lisykset ja


parannukset

Fortran 2003 on laaja pivitys kielen standardiin ja sislt runsaasti uudistuksia:


parannuksia rakenteisten tyyppien ksittelyyn: parametrisoidut tyypit,
parannetut alustimet ja lopettajat
tuki olio-ohjelmoinnille: tyyppien laajennukset ja periytyminen, polymorsmi, dynaaminen tyyppien varaus ja tyyppeihin sidotut proseduurit
datan ksittelyn parantaminen: varattavat komponentit, myhstetyt
tyyppiparametrit, volatile-mre, laajennetut alustimet ja standardiproseduurit
IEEE 754 -standardin mukainen aritmeettisten keskeytysten ksittely
(yli- ja alivuoto jne.)
yhteensopivuus C-kielen kanssa
proseduureihin osoittavat muuttujat
kansainvlisten merkkivalikoimien kytt: ISO 10646, neljn tavun merkist

292

Fortran 95/2003

parannettu tuki kyttjrjestelmn ominaisuuksille: komentoriviargumentit, ympristmuuttujat, virhetilanteet.

16.1.1 Rakenteisten tyyppien rarametrisointi


Fortran 95:ss standardityypeill on lajiparametri ja merkkijonoilla lisksi
pituusparametri. Fortran 2003:ssa rakenteisissa tyypeiss voi kytt parametreja, jotka mritelln vasta kytn yhteydess.
Seuraavassa on esimerkki matriisityypist, jolla on lajiparametri:
TYPE matriisi(laji, m, n)
INTEGER, KIND :: laji = SELECTED_REAL_KIND(10)
INTEGER, LEN :: m, n
REAL(laji), DIMENSION(m,n) :: alkiot
END TYPE

Tmn tyyppinen olio voidaan ottaa kyttn seuraavasti:


TYPE(matriisi(SELECTED_REAL_KIND(6), 100, 101)) :: x

Tuloksena on 100 101 -taulukko, jonka alkiot ovat reaalilukuja.

16.1.2 Mreiden arvojen alustus


Fortran 95 -standardissa ei ollut mahdollista antaa rakenteisten tyyppien
mreille oletusarvoa. Nyt tm on mahdollista:
TYPE date
INTEGER :: day
CHARACTER(LEN = 5) :: month
INTEGER :: year = 1994
END TYPE date

Tss vuoden oletusarvoksi asetettiin 1994. Mrittely voi kytt seuraavasti:


TYPE(date), PARAMETER :: &
today = date(21, Feb., 1995)

16.1.3 Sytn ja tulostuksen parannukset


Fortran 2003:ss on parannettu sytt ja tulostusta seuraavasti:
asynkroninen tiedonsiirto: ohjelma voi jatkaa suoritustaan sytn ja
tulostuksen aikana
sytevirran (stream) ksittely: tiedostoa voi ksitell ilman tietuerakenteen kytt
kyttjn mrittelemt tiedonsiirto-operaatiot rakenteisille tyypeille

16. Fortran 2003

293

kyttjn mrittelemt pyristysoperaatiot formatointimuunnoksissa


FLUSH-lause
nimetyt vakiot valmiiksi yhdistetyille kanaville (preconnected unit)
sytt- ja tulostuslauseiden avainsanojen yhtenistminen
psy sytn ja tulostuksen virheilmoituksiin.

16.1.4 Yhteensopivuus C-kielen kanssa


Fortran 2003:ssa parannetaan yhteensopivuutta C-kielen kanssa sisnrakennetulla moduulilla ISO_BINDING_C sek mreill BIND ja VALUE. Useimmat Fortran-kntjt ovat tukeneet C-kielen kytt, mutta eivt siirrettvll tavalla.
Parannnukset liittyvt seuraaviin piirteisiin:
muuttujatyypit: perustyypit, rakenteiset tyypit ja osoittimet
sek skalaari- ett taulukkomuuttujat
proseduurit (tunnetun kutsumuodon avulla).

16.1.5 Osoittimet proseduureihin


Proseduuriosoittimilla voidaan dataan liitt datan ksittelyn toiminnallisuuksia. Olio-ohjelmoinnin termeill kyseess on metodien liittminen dynaamisesti olioihin. Seuraavassa on tst esimerkki:
PROCEDURE(pros), POINTER :: p => NULL()

Tss proseduuri p viittaa aluksi tyhjn ja sill on sama kutsumuoto kuin


proseduurilla pros.
Jos kytettviss ei ole valmiiksi sopivaa kutsumuotoa, voidaan mritell
abstrakti kutsumuoto:
ABSTRACT INTERFACE
COMPLEX FUNCTION f(x,y,z)
COMPLEX, INTENT(IN) :: x, y, z
END FUNCTION
END INTERFACE

Proseduuriosoittimia voidaan kytt hyvksi rakenteisissa tietotyypeiss:


TYPE matriisi(laji, m, n)
INTEGER, KIND :: laji = SELECTED_REAL_KIND(10)
INTEGER, LEN :: m, n
REAL(laji), DIMENSION(m,n) :: alkiot
PROCEDURE (lu_hajotelma), POINTER :: ratkaisija
END TYPE

294

Fortran 95/2003

Tt tyyppi voidaan kytt seuraavasti:


TYPE(matriisi(SELECTED_REAL_KIND(6), 100, 101)) :: x
...
CALL x%ratkaisija( ... )

16.1.6 Rakenteisen tyypin lopettaminen


Rakenteisissa tyypeiss voi kytt FINAL-aliohjelmia, joilla voidaan hoitaa
tarvittavat toimenpiteet kun kyseisen tyypin olio lakkaa olemasta:
TYPE t
...
CONTAINS
FINAL :: lopetus1, lopetus2
END TYPE t

Kun olio lopetetaan, kutsutaan sit lopetusaliohjelmaa, jonka argumentit


vastaavat rakenteisen tyypin alkioiden tyyppej ja ulottuvuuksia.

16.1.7 Proseduurien liittminen tyyppeihin


Rakenteisen tyypin komponentti voi olla proseduuri, johon viitataan samoin
kuin normaalisti rakenteisen tyypin alkiohin:
TYPE t
...
CONTAINS
PROCEDURE :: pros1 => proseduuri
PROCEDURE :: pros2
GENERIC :: g => aliohj1, aliohj2, aliohj3
END TYPE

Tss proseduureja voidaan kutsua seuraavasti, jos x on kyseist rakenteista


tyyppi:
CALL x%pros1(...)
CALL x%g(...)

Geneeriseksi mritellyn proseduurin x%g tytyy kutsuttaessa olla yksiksitteinen argumenttiensa suhteen.

16.1.8 PASS-mre
Kun rakenteisen tyypin alkioon liittyv proseduuria kutsutaan, tarvitsee se
usein psyn kyseisen olion sisltn. Oletuksena olio vlitetn ensimmisen argumenttina:
CALL x%pros1(x,y)

16. Fortran 2003

295

Tss x vlitetn aliohjelmaan pros1 ensimmisen argumenttina ja x ja


y ovat toinen ja kolmas argumentti. Tllin sanotaan, ett proseduurilla on
PASS-mre. Jos nin ei haluta, voidaan kytt NOPASS-mrett.

16.2

Fortran 2003 ja olio-ohjelmointi

Fortran 2003:n suurimpana ksitteellisen uudistuksena on tuki olio-ohjelmoinnin periaatteiden toteuttamiseksi Fortran-kielell. Kuitenkaan Fortran 2003 -kielt ei voi pit varsinaisesti olio-ohjelmointikielen vaan pikemminkin jonkinlaisena semi-oliokielen.
Olio-ohjelmoinnin paradigma perustuu ksiteanalyysiin, jossa kuvataan sovelluskohteen loogiset yksikt sek niden vliset suhteet perustuen kohdemaailman tietomalliin. Tm paradigma eroaa perinteisemmst proseduraalisesta lhestymistavasta eli niin sanotusta osittavasta ohjemistosuunnittelusta (top down), jossa modularisoinnin periaatteita noudattamalla tarkennetaan toimintoja aliohjelmien ja perusrakenteiden kautta aina yksittisten
kskyjen tasolle asti. Kiinnostunut lukija lyt mainion johdatuksen olioajatteluun esimerkiksi Koskimiehen teoksesta [Kos97].
Osittavan ohjelmistosuunnittelun merkittvin etu suhteessa kohdemaailman tietomalliin perustuvaan lhestymistapaan on se, ett ohjelmiston kehittminen etenee ainakin pieniss sovelluksissa nopeammin. Toisaalta osittavan ohjelmistosuunnittelun merkittvin heikkous on se, ett ohjelmiston
korkean tason muutokset heijastuvat suureen osaan ohjelmistoa eli ohjelmiston rakenne on epjatkuva.
Valistuneen oliomallintamisen ern suurimpana etuna nhdnkin se, ett ohjelmiston muutostarpeet kohdentuvat paikallisiin komponentteihin ja
niiden toteutukseen. Tllin paikalliset muutokset eivt propagoidu ympri ohjelmistoa eli ohjelmiston rakenne on jatkuva, mik helpottaa ohjelmiston yllpitoa. Lisksi valistunut olio-ohjelmointi mahdollistaa ohjelmakoodin useissa sovelluskohteissa esiintyvien korkean tason komponenttien
jlleenkytn (reusability). Usein esiintyvist abstrakteista komponenteista
kytetn nimityst suunnittelumalli (design pattern [GHJV95]).
Kurinalaisella ohjelmoinnilla oliomallintamisen oppeja voidaan soveltaa
mys proseduraalisilla kielill ohjelmoitaessa. Tosin tllin ohjelmointi on
teknisesti tylmp kuin puhtaammilla oliokielill, sill oliokielet mahdollistavat teknisesti helpommin perintmekanismin toteuttamisen, abstraktien tietotyyppien (luokkien) kuvaamisen ja niden ilmentymien (olioiden)
kytn osana dynaamista ymprist. Lisksi klassisen tietoabstraktion perusperiaatteen erota toteutus ja tietorakenteen operaatiot toisistaan noudattaminen on yleens jouhevinta oliokielill.
Fortran 95 -kieless luokka vastaa lhinn moduulin eli siihen kuuluvien
toimintojen ja tietorakenteiden ksitett (abstrakti tietotyyppi). Periytymismekanismia ei tueta suoraan kielen korkealla tasolla. Fortran 2003:ssa rakenteisille tyypeille voi mritell perinthierarkian.

296

Fortran 95/2003

16.3

Fortran 2003:n tuki olio-ohjelmoinnille

Fortran 2003:een sisltyy viisi olio-ohjelmointia helpottavaa ominaisuutta:


tyyppien laajentaminen: tietty tyyppi voi laajentaa toista
polymorsmi: muuttujan tyyppi voi muuttua ajon aikana
dynaaminen tyyppien varaaminen
SELECT TYPE -rakenne
tyyppeihin liitetyt proseduurit.
Fortran 2003 lhestyy ominaisuuksiltaan varsinaisia oliokieli, vaikkei sellainen tydess merkityksess olekaan.

16.3.1 Laajennetut tyypit: perintmekanismi rakenteisille


tyypeille
Rakenteinen tyyppi, jolla ei ole mrett SEQUENCE tai BIND(C), on laajennettavissa. Tss esimerkki:
TYPE, EXTENDS(matriisi) :: matriisi_hajotelma
LOGICAL :: laskettu = .false.
REAL(matriisi%laji) :: hajotelma(matriisi%m, matriisi%n)
END TYPE

Laajennettua tyyppi voi laajentaa edelleen.


Fortran 2003:n olio-ominaisuudet rakentuvat suoraan Fortran 95:n tyyppimrittelyjen varaan. Seuraavassa on esimerkki rakenteisesta tyypist ja laajennetusta tyypist:
TYPE :: vector_2d
REAL :: x, y
END TYPE vector_2d
TYPE, EXTENDS(vector_2d) :: vector_3d
REAL :: z
END TYPE vector_3d

Niden mrittelyjen ansiosta voimme luoda kaksi- ja kolmiulotteisia vektoreita vastaavat muuttujat v2d ja v3d:
TYPE(vector_2d) :: v2d
TYPE(vector_3d) :: v3d

Muuttujan v3d alkioihin voi viitata seuraavasti: v3d%x, v3d%y ja v3d%z.


Voimme tmn jlkeen mritell funktioita, jotka hyvksyvt argumentikseen tyypin vector_2d muuttujia sek tst tyypist laajennettuja muuttujia:

16. Fortran 2003

297

REAL FUNCTION argument(v)


IMPLICIT NONE
CLASS(vector_2d) :: v
argument = ATAN2(v%y,v%x)
END FUNCTION

Vastaavasti seuraava funktio hyvksyy tyypin vector_3d sek tst tyypist


laajennetut tyypit:
REAL FUNCTION azimuth(v)
IMPLICIT NONE
CLASS(vector_3d) :: v
azimuth = ATAN2(v%z,argument(v))
END FUNCTION

16.3.2 Monimuotoisuus
Fortran 2003 -kielen muuttujat voivat olla monimuotoisia (polymorsia).
Monimuotoisuudella tarkoitetaan, ett muuttuja voi viitata annettua rakenteista tyyppi vastaavaan ilmentymn (eli olioon) sek tmn ilmentymn
tyypin laajennettujen tyyppien ilmentymiin. Tllin alkioiden valinta liittyy
dynaamisesti ilmentymn tyyppiin, eik muuttujan tyyppiin. Seuraava esimerkki selvent monimuotoisuuden ideaa:
CLASS(vector_2d), POINTER :: p2
CLASS(vector_3d), POINTER :: p3
TYPE(vector_2d), TARGET :: t2
TYPE(vector_3d), TARGET :: t3

Tmn jlkeen seuraavat osoitinsijoitukset ovat luvallisia:


p2
p2
p3
p2

=>
=>
=>
=>

t2
t3
t3
p3

Sijoitus p3 => t2 on sen sijaan laiton, sill muuttujaa t2 ei ole johdettu


tyypist vector_3d. Vastaavasti sijoitus p3 => p2 on laillinen vain siin
tapauksessa, ett osoitinmuuttuja p2 osoittaa tyypin vector_3d olioon tai
tst tyypist laajennettuun olioon.

16.3.3 SELECT TYPE -rakenne


Monimuotoisten olioiden ksittelyss tarvitaan ohjausrakenne, jolla voidaan
ohjelman ajon aikana valita oikea toimenpide suoritettavaksi. Seuraavassa
on esimerkki SELECT TYPE -rakenteen kytst:
TYPE :: piste
REAL :: x, y
END TYPE
TYPE, EXTENDS(piste) :: piste3d
REAL :: z

298

Fortran 95/2003

END TYPE
TYPE, EXTENDS(piste) :: rgb_piste
INTEGER :: r, g, b
END TYPE
TYPE(piste), TARGET :: p
TYPE(piste3d), TARGET :: p3d
TYPE(rgb_piste), TARGET :: pc
CLASS(piste), POINTER :: p_olio
p_olio => pc
SELECT TYPE (t => p_olio)
TYPE IS (piste3d)
PRINT *, t%x, t%y, t%z
CLASS IS (piste)
PRINT *, t%x, t%y
END SELECT

SELECT TYPE -rakenteessa tutkitaan ensiksi vastaavuutta TYPE IS -kohtien


kanssa, ja jos vastaavuutta ei lydy, CLASS IS -kohtien kanssa.

16.4

Listietoa

Fortran 2003 -kielen ksikirjana voi kytt teosta Fortran 95/2003 Explained [MRC04]. Artikkelissa The New Features of Fortran 2003 [Rei] kuvataan
tiiviiss muodossa Fortran 2003:n uudet piirteet. Johdatukseksi kieleen sek
oppikirjaksi sopii teos Introduction to Programming with Fortran [CS06].

17. Rinnakkaislaskenta HPF:ll ja OpenMP:ll

17
17.1

299

Rinnakkaislaskenta
HPF:ll ja OpenMP:ll

Vlineit rinnakkaislaskentaan

HPF (High Performance Fortran) on Fortran 95:een perustuva datarinnakkainen ohjelmointikieli. Datarinnakkaisessa ohjelmoinnissa suoritetaan samoja operaatioita tietorakenteen (tyypillisesti taulukon) eri alkioille. Siten HPF
ei ole rinnakkaislaskennan yleistykalu, mutta se sopii erityisesti tehtviin,
joissa suoritetaan snnllisi taulukko-operaatioita. HPF soveltuu hienosyiseen (silmukkatason) rinnakkaistamiseen, mutta tllin rinnakkaiskoneessa
tytyy olla nopea viestinvlitys prosessorien vlill.
Kappaleessa 17.3 sivulla 305 kerrotaan rinnakkaistamisesta OpenMP:n avulla. Tm standardi soveltuu vain yhteisen muistin koneille, mutta on usein
HPF: tehokkaampi ja monipuolisempi rinnakkaistusvline.
Rinnakkaistamisen voi tehd mys viestinvlityst kytten. Esimerkiksi MPI
(Message-Passing Interface) mahdollistaa rinnakkaisohjelmoinnin viestinvlityst kytten [HM01]. Mys vanhempaa PVM-jrjestelm voi kytt rinnakkaisohjelmointiin [Saa95].

17.2

HPF-ohjelmoinnin perusteet

HPF-ohjelmointi poikkeaa Fortran 95:st pasiallisesti vain datan jakamista ja taulukko-operaatioiden rinnakkaistusta ohjaavien kntjdirektiivien
osalta.
Ohjelmoija jakaa taulukot prosessoreille kntjdirektiivien avulla, ja kntj huolehtii kaikesta tarvittavasta viestinvlityksest. Koska direktiivit ovat
syntaksiltaan Fortran 95 -kommentteja, ohjelma voidaan periaatteessa knt mys Fortran 95 -kntjll, jolloin tuloksena on HPF-rinnakkaisversion
kanssa samoja tuloksia tuottava tavallinen perkkisohjelma.
Kntjdirektiivit ovat esimerkiksi muotoa
!HPF$ direktiivi

300

Fortran 95/2003

Lause USE HPF_LIBRARY ohjelman alussa mahdollistaa HPF-kirjaston aliohjelmien kytn. Monipuolisten bitinksittely- ja taulukkorutiinien lisksi kirjastossa on funktioita, joilla saa tietoa prosessorihilasta ja taulukoiden jaosta.
Seuraavassa on esimerkki yksinkertaisesta HPF-ohjelmasta:
PROGRAM piste
IMPLICIT NONE
INTEGER :: n
REAL, DIMENSION(:), ALLOCATABLE :: a, b
REAL :: d
!hpf$ distribute a(block)
!hpf$ align b(:) with a(:)
WRITE (*,*) Input the number of points:
READ (*,*) n
ALLOCATE (a(n), b(n))
a = 1.0
b = 2.0
d = SUM(a*b)
WRITE (*,*) d
END PROGRAM piste

Ohjelma laskee kahden vektorin pistetulon. Vektori a jaetaan yht suuriin


osiin eri prosessorien kesken:
!HPF$ DISTRIBUTE A(BLOCK))

Lause
!HPF$ ALIGN B WITH A

puolestaan mr, ett vektoreiden B ja A vastinalkiot tallennetaan samoihin prosessoreihin. Huomaa, ett Fortranin standardifunktio SUM on HPF:ss
rinnakkaistettu.

17.2.1 HPF-esimerkki: Poissonin yhtl


Seuraavassa havainnollistetaan yksinkertaisella esimerkkiohjelmalla HPF:n
tyypillisimpi piirteit. Ohjelma ratkaisee Poissonin yhtln
2u
2u
+
= f (x, y)
x 2
y 2
neliss iteratiivisesti dierenssimenetelmll. Esimerkkiohjelmaa ei ole mitenkn optimoitu.
PROGRAM poistesti
USE poisson, ONLY : p, init, sweep, diff
IMPLICIT NONE
INTEGER, PARAMETER :: n = 160
REAL(KIND=p) :: diff, dnorm = 1.0, tol = 1.0e-6
REAL(KIND=p), DIMENSION(0:n+1,0:n+1) :: a, b, f

17. Rinnakkaislaskenta HPF:ll ja OpenMP:ll

INTEGER :: i = 0, maxiter = 500


!HPF$ DISTRIBUTE a(BLOCK,BLOCK)
!HPF$ ALIGN b(:,:) WITH a(:,:)
!HPF$ ALIGN f(:,:) WITH a(:,:)
CALL init(a,b,f,n)
DO WHILE (dnorm > tol .AND. i <= maxiter)
CALL sweep(a,f,b,n)
CALL sweep(b,f,a,n)
dnorm = diff(a,b,n)
IF (MOD(i,50) == 0) WRITE (*,*) Erotus: , dnorm
i = i + 1
END DO
END PROGRAM poistesti

Varsinaiset laskentarutiinit mritelln moduulissa poisson:


MODULE poisson
IMPLICIT NONE
INTEGER, PARAMETER:: p = SELECTED_REAL_KIND(12,50)
CONTAINS
Rutiinien init, sweep ja diff mrittely
END MODULE POISSON

Tehtv alustetaan rutiinilla init:


SUBROUTINE init(a, b, f, n)
IMPLICIT NONE
INTEGER :: n, i, j
REAL(KIND=p), DIMENSION(0:n+1,0:n+1), INTENT(OUT) :: &
a, b, f
!HPF$ DISTRIBUTE a(CYCLIC,*)
!HPF$ ALIGN b(:,:) WITH a(:,:)
!HPF$ ALIGN f(:,:) WITH a(:,:)
a = 0.0_p
b = 0.0_p
f = 0.0_p
!HPF$ INDEPENDENT, NEW(i)
DO j = 1, n
!HPF$ INDEPENDENT
DO i = 1, n
f(j,i) = EXP(-(REAL(i)/(n+1)-0.5_p)**2 - &
(REAL(j)/(n+1)-0.5_p)**2)
END DO
END DO
END SUBROUTINE init

Iteraatiomatriisi pivitetn seuraavasti:


SUBROUTINE sweep(a, f, b, n)
IMPLICIT NONE
INTEGER :: n, i, j
REAL(KIND=p), DIMENSION(0:n+1,0:n+1), INTENT(IN) :: a, f

301

302

Fortran 95/2003

REAL(KIND=p), DIMENSION(0:n+1,0:n+1), INTENT(OUT) :: b


REAL(KIND=p) :: h
!HPF$ INHERIT a
!HPF$ INHERIT b
!HPF$ INHERIT f
h = 1.0/REAL(n+1)
FORALL (i = 1:n, j = 1:n)
b(i,j) = 0.25_p*(a(i-1,j) + a(i,j+1) + a(i,j-1) + &
a(i+1,j)) - h*h*f(i,j)
END FORALL
END SUBROUTINE sweep

Perkkisten tilojen erotusten summa lasketaan seuraavasti:


FUNCTION diff(a, b, n) RESULT(d)
IMPLICIT NONE
INTEGER :: n, i, j
REAL(KIND=p), DIMENSION(0:n+1,0:n+1), INTENT(IN) :: a, b
REAL(KIND=p), DIMENSION(0:n+1,0:n+1) :: c
REAL(KIND=p) :: d
!HPF$ DISTRIBUTE a *(BLOCK,BLOCK)
!HPF$ DISTRIBUTE b *(BLOCK,BLOCK)
!HPF$ ALIGN c(:,:) WITH a(:,:)
c = 0.0_p
FORALL (i = 1:n, j = 1:n)
c(i,j) = (a(i,j)-b(i,j))**2
END FORALL
d = SUM(c)
END FUNCTION diff

Ohjelma voidaan knt ja ajaa CSC:n Cray T3E -rinnakkaiskoneella seuraavasti:


% module load pghpf
% pghpf poisson.f90 poistest.f90
poisson.f90:
poistest.f90:
Linking:
% mpprun -n 4 ./a.out
Erotus: 2.77748137513380882E-5
Erotus: 2.40804630593270039E-5
Erotus: 2.23112050510325605E-5
...
Erotus: 1.55501436889230665E-5
Erotus: 1.48850843733872855E-5

17.2.2 Taulukoiden jakaminen prosessoreille HPF:ll


Datarinnakkaisuus perustuu HPF:ssa taulukoiden jakamiseen prosessoreille
DISTRIBUTE-direktiivill. Oletusarvoisesti dataa ei jaeta. Esimerkiss poh-

17. Rinnakkaislaskenta HPF:ll ja OpenMP:ll

303

jelman taulukko a jaetaan lohkoittain. Mys useat muut hajautukset ovat


mahdollisia. Direktiivi
!HPF$ DISTRIBUTE a(BLOCK,*)

jakaa taulukon vaakalohkoihin. Asteriski (*) hajautusmuotona tarkoittaa


siis, ett kyseinen dimensio jtetn jakamatta. Muotojen BLOCK ja * lisksi kytettviss on CYCLIC, joka jakaa kohteena oleva taulukon dimension
jaksollisesti. Esimerkiksi direktiivi
!HPF$ DISTRIBUTE a(CYCLIC,*)

antaa taulukon ensimmisen rivin ensimmiselle prosessorille, toisen toiselle ja niin edelleen. Jos ohjelmaa ajettaisiin neljll prosessorilla, rivi viisi
olisi taas ensimmisell prosessorilla.
Direktiivin DISTRIBUTE lisksi taulukoiden jakamiseen voi kytt direktiivi ALIGN, jolla taulukko voidaan jakaa jo jaetun taulukon avulla. Esimerkkiohjelmassa globaalit taulukot b ja f on hajautettu samoin kuin a. Formaatilla * on sama merkitys kuin DISTRIBUTE-direktiivin yhteydess. Niinp
esimerkiksi direktiivi
!HPF$ ALIGN b(:,*) WITH a(*,:)

liitt taulukon b rivit taulukon a sarakkeisiin. Taulukon b alkioiden lopullinen jakautuminen prosessorien kesken riippuu taulukon a hajautuksesta.
Direktiiveill DISTRIBUTE ja ALIGN voi toteuttaa erittin monipuolisia datan
jakoja [KLS+ 94]. Jakoa ja kohdistusta voi mys muuttaa ohjelman suorituksen aikana direktiiveill REDISTRIBUTE ja REALIGN. Tm ei kuitenkaan ole
suositeltavaa ohjelman suorituskyvyn kannalta.

17.2.3 Taulukoiden jako aliohjelmissa


Aliohjelmat voivat jakaa argumenttitaulukkonsa pohjelmasta riippumatta. Edellisten esimerkkien aliohjelmissa on sovellettu erilaisia jakoja lhinn
niiden esittelemiseksi. Datan jakamiseen on kolme tapaa: preskriptiivinen,
transkriptiivinen ja deskriptiivinen.
Preskriptiivisess jaossa aliohjelma vaatii, ett argumentin jako on aliohjelman oman mrittelyn mukainen. Jos jako on erilainen, suoritetaan muunnos automaattisesti. Esimerkki preskriptiivisest jaosta on edell aliohjelmassa init.
Datan uudelleen jakaminen hidastaa ohjelman suoritusta. Tmn estmiseksi voi kytt transkriptiivista jakoa ja kirjoittaa aliohjelman siten ett
parametrin hajautus saa olla mielivaltainen.
Transkriptiivinen jako tarkoittaa argumentin jaon perimist pohjelmasta
(INHERIT-direktiivi). Tllin kaikki jaot kelpaavat ja muunnoksia ei tarvita.
Huomaa, ett tm mekanismi vaatii aliohjelmalta yleisyytt, mik vaikeuttaa optimointia. Aliohjelmassa sweep (sivu 301) on transkriptiivinen jako.
Deskriptiivinen jako vakuuttaa kntjlle, ett aliohjelmalle vlitetyn argumentin jako on sama kuin aliohjelmassa mritelty. Nin muunnoksia ei

304

Fortran 95/2003

tarvita. Deskriptiivisest jaosta on esimerkki aliohjelmassa diff (huomaa


merkki * direktiivin DISTRIBUTE yhteydess). INTERFACE-rakenteen avulla
voi tllaisellekin aliohjelmalle vlitt jaoltaan mielivaltaisia argumentteja.
Suorituskyvyn kannalta aliohjelmien datan jako on erittin trke. Harkitsematon jako voi aiheuttaa paljon kopiointia, mik saattaa hidastaa ohjelmaa
huomattavasti. Suositeltavaa on kytt preskriptiivist jakoa ja INTERFACElohkoja. INTERFACE-rakenne antaa kntjlle enemmn tietoa, mik edist
tehokkaamman koodin tekemist ja virheentarkistusta. Mys INTENT-mreet aliohjelmissa auttavat kntj optimoinnissa.

17.2.4 Datarinnakkaisuus
HPF:n datarinnakkaisuus toteutuu seuraavilla tavoilla:
Fortran 95:n taulukko-operaatiot ja -sijoitukset (WHERE-rakenne mukaan
lukien). Esimerkkiohjelmassa taulukkojen alustaminen nollalla initaliohjelmassa tapahtuu rinnakkain.
Fortran 95:n taulukkofunktiot. Esimerkiksi funktiossa diff taulukon
alkioiden summa lasketaan rinnakkaisesti SUM-funktiolla.
HPF-kirjaston taulukkofunktiot. Moduuli HPF_LIBRARY sislt monia
funktioita mm. taulukoiden reduktioon ja lajittelemiseen.
FORALL-lause. Vaikka FORALL-lause muistuttaa silmukkaa, oikeampi tulkinta on pit sit yleistettyn rinnakkaisena taulukkosijoituksena. Sijoituslauseen oikean puolen arvot lasketaan ensin kaikilla indeksien arvoilla, ja sijoitus tapahtuu vasta tmn jlkeen. Sek evaluointi ett sijoitus tehdn rinnakkain. Esimerkin aliohjelmissa diff ja sweep taulukkosijoitukset on toteutettu FORALL-lauseilla.
INDEPENDENT-direktiivi DO- ja FORALL-lauseiden yhteydess. DO-silmukoiden rinnakkaistamiseen kytetn INDEPENDENT-direktiivi, jolla ohjelmoija kertoo kntjlle, ett silmukan eri kierroksilla suoritettavat
operaatiot ovat toisistaan riippumattomia. Koska tm on rinnakkaistamisen edellytys, kaikkia DO-silmukoita ei voi rinnakkaistaa. Tyypillinen
rinnakkaistumaton silmukka on
DO i = 1,n
a(i) = a(i-1)
END DO

Tss suoritusjrjestys vaikuttaa lopputulokseen.


Aliohjelmassa init on kytetty INDEPENDENT DO -rakennetta. Ulompaa silmukkaa vastaavassa INDEPENDENT-direktiiviss on listietoa antava NEWlause, jonka avulla voidaan rinnakkaistaa esimerkiksi tilapisi muuttujia
sisltvi silmukoita. NEW-lauseessa mritellyt muuttujat ovat iknkuin
silmukan sisisi muuttujia, joita ei ole mritelty sen ulkopuolella. Tss
tapauksessa NEW(i) tarvittiin siskkisen silmukan toteuttamiseksi.

17. Rinnakkaislaskenta HPF:ll ja OpenMP:ll

305

FORALL-lauseen yhteydess ohjelmoija voi kertoa INDEPENDENT-direktiivill


kntjlle, ett eri indeksien arvoja vastaavat evaluaatiot ja sijoitukset ovat
riippumattomia. Nin synkronointi evaluaatio- ja sijoitusvaiheiden vliss
on tarpeeton, ja ohjelman suoritus voi tietyiss tapauksissa nopeutua.
INDEPENDENT-direktiivi antaa kntjlle lisinformaatiota ja mahdollistaa
siten tehokkaamman koodin tekemisen. INDEPENDENT-direktiivi ei saa muuttaa DO- tai FORALL-lauseen merkityst.

17.2.5 EXTRINSIC-aliohjelmat
EXTRINSIC-aliohjelmien avulla HPF:n voidaan liitt muilla ohjelmointikielill ja rinnakkaisohjelmointimalleilla toteutettuja osia. Kytettviss olevat liittymt riippuvat HPF-toteutuksesta.
EXTRINSIC-aliohjelman kutsussa prosessorit saavat omat osansa taulukoista, ja ohjelmoijan on itse huolehdittava tarvittavasta viestinvlityksest prosessorien kesken.

17.2.6 HPF:n etuja ja haittoja


HPF-ohjelmoinnin helppous on seuraus siit, ett kntj huolehtii viestinvlityksest. Toisaalta ohjelmoijan kontrolli samalla vhenee.
Hyv kytttapa HPF:lle on prototyyppityskentely. Koska HPF-ohjelmointi
on helppoa, ensimminen versio rinnakkaisohjelmasta voidaan laatia nopeasti HPF:ll. Jos saatu suorituskyky ei riit, seuraava askel on siirtyminen
viestinvlitysohjelmointiin. Erityisen hydyllinen piirre tllaisessa tyskentelyss on mahdollisuus kokeilla erilaisia tapoja jakaa data prosessien kesken kytten HPF:n DISTRIBUTE- ja ALIGN-direktiivej.

17.2.7 Listietoja HPF:st


HPF:n ksikirja on teos The High Performance Fortran Handbook [KLS+ 94].

17.3

OpenMP: rinnakkaislaskenta yhteisen


muistin koneissa

OpenMP on rinnakkaislaskennan uusi ohjelmointimalli yhteisen muistin tietokoneille. OpenMP mrittelee joukon direktiivej, kirjastorutiineja ja ympristmuuttujia, joita voidaan kytt Fortran- ja C/C++-ohjelmien rinnakkaistamiseen.
Yhteisen muistin koneissa jokainen prosessori nkee koko muistiavaruuden. Rinnakkaisohjelmoinnin standardi, joka hydyntisi yhteisen muistin

306

Fortran 95/2003

arkkitehtuuria, on puuttunut. OpenMP pyrkii tyttmn tmn aukon ja on


varteenotettava vaihtoehto MPI:lle ja HPF:lle.
Message Passing Interface (MPI) soveltuu luonnollisemmin hajautetun muistin koneille kuten Cray T3E:lle, vaikka sit voidaan kytt mys yhteisen
muistin koneissa. Viestinvlitykseen pohjautuvana kirjastona MPI on parhaimmillaan tehokas, mutta tyls ja suhteellisen matalan tason ohjelmointitykalu.
Datarinnakkainen ohjelmointikieli High Performance Fortran (HPF, katso
kappaletta 17.2 sivulla 299) on periaatteessa helppo ja nopea rinnakkaistamistykalu, mutta ei useimmissa sovelluksissa tarjoa optimaalista suorituskyky.
OpenMP:n tavoite on tarjota joustavammin MPI:n tehokkuus ja HPF:n ohjelmoinnin helppous. Samassa sovelluksessa voidaan mys kytt sek
OpenMP:t ett MPI:t.
Rinnakkaistaminen on mahdollista OpenMP:ll sek hienolla ett karkealla
tasolla. OpenMP sallii periaatteessa mys dynaamisen ja siskkisen rinnakkaisten sikeiden luomisen.
OpenMP:t tukevat useat laite- ja ohjelmistovalmistajat, muun muassa Compaq/Digital, Intel, IBM, Cray/SGI, ANSYS, Fluent ja NAG.

17.3.1 Kntjdirektiivit
OpenMP-ohjelmointi perustuu ensisijaisesti kntjdirektiivien kyttn.
Lhdekoodissa merkitn rinnakkaisesti suoritettavat osat ja annetaan rinnakkaistamiseen liittyvi ohjeita. Rinnakkainen osa voi olla silmukkarakenne, jonka suoritus jaetaan eri prosessoreiden kesken, tai joukko erillisi ohjelmalohkoja. OpenMP:ll on siis suhteellisen helppo tehd rinnakkaistettu
versio vanhasta perkkiskoodista.
OpenMP-direktiivit kirjoitetaan kommenttirivein. Rinnakkaistettavat alueet
merkitn direktiiveill
!$OMP PARALLEL mreet
ohjelmalohko
!$OMP END PARALLEL

Ellei toisin mritell, alueen lopussa sikeet jvt odottamaan muita sikeit. Rinnakkaistettavien alueiden ulkopuolella vain yksi sie jatkaa ohjelman suoritusta. Perusmreit rinnakkaistettaville alueille ovat mm. PRIVATE
ja SHARED, joilla annetaan lista yksityisist ja yhteisist muuttujista, sek
REDUCTION, jolla mritelln reduktio-operaatio ja -muuttuja.
Tynjakoa stelevist rakenteista trkein on
!$OMP DO mreet
DO-silmukka
!$OMP END DO

Tll mritelln rinnakkaistettava silmukka. Lisksi erillisi ohjelmalohkoja voidaan luetella !$OMP SECTIONS -rakenteella.

17. Rinnakkaislaskenta HPF:ll ja OpenMP:ll

307

Sikeiden suoritusta voidaan mys synkronoida joukolla direktiivej. Nist


yksinkertaisin on !$OMP BARRIER, joka pysytt kaikki sikeet odottamaan
viimeist.

17.3.2 OpenMP-esimerkki
Kytmme esimerkkin kappaleessa 3.16 (sivu 38) esitelty simulointitehtv, jossa yksikknelin sijoitetaan pistepareja ja lasketaan niiden etisyyden keskiarvo.
Alkuperiseen perkkiskoodiin ei tehd muita muutoksia kuin kommentteina nkyvt !$OMP-direktiivit. Edell esitetyt !$OMP PARALLEL ja !$OMP
DO -direktiivit on yhdistetty !$OMP PARALLEL DO -direktiiviksi OpenMP:n
yksittiselle silmukalle salliman syntaksin mukaisesti. Lisksi kytmme direktiivin jatkorivimerkint.
PROGRAM points
IMPLICIT NONE
INTEGER, PARAMETER :: n = 1000000
INTEGER, PARAMETER :: &
prec = SELECTED_REAL_KIND(12,100)
REAL(KIND=prec), DIMENSION(2) :: a, b
REAL(KIND=prec) :: d, s = 0.0_prec
INTEGER :: i
WRITE (*,*) Number of pairs:, n
!$OMP PARALLEL DO PRIVATE(a,b,i) &
!$OMP REDUCTION(+:s)
DO i = 1, n
CALL RANDOM_NUMBER(a)
CALL RANDOM_NUMBER(b)
s = s + SQRT(SUM((a - b)**2))
END DO
!$OMP END PARALLEL DO
d = s/n
WRITE (*,*) Mean distance:, d
END PROGRAM points

Rinnakkaistettava silmukka jaetaan ohjelman suoritusvaiheessa mritellylle lukumrlle prosessoreita. Silmukan indeksien jakotapa prosessoreiden
kesken voidaan mritell. Yll nin ei ole tehty, jolloin jako on kntjriippuvainen.
Muuttujat a, b ja i ovat yksityisi, eli kullakin prosessorilla tai sikeell on
oma kopionsa nist. Muuttuja s on mritelty reduktiomuuttujaksi. Tllin
kullakin sikeell on oma kopionsa kyseisest muuttujasta, kunnes rinnakkaisen osan lopussa paikalliset muuttujat yhdistetn reduktio-operaatiolla.
Esimerkiss reduktio-operaatio on +, jolloin paikalliset summat lasketaan
lopuksi yhteen, kuten alkuperisess perkkiskoodissa. Oletusarvoisesti
muuttujat ovat yhteisi, kuten tss n.

308

Fortran 95/2003

Ohjelma knnetn ja suoritetaan CSC:n Cedar-koneessa seuraavasti


% f90 -o example example.f90 -mp
% ./example -np 4

Valitsin -np mr suorittavien prosessorien lukumrn. Periaatteessa lukumr voidaan mritell mys dynaamisesti lhdekoodin sisll kullekin
rinnakkaistettavalle alueelle.
Huomaa, ett HPF:st puuttuva reduktiorakenne olisi estnyt esimerkkimme silmukan rinnakkaistuksen, koska silmukka ei olisi ollut muodollisesti
riippumaton. Vaikka summaus onkin todellisuudessa jrjestyksest riippumaton, HPF:n syntaksi kielt direktiivin !HPF$ INDEPENDENT kytn.
MPI:ll ohjelmoijan olisi puolestaan pitnyt huolehtia eksplisiittisesti viestinvlityksest ja tynjaosta.

17.3.3 Muita piirteit ja listietoja OpenMP:st


OpenMP:ss on mritelty mys kirjastorutiineja ja ympristmuuttujia.
Nill voidaan mm. st sikeiden dynaamista ja siskkist luontia.
OpenMP:n kotisivut lytyvt www-osoitteesta
http://www.openmp.org/

Nilt www-sivuilta voi hakea OpenMP:n mrittelyt Fortranille ja C/C++:lle.

Harjoitustehtvi
1. Rinnakkaista kappaleessa 3.16 sivulla 38 esitelty pisteparit-simulointiohjelma kytten HPF:.
2. Kirjoita kappaleessa 14.4 sivulla 247 esitellyst yksiulotteisesta soluautomaatista HPF:ll rinnakkaistettu versio.
3. Kirjoita kappaleessa 14.5 sivulla 253 esitellyst Life-pelist HPF:ll rinnakkaistettu versio.
4. Kirjoita kappaleessa 14.8 sivulla 263 esitellyst dierentiaalievoluutioalgoritmista HPF:ll rinnakkaistettu versio.
5. Kirjoita kappaleessa 14.9 sivulla 269 esitellyst liittogradienttialgoritmista HPF:ll rinnakkaistettu versio.
6. Tutki Poissonin menetelmn ajoajan kyttytymist, kun prosessorien
mr on esimerkiksi 4, 8, 16 tai 32. Ent miten mallikoko vaikuttaa
laskenta-ajan kyttytymiseen?
7. Toteuta tehtvt 16 kytten OpenMP:t.

Liite

310

Fortran 95/2003

Ohjelmoinnin kultaiset
snnt

Seuraavat ohjeet on tarkoitettu muistilistaksi virheiden vlttmiseksi ja turhan ohjelmointityn minimoimiseksi.

A.1 Yleisohjeita
Kun ohjelmakoodi on sinulle itsellesi selkesti luettavaa, ymmrrettv
ja helposti yllpidettv, on se mys kntjlle helppoa ksitell.
Kirjoita jsennetty, selke ja luettavaa ohjelmakoodia. Ohjelmasi seuraava tulkitsija ja lukija olet mit todennkisemmin sin itse muutaman unohduskuukauden jlkeen.
Sano se mit haluat yksinkertaisesti, suoraan ja selkesti; vlt kikkoja.
Anna ohjelman ja tietokoneen tehd likainen ty. Kyt valmiita aliohjelmakirjastoja, l keksi pyr uudestaan.

A.2 Ohjausrakenteet
Vlt sotkuista ohjelmalogiikkaa. Kyt Fortran 95:n kehittyneit ohjausrakenteita harkiten!
Vlt huonoja ja vanhentuvia Fortranin rakenteita. Useimmat nist rakenteista ovat mys koodin optimoinnin esteit. Kyt sen sijaan kielen
hyvi piirteit, kuten ohjausrakenteita
IF . . .

THEN . . .

ELSE . . .

END IF

SELECT CASE . . .

CASE . . .

END SELECT

DO . . .

END DO

DO WHILE . . .

END DO

l numeroi mitn muita lauseita kuin mahdolliset FORMAT-lauseet sek I/O-rutiinien virheenksittelyyn liittyvt lauseet. Ohjelmasi luettavuus paranee.

A. Ohjelmoinnin kultaiset snnt

311

A.3 Mrittele
Mrittele kaikki kyttmsi muuttujat. Kyt lausetta IMPLICIT NONE,
jolloin mahdolliset kirjoitus- ja tyyppivirheet paljastuvat yleens jo
knnsaikana.
Mrittele kyttmsi proseduurit moduuleissa jos mahdollista, tai kyt INTERFACE-rakennetta. Nin saat kyttsi tehokkaan knnsaikaisen virheentarkistuksen.
Mrittele ohjelmassa esiintyville vakioille (esimerkiksi taulukoiden dimensioiden koot) symboliset nimet PARAMETER-mreill. Nin kannattaa tehd vhintnkin silloin, kun vakio esiintyy useassa kohdassa ohjelmaa.
Kyt yhdenmukaisia tyyppimrittelyj, esimerkiksi:
IMPLICIT NONE
INTEGER, PARAMETER :: short = SELECTED_REAL_KIND(6,30)
REAL (KIND=short) :: a
REAL (KIND=short), DIMENSION(100) :: b

A.4 Strukturoi
Koeta erist asiat tehtvsi kannalta loogisiksi ja samalla riittvn pieniksi kokonaisuuksiksi. Ohjelmoi sitten nm kokonaisuudet moduuleihin sijoitetuiksi proseduureiksi. Toisin sanoen: tee ohjelmistasi modulaarisia. Anna tehtvsi mrt ohjelman rakenne.
Jokaisen proseduurin tulee tehd yksi ja vain yksi asia ja tehd se hyvin.
Jokaisen proseduurin tulee piilottaa ohjelman jonkin yksityiskohdan
toteutustekniikka kutsuvilta ohjelmilta. Tee kunkin proseduurin liittymt muihin ohjelmiin mahdollisimman selkeiksi. Toisaalta: l tee turhia rajoituksia proseduuriesi yleisyyden suhteen, jos mahdollista.

A.5 Standardoi
Kirjoita standardia, luettavaa, muutettavaa ja siirrettv ohjelmateksti. Pyri eristmn kone- ja kntjriippuvat asiat omiin, helposti
lydettviin ja muutettaviin paikkoihin, jos niit ylipns tarvitset, ja
dokumentoi ne. l paikkaa huonoa koodia, vaan kirjoita se uusiksi.

A.6 Dokumentointi
Dokumentoi ohjelmasi kunnolla, mys sen kyttm data ja tiedostot.

312

Fortran 95/2003

Kommentoi ohjelmaasi. Hyv kommentti harvoin vain toistaa sanoin


sen mit Fortraniksi on kirjoitettu, vaan selitt miksi niin tehdn. l
kommentoi huonoja ratkaisuja, vaan kirjoita ohjelma paremmin. Kommentoi erityisen tarkkaan laite- ja kntjriippuvuudet, poikkeamat
standardeista ja poikkeaman syyt, ja muut erikoiset ja eptavalliset ratkaisut. l kommentoi trivialiteetteja.

A.7 Luettavuus
l kirjoita ptkspagettia. Kirjoita selkesti, kyt mielekkit nimi,
sisenn ohjelmateksti oikein. Kirjoita ohjelmat siten, ett ne etenevt ylhlt alaspin oikeassa jrjestyksess. Voit jopa kirjoittaa ne ensin jrkevll pseudo-Fortranilla ja knt ne sitten standardi-Fortraniksi.
Kyt selkeit ja mielekkit muuttujien nimi. Fortran 90 -standardi
rajoittaa nimen pituuden korkeintaan 31 merkkiin.
l kyt sarkain- eli tabulaattorimerkkej. Toisaalta voit kytt pieni kirjaimia koodin selkeyden parantamiseksi. Skandinaavisia merkkej (, jne.) ei voi kuitenkaan kytt muuten kuin kommenteissa tai
merkkijonoissa.
Ohjelmakoodin kirjoittamista ja editointia helpottaa Fortran 95
-kielen syntaksia ymmrtv editori, esimerkiksi Emacs, jolla voi mys
muotoilla koodia luettavampaan muotoon.

A.8 Vianetsint ja koodin testaus


Kiinnit huomiota ohjelmasi testaamiseen. Muodosta kattava joukko
testej ohjelmasi eri osa-alueille. Testaa eri ohjelmayksikt mys erikseen.
Kun list ohjelmaasi uusia piirteit, testaa nit piirteit uusilla testeill, mutta l unohda aikaisempia testej.
l pyshdy yhteen lytmsi virheeseen. l luovuta ennen kuin olet
testannut ohjelman perusteellisesti.
Testaa rajatapaukset. Varo taulukkojen indeksien ylityksi ja laskureiden yhdell pieless tilanteita. Tarkkaile muuttujien arvojen erikoistapauksia ja muita erikoistilanteita, jotka usein ovat virheiden syin.
Varmistu erityisesti siit, ett ohjelmasi toimii oikein niiss erikoistilanteissa, joissa sen ei tule tehd mitn.
Varo alustamattomia muuttujia. Alusta kaikki muuttujat, lk luota
siihen, ett ne ovat nollia automaattisesti. Alustuksen voi usein tehd
muuttujan mrittelyn yhteydess tai ohjelmayksikss suoritettavalla
lauseella:

A. Ohjelmoinnin kultaiset snnt

313

IMPLICIT NONE
INTEGER, PARAMETER :: short = SELECTED_REAL_KIND(6,30)
REAL (KIND=short) :: a = 0, t = -1, u
REAL (KIND=short), DIMENSION(100) :: b = 1
...
u = SIN(t + a)
...

Varo pyristysvirheit: 10.0 0.1 on tuskin koskaan 1.0! l vertaa liukulukujen (reaalilukujen) yhtsuuruutta. Kyt numeerisesti luotettavia algoritmeja (tm ei ole triviaali asia!). Kyt apuna asiantuntijoita
ja tunnettuja aliohjelmakirjastoja!

A.9 Ohjelmakoodin optimointi


Aluksi ohjelman kirjoituksen pmrn tulee olla pelkstn oikein
toimivan ohjelman kirjoittaminen. Harkitse kuitenkin kyttmsi algoritmia heti tarkkaan. Usein huomattavasti tehokkaampaa ohjelmaa saa
kyttmll tehokkaampaa algoritmia, ei optimoimalla koodia.
l optimoi ohjelmakoodia liian aikaisin. Kun alat muokata koodia tehokkaammaksi, mittaa ensin, miss ohjelmasi kuluttaa aikaansa. Muokkaa sitten sit vaikkapa 10 %:n aluetta, jossa kuluu noin 90 % ohjelman
ajoajasta. Nin hydyt nopeimmin ja eniten. Kun muokkaat koodia, ohjelmasi pit tietysti pysy oikein toimivana. l koskaan uhraa selkeytt pieniin tehokkuusetuihin.
Siis: ensin oikein, sitten tehokkaammin (ja edelleen oikein)!

A.10 Sytt ja tulostus


Testaa syttarvojen jrkevyys. Tunnista jrjettmt syttarvot, ja toivu tilanteesta jos mahdollista. Testaa erityisesti (odottamattomat) tiedostojen loput.
Tee sytt mahdollisimman yksinkertaiseksi ja helpoksi. Vapaamuotoinen sytt on jrkev pieni kontrolliarvoja varten. Vain suuret dataaineistot kannattaa lukea mrmuotoisena. Tulostus sen sijaan kannattaa useimmiten tehd mrmuotoisesti. Tee syttaineisto helpoksi lukea.
Sijoita sytt ja tulostus selkeisiin proseduureihin. l sirottele tulostuslauseita joka paikkaan (ne estvt koodin optimoinnin).
Kyt syttarvoille oletuksia jos mahdollista. Kaiuta sytetyt arvot tulostukseen tai erityiseen lokitiedostoon.

314

Fortran 95/2003

Vlt mys FORMAT-lauseita ja niiden numerointia. Vain kerran tai kahdesti kytettvn FORMAT-lauseenkin voi sijoittaa nimettyn merkkijonovakiona suoraan sit kyttvn WRITE- tai READ-lauseeseen, jolloin
kyseinen I/O-lause ja sit vastaava formaatti ovat samassa paikassa helposti lydettviss.
Tarkkaile sytt- ja tulostustoimintoja. Tm voi usein olla tehottomin
osa ohjelmaasi. Kyt jrkevi ja tehokkaita I/O-menetelmi. Suunnittele sytt ja tulostus huolella.
Jos luet tai tulostat suuria datamri, saat ohjelmastasi tehokkaamman
kyttmll binrist sytt ja tulostusta.

A.11 Yhteenveto
Trkeimpn kaikista ohjeista:
Harkitse kyttmsi algoritmi tarkkaan!
Aiheesta lytyy lis luettavaa mm. seuraavasta hauskasta ja hydyllisest
kirjasesta: The Elements of Programming Style [KP78].

B. Fortran 95:n hyvt, pahat ja rumat piirteet

315

Fortran 95:n hyvt, pahat


ja rumat piirteet

Esitmme seuraavassa CSC:n kokemuksia Fortran 95:st. Tyyliseikoista ei


kannata tietenkn kiistell, mutta ohjelmoinnissa on syyt olla selke tyyli,
jotta vltytn vrinksityksilt.

B.1 Hyvt piirteet


Lause IMPLICIT NONE poistaa kytst alkukirjainsnnn.
Uuden tyyppiset muuttujamrittelyt tyyliin
REAL, PARAMETER :: ...

selkeyttvt koodia.
Lajiparametrit (kind) parantavat koodien siirrettvyytt.
Fortran 95:n ohjausrakenteet helpottavat ohjelmointia.
Lhdekoodin vapaa syttmuoto parantaa luettavuutta.
Moduulit tekevt ohjelmoinnista luotettavampaa ja tehokkaampaa (esimerkiksi proseduurien argumenttilistojen knnsaikainen tarkistus).
Dynaaminen muistinvaraus helpottaa ohjelmointityt.
Proseduurien argumenttien INTENT-mreet parantavat knnsaikaisia tarkistuksia.
Uudet operaattorimerkinnt (<, >, <=, >=, == ja /=) parantavat koodin
luettavuutta.
Taulukkosyntaksi helpottaa ja tehostaa koodaamista sek parantaa koodin luettavuutta.

316

Fortran 95/2003

B.2 Toisinaan hydylliset piirteet


Rakenteiset muuttujatyypit mahdollistavat monimutkaisten tietorakenteiden luomisen (esimerkiksi listat).
Osoitinmuuttujat mahdollistavat dynaamisen tavan viitata muuttujiin
tai esimerkiksi taulukkojen osiin.
Sisiset proseduurit ovat ktevi pienten apurutiinien mrittelyss.
Valinnaiset argumentit mahdollistavat proseduurikutsut, joissa tarvitsee antaa vain osa argumenteista muille kytetn oletusarvoja.
Avainsana-argumentit voivat selkeytt proseduurien argumenttien merkityst.
Geneeriset proseduurit mahdollistavat saman nkisen proseduurikutsun eri tyyppisille argumenteille, mutta voivat mys tehd koodista vaikeammin ymmrrettv.
NAMELIST-tiedonsiirto on ktev esimerkiksi ohjelman sytttietojen
antamisessa, mutta sit ei kannata kytt suurille tietomrille.
Mreill PUBLIC ja PRIVATE voi sdell moduulissa mriteltyjen olioiden nkyvyytt moduulin ulkopuolelle.
Ohjausrakenne DO WHILE on ktev joissakin tilanteissa. Vertaa seuraavia ekvivalentteja ohjausrakenteita:
DO WHILE (ehtolauseke)
...
END DO
DO
IF (.NOT. ehtolauseke) EXIT
...
END DO

B.3 Mahdollisesti vaaralliset piirteet


Omien operaattorien mrittely voi tehd koodista hyvin vaikeasti ymmrrettv ja tehotonta.
Vanhan FORTRAN 77:n piirteet kuten COMMON, EQUIVALENCE, GOTO jne.
tekevt koodista vaikeasti siirrettv ja hankalasti luettavaa.
Antiikkinen sarakesidonnainen lhdekoodin muoto tekee ohjelmista vaikeasti luettavia.

C. Yhteenveto Fortran 95 -kielen lauseista

317

Yhteenveto Fortran 95
-kielen lauseista

Luettelemme tss liitteess trkeimmt Fortran 95 -kielen lauseet. Kielen


syntaksia ei ole kuvattu tydellisen, vaan sen sijaan esitmme lauseiden
tyypilliset kytttavat.

Merkinttavat
Esitmme Fortran 95 -kielen omat tunnukset isoilla kirjaimilla, esimerkiksi
PROGRAM tunnus

Kyttjn mriteltviss olevat tunnukset on esitetty vinokirjaimilla kuten


edell. Jos lauseen osa ei ole vlttmtn, on kytetty hakasulkuja merkitsemn tllainen kohta. Kolmella pisteell ... merkitn nolla tai useampi
kertaa toistettavissa olevaa lauseen osaa:
CALL aliohjelma[([nimi=]argumentti[, [nimi=]argumentti]...)]

CALL-lauseen vaihtoehtoisia muotoja ovat siis esimerkiksi:


CALL
CALL
CALL
CALL

aliohjelma
aliohjelma(arg1)
aliohjelma(arg1, arg2)
aliohjelma(arg2=arvo, arg1=arvo)

Viimeisess kutsussa kytettiin avainsana-argumentteja.

Yhteenveto Fortran 95:n lauseista


Ohjelmayksikiden mrittely:
[PROGRAM tunnus]
[mrittelylauseet]
[suoritettavat_lauseet]
[CONTAINS
sisinen_proseduuri

318

Fortran 95/2003

[sisinen_proseduuri]...]
END [PROGRAM [tunnus]]
[RECURSIVE] SUBROUTINE tunnus[([argumentti][, argumentti]...])]
[mrittelylauseet]
[suoritettavat_lauseet]
[CONTAINS
sisinen_proseduuri
[sisinen_proseduuri]...]
END [SUBROUTINE [tunnus]]
[tyyppi] [RECURSIVE] [tyyppi] FUNCTION tunnus([argumentti] &
[, argumentti]...) [RESULT(tunnus)]
[mrittelylauseet]
[suoritettavat_lauseet]
[CONTAINS
sisinen_proseduuri
[sisinen_proseduuri]...]
END [FUNCTION [tunnus]]
MODULE tunnus
[mrittelylauseet]
[CONTAINS
moduulin_proseduuri
[moduulin_proseduuri]...]
END [MODULE [tunnus]]
BLOCK DATA [tunnus]
[mrittelylauseet]
END [BLOCK DATA [tunnus]]

Tiedoston sisllyttminen osaksi koodia:


INCLUDE merkkijonovakio

Alkukirjainsnnn kumoaminen:
IMPLICIT NONE

Alkukirjainsnnn asettaminen:
IMPLICIT tyyppi (kirjainmrittely[, kirjainmrittely]...)

Muuttujien tyypin mrittely:


tyyppi[[, mre]... ::] tunnus[, tunnus]...

Esimerkkej muuttujien mrittelyst:


REAL [([KIND=]lajiparametri)]
COMPLEX [([KIND=]lajiparametri)]
INTEGER [([KIND=]lajiparametri)]
CHARACTER [([LEN=]arvo)]
CHARACTER ([LEN=]arvo[, [KIND=]lajiparametri])
CHARACTER (KIND=lajiparametri[, LEN=arvo])
CHARACTER*pituus
LOGICAL [([KIND=]lajiparametri)]

C. Yhteenveto Fortran 95 -kielen lauseista

DOUBLE PRECISION
TYPE (tunnus)

Tyyppimrittelyn mreit (attribuutteja):


ALLOCATABLE
DIMENSION (ulottuvuudet)
EXTERNAL
INTENT (inout_mre)
INTRINSIC
OPTIONAL
PARAMETER
POINTER
PRIVATE
PUBLIC
SAVE
TARGET

Erillisi mrittelylauseita:
ALLOCATABLE [::] taulukko_lista
COMMON [/ tunnus /] lista_olioita
DATA lista_olioista / lista_arvoja /
DIMENSION taulukon_dimensiomrittely
EQUIVALENCE lista
IMPLICIT mrittely
NAMELIST /namelist_ryhmn_nimi/ lista_jsenist
POINTER [::] taulukko_lista
TARGET [::] taulukko_lista

Taulukkojen mrittely:
tyyppi, DIMENSION(arvo[, arvo]...) :: tunnus[, tunnus]...
tyyppi[[, mre]... ::] tunnus(arvo[, arvo]...)
tyyppi, DIMENSION(arvo[, arvo]...), ALLOCATABLE :: &
tunnus[, tunnus]...
tyyppi, DIMENSION(arvo[, arvo]...), TARGET :: &
tunnus[, tunnus]...
tyyppi, DIMENSION(arvo[, arvo]...), POINTER :: &
tunnus[, tunnus]...

Nimetyt vakiot:
tyyppi, PARAMETER :: tunnus[, tunnus]...
PARAMETER ( mrittelylista )

Rakenteisten tyyppien mrittely:


TYPE[, julkisuusmre [::]] tunnus
[PRIVATE]
[SEQUENCE]
[tyyppi[[, mre] ::] komponentit...]...
END TYPE [tunnus]

319

320

Fortran 95/2003

Proseduurien mrittelylauseet:
EXTERNAL [::] tunnus[, tunnus]...
INTRINSIC [::] tunnus[, tunnus]...

Proseduurien argumenttien mreet:


muuttujamrittely, INTENT(IN) :: tunnus[, tunnus]...
muuttujamrittely, INTENT(OUT) :: tunnus[, tunnus]...
muuttujamrittely, INTENT(INOUT) :: tunnus[, tunnus]...
muuttujamrittely, INTENT(IN OUT) :: tunnus[, tunnus]...
INTENT(inout_mre) [::] tunnus[, tunnus]...
mrittely, OPTIONAL :: tunnus[, tunnus]...
mrittely, SAVE :: tunnus[, tunnus]...
OPTIONAL [::] lista
SAVE [[::] lista]

Moduulin kyttminen:
USE moduuli[, paikallinen_nimi => nimi_moduulissa]...
USE moduuli, ONLY : [[paikallinen_nimi =>] nimi_moduulissa &
[, [paikallinen_nimi =>] nimi_moduulissa]...]

Moduulin olioiden julkisuus tai yksityisyys:


PUBLIC [[::] lista_moduulin_olioista]
PRIVATE [[::] lista_moduulin_olioista]

Funktion ja aliohjelman kutsu:


funktio([argumentti][, argumentti]...)
funktio([[nimi=]argumentti][, [nimi=]argumentti]...)
CALL aliohjelma[(argumentti[, argumentti]...)]
CALL aliohjelma[([nimi=]argumentti[, [nimi=]argumentti]...)]

Geneeriset funktiot, aliohjelmat ja operaattorit:


INTERFACE [geneerinen_tunnus]
[kutsumuodon_mrittelylohko]...
[MODULE PROCEDURE tunnus[, tunnus]...]...
END INTERFACE
INTERFACE OPERATOR(operaattori)
[kutsumuodon_mrittelylohko]...
[MODULE PROCEDURE tunnus[, tunnus]...]...
END INTERFACE
INTERFACE ASSIGNMENT(=)
[kutsumuodon_mrittelylohko]...
[MODULE PROCEDURE tunnus[, tunnus]...]...
END INTERFACE

C. Yhteenveto Fortran 95 -kielen lauseista

Toistorakenteet:
[tunniste:] DO
lohko
END DO [tunniste]
[tunniste:] DO muuttuja = lauseke, lauseke[, lauseke]
lohko
END DO [tunniste]
[tunniste:] DO WHILE (ehto)
lohko
END DO [tunniste]
EXIT [tunniste]
CYCLE [tunniste]

FORALL-rakenne ja -lause:
[tunniste:] FORALL (kolmikko[, kolmikko]...[, maski])
lohko
END FORALL [tunniste]
FORALL (kolmikko[, kolmikko]...[, maski]) muuttuja = lauseke
FORALL (kolmikko[, kolmikko]...[, maski]) osoitin => kohde

Tss kolmikko on muotoa


indeksimuuttuja = alku:loppu[:askel]

Ehtorakenteet:
[tunniste:] IF (ehto) THEN
lohko
[ELSE IF (ehto) THEN [tunniste]
lohko]
[ELSE [tunniste]
lohko]
END IF [tunniste]
IF (ehto) lause
IF (numeerinen_lauseke) lausenro, lausenro, lausenro

Tapausrakenne:
[tunniste:] SELECT CASE (lauseke)
[CASE (arvo[, arvo]...) [tunniste]
lohko]...
[CASE DEFAULT [tunniste]
lohko]
END SELECT [tunniste]

WHERE-rakenne:
WHERE (ehto) muuttuja = arvo

321

322

Fortran 95/2003

WHERE (ehto)
[sijoituslause]...
[ELSEWHERE
[sijoituslause]...]
END WHERE

Hyppylause:
GO[ ]TO lausenumero
GO[ ]TO (lausenumero_lista) kokonaislukulauseke
GO[ ]TO kokonaislukumuuttuja[[,] (lausenumero_lista)]
[lausenumero] CONTINUE

Suoritettavia lauseita:
ALLOCATE (lista[, STAT=kokonaislukumuuttuja])
DEALLOCATE (lista[, STAT=kokonaislukumuuttuja])
NULLIFY (osoitinmuuttuja_lista)
ASSIGN lausenumero TO kokonaislukumuuttuja

Sijoituslauseet:
muuttuja = lauseke
osoitinmuuttuja => kohde

Ohjelman suorituksen lopettaminen tai keskeytys:


STOP [merkkijono]
STOP [koodi]
PAUSE [koodi]

Paluu proseduurista kutsuvaan ohjelmayksikkn:


RETURN

Tiedostojen ksittely:
OPEN ([UNIT=]kanava[, FILE=lauseke][,STATUS=lauseke] &
[, ACCESS=lauseke][, RECL=lauseke][, FORM=lauseke] &
[, BLANK=lauseke][, ERR=lausenumero][, IOSTAT=muuttuja])
CLOSE ([UNIT=]kanava[, STATUS=lauseke][, ERR=lausenumero] &
[, IOSTAT=muuttuja])
INQUIRE (FILE=tiedoston_nimi, argumenttilista)
INQUIRE (UNIT=kanava, argumenttilista)
BACKSPACE kanava
BACKSPACE ([UNIT=]kanava[, IOSTAT=iostat][, ERR=err-lause])
REWIND kanava
REWIND ([UNIT=]kanava[, ERR=lausenumero][, IOSTAT=muuttuja])
ENDFILE kanava
ENDFILE ([UNIT=]kanava[, ERR=lausenumero][, IOSTAT=muuttuja])

C. Yhteenveto Fortran 95 -kielen lauseista

Tiedon sytt:
READ muotoilukoodi, muuttujalista
READ *, muuttujalista
READ
[,
READ
[,
READ
[,
READ
[,
READ
[,
READ
[,
READ
[,

([UNIT=]kanava, [FMT=]muotoilukoodi[, END=lauseke] &


ERR=lauseke][, IOSTAT=lauseke]) muuttujalista
([UNIT=]kanava, [FMT=]*[, END=lauseke] &
ERR=lauseke][, IOSTAT=lauseke]) muuttujalista
([UNIT=]*, [FMT=]muotoilukoodi[, END=lauseke] &
ERR=lauseke][, IOSTAT=lauseke]) muuttujalista
([UNIT=]*, [FMT=]*[, END=lauseke] &
ERR=lauseke][, IOSTAT=lauseke]) muuttujalista
([UNIT=]kanava[, END=lauseke] &
ERR=lauseke][, IOSTAT=lauseke]) muuttujalista
([UNIT=]kanava, [FMT=]muotoilukoodi, REC=lauseke &
ERR=lauseke][, IOSTAT=lauseke]) muuttujalista
([UNIT=]kanava, REC=lauseke &
ERR=lauseke][, IOSTAT=lauseke]) muuttujalista

Tiedon tulostaminen:
PRINT *, tulostuslista
PRINT muotoilukoodi, tulostuslista
WRITE ([UNIT=]kanava, [FMT=]muotoilukoodi[, ERR=lauseke] &
[, IOSTAT=lauseke]) tulostuslista
WRITE ([UNIT=]kanava, [FMT=]*[, ERR=lauseke] &
[, IOSTAT=lauseke]) tulostuslista
WRITE ([UNIT=]*, [FMT=]muotoilukoodi[, ERR=lauseke] &
[, IOSTAT=lauseke]) tulostuslista
WRITE ([UNIT=]*, [FMT=]*[, ERR=lauseke] &
[, IOSTAT=lauseke]) tulostuslista
WRITE ([UNIT=]kanava[, ERR=lauseke] &
[, IOSTAT=lauseke]) tulostuslista
WRITE ([UNIT=]kanava, [FMT=]muotoilukoodi, REC=lauseke &
[, ERR=lauseke][, IOSTAT=lauseke]) tulostuslista
WRITE ([UNIT=]kanava, REC=lauseke[, ERR=lauseke] &
[, IOSTAT=lauseke]) tulostuslista

Muotoilukoodin mrittely:
lausenumero FORMAT (muotoilukoodi)

323

324

Fortran 95/2003

ASCII-merkist

ASCII (American Standard Code for Information Interchange) on 7-bittinen


standardi merkkimuotoisen tiedon esittmiseen tietokoneessa. Seuraavassa taulukossa on esitetty ASCII-merkkej vastaavat desimaalikoodit. Kunkin
merkin koodi saadaan laskemalla yhteen rivin ja sarakkeen tunnukset. Esimerkiksi W-kirjaimen ASCII-koodi on 87.

0
10
20
30
40
50
60
70
80
90
100
110
120

^@
^J
^T
RS
(
2
<
F
P
Z
d
n
x

^A
^K
^U
US
)
3
=
G
Q
[
e
o
y

^B
^L
^V

^C
^M
^W
!
+
5
?
I
S
]
g
q
{

^D
^N
^X
"
,
6
@
J
T
^
h
r
|

^E
^O
^Y
#
7
A
K
U
_
i
s
}

^F
^P
^Z
$
.
8
B
L
V

j
t

^G
^Q
ESC
%
/
9
C
M
W
a
k
u
DEL

^H
^R
FS
&
0
:
D
N
X
b
l
v

^I
^S
GS

1
;
E
O
Y
c
m
w

*
4
>
H
R
\
f
p
z

E. Sanasto

325

Sanasto

actual argument: todellinen


argumentti
allocatable array: dynaaminen
taulukko
allocate: varata tilaa
allocation statement: tilanvarauslause
allocation status: varaustilanne
alternate return: vaihtoehtoinen
paluukohta
argument: argumentti
arithmetic if statement: aritmeettinen
ehtolause
array: taulukko
array assignment: taulukkojen
sijoituslause
array constructor: taulukon rakennin,
taulukon alustin, taulukon
muodostin
array element: taulukon alkio
array element order: taulukon
alkiojrjestys
array expression: taulukkkolauseke
array pointer: taulukko-osoitin
array section: taulukon lohko
array specication: taulukon
mrittely
array-valued object: taulukkoarvoinen
olio
assigned goto: epsuora hyppyksky
assignment: sijoitus
assignment statement: sijoituslause
associated pointer: muuttujaan liittyv
osoitin
assumed character length: oletetun
mittainen merkkijono
assumed-shape array: oletetun
muotoinen taulukko
assumed-size array: oletetun kokoinen
taulukko
attribute: mre
automatic array: automaattinen
taulukko

automatic object: automaattinen olio


binary operator: kaksiargumenttinen
operaattori
bit: bitti
blank: vlilyntimerkki
block: lohko
bound: (arvoalueen) raja
byte: tavu
call: kutsua, kutsu
case: tapaus
case construct: tapausrakenne
character: merkki
character data: merkkitieto
character string: merkkijono
coercion: muunnos vahvempaan
tyyppiin
collating sequence: merkkijrjestys,
merkkidatan lajittelujrjestys
comment: kommentti
common block: common-alue
comparison operator:
vertailuoperaattori
compiler: kntj
complex number: kompleksiluku
component: rakenteisen tyypin alkio
component selector: alkion valitsin (%)
computed goto: haarautuva
hyppyksky
concatenation operator:
liitosoperaattori (//)
conformable arrays: samanmuotoiset
taulukot
constant: vakio
construct: rakenne
constructor: rakennin, alustin,
muodostin
continuation line: jatkorivi
control statement: ohjauslause
data type: tietotyyppi
data parallel: datarinnakkainen
deallocate: vapauttaa tilaa
declaration: esittely

326

default kind: oletuslaji


deferred-shape array: myhemmin
varattava taulukko
dened assignment: kyttjn
mrittelem sijoituslause
dened operator: kyttjn
mrittelem operaattori
dened variable: alustettu muuttuja
denition: mrittely
denition of a type: tyypin mrittely
deleted feature: poistettu piirre
derived type, derived data type:
rakenteinen datatyyppi
designator: alkion valitsin
design pattern: suunnittelumalli
digit: numeromerkki
dimension: ulottuvuus
direct access: suorasaanti
directive: direktiivi
disassociated pointer: kohteesta
irrotettu osoitin
dummy argument: muodollinen
argumentti
dummy array: muodollinen
taulukkoargumentti
dummy pointer: muodollinen
osoitinargumentti
dyadic (binary) operator:
kaksiargumenttinen operaattori
edit descriptor: muotoilukoodi
element: alkio
elemental: alkioittainen
elemental intrinsic procedure:
alkioittainen standardiproseduuri
executable program:
suorituskelpoinen ohjelma
executable statement: suoritettava
lause
explicit interface: tunnettu
kutsumuoto
explicit-shape array: tunnetun
muotoinen taulukko
expression: lauseke
extension: laajennus
extended type: laajennettu tyyppi
extent: (taulukon ulottuvuuden) koko
external: ulkoinen
external procedure: ulkoinen
proseduuri
le: tiedosto
nalizer: lopettaja
xed source form: sarakesidonnainen
lhdekoodin muoto
oating point number: liukuluku,
reaaliluku

Fortran 95/2003

format: tiedon esitysasu, muotoilu


format specier: muotoilukoodi
free source form: vapaa lhdekoodin
muoto
function: funktio
generic interface: geneerinen
kutsumuoto
generic procedure: geneerinen
proseduuri
global entity: globaali tunnus
goto statement: hyppylause
host: isnt
host association: viittaus isnnn
mrittelemiin olioihin
host scoping unit: isnnn
nkyvyysalue
implicit declaration:
alkukirjainsntn perustuva
tyyppimrittely
implicit interface: oletusarvoinen
kutsumuoto
implied DO list: toistolista
index: indeksi
inheritance: periytyminen
input: syte, sytt
inquiry function: kyselyfunktio
integer: kokonaisluku
intent: proseduurin argumentin
tiedonsiirron suunta
interface block: kutsumuodon
mrittelylohko
internal le: sisinen tiedosto
internal procedure: sisinen
proseduuri
intrinsic data type: standardityyppi,
sisinen tietotyyppi
intrinsic function: standardifunktio
intrinsic operator:
standardioperaattori
intrinsic procedure:
standardiproseduuri
intrinsic subroutine:
standardialiohjelma
keyword argument:
avainsana-argumentti
kind: laji
kind type parameter: lajiparametri
label: lausenumero
lexical token: (ohjelmointikielen)
perusalkio
list-directed formatting: listan ohjaama
muotoilu
literal constant: (nimetn) vakio
local: paikallinen
local entity: paikallinen olio

E. Sanasto

logical operator: looginen operaattori


logical value: looginen arvo
loop: silmukka
lower bound: alaraja
main program: pohjelma
mask: peite
message-passing: vistinvlitys
mixed-mode expression: eri tyyppej
sisltv lauseke
module: moduuli
module procedure: moduulin
proseduuri
monadic (unary) operator:
yksiargumenttinen operaattori
named constant: nimetty vakio
named object: nimetty olio
non-advancing input/output: osittaista
tietuetta ksittelev sytt tai
tulostus
numeric expression: numeerinen
lauseke
numeric type: kompleksiluku,
kokonaisluku tai reaalilukutyyppi
object: olio
object-oriented programming:
olio-ohjelmointi
obsolescent feature: vanhentuva piirre
operand: operandi
operator: operaattori
optional argument: valinnainen
argumentti
output: tuloste, tulostaa
output list: tulostettava tieto
overloading: ylilataus
parameter: (nimetty) vakio
parameter attribute: vakiomre
preconnected: valmiiksi mritellyt
kanavanumerot (5, 6 ja 7)
pointer: osoitin
pointer array: osoitintaulukko
pointer assignment: osoitinten
sijoituslause (=>)
pointer association: osoittimen
kohdistaminen dataan
polymorphism: monimuotoisuus,
polymorsmi
private: yksityinen
procedure: proseduuri (funktio tai
aliohjelma)
procedure interface: proseduurin
kutsumuoto
program: ohjelma
program unit: ohjelmayksikk
public: julkinen
pure: sivuvaikutukseton (proseduuri

327

tms.)
quote: heittomerkki
range: vaihteluvli
rank: ulottuvuuksien lukumr
real number: reaaliluku
record: tietue, jono arvoja joita
ksitelln yhten kokonaisuutena
tiedostossa
recursive: rekursiivinen
relational operator: vertailuoperaattori
reshape: muokata
return: paluu proseduurista,
rivinvaihto
return value: funktion palauttama
arvo
scalar variable: skalaarimuuttuja
scope, scoping unit: nkyvyysalue
selector: (tapauslauseen) valitsin
sequence: jono
sequential: perkkinen
shape: muoto
side-eect: sivuvaikutus
sign: etumerkki
size: (taulukon) koko, alkioiden
lukumr
source form: lhdekoodin syttmuoto
source program: lhdekielinen
ohjelma, lhdekoodi
specic name: erityisnimi
specication expression:
mrittelylauseke
specication statement:
mrittelylause
statement: lause
statement function: lausefunktio
statement label: lausenumero
storage association: alkioiden yhteinen
sijoittelujrjestys muistissa
stride: askelpituus
structure: rakenne
structure constructor: rakenteen
alustin
subarray: osataulukko
subexpression: osalauseke
subroutine: aliohjelma
subscript: (taulukko)indeksi
subscript triplet: taulukon lohko, joka
koostuu alkuarvosta,
loppuaskeleesta ja askeleesta
kaksoispisteell (:) erotettuna
substring: osamerkkijono,
alimerkkijono
target: kohde
transformational function:
muunnosfunktio

328

type: tyyppi
type declaration statement: tyypin
esittelylause
type parameter: tyypin parametri
unary operator: yksiargumenttinen
operaattori
undened: mrittelemtn
undened variable: mrittelemtn
muuttuja
unformatted input/output: binrinen
tiedonsiirto

Fortran 95/2003

unit: kanava
unit number: kanavanumero
upper bound: ylraja
user-dened assignment: kyttjn
mrittelem sijoituslause
user-dened operator: kyttjn
mrittelem operaattori
variable: muuttuja
variable declaration: muuttujan
esittely
vector subscript: vektori-indeksi

Kirjallisuutta

329

Kirjallisuutta
[ABM+ 92] Jeanne C. Adams, Walter S. Brainerd, Jeanne T. Martin, Brian T. Smith ja
Jerrold L. Wagener. Fortran 90 Handbook. Complete ANSI/ISO Reference.
McGraw-Hill, 1992.
[ABM+ 97] Jeanne C. Adams, Walter S. Brainerd, Jeanne T. Martin, Brian T. Smith ja
Jerrold L. Wagener. Fortran 95 Handbook. Complete ISO/ANSI Reference.
The MIT Press, 1997.
[BGA94]

Walter S. Brainerd, Charles H. Goldberg ja Jeanne C. Adams. Programmers Guide to Fortran 90. UNICOMP, 1994.

[CLR90]

Thomas H. Cormen, Charles E. Leiserson ja Ronald L. Rivest. Introduction


to Algorithms. The MIT Press, 1990.

[CS06]

Ian Chivers ja Jane Sleightholme. Introduction to Programming with


Fortran. Springer, 2006.

[EPL94]

T. M. R. Ellis, Ivor R. Philips ja Thomas M. Lahey. Fortran 90 Programming. Addison-Wesley, 1994.

[Geh96]

Wilhelm Gehrke. Fortran 95 Language Guide. Springer, 1996.

[GHJV95] Erich Gamma, Richard Helm, Ralph Johnson ja John Vlissides. Design
Patterns. Addison-Wesley, 1995.
[Haa98]

Juha Haataja, toim. Matemaattiset ohjelmistot. CSC Tieteellinen laskenta Oy, 1998. URL http://www.csc.fi/oppaat/mat.ohj/.

[Haa02]

Juha Haataja, toim. Alkurjhdyksest knnykkn nkkulmia laskennalliseen tieteeseen. CSC Tieteellinen laskenta Oy, 2002.

[Haa04]

Juha Haataja. Optimointitehtvien ratkaiseminen. CSC Tieteellinen


laskenta Oy, 2004.

[HHL+ 02] Juha Haataja, Jussi Heikonen, Yrj Leino, Jussi Rahola, Juha Ruokolainen
ja Ville Savolainen. Numeeriset menetelmt kytnnss. CSC Tieteellinen laskenta Oy, 2002.
[HJKR02] Juha Haataja, Jari Jrvinen, Jari Koponen ja Peter Rback, toim. Laskennallinen tuotekehitys: suunnittelun uusi ulottuvuus. CSC Tieteellinen
laskenta Oy, 2002.
[HJL00]

Juha Haataja, Jari Jrvinen ja Yrj Leino. Sillanrakennuksesta lkeainesuunnitteluun Matemaattinen mallintaminen suomalaisissa yliopistoissa. CSC Tieteellinen laskenta Oy, 2000. http://www.csc.fi/
raportit/mallinnus/.

[HM01]

Juha Haataja ja Kaj Mustikkamki. Rinnakkaisohjelmointi MPI:ll. CSC


Tieteellinen laskenta Oy, 2001. http://www.csc.fi/oppaat/mpi/.

[Kar01]

Hannu Karttunen. Datan ksittely. CSC Tieteellinen laskenta Oy, 2001.

330

Fortran 95/2003

[Ker93]

James F. Kerrigan. Migrating to Fortran 90. OReilly & Associates, 1993.

[KLS85]

J. Korpela, T. Larmela ja J. Salmela. FORTRAN 77. OtaDATA ry, 1985.

[KLS+ 94]

C. Koelbel, D. Loveman, R. Schreiber, G. Steele Jr. ja M. Zosel. The High


Performance Fortran Handbook. MIT Press, 1994.

[Kos97]

Kai Koskimies. Pieni oliokirja. Suomen ATK-kustannus, 1997.

[KP78]

Kernighan ja Plauger. The Elements of Programming Style. McGraw-Hill,


1978.

[MR96]

Michael Metcalf ja John Reid. The F Programming Language. Oxford


University Press, 1996.

[MRC04]

Michael Metcalf, John Reid ja Malcolm Cohen. Fortran 95/2003 Explained. Oxford University Press, 2004.

[Rei]

John Reid. The New Features of Fortran 2003. Raportti.

[RR96]

Cecil C. Rousseau ja Otto G. Ruehr, Problems and Solutions: Expected


Wire Length Between Two Randomly Chosen Terminals, Siam Review,
2/1996, Volume 38, sivut 321324. Problem 95-6 by David M. Lazo
and solution by W. Boehm.

[Saa95]

Sami Saarinen. Rinnakkaislaskennan perusteet PVM-ympristss. CSC


Tieteellinen laskenta Oy, 1995.

[Sed84]

Robert Sedgewick. Algorithms. Addison-Wesley, 1984.

Hakemisto

331

Hakemisto
Symbolit
!, 45, 46
!HPF$, 299
", 46, 53
, 46
, 53
(, 46
(/, 168, 241
), 46
*, 46, 67, 142, 143, 193, 194, 197
**, 67, 142, 143
+, 46, 67, 142, 143
,, 46
-, 46, 67, 142, 143
., 46
/, 46, 67, 142, 143
/), 168, 241
//, 53, 73, 143
/=, 77
:, 46, 164166, 179, 203, 320
::, 53, 318
;, 44, 46
<, 46, 7779
<=, 77
=, 46, 147, 320, 322
==, 7779
=>, 320, 322
>, 46, 7779
>=, 77
?, 46
$, 46
%, 46, 152
&, 36, 44, 46
_, 26

A
ABS, 222, 230
abstrakti tietorakenne, 126
ACHAR, 79, 223
ACOS, 113, 220, 230
ADJUSTL, 74, 223
ADJUSTR, 74, 223
ADVANCE=NO, 204

AIMAG, 222, 230


AINT, 222, 230
ajaminen, 128
ajokelpoinen ohjelma, 13, 26
alaviiva, 24, 26, 58
alias, 183
ALIGN, 303
alimerkkijono, 73
aliohjelma, 99, 320
kutsu, 39, 104
mrittely ja kytt, 104
rekursiivinen, 112
aliohjelmakirjasto, 19, 21, 100
alkioiden sijoittelujrjestys muistissa,
288
alkioittainen proseduuri, 115, 215
alkuarvo, 54
alkukirjainsnt, 55, 318
alkulukuteoreema, 191
alkuluvut, 182
ALL, 175, 176, 224
ALLOCATABLE, 178, 179, 189, 319
ALLOCATE, 179, 182, 189, 238, 250,
322
ALLOCATED, 175, 217
alternate return, 286
alustin
rakenteen, 153
taulukko, 168, 241
alustuslauseke, 54
.AND., 77, 78
ANINT, 222, 230
annuiteetin laskeminen, 35
ANSI-standardi, 23
ANY, 171, 175, 176, 224
argumentti, 101
avainsana-argumentti, 118, 120
muodollinen, 101, 105
proseduuri, 113
todellinen, 101, 105
valinnainen, 118, 119
argumenttilistojen tarkistus, 116
aritmeettinen IF-lause, 285

332

aritmeettiset lausekkeet, 67
array, 164
array constructor, 168
array element order, 169
ASCII-merkist, 46, 56, 79, 223, 324
ASIN, 113, 220, 230
ASSIGN, 286, 322
assigned goto, 284, 286
ASSOCIATED, 186, 217
assumed length, 285
assumed-shape array, 171
assumed-size array, 172
asteriski, 46
asuntolainan lyhennykset, 35
ATAN, 113, 220, 230
ATAN2, 70, 220, 230
automaattinen taulukko, 165
automatic array, 165
avainsana-argumentti, 118, 120

B
BACKSPACE, 204, 322
binripuu, 256
binritiedostot, 206
bisection, 135
BIT_SIZE, 226, 227
bittien ksittely, 226
BLOCK DATA, 47, 127, 281, 288, 317
bottom up, 99
BTEST, 226, 227

C
CALL, 39, 216, 317, 320
CASE, 88, 321
CASE DEFAULT, 88, 89, 321
CEILING, 71, 222
CHAR, 79, 223, 254
CHARACTER, 51, 55, 73, 318
CLASS, 297
CLASS IS, 297
CLOSE, 204, 206, 322
CMPLX, 81, 221
COMMON, 127, 278, 280, 281, 288, 319
korvaus moduuleilla, 283
compiler, 17
COMPLEX, 51, 318
computed goto, 283, 284, 288
conformable, 190
CONJG, 222, 230
conjugate gradient method, 269
CONTAINS, 31, 47, 48, 101, 108, 129,
317
CONTINUE, 95, 286, 322
COS, 220, 230
COSH, 220, 230

Fortran 95/2003

COUNT, 175, 176, 183, 224


cpu-aika, 217
CPU_TIME, 217
CSC:n yhteystiedot, 14
CSHIFT, 177, 224, 250, 255
CSIN, 230
CYCLE, 92, 321

D
DATA, 319
datarinnakkaisuus, 304
DATE_AND_TIME, 217
DBLE, 221
DE-menetelm, 263
DEALLOCATE, 179, 189, 322
debugger, 21, 192
deferred-shape array, 165
deleted feature, 284
derived type, 152
desimaaliesitys, 199
desimaalipiste, 27, 52
dierenssimenetelm, 300
dierentiaalievoluutio, 263
DIGITS, 221
DIM, 222, 230
DIMENSION, 33, 164, 189, 280, 319
disassociated, 158
DISTRIBUTE, 302, 304
DO WHILE, 9294, 321
DO-rakenne, 90, 91, 93, 321
DO-silmukka, 32
dokumentointi, 19
dollarin merkki, 46
DOT_PRODUCT, 175, 225
DOUBLE PRECISION, 60, 278, 281, 288,
318
DPROD, 222, 230
DSIN, 288
dynaaminen tilanvaraus, 178
dynaamisesti varattava taulukko, 165
dynamic array, 165

E
ehdollinen suoritus, 86
ehtolause, 37
ehtorakenne, 321
eksponenttiesitys, 27, 199
eksponenttifunktiot, 71, 220
eksponenttinotaatio, 52, 57
ELEMENTAL, 115
elemental, 215
ELSE, 87, 321
ELSE IF, 87, 321
ELSEWHERE, 176, 255
Emacs, 20

Hakemisto

END, 47
END BLOCK DATA, 317
END DO, 90, 93, 286, 321
END FUNCTION, 100, 317
END IF, 87, 321
END INTERFACE, 147, 320
END MODULE, 129, 317
END PROGRAM, 25, 26, 47, 317
END SELECT, 88, 321
END SUBROUTINE, 104, 317
END TYPE, 319
ENTRY, 288
EOSHIFT, 177, 224
EPSILON, 132, 221
epsuora hyppylause, 286
.EQ., 77
EQUIVALENCE, 278, 279, 281, 288, 319
.EQV., 77, 78
Erastotheneen seula, 182
erikoismerkit, 46, 79
erisuuruus, 77
erityisnimi, 229231, 288
Esc -merkki, 255
esimerkkiohjelmat, 234
et-merkki, 46
etsint, 257
etumerkin vaihto, 67
etumerkki, 27
Euklideen algoritmi, 93
evoluutiostrategiat, 263
exclusive or, 76
EXIT, 91, 92, 94, 321
EXP, 220, 230
explicit interface, 116
explicit-shape array, 164, 171
EXPONENT, 221
EXTENDS, 296, 297
EXTERNAL, 47, 110, 113, 319, 320
external procedure, 108
EXTRINSIC, 305

F
FINAL, 294
xed source form, 44
FLOOR, 71, 222
FORALL, 177, 304, 305, 321
FORMAT, 47, 197, 284, 286, 323
format, 35
formatoimaton tiedonsiirto, 206
formatoitu tiedonsiirto, 206
Fortran 2003, 291
FORTRAN 66 -kieli, 23
FORTRAN 77 -kieli, 23
huonoja puolia, 276

333

muuntaminen Fortran 95:een, 275,


279
Fortran 95 -kieli, 23
siirtyminen kieleen, 275
FORTRAN IV -kieli, 23
FRACTION, 222
free source form, 44
FUNCTION, 47, 100, 317
function, 100
funktio, 99, 100, 320
kytt, 30, 100
mrittely, 30, 100
rekursiivinen, 111
taulukkoarvo, 173
funktiokutsu, 31, 101

G
.GE., 77
geneerinen
aliohjelma, 320
funktio, 320
nimi, 140
operaattori, 320
proseduuri, 140, 238
globaali data, 128
globaali muuttuja, 109
globaali optimointi, 264
GOTO-lause, 95, 322
.GT., 77

H
haarautuva hyppylause, 283, 284, 288
hakuavain, 257
heittomerkki, 46
High Performance Fortran, 25, 299
Hollerith-merkkijonovakiot, 287
host, 108
HPF, 25, 299
!HPF$, 299
HPF_LIBRARY, 300, 304
HUGE, 221, 282
huutomerkki, 46
hyppylause, 86, 95, 322
haarautuva, 283, 284, 288

I
IACHAR, 79, 223
IAND, 226, 227
IBCLR, 226
IBITS, 226
IBSET, 226
ICHAR, 79, 223
IEEE-liukulukuaritmetiikka, 69
IEOR, 226
IF-lause, 88

334

IF-rakenne, 37, 86, 87, 321


ikuinen silmukka, 91
IMPLICIT, 318, 319
implicit interface, 116
IMPLICIT NONE, 28, 47, 55, 109, 128,
277, 280, 318
implicit typing, 55
implied do list, 168
IMSL, 100
INCLUDE, 48
INDEPENDENT, 304
INDEX, 74, 209, 223, 230
INHERIT, 303
initialization expression, 54
inlining, 114
INQUIRE, 204, 207, 208, 322
inquiry functions, 215
insertion sort, 237
INT, 71, 81, 221, 231
INTEGER, 29, 51, 318
integer kind, 52
INTENT, 105, 106, 304, 319, 320
INTENT(IN), 106
INTENT(INOUT), 106
INTENT(OUT), 106
INTERFACE, 47, 116, 117, 123, 136,
147, 304, 320
INTERFACE
ASSIGNMENT, 146, 147, 149, 320
OPERATOR, 143, 145, 147, 149,
320
interface, 116
interface body, 121
internal procedure, 108
INTRINSIC, 47, 215, 319, 320
intrinsic function, 174
intrinsic type, 51
IOR, 226
ISHFT, 226
ISHFTC, 226, 227
ISO-standardi, 23
isot kirjaimet, 26, 45
isnt, 108

J
jakojnns, 94
jakolasku, 27, 67
jatkorivi, 36, 279, 280
jatkorivin merkki, 44
julkiset tunnukset, 133
julkisuus, 126, 131, 132, 159, 320
juurisolmu, 257

K
kaksiargumenttinen operaattori, 143,
147

Fortran 95/2003

kaksoispiste, 46
kaksoistarkkuus, 60
kanava, 35, 204, 323
ominaisuuksien kysely, 207
kanavanumero, 196, 204
karteesiset koordinaatit, 69
katkaisuoperaatiot, 71
kellonaika, 217, 219
kentn leveys, 144, 197
kertolasku, 27, 67
keskiarvo, 33, 34, 118
keskusmuisti, 18
keskusyksikk, 17
KIND, 221
kind, 56
KIND-parametri, 69
kirjoitus merkkijonoihin, 209
kokonaisluku, 51, 198
jakolasku, 27, 68
kokonaislukulaji, 52
kokonaislukumuuttuja, 28
kokonaislukutyyppi, 29, 51
kokonaislukuvakio, 27, 51
kommentti, 45
kommenttimerkki, 279, 280
kompleksiargumentti, 71
kompleksiluku, 51, 52, 58, 200
kompleksilukuvakio, 52, 217
komponenttiajattelu, 99
konekieli, 17
koneksky, 17
koodin optimointi, 21
koordinaattimuunnokset, 69
kosini, 71
kutsumuoto, 116, 136
mrittely, 116
kutsurajapinta, 99
kyselyfunktiot, 215
kysymysmerkki, 46
kyttjn mrittelem operaattori, 143
knnsaikaiset tarkistukset, 31, 110
knnslistaus, 20
kntj, 17
kntminen, 13, 19, 26, 40, 128

L
lainausmerkki, 27, 46, 53
laji, 52
lajimre, 56, 63
lajiparametri, 57, 58, 69
lajittelu, 80, 126, 237
nousuindeksi, 240
laskentajrjestys, 81
lause, 44
jrjess, 47

Hakemisto

jrjestys, 47
tiivistelm lauseista, 317
lausefunktio, 281, 288
lauseke, 67
lausenumero, 95, 322
LBOUND, 175, 179, 225
.LE., 77
LEN, 73, 74, 223, 230
LEN_TRIM, 74, 75, 223
LGE, 79, 223
LGT, 79, 223
Life-peli, 253
Life-soluautomaatti, 253
liitosoperaatio, 53, 73, 143
liittogradienttimenetelm, 269, 270
lineaariset yhtlryhmt, 269
linkitys, 19, 21
linkitystaulu, 21
list-directed formatting, 194
listan ohjaama muotoilu, 194
liukulukuesitys, 57
LLE, 79, 223
LLT, 79, 223
LOG, 220, 230
LOG10, 220, 230
logaritmifunktiot, 71, 220
LOGICAL, 51, 221, 318
looginen ekvivalenssi, 77
looginen epekvivalenssi, 77
looginen ja, 77
looginen negaatio, 77
looginen tai, 77
loogiset lausekkeet, 76, 77
loogiset operaatiot, 77
lopettaminen, 294
lopetusehto, 111
.LT., 77
luku merkkijonoista, 209
lhdekoodin muoto, 24, 44
lhdekooditiedosto, 19

M
matemaattiset funktiot, 222
MATMUL, 174, 225
matriisi, 168, 169
alkioiden summa, 174
alkioiden tulo, 174
tulo, 174
MAX, 222, 231
MAXEXPONENT, 221
MAXLOC, 175, 225
MAXVAL, 175, 225
MERGE, 176, 224
merkkien jrjestys, 79
merkkijono, 51, 53, 201

335

ksittely, 74, 223


lajittelu, 80
oletetun pituinen, 106
sijoitus, 82
vaihtelevan pituinen, 73
vertailu, 78
merkkijonomuuttujat, 73
merkkijonotaulukot, 75
merkkijonovakio, 27, 53
merkkitaulukot, 75
merkkityyppi, 53
merkkivalikoima, 45
Message-Passing Interface, 299
miinusmerkki, 46
MIL-STD 1753, 226
MIN, 222, 231
MINEXPONENT, 221
minimilevyinen kentt, 203
MINLOC, 175, 225
MINVAL, 175, 225
MOD, 94, 222, 230
modulaarinen ohjelmointi, 126
modulaarisuus, 126
MODULE, 47, 129, 317
MODULE PROCEDURE, 141, 147, 320
module procedure, 108
MODULO, 222
moduuli, 40, 48, 126
kytt, 129, 320
kyttmahdollisuudet, 148
vakioiden mrittely, 128
moduulin proseduuri, 108, 123, 317
MPI, 299
mukautuvan muotoinen taulukko, 165
muodollinen argumentti, 31, 101
muoto, 164
muotoilukoodi, 35, 144, 197199, 202,
323
kokonaisluvut, 198
kompleksiluvut, 200
merkkijonot, 201
mrittely, 323
reaaliluvut, 199
tiedon sytt, 198
yleinen, 201
muunnosfunktiot, 215
muuttuja, 28, 53
mrittely, 53, 318
nimi, 46
tyyppi, 29
muuttujien alustaminen, 107
muuttujien arvon silyminen, 107
MVBITS, 226, 227
mrittelylause, 319

336

Fortran 95/2003

NAG, 100, 117


NAGWare, 14
NAMELIST, 127, 148, 210, 254, 319
.NE., 77
NEAREST, 222
nelilaskin, 234
nelijuuri, 30, 71
.NEQV., 7678
NEW, 304
Newtonin menetelm, 90, 91
nimetty vakio, 33, 54, 63, 319
NINT, 71, 222, 230
nollakohdan haku, 135
NOPASS, 294
normaalijakautuneet satunnaisluvut,
138
NOT, 226
.NOT., 77
nousuindeksi, 240
NULL, 158, 186, 217
NULLIFY, 186, 322
numeerinen tarkkuus, 221
numeeriset funktiot, 222
numeeriset konversiot, 221
nkyvyysalue, 121, 122
nppimist, 18

omat funktiot, 30
ominaisuuksien kysely, 207
ONLY, 133, 320
OPEN, 204206, 210, 322
OpenMP, 305
operaation mrittelyalueen laajentaminen, 140
operaattori, 83, 126
kaksiargumenttinen, 143
kyttjn mrittelem, 143
mrittely, 142, 147
yksiargumenttinen, 143, 144
ylilataaminen, 142
operator overloading, 142
optimointi, 21, 83, 84
optimointitehtvien ratkaiseminen, 263
OPTIONAL, 47, 119, 319, 320
optional, 118
.OR., 77, 78
osoitin, 183
kohde, 190
kohteesta irroitettu, 158
muuttuja, 178, 180, 183, 185, 186,
190
otsikkolause, 47, 101, 102
otsikkorivi, 31
overloading, 140

objektikooditiedosto, 19, 21
obsolescent feature, 284
ohjauskoodi, 202
ohjausrakenteet, 86
ohjelma, 17
ajaminen, 13, 40
konekielinen, 17
ohjelmayksikk, 127
perusrakenne, 44
pohjelma, 127
suorituksen keskeytys, 322
ohjelmakoodi, 19
rakenne, 44
ohjelmalohko, 86
ohjelmayksikiden mrittely, 317
ohjelmayksikk, 20, 127
ohjelmisto, 16
ohjelmointi, 16, 17
eri vaiheet, 18
ohjelmointikieli, 17
oikea sulje, 46
oletetun kokoinen taulukko, 172
oletetun muotoinen taulukko, 171
oletusarvoinen kutsumuoto, 116
oletusmuotoilu, 35, 194
olio-ohjelmointi, 295

PACK, 176, 183, 224


paikallinen muuttuja, 101, 109
paikallinen tunnus, 109
pallokoordinaatit, 69
PARAMETER, 33, 55, 63, 165, 280, 319
parametrisoitu tyyppimrittely, 56
PASS, 294
PAUSE, 287, 322
peruslaskutoimitukset, 27
perustyypit, 51, 63
lajimreet, 56
perkkisyys, 86
perkkissaantitiedosto, 206
PGM-formaatti, 251
pienempi kuin, 77
pienempi kuin -merkki, 46
pienet kirjaimet, 26, 45
pilkku, 46
pino, 235, 236
piste, 46
pituusyksikt, 128
plusmerkki, 46
poikkeusten ksittely, 94
POINTER, 178180, 183, 190, 319
Poissonin yhtl, 300
poistetut piirteet, 284

Hakemisto

portable graymap, 251


portable pixmap, 252
potenssiin korotus, 27, 67
potenssiinkorotusoperaattori, 68
PPM-formaatti, 252
PRECISION, 215, 221
PRESENT, 119, 217
PRINT, 193, 196, 323
PRIVATE, 131, 132, 134, 148, 319, 320
procedure, 100
PROD, 174
PRODUCT, 175, 225
PROGRAM, 25, 26, 47, 317
proseduuri, 99
alkioittainen, 115
argumenttien mreet, 320
argumenttina, 113
moduulin proseduuri, 130, 141
mrittelylauseet, 320
paluu kutsuvaan ohjelmayksikkn, 322
sisinen, 108
sivuvaikutukseton, 115
ulkoinen, 108, 110
prosenttimerkki, 46
PUBLIC, 132, 134, 319, 320
puolipiste, 44, 46
puolitushaku, 135, 136
PURE, 115
PVM, 299
pyristysoperaatiot, 71
pivmr, 217
pohjelma, 26, 47, 127

Q
Quicksort-algoritmi, 273

R
RADIX, 221
rakenteinen tietotyyppi, 152
rakenteinen tyyppi, 33, 34
mrittely, 319
rakenteiset tyypit, 296
random walk, 244
RANDOM_NUMBER, 39, 138, 139, 218,
245, 249
RANDOM_SEED, 218
RANGE, 221
rank, 164
reaaliluku, 27, 51, 52, 199
reaalilukulaji, 131
reaalilukutyyppi, 29
reaalilukuvakio, 27, 52
laji, 58
READ, 193, 196, 204, 323

337

REAL, 29, 51, 81, 221, 231, 318


REAL-funktio, 61, 62
REALIGN, 303
RECURSIVE, 111, 112, 317
REDISTRIBUTE, 303
rekursiivinen aliohjelma, 112
rekursiivinen funktio, 111
rekursiivinen proseduuri, 111
rekursio, 111
lopetusehto, 111
REPEAT, 144, 223
RESHAPE, 168, 175, 224, 289
RESULT, 100103, 111
RETURN, 94, 322
REWIND, 204, 206, 322
rinnakkaislaskenta, 25, 299
HPF, 299
OpenMP, 305
rivi, 44
RRSPACING, 221
ryhmittely, 27

S
sarakesidonnainen syttmuoto, 44,
277
sarjan summa, 60
satunnaiskulku, 244
satunnaisluvut, 218, 219
normaalijakautuneet, 138
SAVE, 108, 319, 320
SCALE, 221
SCAN, 74, 223
scope, 121
SELECT CASE, 88, 321
SELECT TYPE, 297
SELECTED_INT_KIND, 56, 57, 221
SELECTED_REAL_KIND, 57, 118, 221
selection sort, 80
SEQUENCE, 155, 296, 319
SET_EXPONENT, 221
SHAPE, 171, 175, 225
shape, 164
Shellsort-algoritmi, 241
sidontajrjestys, 67, 83
SIGN, 69, 222, 230
siirrettvyys, 17, 24
sijoituslajittelu, 237
sijoituslause, 29, 67, 81, 322
sijoitusoperaatio, 142
mrittely uudelleen, 146
sijoitusoperaattori, 147
silmukka, 90
silmukkamuuttuja, 32, 90
reaalilukutyyppi, 285
simulointi, 38

338

SIN, 140, 215, 220, 230, 288


SINH, 220, 230
sinifunktio, 71, 215
sisinen funktio, 31, 47, 101
sisinen proseduuri, 108, 123, 127,
317
sisinen tiedosto, 209
sivuvaikutukseton proseduuri, 115
sivuvaikutus, 83, 103
SIZE, 174, 175, 225, 237
skandinaaviset kirjaimet, 46, 79
sokkelo, 243
soluautomaatti
Life-peli, 253
yksiulotteinen, 247
SPACING, 221
specic name, 229, 288
SPREAD, 176, 224, 253
SQRT, 30, 222, 230
standard error, 205
standard input, 205
standard output, 205
standardi, 23, 25
standardialiohjelma, 114, 215
standardifunktio, 30, 69, 215
erityisnimi, 229, 288
standardioperaattori, 142
standardiproseduuri, 215
standardityypit, 51
statement function, 281, 288
STOP, 94, 322
storage association, 288
SUBROUTINE, 47, 104, 317
subroutine, 100, 104
sukupuu, 261
sulkumerkki, 27
sulut, 67
SUM, 34, 174, 175, 215, 225
suorasaantitiedosto, 206
suoritusjrjestys, 143
suorituskelpoinen ohjelma, 19
suurempi kuin, 77
suurempi kuin -merkki, 46
suurin yhteinen tekij, 93
syntaksi, 44
SYSTEM_CLOCK, 217
sytt, 192, 193, 323
ohjauskoodit, 202
syttkanava, 205
syttlause, 192, 193, 196
sytttiedot, 17

T
TAN, 220, 230
TANH, 220, 230

Fortran 95/2003

tapausrakenne, 321
tapausten ksittely, 88
TARGET, 184, 190, 319
tarkkuus, 69
taulukko, 33, 164
alkio, 166
alkioiden summa, 34
alkiojrjestys, 169
alustin, 168, 241
automaattinen, 165
dynaaminen, 165
indeksointi, 167
mrittely, 319
mukautuva, 165
muoto, 164
oletettu koko, 172
oletettu muoto, 171
operaatiot, 39, 174, 224, 225
rajojen tarkistukset, 21
standardifunktiot, 174
tunnettu muoto, 164, 171
ulottuvuudet, 164
vlitys, 170
vakio, 165
testaus, 19, 21
THEN, 87, 321
tiedon hakeminen, 257
tiedon jrjestminen, 256
tiedon sytt, 192, 198, 323
tiedon tulostaminen, 192, 323
tiedonsiirto, 193
erikoistilanteet, 95
tiedosto, 18
avaaminen, 204
ksittely, 204, 322
ominaisuuksien kysely, 207
tietoabstraktio, 257
tietokone, 17
tietotyypit
abstraktit, 152
perustyypit, 51
rakenteiset, 152
tietueen osan tulostaminen tai lukeminen, 204
tiivistelm lauseista, 317
tilapistiedosto, 206
TINY, 221
todellinen argumentti, 31, 101
toisen asteen yhtl, 72
toisto, 86, 90
toistolista, 168
toistorakenne, 32, 321
top down, 99
totuusarvo, 51, 52
TRANSFER, 224, 279

Hakemisto

transformational functions, 215


TRANSPOSE, 174, 225
trigonometriset funktiot, 220
TRIM, 74, 75, 223
tulostaminen, 192, 193, 323
minimilevyinen kentt, 203
tulostuksen muotoilu, 35
tulostuksen ohjauskoodit, 202
tulostusasu, 35
tulostuskanava, 35
tulostuslause, 192, 193, 196
tulostuslista, 202, 203, 323
tunnettu kutsumuoto, 116
tunnetun muotoinen taulukko, 164,
171
tunniste
DO-rakenne, 93
IF-rakenne, 87
SELECT CASE -rakenne, 88
tunnus, 26, 45
nimeminen, 26
paikallinen, 109
tutkintaohjelma, 21
TYPE, 318, 319
TYPE IS, 297
tyyppi, 318
tyyppimrittelyn mreet, 319

U
UBOUND, 175, 225
ulkoinen proseduuri, 108, 110, 123,
127
geneerinen nimi, 142
ulottuvuudet, 164
unit, 35
UNPACK, 177, 224
USE, 47, 128, 132, 320
USE-lause, 133
uudet piirteet, 276

V
vaihtelevan pituinen merkkijono, 73
vaihtoehtoinen paluukohta, 286
vakio, 27
mrittely, 128
nimi, 46
tyyppi, 51
vakiotaulukko, 128, 165
valinnainen argumentti, 118, 119
valinta, 86
valintalajittelu, 80
vanhentuvat piirteet, 284
vapaa lhdekoodin muoto, 24, 44, 280
vasen sulje, 46
VERIFY, 74, 75, 223

339

versionhallintajrjestelm, 279
vertailuoperaatio, 77
vertailuoperaattori, 77, 78
vinoviiva, 46
virheenjljitysohjelma, 21
virheentarkistus, 110
virhetilanne, 94
virhetilanteiden ksittely, 210, 211
VT100, 255
vhennyslasku, 27, 67
vlilynti, 45, 46, 277
vltettvt piirteet, 287

W
WHERE, 176, 246, 255, 267, 321
World Wide Web, 14
WRITE, 25, 196, 204, 323
www, 14

Y
yhteenlasku, 27, 67
yhtlisyysmerkki, 46
yhtsuuruus, 77
yksiargumenttinen operaattori, 143,
144, 147
yksityisyys, 126, 131, 159, 320
yleinen muotoilukoodi, 201
ylilataaminen, 140, 156
yllpito, 19

You might also like