Cursus Programmeren
A.P. van den Berg J. van Hunen J.H. de Smet
januari 1999
Theoretische Geofysica Universiteit Utrecht
2
Inhoudsopgave 1 Inleiding 1.1 Algemeen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.2 Overzicht van de te behandelen onderwerpen . . . . . . . . . . . . . . . . . . . . 1.3 Gebruikte notaties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2 Organisatie van computersystemen 2.1 Defini¨erende eigenschappen van een computer . 2.2 Organisatie van de apparatuur in een computer 2.3 Het besturingssyteem . . . . . . . . . . . . . . 2.3.1 Het UNIX besturingssysteem . . . . . . 2.4 Het hierarchisch file systeem . . . . . . . . . . . 2.4.1 Files en directories . . . . . . . . . . . . 2.4.2 Padnamen . . . . . . . . . . . . . . . . . 2.5 Uitvoering van een programma . . . . . . . . . 2.5.1 Programma executie . . . . . . . . . . . 2.5.2 Afbreken van een lopend programma .
. . . . . systeem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3 Het ontwikkelen van programmatuur 3.1 Beschikbare programmeertalen . . . . . . . . . . . . 3.2 Programma ontwikkelings cyclus . . . . . . . . . . . 3.2.1 Probleemanalyse . . . . . . . . . . . . . . . . 3.2.2 Struktuurdiagrammen . . . . . . . . . . . . . 3.2.3 Codering . . . . . . . . . . . . . . . . . . . . 3.2.4 Invoering en compilatie van programma’s . . 3.2.5 Programma uitvoering en programmeerfouten 3.2.6 Het voorkomen van programmeerfouten . . .
7 7 7 7
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
9 9 9 10 10 10 10 11 12 13 13
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
15 15 15 15 17 17 17 18 18
4 Overzicht van Fortran 4.1 De programmeertaal Fortran . . . . . . . . . . . . . . 4.1.1 Een onderverdeling van de Fortran syntax . . . 4.2 Programma struktuur . . . . . . . . . . . . . . . . . . 4.2.1 Hoofdprogramma . . . . . . . . . . . . . . . . . 4.2.2 Struktuur van een Fortran tekstregel . . . . . . 4.2.3 Initi¨ele regels en continueringsregels in Fortran 4.2.4 Volgorde van statements . . . . . . . . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
21 21 21 22 22 23 24 24
5 Data typen in Fortran 5.1 Type declararaties van intrinsieke datatypen . . . . . . . . . . . . . . . . . . . . .
27 27
6 Expressies in Fortran 6.1 Rekenkundige expressies 6.1.1 Type conversie in 6.2 Character expressies . . 6.3 Relationele expressies . 6.4 Logische expressies . . .
29 29 30 30 32 33
. . . . . . . . rekenkundige . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . expressies . . . . . . . . . . . . . . . . . .
. . . . .
. . . . .
7 Voorbeelden van eenvoudige Fortran programma’s 3
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
35
8 Procedures (modulair programmeren) 8.1 Opsplitsing van een programma in procedures . . . . . . . . . . . . 8.2 Procedures in Fortran . . . . . . . . . . . . . . . . . . . . . . . . . 8.2.1 Functies . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8.2.2 Subroutines . . . . . . . . . . . . . . . . . . . . . . . . . . . 8.2.3 Dummy procedures als parameters van Fortran procedures 8.2.4 Geneste procedure aanroepen . . . . . . . . . . . . . . . . . 8.3 Een voorbeeld van het gebruik van functies en subroutines . . . . . 8.4 Interface definities . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
39 39 40 40 43 45 46 47 49
9 Struktuur diagrammen
51
10 Control structures in Fortran 10.1 Enkele toepassingen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
55 59
11 Ge¨ındiceerde variabelen (arrays) 11.1 Array declaratie . . . . . . . . . . . . . . . . . . . . 11.1.1 Array declaratie in Fortran 77 . . . . . . . . 11.1.2 Array declaratie in Fortran 90 . . . . . . . . 11.2 Geheugenopslag van arrays . . . . . . . . . . . . . . 11.3 De juiste array lengten en geheugenallocatie . . . . . 11.3.1 Statische geheugenallocatie in Fortran 77 . . 11.3.2 Automatic arrays . . . . . . . . . . . . . . . . 11.3.3 Dynamische geheugen allocatie in Fortran 90 11.4 Arrays als parameters van procedures . . . . . . . . 11.5 Uitgebreide array syntax in Fortran 90 . . . . . . . . 11.5.1 Array shape en size . . . . . . . . . . . . . . 11.5.2 Assumed-shape arrays en interface definities . 11.5.3 Elemental array operaties . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
63 63 63 64 64 66 66 66 67 68 70 70 70 71
12 Modulen en common block’s 12.1 common block’s . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12.1.1 Common blocks in combinatie met formele of actuele parameters 12.1.2 Het gebruik van common blocks in include-files . . . . . . . . . . 12.2 Fortran 90 modulen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12.2.1 ‘Derived types’ en ‘structures’ . . . . . . . . . . . . . . . . . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
73 73 74 74 75 76
13 Input en output in Fortran programma’s 13.1 Het gebruik van input en output files . . . . . . . . . . . 13.1.1 Input/output redirection van/naar een file . . . . 13.1.2 Input/output van/naar een file met logical units 13.2 Uitgebreider I/O syntax . . . . . . . . . . . . . . . . . . 13.3 Verschillende file toegangsvormen in Fortran . . . . . . . 13.3.1 Sequentieel toegankelijke files . . . . . . . . . . . 13.3.2 Direct toegankelijke files . . . . . . . . . . . . . . 13.4 I/O formats . . . . . . . . . . . . . . . . . . . . . . . . . 13.4.1 Ongeformatteerde I/O . . . . . . . . . . . . . . . 13.4.2 Free format of list directed I/O . . . . . . . . . . 13.4.3 I/O met format specificaties . . . . . . . . . . . . 13.5 Internal files . . . . . . . . . . . . . . . . . . . . . . . . . 13.6 Opslag van data - files en records . . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
81 81 81 81 83 84 84 84 84 84 84 85 90 91
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
14 Het data type complex
93
A Handleiding bij de unix screen editor vi A.1 Algemeen . . . . . . . . . . . . . . . . . A.2 Programma aanroep . . . . . . . . . . . A.3 Programma modes . . . . . . . . . . . . A.4 Lijst van veel gebruikte commando’s . . A.5 Editor quick reference . . . . . . . . . . 4
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
95 95 95 95 96 97
B Data representatie in computers B.1 Alfanumerieke data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . B.2 Numerieke data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . B.2.1 Data van het type integer . . . . . . . . . . . . . . . . . . . . . . B.2.2 Data van het type real . . . . . . . . . . . . . . . . . . . . . . . . B.2.3 Effect van eindige machine precisie bij rekenkundige bewerkingen
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
101 101 102 102 102 103
C Het installeren (compileren en linken) C.1 Compilereren en linken . . . . . . . . . C.1.1 Libraries en ar . . . . . . . . . C.2 Make . . . . . . . . . . . . . . . . . . . C.2.1 Een eenvoudige Makefile . . . C.2.2 Macro’s . . . . . . . . . . . . . C.2.3 ‘Make rules’ en ‘default rules’ .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
105 105 105 106 106 107 108
van programma’s . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
D Intrinsieke Fortran functies 111 D.1 Intrinsieke functies in Fortran 77 . . . . . . . . . . . . . . . . . . . . . . . . . . . 111 D.2 Intrinsieke functies in Fortran 90 . . . . . . . . . . . . . . . . . . . . . . . . . . . 117
5
6
Hoofdstuk 1
Inleiding 1.1
Algemeen
Het programma onderdeel programmeren 2 is een vervolg op de cursus programmeren 1, die o.a. een beknopte inleiding in de programmeertaal Fortran bevat. In de cursus programmeren 2 wordt de kennis van het gebruik van Fortran verder uitgebouwd. Daarnaast worden in de meer algemene inleiding enige aspecten van computersystemen behandeld in verband met verdere toepassingen. De bedoeling van de cursus is o.a. het stimuleren van gestruktureerd programmeren, met een top-down ontwerp aanpak. Hiertoe wordt o.a. het gebruik van struktuurdiagrammen bij het ontwerpen van programma’s ge¨ıntroduceerd. Bij de daarop volgende behandeling van de programmeertaal Fortran wordt het modulair programmeren met gebruik van subroutines en functies benadrukt. Het doel van deze cursus is o.a. de vaardigheid in het ontwikkelen van computerprogrammatuur op een voldoende hoog niveau te brengen voor vruchtbare toepassing in het verdere onderwijs en eigen onderzoek. In de cursus worden naast de gangbare taalversie Fortran 77 ook de belangrijkste elementen van de nieuwere versie Fortran 90 behandeld. De Fortran 77 syntax blijft onderdeel van de Fortran 90 definitie, wat inhoudt, dat de grote investeringen in bestaande programmatuur worden beschermd. Verwacht mag worden, dat programmatuur in beide taalversies nog de nodige jaren naast elkaar zullen worden gebruikt.
1.2
Overzicht van de te behandelen onderwerpen
In de cursus programmeren worden de volgende onderwerpen behandeld: 1. enkele eigenschappen van digitale computers, relevant voor de hier te behandelen problemen (bijv. architectuur, getal representatie, eindige precisie) 2. het besturingssysteem van een computer. Enige kennis hiervan is noodzakelijk om met het computersysteem te kunnen werken. 3. algemene oplosmethoden, die gebruik maken van digitale computers (hoe maak je een probleem computer-rijp?) 4. het systematisch ontwikkelen van gestruktureerde computer programma’s (top-down strategy) 5. het implementeren van gevonden oplossingen in de programmeertaal Fortran
1.3
Gebruikte notaties
De volgende notaties worden in verdere hoofdstukken gebruikt: • Letterlijke tekst in programmeertaal (bv. Fortran) of besturingssysteemtaal (bv. UNIX) wordt in het courier lettertype genoteerd ( dit is courier). • Het italic lettertype wordt gebruikt voor de volgende zaken: 7
– aanduidingen voor nader te specificeren onderdelen van programma tekst of shell tekst – nader verklaarde computer- en programmeer-termen. (dit is italic) • Tussen de rechte haken [ en ] worden onderdelen van programmatekst gegeven, die optioneel zijn. De rechte haken behoren zelf niet tot de Fortran syntax.
8
Hoofdstuk 2
Organisatie van computersystemen 2.1
Defini¨ erende eigenschappen van een computer
Een computer wordt wel gedefini¨eerd als een gegevens verwerkende automaat, met de volgende componenten resp. eigenschappen: 1. Er zijn input/output (I/O) mogelijkheden (toetsenbord/beeldscherm, magnetische schijfgeheugens (disk), printer, tape unit). 2. Er is een intern geheugen, voor opslag van data en instructies. 3. De machine beschikt over een reken eenheid voor rekenkundige / logische bewerkingen (arithmetic and logical unit (ALU)). 4. Het instructie repertoire bevat een beslissingsopdracht, waarmee uit alternatieve reeksen van instructies kan worden gekozen (branching). 5. Data en instructies worden in het zelfde geheugen opgeslagen (John von Neumann, 1945).
2.2
Organisatie van de apparatuur in een computer systeem
De apparatuur in een representatief middelgroot computer systeem bestaat uit: • central processing unit (CPU) (e.v.t meerdere), waarin zich o.a. de bovengenoemde ALU bevindt • Intern geheugen (central memory (CM in figuur 2.1)) • Randapparatuur (peripherals) waaronder: – schijfgeheugen (disk) – tape eenheden (cassette tape, mag. tape (MT)) – (graphics) terminals (T) – printers (LPR) – plotters (PL) – Laser disks (LD) (zoals CD-ROM) Computer systemen zijn vaak opgenomen in een Local Area Network (LAN) , waardoor faciliteiten zoals schijfgeheugen en printers door verschillende computers in het netwerk gebruikt kunnen worden. Zie voor een uitvoerige beschrijving bijvoorbeeld (Tanenbaum, 1984)1 . 1 Tanenbaum,
Structured computer organization, 1984
9
disk
MT
CPU
CM
T
LPR
PL
Figuur 2.1: Organisatie van de apparatuur in een middel groot computer systeem.
2.3
Het besturingssyteem
Naast de programmatuur die wordt gebruikt voor meer speciale taken (applicatie programmatuur) is er behoefte aan programmatuur voor uitvoering van taken van meer algemene aard. Te denken valt aan het opzoeken en starten van programma’s, het kopi¨eren van files, het afdrukken van een tekst file op een printer. In de behoefte aan deze algemene ondersteunende software wordt voorzien door een verzameling programmatuur, aangeduid als het besturingssysteem of operating system. E´en van de meest verbreide besturingssystemen, gemeten in het aantal verschillende typen computers waarop ze worden gebruikt, is UNIX, welke in sectie 2.3.1 aangestipt wordt. Andere veel gebruikte systemen zijn MS-DOS en MS-Windows.
2.3.1
Het UNIX besturingssysteem
UNIX is een zogenaamd multi-user besturingssysteem. Bij een multi user systeem moeten de systeem faciliteiten (centrale processor, geheugen, etc.) over de verschillende gebruikers worden verdeeld. De tegelijkertijd ingetypte commando’s van de verschillende gebruikers zullen dan schijnbaar simultaan worden verwerkt. Bij de verdeling van de CPU over de verschillende gebruikers (processen) vindt time sharing plaats, doordat de CPU achtereenvolgens voor een korte tijd (enkele miliseconden) aan verschillende processen wordt toegewezen. Een overzicht van actieve processen op een UNIX systeem kan worden geproduceerd met het commando top. Van het UNIX systeem bestaan verschillende versies. In de computerpracticumzaal is een PC-versie van UNIX, Linux aanwezig. Er is een tendens naar grotere eenheid in besturingssystemen (UNIX als standaard systeem?) o.a. ingegeven door de wens te komen tot grotere onafhankelijkheid van hardware leveranciers. Zo treft men nu versies van UNIX aan op uiteenlopende machines, van PC’s, via workstations (SUN, SGI, HP) tot supercomputers (CRAY). Het computer systeem bij Geofysica werkt ook met een versie van UNIX. Diverse onderdelen van het geofysica studieprogramma bevatten werkopdrachten op het computersysteem (vooral het eigen onderzoek), dus enige kennis van UNIX is zeer relevant. Een lijst met de belangrijkste UNIX-commando’s is als html- document op Internet te vinden: www.geof.ruu.nl/geophysics/html/cuip/infopage.html Een goede inleiding tot het UNIX systeem is te vinden in (Austen & Thomassen, 1985) 2 .
2.4
Het hierarchisch file systeem
2.4.1
Files en directories
Een samenhangende hoeveelheid gegevens wordt een file of data bestand genoemd, een eenheid, die meestal fysiek aanwezig is in het disk geheugen. Moderne besturingssystemen (waaronder 2 G.J.M.
Austen, H.J. Thomassen, ‘UNIX het standaard operating system’, Academic Service, 1985.
10
root
usr
bin
dev
geodyn
xplor
geof
prog2
prog201 prog202
opg1
opg1.f
data
prog2nn
opgnn
verslag
Figuur 2.2: Voorbeeld van de indeling van een deel van het file systeem op het UNIX systeem bij geofysica. De files van cursist 1 bevinden zich alle in de subdirectory prog2, die weer onderdeel vormt van de directory geodyn.
UNIX, MS-DOS en MS-Windows) zijn file georienteerd, d.w.z. de meeste systeem commando’s en functies hebben betrekking op files (data bestanden). Zowel UNIX als MS-DOS (vanaf versie 2.0) en de verschillende versies van MS-Windows bezitten een hierarchisch file systeem: de in het achtergrond geheugen aanwezige files zijn gerangschikt volgens een bepaalde struktuur, namelijk die van een omgekeerde boom (zie figuur 2.2). Het file systeem bevat: 1. files (databestanden), die willekeurige gegevens kunnen bevatten. 2. directories, welke weer een lijst van namen van (sub)directories en files kan bevatten. In figuur 2.2 wordt een gedeelte van de indeling van het file systeem op het (UNIX) computer systeem bij geofysica weergegeven. De bovenste laag in de figuur bestaat uit de root directory van het file systeem (de wortel van de omgekeerde boom struktuur). De volgende laag bestaat o.a. uit systeem directories /usr, /bin en /dev, waarin zich files en (sub)directories voor het besturingssysteem bevinden. Zo bevat /bin de programma code file (binary) van de meeste UNIX commando’s. De directory /geodyn bevat in deze indeling o.a. een subdirctory prog2, die weer onderverdeeld is in directories prog201, . . . ,prog2nn. Dit zijn de home directories van cursisten 1, . . . , nn. Home directories kunnen weer worden onderverdeeld in subdirectories, etcetera. Het grote voordeel van een hierarchisch file systeem is, dat hiermee een overzichtelijke gelaagde struktuur kan worden aangebracht in grote aantallen files in een systeem. Dit geldt niet alleen voor files, die het besturingssysteem zelf gebruikt - zoals op de directories /usr, /bin, /dev etc. Individuele gebruikers kunnen hun eigen priv´e file systeem opzetten, met aparte directories voor verschillende projecten, zoals de directories opg1, . . . ,opgnn in figuur 2.2.
2.4.2
Padnamen
Wanneer men in uit te voeren commando’s naar een file wil verwijzen - bijvoorbeeld om deze te laten kopi¨eren - dan moet de plaats van de file in de bovengenoemde boomstruktuur worden gespecificeerd. Dit kan op de volgende manieren: 1. Met een absolute padnaam: Hierbij wordt de positie t.o.v. de root directory - aangeduid door een slash / - aangegeven. Dit gebeurd door de namen van alle directories tussen de rootdirectory en de te specificeren 11
file, gescheiden door / tekens op te geven. De file met data voor opgave1 in het eerder genoemde voorbeeld wordt dan aangeduid met: /geodyn/prog2/prog201/opg1/data
2. Met een relatieve padnaam: Een vaker gebruikte kortere notatie neemt het padnaam vanuit de working directory ook wel current directory. Dit is de directory waarvandaan men op het moment opereert. Stel dat de working directory van cursist1 in bovenstaand voorbeeld op een zeker moment de directory prog201 is, dan wordt de relatieve pad naam van de eerder genoemde data file: opg1/data
OPDRACHT 2-1 Ga na dat beide padnamen afzonderlijk unieke aanduidingen zijn van een file of directory.
Het UNIX-commando cd nieuw verandert de working directory. Hierin is nieuw de pad naam van de nieuwe working directory. De behandelde organisatie wordt ook in het MS-DOS besturings systeem gebruikt. De notatie van de pad namen wijkt enigszins af, doordat de backslash \ in plaats van de slash / zoals in UNIX wordt gebruikt.
2.5
Uitvoering van een programma
Een computer programma bestaat uit een reeks door de computer uit te voeren instructies. Digitale computers beschikken over een (beperkte) instructie set, die kan worden aangewend om opdrachten mee uit te voeren. De verwerking van een programma in een ‘stored program computer’ (zie eigenschap 5 in §.2.1) gebeurt op een cyclische manier3 , zoals aangegeven in figuur 2.3. Hierbij wordt telkens een volgende machine instructie uit het intern geheugen opgehaald. Een pointer naar de geheugen locatie van de op te halen instructie (het instructie adres) is opgeslagen in de program counter - een van de registers van de centrale verwerkings eenheid. Met het mechanisme in figuur 2.3 kunnen nu ook onderbrekingen van de sequenti¨ele programma flow worden verkregen. Door de machine te voorzien van een sprong instructie, die de inhoud van de program counter kan veranderen, kan men de programma code op niet sequenti¨ele wijze doorlopen (zie eigenschap 4 in 2.1). De uitvoering van een programma verloopt nu meestal als volgt: 1. Het systeem krijgt opdracht een bepaald programma uit te voeren 2. Het systeem zoekt naar de locatie van de ‘file’ met het uitvoerbaar programma ( executable file) in het achtergrond geheugen (disk). 3. Deze executable file wordt vanaf de disk in het interne geheugen geladen. 4. De uitvoer van het programma wordt gestart, d.w.z. het adres van de eerste instructie van het programma (start adres) wordt in de program counter geladen en de programma executiecyclus, zoals in figuur 2.3 beschreven, wordt gestart. 3 Hier wordt alleen een z.g.n. sequenti¨ ele machine beschouwd (von Neumann machine) en niet naar paralelle machines, waarin meedere instructies simultaan (door meerdere processoren) worden uitgevoerd.
12
start
laad instructie p increment p
data nodig?
F
T
laad data
exec. instructie
T
p
stop
Figuur 2.3: Executie cyclus in een ‘stored program’ computer.
2.5.1
Programma executie
Type de filenaam van de file met het executeerbare programma, dus in het voorbeeld prog. Het besturingssysteem zoekt dan naar de executable file prog, eerst in de current directory, vervolgens in de directories, die in de environment variabele4 PATH voorkomen. De eerste file, die zo gevonden wordt, wordt nu ge¨executeerd.
2.5.2
Afbreken van een lopend programma
Dit doet men door in te typen ctrl c (ook wel genoteerd ^c), Dit is bijvoorbeeld nuttig bij een programma, dat in een ‘endless loop’ terecht komt of anderszins ‘op hol’ slaat.
4 zie
G.J.M. Austen, H.J. Thomassen, ‘UNIX het standaard operating system’, Academic Service, 1985.
13
14
Hoofdstuk 3
Het ontwikkelen van programmatuur 3.1
Beschikbare programmeertalen
Computer programmatuur kunnen we in een aantal niveau’s onderverdelen. Een onderverdeling in oplopende mate van abstractie is de volgende: 1. Het is mogelijk een computer te programmeren door een reeks van machine instructies in opeenvolgende adressen van het intern geheugen op te slaan en deze door de computer te laten uitvoeren. Deze werkwijze, programmeren in machine taal, werkt zeer moeizaam en is zeer gevoelig voor fouten. In de jaren veertig was dit de enig beschikbare methode. 2. Een verbetering ten opzichte van machine taal is het gebruik van een assembler taal m.b.v. een assembler programma. Hierin wordt o.a. een symbolische ‘makkelijk’ leesbare notatie voor de machine instructies gebruikt. 3. Een volgende verbetering is het gebruik van een hogere programmeertaal, (BASIC, C, Fortran, PASCAL), gecombineerd met een compiler (vertaalprogramma). Een statement in een hogere taal wordt door de compiler (voor die betreffende taal) omgezet in een reeks van assembler/machine code instructies. 4. Vierde generatie talen (4GL), (probleem georienteerd) met een syntax, die op de syntax van een natuurlijke taal lijkt. De 4GL statements worden door een pre-processor omgezet in een serie statements in een hogere programmeertaal, die als in 3 worden verwerkt. In deze cursus houden we ons uitsluitend bezig met het ontwerpen van programmatuur van niveau 3 geschreven in de programmeertaal Fortran 77 en Fortran 90.
3.2
Programma ontwikkelings cyclus
Het proces van probleem oplossing met behulp van een computer, door eigen programmatuur ontwikkeling verloopt cyclisch. Een en ander wordt schematisch weergegeven in figuur 3.1. Het is van groot belang de verschillende stappen in fig.3.1 gescheiden te houden.
3.2.1
Probleemanalyse
Een computer systeem kan worden gebruikt om een (daartoe geschikt) probleem mee op te lossen, door een beschikbare oplossing voor het probleem op te splitsen in deelproblemen, die in een beschikbare programmeertaal kunnen worden beschreven en deze door het systeem te laten uit voeren. Essentieel zijn de volgende criteria: 1. Er is een oplossing voor handen voor het probleem. 2. De oplossing laat zich in elementaire onderdelen in een programmeertaal uitdrukken. 15
start
1 - probleem analyse
2 - stapsgew. spec. oplossing + struct. diagram + documentatie
3 - codering in prog.taal
4 - invoer prog.tekst
5 - compileren / laden
syntax fouten
T
F 6 - executeren
result. OK?
F
T klaar
Figuur 3.1: Stroomschema voor het proces van probleem oplossing m.b.v. van een computer via eigen programmatuur ontwikkeling. Documentatie van de programmatuur behoort voornamelijk tijdens de specificatie fase plaats te vinden. Een veel gemaakte fout is het samenvoegen van stappen 1 tm 4. VOORBEELD Men wil het gravitatie effect van een intrusief lichaam V 0 bepalen. De intrusie is te modelleren door een verstoring ρ0 van de normale massa dichtheids verdeling ρ0 , met ρ = ρ0 + ρ0
,
ρ0 (x) = 0
,
x 6∈ V 0
(3.1)
De gezochte gravitatie stoorpotentiaal wordt gegeven door de Coulomb potentiaal van de massa verstoring, u0 (x) = γ
Z
V0
ρ0 (x0 ) dV 0 |x − x0 |
(3.2)
(γ de universele gravitatie constante)1 . Hiermee is het probleem wiskundig opgelost en is aan criterium 1 voldaan. De gevonden Coulomb potentiaal voldoet echter niet aan de bovengenoemde criterium 2, omdat ze niet zonder meer in een gangbare programmeer taal is uit te drukken. Het evalueren van integralen behoort namelijk niet tot het standaard repertoire van gangbare talen. We kunnen de Coulomb oplossing eerst nog in een andere vorm gieten, die wel aan de criterium (2) voldoet. Hiertoe gebruiken we een numerieke 1 W.M.
Telford, L.P. Geldart, R.E. Sheriff, D.A. Keys, Applied Geophysics. Cambridge university press,1976
16
methode, die een benaderende oplossing voor u0 (x) oplevert. De integraal in 3.2 is op te vatten als een Riemann som: u0 (x) = lim γ n→∞
n X ρ0 (x0j )∆Vj0 j=1
|x − x0j |
= lim Sn (x), n→∞
V 0 = lim
n→∞
n [
∆Vj0
(3.3)
j=1
Afbreken van deze somformule bij n = N levert dan de benaderende uitdrukking u0 (x) ≈ γ
N X ρ0 (x0j )∆Vj0 j=1
|x − x0j |
= SN (x)
(3.4)
Ga na, dat deze uitdrukking is te herleiden tot een vorm, die uitsluitend vermenigvuldigingen optellingen aftrekkingen en wortels van getallen bevat. Deze genoemde operaties behoren tot het repertoire van hogere programmeer talen, zodat aan het criterium (2) is voldaan. Ga na dat 3.4 geevalueerd voor een aantal discrete ‘observatie punten’ xi is te schrijven als een matrix vector vermenigvuldiging u0i = u0 (xi ) =
N X
Aij ρ0j
(3.5)
j=1
met de vector ρj = ρ(xj ). De uitdrukking 3.5 zou in een modelerings programma kunnen worden toegepast om efficient het zwaartekrachtseffect van veschillende massa perturbaties met gelijke geometrie te berekenen. Op die manier zou men via ‘trial and error’ een model (de vector ρj ) kunnen aanpassen aan beschikbare velddata.
3.2.2
Struktuurdiagrammen
Een struktuurdiagram is een schema, waarin de logische struktuur van een op te zetten programma duidelijk wordt weergegeven. Struktuurdiagrammen zijn een hulpmiddel waarmee te programmeren algoritmen op een overzichtelijke manier in een programmeertaal-onafhankelijke notatie worden beschreven. In hoofdstuk 9 wordt dit onderwerp verder behandeld.
3.2.3
Codering
De omzetting van een struktuur uit het struktuurdiagram in concrete Fortran code wordt in de verdere syllabus behandeld aan de hand van de Fortran syntax(a.h.w. de ’grammatica’ van een Fortran-’zin’) . Meer organisatorisch zijn de volgende coderingstaken: • het opstellen van een variabelen lijst (zie hoofdstuk 5) per procedure (zie hoofdstuk 8) • het opstellen van een lijst met variabelen, die van/naar een procedure moet worden doorgegeven (interface definities) • het per variabele bepalen van de wijze van doorgifte van/naar de procedures (via de parameterlijst (hoofdstuk 8) of via modulen en common blocks (hoofdstuk 12))
3.2.4
Invoering en compilatie van programma’s
De Fortran code moet worden opgeslagen in een file. De compiler is (bij syntactisch juiste codering) instaat deze Fortran code om te zetten in een executable file met machine code. Op het UNIX-systeem worden de compilers f77 en f90 gebruikt voor resp. Fortran 77 en Fortran 90 code. De f77-compiler verwacht Fortran 77 code in een file, die de file extensie .f heeft, bv. prog1.f . De f90-compiler verwacht gecombineerde (Fortran 77 + Fortran 90) code in een file, die de file extensie .f heeft, maar ’zuivere’ Fortran 90 code mag de extensie .f90 krijgen. Deze laatste optie moet gebruikt worden in situaties, waarin free format brontekst wordt toegepast (zie hoofdstuk 4). Compilatie van Fortran 77 code gebeurt met het volgende UNIX-commando: f77 [ -oexecutable] code waarin executable een filenaam is voor de executable file in machinetaal, en code een opsomming 17
is van de te compileren Fortran 77 files. Compilatie van Fortran 90 code gebeurt op dezelfde manier met het UNIX-commando f90. Appendix C bevat meer informatie omtrent compilatie methoden en technieken (o.a. het verbinden (linken) van libraries en het gebruiken van de make-utility.
3.2.5
Programma uitvoering en programmeerfouten
Uitvoering Het uitvoeren van een programma gebeurt, door simpelweg de naam van de executable als UNIX-commando in te typen.
3.2.6
Het voorkomen van programmeerfouten
Het gezegde ‘Voorkomen is beter dan genezen’ is zeker van toepassing op programmeerfouten. Uiteindelijk levert het toepassen van enkele eenvoudige programmeerconvecties bijna altijd tijdwinst op. Een aantal veel gebruikte conventies zijn: • Gebruik alleen expliciete declaratie van variabelen (implicit none , §5.1). • Print ingevoerde waarden (hoofdstuk 13) altijd op het scherm (’echo-check’) ter controle van de input. • Print tussentijdse resultaten, zolang het eindresultaat nog niet correct is. In de uiteindelijke versie kunnen de print statements van de tussenresultaten verwijderd worden. • Voeg aan ieder if statement (hoofdstuk 10) een else optie toe met een foutmelding en een stop statement om verkeerde variabele-waarden direct te detecteren (error-trap). Werkwijze Het opsporen van programmeerfouten kan een zeer tijdrovende bezigheid worden als niet systematisch te werk gegaan wordt. Bij de volgende aanpak wordt de tijd, nodig voor debugging (lett. het ontluizen), zoveel mogelijk beperkt: • Nadat de code is ingetikt, worden de eerste fouten (meestal typefouten) verwijderd met de syntax-checker ftnchek (zie verderop) en de compiler f77 of f90 (zie appendix C). • Als de compiler de code heeft ‘goedgekeurd’, kan het programma toch ‘vastlopen’, bijvoorbeeld met een van de volgende foutmeldingen: Segmentation Fault, of Bus Error. Met een debug programma of met de nodige print-statements op de juiste plaats kan uitgezocht worden waar het programma precies ‘vastloopt’. De fout is dan meestal snel gevonden. • De moeilijkst traceerbare fouten zitten vaak in een programma, dat niet vastloopt, maar wel foute resultaten geeft. Door het printen van relevante tussenresultaten kan dan uitgezocht worden, tot waar het programma nog wel de juiste resultaten produceert. Hiermee kan de oorzaak van de fout meestal worden gelocaliseerd. Syntax-checkers en debug software Om programmeerfouten op te sporen, zijn een aantal utility programma’s bruikbaar: ftnchek Een goed hulpmiddel bij het opsporen van programmeerfouten is het programma ftnchek. Dit programma controleert een Fortran tekst op eventuele syntax fouten (spellings- en grammatica fouten van de programmeertaal) en geeft foutmeldingen en waarschuwingen. Een beperking is, dat het programma slechts toepasbaar is op Fortran 77 code, maar voor Fortran 90 is een syntax-checker in de compiler ingebouwd (zie verder op in deze sectie). De aanroep van ftnchek is alsvolgt: ftnchek code 18
waarbij code een opsomming van Fortran-files is. Door Fortran programmatuur zodanig te coderen, dat ftnchek geen warnings of errors geeft, voorkomt men de nodige programmeerfouten of onbedoelde resultaten. Verdere uitleg over het gebruik van ftnchek en de interpretatie van de ’output’ wordt gegeven in de UNIX manual pages: man ftnchek Het gebruik van ftnchek wordt verplicht gesteld bij het programmeer prakticum. dbx en debugger Wanneer de compiler geen fouten meer geeft, maar het programma toch foute resultaten geeft, kan gebruik gemaakt worden van een debug-programma, of kortweg debugger. In plaats van de executable rechtstreeks vanuit de UNIX-prompt aan te roepen, wordt nu de debugger opgestart, die de aanroep en uitvoering van de executable regelt. Het programma kan onderbroken worden (door de debugger met een foutmelding, maar ook ’handmatig’ door de gebruiker) en tussen- en eindwaarden van variabelen kunnen opgevraagd worden. Wanneer de debugger het programma gestopt heeft, wordt een vaak iets uitgebreidere foutmelding gegeven. De debugger geeft ook aan in welke Fortran-regel, het programma vastgelopen is. Daarmee vormt het een handig interactief alternatief voor de toevoeging van print-statements aan de programma code. Om een Fortran executable ‘leesbaar’ te maken voor de debugger, moet tijdens de compilatie de vlag -g toegevoegd worden. Het Geofysica-computer systeem kent twee debuggers: dbx en z’n XWindows-versie debugger. De respectievelijke aanroepen zijn alsvolgt: dbx executable debugger executable & Meer informatie wordt gegeven in de UNIX-manual pages: man dbx man debugger f90 als syntax-checker De compiler voor Fortran 90 heeft een ingebouwde syntax checking optie. Door de ’vlag’ -Xlist toe te voegen aan de compileer-opdracht f90, wordt geen executable aangemaakt, maar een file met de extensie .lst, waarin (veel) informatie staat. Veel van deze informatie is slechts bruikbaar voor vergevorderde Fortran 90 programmeurs, maar onder de ‘sectie’ Considerata in deze file worden bruikbare en ‘leesbare’ waarschuwingen en aanwijzingen gegeven. Zie voor meer informatie de UNIX-manual pages: man f90
19
20
Hoofdstuk 4
Overzicht van Fortran 4.1
De programmeertaal Fortran
Fortran (FORmula TRANslator) is in de vijftiger jaren ontwikkeld, aanvankelijk bij IBM, als alternatief voor programmeren in assembler taal. Fortran is traditioneel de meest gebruikte programmeertaal voor technisch-wetenschappelijke toepassingen. De taal heeft sinds het ontstaan in de vijftiger jaren een sterke ontwikkeling doorgemaakt. In 1978, na een discussie vanaf begin zeventiger jaren, over de voordelen van gestruktureerd programmeren is de Fortran 77 standaard (ANSI X3.9 - 1978) aangenomen. De hierin gespecificeerde versie van de taal wordt aangeduid als Fortran 77. Sinds 1992 is een volgende gestandaardiseerde versie, Fortran 90 beschikbaar. Eind 1996 is de Fortran 95 1 standaard gepubliceerd. Deze bevat slechts geringe uitbreidingen op de standaard Fortran 90 De volgende gestandaardiseerde versie Fortran 2000 is gepland voor het jaar 2001. Fortran 77 is als subset aanwezig in de laatste versie van de programmeertaal. Hiertoe is besloten, opdat een geleidelijke overgang naar Fortran 90 mogelijk is en de grote investering in bestaande Fortran programmatuur beschermd wordt. In deze cursus zal het accent liggen op de behandeling van de taalversie Fortran 77, maar een aantal belangrijke uitbreidingen in de taalversie Fortran 90 zullen eveneens worden behandeld.
4.1.1
Een onderverdeling van de Fortran syntax
De taal Fortran kan worden onderverdeeld in de volgende elementen: Gegevens (data) : Gegevens komen voor als constanten of als variabelen. Gegevens zijn van een beperkt aantal verschillende data typen. Fortran 77 kent de volgende data typen - integer, real, character, logical, complex, double precission. Fortran 90 kent naast de genoemde (standaard) data typen (intrinsic types) de mogelijkheid voor de programmeur om nieuwe samengestelde datatypen, derived types te defini´eren, gebruik makend van standaard datatypen, in zogenaamde structures (zie ook hoofdstuk 5, 12) Statements (volzinnen) : Hierin wordt de volgende onderverdeling gemaakt: • non-executable statements (compiler sturingen zoals directives (bijvoorbeeld include, zie hoofdstuk 12) en declaratie statements) • executable statements (data operaties en programma-flow statements) Non-executable statements worden onderverdeeld in: 1. Specificatie statements (hoofdstuk 4) 2. Data initialisatie statements (hoofdstuk 8) 3. Statement functies (hoofdstuk 8) 4. Subroutine statements (hoofdstuk 8) 1 Een
overzicht van de ontwikkeling van de programmeertaal Fortran sinds de eerste versie wordt gegeven in M. Metcalf & J. Reid, 1996, FORTRAN 90/95 Explained, Oxford University Press. Een behandeling van Fortran 90 is ook te vinden in: W.S. Brainerd, Ch.H. Goldberg & J.C. Adams, 1990, Fortran 90, Academic Service.
21
5. Function statements (hoofdstuk 8) 6. Program statements (hoofdstuk 4) 7. Format statements (hoofdstuk 13) Executable statements worden onderverdeeld in: 1. Assignment statements: (hoofdstuk 6) • rekenkundige (arithmetic) (bijv. x = y + 1.0)
• character (bijv. string = ’tekst’ ) • logical (bijv. logvar = x .eq. y )
( Merk op, dat de variabele namen string en logvar zodanig zijn gekozen, dat ze informatie geven over de betekenis of de functie van de variabelen, zie verder hoofdstuk 5. ) 2. Control statements: (hoofdstuk 10) • block if
• logical if
• call, return
• go to ( Het gebruik van go to wordt afgeraden, behalve in het geval van een ‘do-while’construct , zie verder hoofdstuk 10. )
3. Input/output statements: (hoofdstuk 13) • read
• print, write • open, close
4.2
Programma struktuur
Een Fortran programma bestaat uit een of meerdere programma eenheden. Een eenheid bestaat uit een aantal statements en comment regels en kan van de volgende typen zijn: 1. Hoofd programma (Main program) (hoofdstuk 4, 8) 2. Subroutine (hoofdstuk 8) 3. Function (hoofdstuk 8) 4. Module (hoofdstuk 12) 5. Block data (hoofdstuk 12) De laatste regel van een Fortran eenheid is een end statement. Modulen zijn gedefinieerd in Fortran 90 en latere versies (zie hoofdstuk 12).
4.2.1
Hoofdprogramma
Een hoofdprogramma is een programma eenheid die niet met een function, subroutine of block data statement begint. Een Fortran programma bevat precies ´e´en hoofdprogramma. Program statement Een program statement kan als eerste statement van een hoofdprogramma optreden. De syntax is: syntax program naam Waarin naam de symbolische naam van het programma is. 22
4.2.2
Struktuur van een Fortran tekstregel
Er wordt onderscheid gemaakt tussen de vrije brontekstvorm (free source format) en de vaste vorm (fixed source format) . Het free source format is beschikbaar in Fortran 90 en latere versies en het wordt gebruikt in combinatie met de source file extensie .f90. Het fixed source format In het fixed source format wordt een Fortran regel onderverdeeld in 3 velden (zie figuur 4.1).
1
2
3
4
label veld
5
6
7
8
cont. veld
...........
71
72
statement veld
Figuur 4.1: Onderverdeling van een Fortran regel in drie velden: Label veld - kolom 1-5. Continuerings veld - kolom 6. Statement veld - kolom 7-72. Characters na kolom 72 in een regel worden door de compiler genegeerd. Het free source format De brontekst regels worden hierbij niet in aparte velden onderverdeeld. Tekstregels in dit format zijn maximaal 132 karakters lang. Karakters vanaf plaats 133 worden door de compiler genegeerd. Commentaar tekst In programma tekst kan commentaar tekst voorkomen ter verhoging van de leesbaarheid van de programma code. In Fortran 77 is een comment regel een regel, met in de eerste kolom een c of een *. In Fortran 90 wordt alle tekst in een programmaregel, volgend op een ‘!’, tot het eind van de regel, als commentaar beschouwd. Comment tekst wordt door de compiler genegeerd. Het verdient aanbeveling om programma teksten van commentaar te voorzien, waarmee beknopt de functie en werking van programma onderdelen kan worden verduidelijkt. Dit kan de leesbaarheid van een programma aanzienlijk vergroten. Afhankelijk van de programma tekst, geeft 25 tot 50 % commentaarregels een goede leesbaarheid. VOORBEELDEN • Hieronder is een voorbeeld gegeven van een (fixed source format) Fortran programma, dat de wortels van een tweede graadsvergelijking berekent en afdrukt: program abc1 implicit none real a,b,c,x1,x2 a = 1.0 b = 2.5 c = 1.5 x1 = (-b + sqrt(b**2 - 4.0*a*c))/(2.0 * a) x2 = (-b - sqrt(b**2 - 4.0*a*c))/(2.0 * a) print*,’x1=’, x1,’, x2=’,x2 end • De leesbaarheid van dit programma kan aanzienlijk worden verhoogd door commentaar toe te voegen:
c c c c c
program abc2 implicit none -----------------------------------------------------------| doel: Dit programma berekent de nulpunten van de parabool: | a*x**2 + b*x + c = 0 -----------------------------------------------------------* Variabelen declaratie:
23
real a, b, c, x1, x2 c * Code: a = 1.0 b = 2.5 c = 1.5 print*,’a, b en c zijn resp.:’,a, b, c c -------------------------------------------------------c | -b +/- WORTEL(b**2 - 4*a*c) c | De abc-formule luidt: x = --------------------------c | 2*a c -------------------------------------------------------x1 = (-b + sqrt(b**2 - 4.0*a*c))/(2.0 * a) x2 = (-b - sqrt(b**2 - 4.0*a*c))/(2.0 * a) print*,’De nulpunten van de parabool zijn: x1=’, x1,’, x2=’,x2 end
4.2.3
Initi¨ ele regels en continueringsregels in Fortran
Een initi¨ele regels is een regel, die geen comment of blanke regel dan wel continueringsregel is. Met continueringsregels kunnen statements worden gebruikt, die meerdere (max. 20) regels tekst beslaan. Een continueringsregel wordt in het fixed source format gekenmerkt door een willekeurig, van een spatie of een nul verschillend character in de zesde kolom. In het free source format geldt een maximum aantal van 40 continueringsregels. Een regel wordt gecontinueerd met een ampersand teken ‘&’ aan het eind van de te vervolgen regel. VOORBEELDEN • Een voorbeeld van een continueringsregel in het fixed source format zijn de volgende regels, waarin een spatie met een -teken is weergegeven: x1 = teller_van_de_abc_formule_breuk 1 / noemer_van_de_abc_formule_breuk • Een voorbeeld van een continueringsregel in het free source format is: x1 = teller_van_de_abc_formule_breuk & / noemer_van_de_abc_formule_breuk
4.2.4
Volgorde van statements
In iedere programma eenheid moeten de non-executable statements (declaraties, etc.) vooraf gaan aan de executable statements. VOORBEELD De struktuur van een Fortran programma wordt aan de hand van dit voorbeeld weergegeven: program struktuur_vb c * Declaratie blok: de non-executables: implicit none integer iteller character*7 cnaam c * Executable block: de uitvoerende statements: iteller = 66 iteller = iteller + 11 cnaam = ’Fortran’ c * Deze commentaarregel doet helemaal niets.
24
+
print*,’Dit is een ’,cnaam, iteller,’ programma.’, ’ Dit is een continueringsregel.’ end
Data statements treden op als laatste non-executable statements. Met het data-statement kan men voorafgaand aan de uitvoering van het programma, waarden (door het linker programma) toe laten kennen aan variabelen, die eventueel tijdens de uitvoering van het programma worden veranderd. Dit kan nuttig zijn bij de initialisatie van variabelen. Het save-statement zorgt ervoor, dat bij het verlaten van een procedure, de waarde van de variabele onthouden wordt, zodat bij volgende aanroepen van dezelfde procedure, die waarde nog bekend is. De syntax van het data- en het save-statement zijn: syntax data var.namen / var.waarden/ save var.namen waarbij var.namen een lijst variabelen is, gescheiden door komma’s en var.waarden een lijst met de respectievelijke waarden, die aan de variabelen moeten worden toegekend, eveneens gescheiden door komma’s. VOORBEELD Het hieronder gegeven programma data_en_save_vb geeft een voorbeeld van het gebruik van het data- en save- statement. program data_en_save_vb c -------------------------------------------------------------------c | Voorbeeld van een toepassing van de data en save opdracht c -------------------------------------------------------------------c -> De subroutine subr wordt 3x aangeroepen: call subr call subr call subr end c====================================================================== subroutine subr implicit none integer iteller save iteller data iteller /1/ c -> iteller heeft de waarde 1 alleen bij de 1e aanroep. c > Bij volgende aanroepen is de waarde intussen opgehoogd. c > De waarde van iteller wordt onthouden. c -> Print de waarde van iteller en verhoog deze waarde met 1: print*,’ Dit is de ’,iteller,’-e aanroep van subr.’ iteller = iteller + 1 return end
25
26
Hoofdstuk 5
Data typen in Fortran In Fortran kunnen variabelen en constanten worden gebruikt van een beperkt aantal data typen. Er zijn de volgende numerieke data typen: integer Deze komen overeen met positieve of negatieve gehele getallen. real Deze komen overeen met re¨ele (eigenlijk rationale) getallen. double precision Het zelfde als real getallen maar met een dubbele lengte. complex Dit type correspondeert met de complexe getallen (eigenlijk met de verzameling van getallen met rationaal imaginair en reel deel). double complex Dit data type behoort niet tot de standaard maar wordt door sommige compilers als extensie herkend. Vanaf de taal versie 77 kent Fortran de volgende niet numerieke data typen: character Hiermee kan tekst in de vorm van ‘text strings’ worden weergegeven. logical Grootheden van dit type kunnen de waarden .true. en .false. aannemen. Zij worden bijvoorbeeld gebruikt als programma vlaggen. In Fortran 90 zijn bovenstaande data typen beschikbaar als zogenaamde intrinsic data typen Daarnaast biedt Fortran 90 de programmeur de mogelijkheid zogenaamde derived types te defini¨eren, gebruik makend van de ‘intrinsic types’. Hiermee kunnen elementen van verschillende data typen worden samengevoegd tot een ‘structure’, waarmee complexe data structuren op een eenvoudige wijze kunnen worden gemanipuleerd. Derived types zullen worden behandeld in hoofdstuk 12.
5.1
Type declararaties van intrinsieke datatypen
In Fortran is het niet altijd vereist om variabelen m.b.v. een type declaratie expliciet te declareren. Het is echter ten zeerste aan te bevelen om elke variabele altijd expliciet te declareren. Expliciete type declaraties van variabelen zijn van de vorm syntax datatype varlist met varlist een lijst van namen van variabelen gescheiden door komma’s en datatype een van de volgende type identifiers: integer, real, double precision, complex, double complex, character[*len] of logical. Hierin is len een integer groter dan nul (het aantal karakters in de character variabele). Een potentieel gevaarlijke en daarom af te raden voorziening in Fortran is de impliciete type declaratie. Met een implicit type declaratie kunnen data typen aan variabelen worden toegekend aan de hand van de eerste letter van de variabele naam. Een implicit-statement is van de gedaante: syntax implicit datatype ( a1 [, . . . , aN ] ) 27
met datatype gedefinieerd als boven en ai een van de letters a-z of een opeenvolgende rij van letters aangegeven door de eerste en laatste gescheiden door een min teken als in a-h, i-n, o-z. In Fortran geldt de volgende standaard Fortran type conventie, voor integer en real variabelen: implicit integer (i-n) implicit real (a-h, o-z) Dit heeft als consequentie, dat niet gedeclareerde variabelen als integer of als real worden geinterpreteerd afhankelijk van de eerste letter van de naam van de variabele. Onbedoeld kan dit aanleiding geven tot fouten, bijvoorbeeld vanwege de intern toegepaste type conversie bij de evaluatie van expressies in variabelen van verschillend type (zie §.6). VOORBEELD Dat het niet expliciet declareren van alle variabelen tot onbedoelde resultaten kan leiden, toont het volgende programma: program declaratie1 c * Dit programma berekent de waarde van de breuk: teller/noemer teller = 5.0 noemer = 10.0 k = teller / noemer print*,’teller/noemer = ’,k end Het programma levert de volgende output: teller/noemer =
0
Omdat k een (impliciet gedeclareerde) integer is, wordt het resultaat van de evaluatie van de expressie k ook naar een integer getrunceerd (zie §.6.1.1). Deze fout had voorkomen kunnen worden door expliciete variabelen declaratie: program declaratie2 implicit none c * Dit programma berekent de waarde van de breuk: teller/noemer c * Declaraties: real teller, noemer, k c * Code: teller = 5.0 noemer = 10.0 k = teller / noemer print*,’teller/noemer = ’,k end
Met implicit none wordt de impliciete type toekenning in een programma eenheid uitgeschakeld en moeten alle variabelen expliciet gedeclareerd worden (het gebruik van implicit none is verplicht bij het programmeerpractikum). Het niet expliciet declareren van een variabele resulteert bij het compileren van de programma tekst met implicit none in een syntax foutmelding. Door alle variabelen op dezelfde wijze expliciet te declareren, voorkomt men dus, dat een variabele impliciet met een verkeerd gegevenstype wordt gedeclareerd. Ook voorkomt men, dat onbedoeld ongedeclareerde en dus ongedefini¨eerde variabelen worden gebruikt: als bijvoorbeeld nummer de expliciet gedeclareerde variabele is en men gebruikt ‘per ongeluk’ de ongedeclareerde variabele naam mummer, dan resulteert dat in een foutmelding door de compiler, wat bij impliciete declaratie niet gebeurd zou zijn. Ongedefini¨eerde numerieke variabelen krijgen meestal (niet altijd, systeemafhankelijk!) de waarde nul. Dit kan aanleiding geven tot lastig op te sporen fouten in computerprogramma’s. In bovenstaande voorbeelden worden slechts enkelvoudige variabelen gedeclareerd. Fortran heeft daarnaast de mogelijkheid voor meerdimensionale variabelen, array’s genaamd, welke in Hoofdstuk 11 besproken worden. Op deze arrays is dezelfde set van data typen van toepassing. 28
Hoofdstuk 6
Expressies in Fortran Een expressie is een geordende verzameling van operanden (variabelen, constanten, expressies) verbonden door operatoren. Een eenvoudige expressie bestaat uit een enkele operand in de vorm van een variabele of constante. Er zijn in Fortran 77 vier typen expressies: rekenkundige, character, relationele en logische expressies.
6.1
Rekenkundige expressies
Met een rekenkundige expressie wordt een numerieke berekening beschreven. Evaluatie (berekening) van een rekenkundige expressie resulteert in een numerieke uitkomst. Aangezien Fortran vooral wordt gebruikt in numerieke toepassingen is dit het meest voorkomende type. De in deze expressies voorkomende rekenkundige operatoren zijn (in volgorde van prioriteit): operator ∗∗ ∗ / + −
bewerking machtsverheffen vermenigvuldigen delen optellen aftrekken
Enkele syntactische regels, waaraan een rekenkundige expressie moet voldoen zijn: 1. Operators mogen niet direct aansluiten. Bijvoorbeeld x * -y is fout en x * (-y) is syntactisch correct. 2. Haakjes ( en ) mogen worden gebruikt om deelexpressies te vormen. Deze deelexpressies mogen worden genest als in een algebraische formule, bijvoorbeeld (((( a5*x + a4)*x + a3)*x + a2)*x + a1)*x + a0 Hiermee kan de volgorde van de evaluatie van de expressie worden beinvloed. Bovendien is het mogelijk door het gebruik van haakjes de leesbaarheid van een gecompliceerde expressie te verhogen. Expressies worden ge¨evalueerd door achtereenvolgens alle operaties in de expressie uit te voeren. Dit gebeurt volgens een hierarchisch bepaalde volgorde. De bijbehorende prioriteit is: 1. Machtsverheffen. Bij herhaalde machtsverheffing vindt evaluatie van rechts naar links plaats a ** b ** c
c
wordt dus berekend als ab en niet als abc
2. Vermenigvuldigen en delen hebben gelijke prioriteit. De evaluatie volgorde is van links naar rechts. a / b * c
resulteert bijvoorbeeld in (a/b) × c
3. Optellen en aftrekken hebben gelijke prioriteit. 29
In het algemeen vindt bij gelijke prioriteit van opeenvolgende operaties evaluatie van links naar rechts plaats, met uitzondering van herhaalde machtsverheffing. Door haakjes te gebruiken kan van de hierarchisch bepaalde volgorde worden afgeweken. Bijvoorbeeld wordt geevalueerd als abc
(a ** b) ** c en
als a/(b × c)
a / (b * c)
6.1.1
Type conversie in rekenkundige expressies
In rekenkundige expressies met variabelen van verschillend data type worden type conversies uitgevoerd. Belangrijk is vooral de conversie tussen integer en real variabelen. bij de conversie van real naar integer wordt de waarde getrunceerd (en niet afgerond): Voor een real variabele x en gehele getallen n1 en n2 geldt: n1 ≤ x ≤ n 2 xtruncated =
,
n1 ∈ Z
n2 ∈ Z
,
,
n2 − n1 = 1
n1 , x ≥ 0 n2 , x < 0
Truncatie van een getal is afronding naar het naburige gehele getal, dat het dichtst bij nul ligt. OPDRACHT 6-1 Ga na dat een correcte afronding - d.w.z. naar het dichtstbijgelegen gehele getal - gedefinieerd door: x≥0 x<0
, xrounded =
, xrounded =
n2 , n1 ,
x ≥ n1 + 0.5 x < n1 + 0.5
n2 , n1 ,
x > n1 + 0.5 x ≤ n1 + 0.5
wordt verkregen met het ‘algoritme’ i_x_rounded =
x + 0.5, x − 0.5,
x≥0 x<0
Overigens is er in Fortran een intrinsic function nint (Nearest INTeger, (zie §.8.2.1), die (echte) afronding uitvoert. De aanroep is i_x_rounded = nint(x)
6.2
Character expressies
Character expressies worden gebruikt om character strings samen te stellen. Onder een character string verstaat men een geordende verzameling characters. De enige character operator in Fortran is de concatenatie (samenvoegings) operator, aangeduid met een ’dubbele slash’ //. Hiermee voegt men twee character strings samen. VOORBEELD program concat1 implicit none character*4 a, b character*8 c a = ’alfa’ b = ’beta’ c = a//b print *, a,b,c end resulteert in de output
30
alfa
beta
alfabeta
Merk op, dat de lengte van het resultaat (in characters) gelijk is aan de som van de gedeclareerde lengte van de operanden. De gedeclareerde lengte van een character variabele mag groter zijn dan het aantal characters in de definitie van de variabele. In dit geval wordt de gedefini¨eerde waarde tot de gedeclareerde waarde aangevuld met spaties. Als een tekststring langer is dan de gedeclareerde lengte van de betreffende character variabele, dan wordt het getrunceerd tot de gedeclareerde lengte. De gedeclareerde lengte van een character variabele wordt bepaald met de intrinsieke functie len (zie §.8.2.1). Er is geen intrinsieke functie voor het bepalen van de non-blank lengte (de lengte zonder de spatie-aanvulling) van character variabelen. In §10.1 wordt een eenvoudig algoritme beschreven, waarmee de non-blank lengte is te bepalen. VRAAG 6-1 Wat verandert er in bovenstaand voorbeeld, als we defini¨eren: a = ’alpha’ i.p.v. a = ’alfa’ ?
Een mechanisme, waarmee o.a. het tegengestelde van concatenatie is te bereiken is het zogenaamde substring mechanisme. Hiermee kan men de waarde van een substring van een character variabele bepalen of toekennen. De algemene vorm van de substring expressie is a([ b]:[ e]) met b en e de posities van het eerste respectievelijk het laatste character in de substring. De rechte haken geven aan, dat b en e optioneel zijn. Bij het weglaten van b of e worden ‘default’ respectievelijk het begin of eind van de hele gedeclareerde character variabele genomen. VOORBEELDEN • Een voorbeeld van het gebruik van substrings is het volgende programma: program fietsen implicit none character*20 a, b, c a = ’fietsenstalling’ b = a(:5) c = a(8:11) print*, a,b,c end met de output fietsenstalling
fiets
stal
Merk op dat hier de volledige gedeclareerde lengte van de string, inclusief ‘padding’ met spaties afgedrukt wordt. • Een voorbeeld van substring assignment is: program concat2 implicit none character*(20) name name(1:4) = ’file’ name(5:8) = ’.001’ print *, name end met de output file.001
31
In dit voorbeeld wordt de character variabele gedefinieerd met behulp van character constanten. In §.13.5 wordt een methode beschreven om character strings te defini¨eren met behulp van de inhoud van (bijvoorbeeld numerieke) variabelen.
6.3
Relationele expressies
Relationele expressies bestaan uit twee rekenkundige expressies of twee character expressies, gescheiden door een relationele operator. De waarde van een relationele expressie is van het type logical. De volgende relationele operatoren zijn beschikbaar: Fortran77 operator .gt. .ge. .lt. .le. .eq. .ne.
Fortran90 operator > >= < <= == /=
wiskundig symbool > ≥ < ≤ = 6=
betekenis groter dan groter of gelijk aan kleiner dan kleiner of gelijk aan gelijk aan ongelijk aan
Bij numerieke operanden (rekenkundige expressies) worden de numerieke waarden van de operanden direct vergeleken. VOORBEELD Een voorbeeld programma met een numerieke relationele operatie is: program groterkleiner implicit none real x print*, ’ Geef een getal’ read*, x if ( x .lt. 0.0) then print*,’ Getal = ’, x, ’ is kleiner dan nul’ else if ( x .gt. 0.0) then print*,’ Getal = ’, x, ’ is groter dan nul’ else print*,’ Getal is precies nul’ endif end
Bij alfanumerieke operanden (character expressies) worden de operanden lexicografisch vergeleken, waarbij de ASCII ‘collating sequence’ als referentie alfabet dient (zie appendix B) (een character string bevat i.h.a. ook andere symbolen dan de 26 letters van het alfabet). VOORBEELD Een voorbeeld programma met een alfanumerieke relationele operatie is: program hoofdklein implicit none character*1 cnaam
c c
print*,’Geef een karakter’ read(5,’(a)’) cnaam * Er wordt gekeken, of karakter een hoofdletter of kleine letter is: if ( cnaam .ge. ’A’ .and. cnaam .le. ’Z’) then print*,’karakter ’, cnaam, ’ is een hoofdletter.’ else if ( cnaam .ge. ’a’ .and. cnaam .le. ’z’) then
32
print*,’karakter ’, cnaam, ’ is een kleine letter.’ else print*,’karakter ’, cnaam, ’ is geen letter.’ endif end
6.4
Logische expressies
Evaluatie van een logische expressie levert de logische waarde .true. of .false. op. De operanden in een logische expressie zijn relationele expressies of logische expressies. De volgende logische operatoren zijn beschikbaar: Operator .not. .and. .or.
betekenis negatie logische produkt logische som
De operatoren werken volgens een hierarchisch bepaalde volgorde overeenkomend met de volgorde van de bovenstaande tabel. Door het gebruik van haakjes kan hiervan worden afgeweken. De meest voorkomende toepassing van logische expressies is in het formuleren van test condities in een programma. Hiermee kan de normale (sequenti¨ele) programma flow worden onderbroken aan de hand van de uitkomst van een test De syntax is alsvolgt: syntax if ( logical expression ) then statement block endif In deze ‘control structure’ (zie H.10) worden de statements tussen if en end if alleen dan uitgevoerd als logical expression de waarde .true. aanneemt.
VOORBEELD program logical implicit none logical xcheck real x, y xcheck = .true. read*, x, y if (xcheck) then if ( x.gt.y ) then print *, ’x is greater than y’ else print *, ’x is not greater than y’ endif endif end
33
OPDRACHTEN 6-2 a) Schrijf een programma, dat een woord van maximaal 5 letters inleest, en deze letter voor letter onder elkaar afdrukt, bv.: input: Hallo output: H a l l o 6-2 b) Maak een programma, dat een woord van max. 3 letters inleest en de letters gerangschikt op ASCII alfabetische volgorde op het scherm afdrukt, bv.: input: het output: eht 6-2 c) Schrijf een programma, dat 3 re¨ele getallen inleest en het gemiddelde berekent en op het scherm afdrukt.
34
Hoofdstuk 7
Voorbeelden van eenvoudige Fortran programma’s We beginnen met een programma met slechts ´e´en executable statement. Dit programma kan bijvoorbeeld worden gebruikt om de benodigde procedures voor het invoeren, compileren, laden en excuteren van programma’s uit te proberen. program printing print *, ’ik doe dus ik ben’ end Een iets uitgebreider voorbeeld met rekenkundige expressies: program printnumbers implicit none real x, y, z print *, ’type een getal’ read *, x y = x ** 0.5 z = x ** 2 print *, x,y,z end In de bovenstaande programma’s treffen we de volgende taalelementen van Fortran aan (zie het overzicht in H.4): 1. De program en end statements zijn voor sommige compilers optioneel. Het gebruik ervan verhoogt echter de leesbaarheid van programma teksten en is daarom aan te bevelen. 2. implicit none en expliciete type declaraties (zie §.5.1) 3. Input/output (I/O) statements (read, resp. print). Een uitgebreider bespreking van I/O statements is te vinden in hoofdstuk H.13. 4. Assignment (toekennings) statements met rekenkundige expressies. y = x ** 0.5
en z = x ** 2
5. Constanten van het type character (text strings). ik doe dus ik ben
35
en type een getal 6. Quotes. De enkele aanhalingstekens dienen om begin en eind van de text string aan te geven, ze behoren dus zelf niet tot de string. Met deze notatie kan men spaties aan het begin of eind van een string opnemen. Spaties worden namelijk in Fortran genegeerd. Om een quote in een string aan te geven moet deze eenmaal worden herhaald in de string. De tekst string ’programma’s’ wordt alsvolgt als character constante genoteerd: ’programma’’s’. 7. Constanten van het type real (0.5) en integer (2). 8. Variabelen x, y, z van het type real Bevat een programma naast eventuele character constanten ook nog character variabelen, dan moeten die altijd worden gedeclareerd. Willen we bijvoorbeeld, dat een programma een tekst krijgt ingevoerd via het toetsenbord, dan kan dit met program read_string implicit none character*10 string print *, ’type max. 10 characters’ read *, string print *, ’Ingevoerde tekst: ’,string end
Bij dit voorbeeld moet de in te typen tekst door aanhalingstekens worden omgeven. Dus bijvoorbeeld de input ’dit is een te lang input voorbeeld’ geeft als output Ingevoerde tekst: dit is een Bij de declaratie van character variabelen moet het aantal characters in de string worden gedeclareerd. Er wordt dan bij de installatie van het programma voldoende geheugen ruimte voor de string gereserveerd.
VOORBEELD Het volgend voorbeeld illustreert het effect van interne type conversie bij de evaluatie van rekenkundige expressies: program typconv implicit none real x x = 1/4 + 0.25 write(6,’(’’x = 1/4 + .25 , x -> ’’,f5.2)’) x x = 1/4 * 4.0 write(6,’(’’x = 1/4 * 4.0 , x -> ’’,f5.2)’) x x = 1./4 * 4.0 write(6,’(’’x = 1./4 * 4.0 , x -> ’’,f5.2)’) x x = float(1/4) * 4.0 write(6,’(’’x = float(1/4) * 4.0 , x -> ’’,f5.2)’) x x = float(1)/4 * 4.0 write(6,’(’’x = float(1)/4 * 4.0 , x -> ’’,f5.2)’) x end
36
De hier gebruikte I/O statements met output format specificaties worden uitvoeriger behandeld in H.13. Bovenstaande voorbeeld genereert de volgende output: x x x x x
= = = = =
1/4 + .25 , x -> 0.25 1/4 * 4.0 , x -> 0.00 1./4 * 4.0 , x -> 1.00 float(1/4) * 4.0 , x -> float(1)/4 * 4.0 , x ->
0.00 1.00
Dit voorbeeld illustreert hoe een expressie wordt opgedeeld in sub-expressies, die afzonderlijk worden ge¨evalueerd. De uitkomst van een sub-expressie met integer operanden wordt naar een integer geconverteerd. Indien dit onbedoeld gebeurt kan dit tot fouten aanleiding geven, die moeilijk zijn te localiseren. OPDRACHTEN 7-1 a) Zoek vier fouten op in onderstaand programma: program opp_cirkel implicit none c c c c
* * Dit programma berekent de oppervlakte van een cirkel met straal r * * Declaraties: real r, opp
c * Code: pi = 3.14159 c
* input print*,’ Geef straal cirkel in m.’ read*, r if ( r .lt. 0.0) then print*,’Negatieve straal niet toegestaan’ stop
c
* berekening opp = pi r**2
c
* output print*,’ Berekende oppervlakte van de cirkel met straal r = ’, r,’ is ’, opp, ’ vierkante meter end
7-1 b) Schrijf een programma, dat een zin inleest en op het scherm afdrukt, of de zin wel of niet met een hoofdletter begint. 7-1 c) Schrijf een programma, dat een (geologische) ouderdom in miljoenen jaren inleest en de bijbehorende periode naam ( Quartair, Tertiair, Krijt, Jura, Trias, Perm, Carboon, Devoon, Siluur, Ordovicium, Cambrium of Precambrium) op het scherm afdrukt.
37
38
Hoofdstuk 8
Procedures (modulair programmeren) 8.1
Opsplitsing van een programma in procedures
In een programma kan de normale sequenti¨ele programma flow (zie §.2.5) worden onderbroken om naar een ‘apart blok programma code’, een procedure, te springen (de procedure wordt zogezegd aangeroepen, dit blok uit te voeren en vervolgens het programma te hervatten op de positie van voor de sprong, zoals ge¨ıllustreerd in onderstaande figuur. In Fortran programma’s kunnen deze procedures voorkomen in de vorm van functies en subroutines. Vaak wordt bij de bespreking van programmeertalen in leerboeken dit onderwerp vrij laat behandeld, nadat alle meer elementaire bouwstenen van programma’s zijn behandeld. In deze cursus wordt het gebruik van routines en functies vrijwel vanaf het begin behandeld en gepropageerd. Het vroegtijdig gebruiken van deze hulpmiddelen bevordert de ontwikkeling van een modulaire programmeerstijl en zal dus eerder leiden tot toepassing van principes van gestructureerd programmeren. Bij de praktikumopgaven wordt vanaf het begin van routines en functies gebruik gemaakt. Een voor de hand liggende reden voor een dergelijke programma organisatie is besparing van programma code in een programma, waarin meerdere malen op verschillende plaatsen vergelijkbare (misschien niet identieke) processen worden uitgevoerd (zie figuur 8.1). Vier andere belangrijke argumenten voor het opbreken van programma’s in procedures zijn: Structuur verbetering van het programma: Het is zeer aan te bevelen op zichzelf staande onderdelen in een programma in verschillende onafhankelijke procedures onder te brengen en in het programma de verschillende procedures aan te roepen. De overzichtelijkheid van het programma kan hiermee worden verbeterd. Brede toepasbaarheid van (algemeen opgezette) programma procedures:
A
A
X B B X
X
C
C
Figuur 8.1: Programma flow a) sequentieel b) opgebroken in procedures, die herhaald kunnen worden aangeroepen.
39
Verzamelingen van procedures kunnen worden opgenomen in een algemeen bruikbare programma bibliotheek, van gecompileerde procedures. Deze procedures hoeven niet door de gebruiker te worden gecompileerd. de verbindingen tussen de verschillende procedures van een programma (calls en returns) worden tijdens de programma ‘linking’ fase van programma installatie door de linker/loader (zie §.3.1) gelegd. Dit betekent dat voor veel problemen kant en klare programmatuur beschikbaar is die niet telkens op nieuw hoeft te worden ontwikkeld. Top down design strategy: Sinds het begin van de jaren zeventig wordt deze methode van programma-ontwerp gepropageerd1. Bij deze methode wordt een programma eerst in grote lijnen gespecificeerd, opgesplitst in procedures, zodanig dat iedere procedure een afgeronde deeltaak van het programma uitvoerd. De afzonderlijke procedures worden dan zo mogelijk onafhankelijk van elkaar verder onderverdeeld, uitgewerkt, ge¨ımplementeerd en getest. Het programma wordt dus ontworpen als een verzameling procedures, die elk een specifieke functie vervullen en niet als een verzameling regels programma code. Deze ontwerp strategie wordt beschouwd als belangrijk element van ‘gestructureerd programmeren’. Economische overwegingen: De vereiste tijd T nodig voor de ontwikkeling van een programma blijkt sneller dan lineair met de lengte L van het programma toe te nemen. Een conservatieve schatting is een kwadratisch verband, stel T = a · L2 . Splits het programma op in N procedures, die afzonderlijk worden getest. Dan is TN
L =N ·a· N
2
=a·
L2 N
Dit suggereert, dat het aantal procedures zo groot mogelijk gekozen moet worden. In de praktijk zal echter de complexiteit van het totale programma op het ‘boven procedure’ niveau hierdoor toenemen waardoor de ontwikkeltijd zal toenemen. Aangenomen kan worden dat er een optimum situatie zal optreden voor zekere waarde van L. De volgende vuistregels voor een optimale procedure grootte worden wel gepropageerd: • ± 50 regels code per procedure
• 1 pagina printer text per procedure (inclusief begeleidende commentaarregels)
• 1 ‘taak’ per procedure
8.2
Procedures in Fortran
In Fortran worden twee typen van procedures gebruikt, de function en de subroutine. Beide vormen worden in het volgende behandeld. Deze procedures kunnen onafhankelijk van elkaar worden gecompileerd met de Fortran compiler, wat het afzonderlijk ontwikkelen en testen aanzienlijk vereenvoudigd. Fortran procedures (uitgezonderd statement functions - zie onder) zijn binnen een programma globaal gedefinieerd, d.w.z. ze kunnen vanuit iedere programma procedure worden aangeroepen. Roept een procedure zichzelf aan, wat in Fortran 77 officieel niet is toegestaan, dan spreekt men van een recursieve procedure. Veel Fortran 77 compilers ondersteunen toch een recursieve procedure, en in Fortran 90 behoren recursieve procedure aanroepen tot de standaard.
8.2.1
Functies
De eerste procedure vorm is de Fortran function. Een function is een procedure, die tevens dienst doet als variabele, waaraan een waarde toegekend kan worden. Deze waarde is de uitkomst van de berekening, die in de function wordt uitgevoerd. functions moeten daarom, net als ’gewone’ variabelen gedeclareerd worden, zowel in de aanroepende routine als in de functionprocedure zelf. Functions worden aangeroepen door de functie (gevolgd door een lijst van parameters) als operand in een expressie op te nemen zoals in onderstaand voorbeeld van het gebruik van een functie voor de berekening van een sinus 1 zie
bijvoorbeeld het speciale nummer van ‘ACM computing survey’s’ december 1974, over dit onderwerp
40
coskwd = 1.0 - sin(x)**2 In bovenstaande aanroep wordt x de actuele parameter van de functie sin genoemd. Functions zijn te onderscheiden in intrinsic en external functions. Intrinsic functions Intrinsieke functions (of kortweg intrinsics zijn opgenomen in de Fortran bibliotheek van standaard procedures. De set van intrinsieke functies verschilt per Fortran implementatie in zoverre dat alle leveranciers verschillende uitbreidingen op de standaard set hanteren. Tot de standaard behoren functies als sqrt(x), sin(x), atan(x) voor resp. de vierkantswortel, de sinus en de arctangens funties. Voor een volledig overzicht moet men het betreffende Fortran manual raadplegen. Een lijst van de belangrijkste intrinsics voor Fortran 77 en Fortran 90 is te vinden in de tabellen in appendix D. In Fortran 77 zijn de ‘intrinsics’ zogenaamde ‘generic functions’, d.w.z. dat het datatype van de functie uitsluitend wordt bepaald door het datatype van de gebruikte functieargumenten en niet door de functie naam. Bijvoorbeeld worden door: i = z =
max(k,l) max(x,y)
achtereenvolgens het maximum van twee integers en twee reals bepaald met dezelfde (generieke) functie. VRAAG 8-1 Welke output geeft het volgende programma? program maxima implicit none integer n real x, y x = 1.5 y = 2.5 n = max(x,y) print*,’ max. van x en y is ’, n end
External functions Alle overige functions zijn external en niet aanwezig in de lijst vasn intrinsieke functies, die in de linking fase van de programma installatie automatisch wordt geraadpleegd. Hiervan moet de code bij de programma installatie worden meegeleverd (eventueel door gebruik van een library). De Fortran tekst van een function onderscheidt zich van een hoofdprogramma (main program) door de volgende kenmerken: • De eerste statement is altijd een function statement (zie onder). Een hoofdprogramma begint (bij sommige compilers optioneel) met een program statement. • Een function kan niet op zichzelf staand worden gebruikt, maar wordt ‘aangeroepen’ door de functie als operand in een expressie te gebruiken in een andere routine of in een hoofdprogramma. • Een function wordt normaliter be¨eindigd met een return statement gevolgd door een end statement en niet met een stop statement. Een external function procedure begint met een function statement: syntax [type] function funaam [(parlist)]
Met het invullen van type, fungeert het function statement tevens als declaratie van de geassocieerde variabele funaam, waarmee het functie resultaat wordt aangeduid. De impliciete data type conventie (§.5.1) geldt ook voor functions. funaam is een unieke, geldige symbolische 41
naam, waaronder de function binnen het programma bekend is. De argumentenlijst parlist is een optionele lijst tussen ronde haken van z.g.n. formele parameters (ook wel dummy parameters), gescheiden door komma’s. Als formele parameters kunnen in de function statement voorkomen: • Namen van variabelen (waaronder arrays, zie hoofdstuk 11) • Procedure namen (zie §.8.2.3) • Niet toegestaan zijn expressies (bijv. constanten) of array elementen. Deze mogen wel in de functie aanroep gebruikt worden (als actuele parameters). Om dezelfde reden als in het hoofdprogramma wordt ook in de external functions het gebruik van implicit none en de daarbij behorende expliciete variabelen declaratie sterk aangeraden. Zowel funaam als z’n argumenten parlist moeten net als de lokaal gebruikte variabelen gedeclareerd worden. VOORBEELD In onderstaand programma wordt het kwadraat van de afstand tussen twee punten a en b uitgerekend met een aanroep van een (external) function. program prog implicit none c c c c c
* * Dit programma berekent de afstand tussen 2 punten met coordinaten: * (ax,ay,az) en (bx,by,bz) * * Declaratie real afstkw, ax, ay, az, bx, by, bz real afst2 external afst2
c * Code print*,’Geef x,y en z van punt1’ read*, ax, ay, az print*,’Geef x,y en z van punt2’ read*, bx, by, bz afstkw = afst2(ax,ay,az,bx,by,bz) print*,’De afstand tussen de 2 punten is:’, sqrt(afstkw) end c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - real function afst2(x1,y1,z1,x2,y2,z2) implicit none real x1, x2, y1, y2, z1, z2 afst2 = (x2-x1)**2 + (y2-y1)**2 + (z2-z1)**2 return end
Merk op dat: 1. het resultaat van de function afst2 van het type real is en dat het resultaat via een assignment statement wordt toegekend aan de variabele afst2 met dezelfde symbolische naam (en datatype) als de function zelf. 2. Het external statement is hier niet verplicht, maar vergroot de leesbaarheid van de programmatekst (zie ook §.8.2.3). 3. de namen van de parameters in de functie aanroep (actuele parameters) mogen verschillen van die in het function statement (formele parameters).
42
Statement functions Een simpele vorm van functions in Fortran zijn de statement functions. Ze wijken in een aantal opzichten af van de hierboven behandelde vormen: • Statement functions bestaan uit een enkel Fortran statement. • Statement functions moeten worden gedeclareerd in het declaratie blok van het programma, waarin ze worden aangeroepen met een declaratie van de vorm: syntax funaam(arg1 ,. . .,argn ) = expressie(arg1 ,. . ., argn ) met argi de parameters (argumenten) van de function en in het rechterlid een expressie in termen van de argumenten.
• Statement functions zijn alleen lokaal - in de programma eenheid waarin ze zijn gedeclareerd - gedefineerd. VOORBEELD Statement functions kunnen bijvoorbeeld worden gebruikt wanneer meerdere malen in een procedure dezelfde expressie wordt ge¨evalueerd. Een toepassing van een statement function analoog aan de hiervoor behandelde function afst2 is het volgende:
c c c c c c
program prog implicit none -----------------------------------------------------------------| Dit programma berekent de afstand tussen 2 punten met coord.: | (ax,ay,az) en (bx,by,bz) -----------------------------------------------------------------* Declaratie real afstkw, ax, ay, az, bx, by, bz -> statement function real afst2, x1, y1, z1, x2, y2, z2 afst2(x1,y1,z1,x2,y2,z2) = (x2-x1)**2 + (y2-y1)**2 + (z2-z1)**2
c * Code print*,’Geef x,y en z van punt1’ read*, ax, ay, az print*,’Geef x,y en z van punt2’ read*, bx, by, bz afstkw = afst2(ax,ay,az,bx,by,bz) print*,’De afstand tussen de 2 punten is:’, sqrt(afstkw) end
Merk op dat de namen van de parameters in de aanroep verschillen van die in de declaratie.
8.2.2
Subroutines
Een subroutine is de meer gebruikelijke vorm van een procedure in Fortran. Subroutines onderscheiden zich van functies, doordat het resultaat van een subroutine aanroep niet noodzakelijk enkelvoudig is. Subroutines vormen in dit opzicht een generalisatie van functies. Een subroutine onderscheidt zich van een hoofdprogramma door de volgende kenmerken: • De eerste statement is altijd een subroutine statement. Een hoofdprogramma begint (bij sommige compilers optioneel) met een program statement. • Een subroutine kan niet op zichzelf staand worden gebruikt, maar wordt ‘aangeroepen’ met een call statement door een andere routine of door een hoofdprogramma. • Een subroutine wordt normaliter be¨eindigd met een return statement gevolgd door een end statement en niet met een stop statement. 43
De subroutine statement heeft de volgende vorm: syntax subroutine subnaam [(parlist)] Hierin is subnaam een unieke, geldige symbolische naam, waaronder de routine binnen het programma bekend is en parlist een optionele lijst van z.g.n. formele of dummy parameters, gescheiden door komma’s. Als formele parameters kunnen in de subroutine statement voorkomen: • Namen van variabelen (waaronder arrays, zie H.11) • Procedure namen (zie §.8.2.3) • Niet toegestaan zijn expressies (bijv. constanten) of array elementen. Om dezelfde reden als in het hoofdprogramma wordt ook in de subroutines het gebruik van implicit none en de daarbij behorende expliciete variabelen declaratie sterk aangeraden. De parameters uit parlist moeten net als de lokaal gebruikte variabelen gedeclareerd worden. Het aanroepen van een subroutine gebeurt met een call statement: syntax call subnaam [(parlist)] De symbolische naam is identiek aan die in de corresponderende subroutine statement. parlist is hier de lijst van actuele parameters. Als actuele parameters kunnen optreden: • Variabelen, waaronder arrays en array elementen • Expressies, waaronder constanten • Procedurenamen (subroutine- of functienaam) De namen van actuele en formele parameters hoeven niet overeen te komen. Dit vergroot de algemene toepasbaarheid van subroutines. Voorzichtigheid is geboden wanneer expressies, zoals constanten als actuele parameters worden ‘doorgegeven’. De corresponderende formele parameter moet dan in de aangeroepen routine niet een andere waarde krijgen. VOORBEELD Een berucht voorbeeld waarin een ’constante’ van waarde verandert is het volgende programma: program partest call eennul(1.) x = 1. print *, "x= ",x end subroutine eennul(par) par = 0. return end Dit programma produceert als output: x=0
Blijkbaar wordt niet de waarde van de actuele parameter aan de subroutine doorgegeven maar het adres van een interim variabele, die de waarde van de constante heeft. Bovendien wordt in het programma waarin de aanroep voorkomt eveneens de constante door de interim variabele vervangen. Hierdoor is het dan mogelijk geworden dat een aangeroepen routine de waarden van constanten in de aanroepende routine kan veranderen! In Fortran worden de procedure parameters gebruikt voor in- `en uitvoer. In bovenstaand voorbeeldprogramma partest wordt immers de waarde van de constante 1. doorgegeven aan de variabele par, maar ook andersom. In Fortran 90 kan binnen elke procedure m.b.v. het intentie-attribuut ´e´en van de intenties in, out of inout worden meegegeven aan elke parameter. De declaratie van een parameter met specifieke intentie is: 44
syntax datatype, intent (intentie) :: parlist
Als een parameter de intentie in gekregen heeft, en de procedure zou de waarde van de parameter (kunnen) veranderen, dan geeft de compiler een foutmelding. Hierin is intent (intentie) een z.g.n. attribute van de variabele in parlist. Meerdere attributes kunnen voorkomen, gescheiden door komma’s en afgesloten door twee maal een dubbele punt.
8.2.3
Dummy procedures als parameters van Fortran procedures
Wanneer procedure namen als parameter worden doorgegeven dan moeten deze procedures worden gedeclareerd met een external of een intrinsic statement. VOORBEELD Als voorbeeld van het gebruik van dummy procedures dient het volgende programma voor de numerieke evaluatie van een integraal I=
Z
b
f (x)dx a
In dit voorbeeld wordt hiervoor de ‘trapezium regel’ gebruikt, d.w.z. de integraal wordt benaderd door I ≈ I¯ =
n X
wj f (xj )
j=1
, xj = a + (j − 1) · ∆x
, ∆x =
(b − a) (n − 1)
De integratie gewichten zijn gedefinieerd door
wj =
(
∆x/2 ∆x ∆x/2
, , ,
j=1 1<j
In onderstaande code wordt gebruik gemaakt van een ‘do loop’ iteratie mechanisme (zie H.10) voor het uitrekenen van de som formule.
c c c c c
program testint implicit none -------------------------------------------------------------------| Hoofdprog. voor aanroep van num.integr. routine mbv trapezium regel -------------------------------------------------------------------* Declaraties -> verschillende procedures voor de berekening van integrand: real fint, sin external fint intrinsic sin real x1, x2, res1, res2
c * Code c print*,’ Geef begin-, eind- en aantal x’ read*, x1, x2, nx c * integreer de functies gegeven door fint en sin call trapez(fint,x1,x2,nx,res1) call trapez(sin ,x1,x2,nx,res2) print*,’ integraal van ’,x1,’ tot ’,x2,’ over fint = ’,res1 print*,’ integraal van ’,x1,’ tot ’,x2,’ over sin = ’,res2 end c====================================================================== subroutine trapez(func,tond,tbov,nt,result) implicit none c -------------------------------------------------------------------c | Voert numerieke integratie uit mbv trapezium regel c -------------------------------------------------------------------c * Declaraties c ==> formele parameters:
45
c
c
-> de ’dummy procedure’ voor de integrand als external: real func external func real tond, tbov, result integer nt ==> locals: integer jt real som, dt, t
c * Code c * numerieke benadering door gewogen somm. van integrand waarden dt = (tbov - tond)/(nt-1) t = tond c -> bijdrage van de ondergrens som = 0.5 * func(t) do 100 jt = 2,nt-1 t = t + dt som = som + func(t) 100 continue c -> bijdrage van de bovengrens t = t + dt som = som + 0.5 * func(t) result = som * dt return end c====================================================================== real function fint(x) implicit none c -------------------------------------------------------------------c | Te integreren functie: F = (expressie) c -------------------------------------------------------------------c * Declaratie real x, locale variabelen c * Code fint = expressie return end
Met de beschreven programma opzet - gebruik makend van dummy procedures - kan de routine trapez voor willekeurige integrand f (x) worden toegepast, zonder dat de code van de integratie routine hoeft te worden aangepast.
8.2.4
Geneste procedure aanroepen
Procedures kunnen op hun beurt weer andere procedures aanroepen. Deze zogenaamde geneste procedure aanroepen worden afgewikkeld via een ‘stack mechanisme’. Dit houdt in dat de laatst aangeroepen procedure het eerst wordt be¨eindigd (een ‘last in first out’ (LIFO) mechanisme). 46
OPDRACHT 8-1 Ga na dat het volgende programma stack de onderstaande output produceert: program stack print *, ’stack’ call sub1 print *, ’stack’ end c====================================================================== subroutine sub1 print *, ’sub1’ call sub2 print *, ’sub1’ return end c====================================================================== subroutine sub2 print *, ’sub2’ return end output: stack sub1 sub2 sub1 stack
8.3
Een voorbeeld van het gebruik van functies en subroutines
VOORBEELD In onderstaand voorbeeld wordt het gebruik van subroutines en functies ge¨ıllustreerd.
c c c c c
program bal implicit none -------------------------------------------------------------------| Bereken snelheid en hoogte van bal met beginsnelheid v0 en begin| hoogte h0 op gewenst tijdstip t -------------------------------------------------------------------* Declaraties: real h, h0, v, v0, t real hoogte, snelheid external hoogte, snelheid
c * Code: c * input call inputsubr(t, h0, v0) c * berekeningen: h = hoogte(t, h0, v0) v = snelheid (t, v0) c * output: call outputsubr(h, v) end c====================================================================== subroutine inputsubr(t, h0, v0) implicit none c -------------------------------------------------------------------c | de input van het programma bal
47
c -------------------------------------------------------------------c * Declaraties: real t, v0, h0 c * Code print*,’Geef beginsnelheid naar boven (>0) in m/s’ read(*,*) v0 print*,’Geef beginhoogte tov aardoppervlak in m.’ read(*,*) h0 print*,’Geef tijstip na omhooggooien (in sec.)’ read(*,*) t c * input controle: if (v0 .le. 0 .or. h0 .lt. 0 .or. t .le. 0) then print*,’Een van de input gegevens < 0: STOP’ stop endif return end c====================================================================== subroutine outputsubr(height, velocity) implicit none c -------------------------------------------------------------------c | verzorgt de output van het programma bal c -------------------------------------------------------------------c * Declaraties: real height, velocity c * Code if (height .le. 0) then print*,’ Bal ligt weer stil op de grond’ else print*,’Bal is op een hoogte van ’, height, ’ meter.’ print*,’snelheid(bal) is ’, velocity,’ m/s.’ if (velocity .lt. 0 ) then print*,’ naar beneden.’ else print*,’ naar boven.’ endif endif return end c====================================================================== real function hoogte(t, ini_h, ini_v) implicit none c ------------------------------------------------------------------c | Berekent hoogte v/e voorwerp mbv. beginhoogte en -snelheid en tijd c ------------------------------------------------------------------c * Declaratie: real t, ini_h, ini_v, g parameter (g = 9.81) c * Code: hoogte = ini_h + ini_v * t - 0.5 * g * t**2 return end c====================================================================== real function snelheid(t, ini_v) implicit none c -------------------------------------------------------------------c | Berekent snelheid v/e voorwerp mbv. beginsnelheid en tijd c -------------------------------------------------------------------c * Declaratie: real t, ini_v, g parameter (g = 9.81)
48
c * Code: snelheid = ini_v - g * t return end
8.4
Interface definities
In Fortran 90 bestaan z.g.n. expliciete interface definities van procedures. Hiermee worden de procedure naam en type en de in- en output parameters gespecificeerd. Tijdens compilatie kan de compiler de interface definitie gebruiken om de consistentie van procedure aanroepen te controleren in de vorm van aantal en type van de actuele en formele parameters. Hiermee kunnen anders lastig traceerbare fouten via compiler meldingen worden gesignaleerd. Daarnaast is het gebruik van een expliciete interface definitie noodzakelijk voor procedures waarin bepaalde meer geavanceerde array syntax (hoofdstuk 11) wordt gebruikt. Een expliciete interface definitie vindt plaats in een interface block en dit komt inhoudelijk overeen met de header van de Fortran tekst van de procedure. VOORBEELD Een voorbeeld programma in Fortran 90 voor het gebruik van interface definities is het volgende: program exmpinterf implicit none real x1,y1,z1,x2,y2,z2 real afst, dist interface afst function afst(xa,ya,za,xb,yb,zb) real xa,ya,za,xb,yb,zb real afst end interface afst ... dist = afst(x1,y1,z1,x2,y2,y3) ... end program exmpinterf function afst(xa,ya,za,xb,yb,zb) implicit none real xa,ya,za,xb,yb,zb real afst afst = sqrt ( (xa-xb)**2 + (ya-yb)**2 + (za-zb)**2 ) end function afst
Merk op dat de naamgeving van de variabelen in het interface block en de functie aanroep verschillen. Het interface block bevindt zich in het declaratie gedeelte van een programma onderdeel van waaruit de procedure wordt aan geroepen. Wanneer een procedure met een expliciete interface in verschillende programma onderdelen wordt aangeroepen dan zou de interface definitie op meerdere plaatsen moeten worden herhaald, wat behalve onhandig ook riskant is i.v.m. de kans op introductie van verschillende, inconsistente versies in programmatuur die in ontwikkeling is. Vandaar dat het beter is unieke interface definities te gebruiken m.b.v. een Fortran 90 module die wordt behandeld in hoofdstuk 12. OPDRACHT 8-2 Schrijf een subroutine, die 2 woorden samenvoegt, waarbij een slash (/) tussen de woorden in wordt gevoegd. Deze routine kan gebruikt worden om padnamen te genereren: /geof en info/vi.hlp wordt: /geof/info/vi.hlp
49
50
Hoofdstuk 9
Struktuur diagrammen Sinds ongeveer 1970 wordt bij de bespreking van programmeertechnieken het belang van gestructureerd programmeren benadrukt. Verschillende definities van gestructureerd programmeren worden gehanteerd. De volgende definitie is van N. Wirth1 : Structured programming is the formulation of programs as hierarchical, nested structures of statements and objects of computation. In hoofdstuk 3 is het proces van probleem oplossing m.b.v. programma ontwikkeling onderverdeeld in een aantal stappen, waarvan de eerste twee zijn ‘probleem analyse’ en ‘stapsgewijze specificatie van de oplossing’. Pas later in het proces komt het coderen van een oplossing in een programmeertaal aan de orde. De specificatie van de oplossing van een probleem kan voor eenvoudige problemen in natuurlijke taal gebeuren. Voor complexere oplossingen of bij een gedetailleerde beschrijving is er behoefte aan een ‘specificatie taal’, waarmee algoritmen, minder dubbelzinnig dan met natuurlijke taal mogelijk is, kunnen worden beschreven. De hier te behandelen Nassi-Schneiderman2 of N-S struktuurdiagrammen zijn te gebruiken als specificatie taal. Struktuurdiagrammen zijn een alternatief voor flow diagrammen. Toepassing van struktuurdiagrammen stimuleert het gestructureerd programmeren, toepassing van flowdiagrammen doet dat niet. struktuurdiagrammen worden opgebouwd uit een klein aantal simpele bouwstenen of primitieven. Complexe diagrammen kunnen dan worden samengesteld door samenvoegen en nesten van zulke primitieven. We behandelen de volgende primitieven: Een process box: Hiermee wordt een blok van opeenvolgende acties (‘statement block’ in een programma) aangegeven. De inwendige struktuur van een process box wordt vaak verder uitgewerkt in een afzonderlijk struktuurdiagram (‘stepwise refinement’). Merk de analogie op met het gebruik van procedures uit H.8.
block
Figuur 9.1: Een ‘process box’. ‘block’ staat voor een ‘statement block’ in een programma tekst.
Een decision box: Hiermee wordt een vertakkingspunt in een algoritme aangeduid. Met het symbool ‘e’ wordt een uit te voeren test beschreven, waarvan de uitkomst ‘waar’ danwel ‘niet waar’ oplevert. De acties behorend bij de process box aangeduid met ‘block’ zullen alleen worden uitgevoerd als de test de waarde ‘waar’ oplevert. De symbolen ’T’ (true) en ’F’ (false) in de driehoeken boven beide boxen geven de waarde van de test uitkomst aan waarvoor de acties in de corresponderende box worden uitgevoerd. 1 N.
Wirth, 1974, On the Composition of Well-Structured Programs, Computing Surveys, vol.6, no.4. Nassi, B. Schneiderman, 1973. Flowchart techniques for structured programming. SIGPLAN Notices , 12-26, aug.73. 2 I.
51
e
F
T block
Figuur 9.2: Een ‘decision box’, met ‘e’ een logische expressie. Als e = true dan wordt ‘block’ uitgevoerd, als e = false niet.
Een case box: Met de decision box wordt een alternatief beschreven, waarbij uit precies twee mogelijke acties wordt gekozen. Met een case box beschrijft men de situatie waarin uit meer dan twee acties wordt gekozen. Het symbool ‘e’ staat in dit geval voor een test expressie, die bij evaluatie de waarden e1 , e2 of e3 kan opleveren. De verschillende alternatieven blocki worden uitgevoerd als geldt e = ei .
e1
e
e2 block 1
block 2
e3 block 3
Figuur 9.3: Een ‘case box’ met drie paden blocki . Hierin is ‘e’ een expressie en de programma flow verloopt volgens een van de drie alternatieven, afhankelijk van de waarde van ‘e’. Uitgevoerd wordt blocki als e = ei . Een iteration box: Hiermee wordt aangegeven, dat de acties aangeduid met ‘block’ herhaaldelijk (iteratief) worden uitgevoerd. Het aantal iteraties dan wel een stopcriterium voor de iteratie wordt aangegeven met het symbool spec.
spec block
Figuur 9.4: Een ‘iteration box’. Hierin is ‘spec’ een specificatie van de te doorlopen programma loop (‘do loop’ spec’s, of ‘do while’ conditie, zie ‘Control structures in Fortran’).
Voorbeelden van de iteratie specifier spec zijn: do i=1, imax, 1 Hiermee wordt aangegeven dat de iteratie loop moet worden doorlopen m.b.v. een loop index i, die per iteratie met 1 wordt opgehoogd net zo lang tot dat i > imax waarna de iteratie wordt afgebroken. whiledelta < dmax Dit geeft aan de iteratie loop net zo vaak moet worden uitgevoerd tot dat de waarde van de variabele delta beneden de waarde valt, waarna de loop wordt afgebroken. VOORBEELD Een eenvoudig voorbeeld van het gebruik van N-S diagrammen is het volgende (zie voor andere voorbeelden §.10). Gegeven het probleem van drie getallen het grootste af te drukken. Het N-S diagram van een mogelijke oplosssing is dan figuur 9.5.
52
a>b
T
T print a
a>c
F
F
T
print c
print b
b>c
F print c
Figuur 9.5: Een N-S diagram voor het afdrukken van het grootste getal a, b of c.
VOORBEELD We willen m.b.v. de methode van succesieve substitutie een oplossing x bepalen van de vergelijking x − f (x) = 0, waarin f (x) een gegeven functie van x is (zie ook hoofdstuk 10). De oplossing wordt hierbij iteratief berekend d.m.v. de iteratie formule, xn+1 = f (xn ). Aangetoond kan worden dat dit procede convergeert als geldt |df /dx| < 1 op een omgeving van de wortel x = ξ. We willen de iteratie afbreken wanneer de maximale ‘machine precisie’ is bereikt (zie appendix B). Onderstaand NS diagram beschrijft het algoritme waarmee ξ (in benadering) kan worden berekend voor gegeven startwaarde x0 .
x2 = x0 x1 = x2 + 1 while x 2 ≠ x 1 x1 = x2 x 2 = f (x 1 ) ξ = x2
Figuur 9.6: Een NS diagram van een algoritme voor het iteratief oplossen van x − f (x) = 0.
Meer voorbeelden van toepassingen van N-S struktuurdiagrammen worden gegeven in het hoofdstuk 10 van deze syllabus. OPDRACHT 9-1 Herschrijf in bovenstaand voorbeeld het NS-diagram voor het geval niet van drie, maar van n getallen de grootste moet worden afgedrukt. Maak hierbij gebruik van de iteration box en van geindiceerde variabelen (zie hoofdstuk 11).
53
54
Hoofdstuk 10
Control structures in Fortran De in het vorige hoofdstuk besproken primitieven worden naar zogenaamde control structures in Fortran omgeschreven. In Fortran zijn verschillende ‘control structures’, of kortweg constructs, beschikbaar. Fortran 90 beschikt over een uitgebreider verzameling constructs dan Fortran 77. Hieronder worden de belangrijkste Fortran constructs worden behandeld. Als een construct uitsluitend in Fortran 90 beschikbaar is dan wordt dat bij de bespreking expliciet aangegeven. Met behulp van constructs kan in een programma van de normale, sequenti¨ele programma flow worden afgeweken. De belangrijkste is de ‘block if’ construct. Hiermee wordt een statement block alleen dan uitgevoerd als aan een conditie voldaan is. De programma statements in ‘block’ zullen worden uitgevoerd als de logische expressie ‘e’ in het if () then statement de waarde .true. oplevert. In het andere geval wordt het programma direkt na de end if voortgezet. Rechts in de figuur is het bijbehorend Nassi-Shneiderman struktuur diagram van deze construct weergegeven. E.e.a. is schematisch weergegeven in figuur 10.1.
if (e) then F
e
T
block block
end if
Figuur 10.1: Fortran block if construct. VOORBEELD if (x .ne. 0) then xinv = 1.0 / x end if
Een uitbreiding op het voorgaande is de if then else construct, schematisch weergegeven in figuur 10.2.
if (e) then
block 1
F
e
T
else
block 2
block1
end if
Figuur 10.2: Fortran if then else construct. VOORBEELD 55
block2
if (x .lt. 0) then isignx = -1 else isignx = 1 end if
Hierin is sprake van een alternatief, er wordt precies ´e´en van beide blocks uitgevoerd. Een volgende construct is de if then else if , weergegeven in figuur 10.3.
if (e1 ) then
e1
T
block 1
F
else if (e2 ) then
e2
T
block 2
F
block 1
end if
block 2
Figuur 10.3: Fortran if then else if construct.
VOORBEELD if (x .lt. 0) then isignx = -1 else if (x .gt. 0) then isignx = 1 else isignx = 0 end if
Merk op, dat bij de else if geen afzonderlijke end if hoort. De genoemde constructs kunnen weer ‘genest’ worden, bijvoorbeeld voor een geneste block if
if (e1 ) then
F
block 1
e1
T
block 1
if (e1.1 ) then
block 1.1
F e1.1
end if
T
block 1.1
end if
Figuur 10.4: Fortran nested block if construct.
Fortran 90 beschikt, in tegenstelling tot Fortran 77 over een ‘case’ construct (zie hoofdstuk 9), waarvan de algemene syntax in figuur 10.5 weergegeven is. 56
select case( expressie ) case selector
1
e
2
block 1 . . . case selector
block 1
block 2
3
block 3
block n end select
Figuur 10.5: Syntax van een ‘select case’ statement voor een case construct met n alternatieven. Het N-S diagram is voor het geval van drie alternatieven. De case selector kan bijvoorbeeld bestaaan uit een met komma’s gescheiden lijst van waarden (value1, ..., valuen) en/of, een bereik van waarden met grenzen gescheiden door een dubbele punt (value1, ... , range1 min:range1 max, ... ). VOORBEELD Een en ander wordt geillustreerd met het volgende voorbeeld waarin het teken van een integer variabele number wordt gecodeerd m.b.v. integer waarden -1,0 en 1. select case( case (:-1) nsign = case (0) nsign = case (1:) nsign = end select
number ) -1 ! waarden < 0 0 ! waarde = 0 1 ! waarden > 0
Fortran 77 kent geen ‘case’ statement, in plaats daarvan is er de ‘computed go to’ statement van de vorm
l1
go to ( l 1 , l 2 , . . . , l n ) e continue
1 2
block 1
go to l n+1
block 1
. . .
ln
block 2
e 3
block 3
continue
block n l n+1 continue
Figuur 10.6: Syntax van een ‘computed goto’ statement voor een case construct met n alternatieven. Merk op, dat er meerdere goto’s zijn. Het N-S diagram is voor het geval van drie alternatieven.
Hierin is e een integer expressie. Het ie alternatief wordt uitgevoerd door een sprong naar statement label li als geldt e = i , i = 1, 2, . . . n. In het geval dat e < 0 of e > n wordt de eerstvolgende statement na het computed goto block uitgevoerd. De ‘computed go to’ heeft in de Fortran 90 standaard de status ‘obsolescent’ gekregen. Dit betekent dat deze construct nog wel in de Fortran 90 standaard beschikbaar is maar dat deze op de lijst van in de toekomst te verwijderen Fortran elementen is geplaatst. Het is dus af te raden om deze construct nog in nieuwe Fortran codes te gebruiken, o.a. omdat goede alternatieven beschikbaar zijn. Het is in Fortran 77 ook mogelijk om een ‘case construct’ samen te stellen met behulp van een (herhaalde) if then else construct.
57
De ‘do loop’ is het fundamentele iteratie mechanisme in Fortran. Een do loop wordt met name toegepast bij iteratieve processen met een bekend aantal iteraties.
do label index=i1,i2,istep
block label
do index=i1,i2,istep
block
continue
Figuur 10.7: Een Fortran ‘do loop construct’. De werking van een ‘do loop’ wordt verder verduidelijkt door onderstaand flow diagram.
index=i1
index ≤ i2
T
F
block
index=index+istep
Figuur 10.8: Stroomschema voor een Fortran do-loop. VOORBEELD Voorbeeld van een do-loop:
c c c c
program oneven implicit none -------------------------------------------------------------------| Drukt oneven getallen af van 1 t/m 49 -------------------------------------------------------------------* Declaraties: integer i
c * Code c * aanroep do-loop van 1 t/m 50 in stappen van 2: do 10 i = 1, 50, 2 print*, i 10 continue end
De syntax van de ‘do loop’ kent in Fortran 90 uitgebreider mogelijkheden. Allereerst kan de verwijzing met een label nummer naar de afsluitende statement van de do loop worden weggelaten. In dat geval moet de loop worden afgesloten met end do. De syntax voor het do-enddo construct is: 58
syntax
do index = ibegin, iend, istep statement block end do Een construct, die in Fortran 90 beschikbaar is maar niet in Fortran 77, is de ‘do while’ . Een do while construct wordt gebruikt bij iteraties, waarbij een statement block een i.h.a. niet a priori bekend aantal malen wordt doorlopen. In Fortran 90 kan dit op twee verschillende manieren worden geimplementeerd. Bij de eerste methode wordt gebruik gemaakt van een exit statement, terwijl de index definitie in de do stament wordt weggelaten, do if (.not. e ) exit statement block end do Wanneer de exit statement wordt uitgevoerd dan wordt de programma executie voortgezet bij het eerstvolgende statement na de end do. Merk op dat met een do statement zonder index definitie een oneindige loop kan worden geprogrammeerd. Fortran 90 kent ook een expliciete ‘do while’ maar de bovenstaande variant met een exit verdient de voorkeur vanwege grotere efficientie.
while (e)
do while (e)
block
block
end do
Figuur 10.9: Fortran 90 Do while construct. In Fortran 77 kan deze construct worden samengesteld met een block if en een enkele go to statement. label
continue if (e) then block go to label end if
Figuur 10.10: Implementatie van een do while construct in Fortran 77.
10.1
Enkele toepassingen
VOORBEELDEN • Een ‘do while’ construct kan bijvoorbeeld worden gebruikt om een iteratief proces af te breken zodra aan een zeker stop criterium voldaan is. Dit wordt ge¨ıllustreerd in onderstaand voorbeeld van de berekenening van de wortel van de niet-lineaire vergelijking x − cos(x) = 0 op het interval (0, 1) m.b.v. succesieve substitutie. Het algoritme berekent xi+1 = cos(xi ) en stopt zodra ∆x = |xi+1 − xi | < .
100
xi = 1 xi1 = 0 dx = abs(xi1-xi) if (dx.ge.eps) then xi = xi1 xi1 = cos(xi) go to 100 end if
Figuur 10.11: Toepassing met een Fortran 77 implementatie van een do while construct bij het iteratief oplossen van een niet-lineaire vergelijking m.b.v. succesieve substitutie. 59
• Een voorbeeld van een ‘geneste’ if then else construct is de Fortran implementatie van een algoritme dat het grootste van drie getallen uitvoert (zie de bespreking in H.9 en figuur 9.5). Het N-S diagram voor dit algoritme kan direct in Fortran code worden omgezet: if (a.gt.b) then if (a.gt.c) then print *, a else print *, c end if else if (b.gt.c) then print *, b else print *, c end if end if Merk op dat het gebruik van inspringende programma tekst (indentatie) hier de leesbaarheid sterk vergroot. In Fortran is de intrinsieke functie max beschikbaar voor de bepaling van het maximum van een aantal getallen, zie hoofdstuk 8 en appendix D. • Een volgend voorbeeld is een functie voor de bepaling van de (’non-blank’) lengte van een character string. Sommige Fortran implementaties bevatten een intrinsieke functie lnblnk voor dit doel. Deze lengte wordt hier gedefinieerd als de positie van het laatste van een spatie verschillende teken in de string. Een string van uitsluitend spaties heeft dan de lengte nul. Merk op dat de zo gedefini¨eerde lengte verschilt van de ‘gedeclareerde’ lengte van de character variabele corresponderend met de string. Deze gedeclareerde lengte kan worden bepaald met de intrinsieke functie len. Een algoritme voor een functie lnblnk wordt gegeven door het volgende N-S struktuurdiagram:
i=len(string) while (i > 0 .and. string(i:i).eq.’ ’) i=i-1 lnblnk = i
Figuur 10.12: N-S diagram voor het algoritme voor de bepaling van de lengte van een character string. Een Fortran 77 implementatie hiervan is
100
integer function lnblnk(string) implicit none integer i, ldec character*(*) string ldec = len(string) i = ldec continue if ( i.gt.0 .and. string(i:i).eq.’ ’ ) then i = i-1 go to 100 end if lnblnk = i return end
Merk op dat de gedeclareerde lengte van de formele parameter ‘string’ niet opnieuw in de functie wordt gespecificeerd. Wanneer in Fortran een character variabele als parameter aan een procedure wordt doorgegeven wordt naast het geheugen adres ook
60
de gedeclareerde lengte doorgegeven (niet zichtbaar voor de programmeur). Daarom kan, als in de aangeroepen procedure de gedeclareerde lengte van de string worden gebruikt, hiervoor de intrinsieke functie len worden gebruikt. Dit maakt een flexibeler gebruik van character parameters mogelijk.
OPDRACHT 10-1 Schrijf de lnblnk-functie om naar Fortran 90. Gebruik hierbij de do-while construct. Geef vervolgen een versie die gebruik maakt van een do-enddo construct.
• Een volgend voorbeeld behandelt een algoritme voor het sorteren van een rij getallen ai naar oplopende grootte. We bekijken het bubble sort-algoritme, waarin getallen net zo lang naar het begin van de rij worden geschoven tot ze groter zijn dan alle voorafgaande getallen in de rij. Een puntsgewijze specificatie van dit algoritme is als volgt: 1. Vergelijk het eerste en tweede getal. Als a2 < a1 verwissel dan a1 en a2 . 2. Vergelijk vervolgens het tweede en het derde getal. Als a3 < a2 verwissel dan a2 en a3 en voer daarna 1 weer uit. 3. Vergelijk vervolgens het derde en het vierde getal. Voer vervolgens de stappen 2 en 1 weer uit, enz., totaan het eind van de lijst met te sorteren getallen. Een en ander wordt duidelijker met het volgende N-S diagram:
do j=2,n i=j while (ai < ai−1 .and. i > 1) verwissel ai , ai−1 i=i-1
Figuur 10.13: N-S diagram voor het bubblesort algoritme In dit algoritme wordt gebruik gemaakt van ge¨ındiceerde variabelen (ai , i = 1, 2, . . .). Een Fortran implementatie van dit algoritme wordt gegeven in hoofdstuk 11, na de bespreking van ge¨ındiceerde variabelen. • Een laatste voorbeeld betreft een algoritme voor het bepalen van wortels van nietlineaire vergelijkingen. We zoeken de wortels van de vergelijking f (x) = 0 op het interval [a, b]. We bespreken hier het ‘bisectie algoritme’, waarvan een puntsgewijze specificatie luidt: 1. Zoek eerst grofweg de plaats van een tekenwisseling van de functie y = f (x), door de functie achtereenvolgens te evalueren op een grid van equidistante punten met onderlinge afstand dx, te beginnen in de ondergrens van het te doorzoeken interval [a, b]. Het algoritme stopt wanneer de bovengrens van het interval [a, b] wordt overschreden. 2. Wanneer een tekenwisseling gevonden wordt dan wordt het deelinterval dat de wortel bevat - met aanvankelijke grootte dx - verkleind door het succesief te halveren. 3. Na iedere halvering wordt getest of de wortel links of rechts van het midden licht en het te doorzoeken deelinterval wordt overeenkomstig aangepast. 4. Als het interval dat de wortel bevat kleiner is dan een opgegeven tolerantie dan wordt de iteratie gestopt en de ligging van de wortel wordt benaderd door het midden van het laatst gevonden deelinterval. De zo gevonden wortel wordt opgeslagen. 5. Vervolgens wordt de volgende tekenwisseling met ‘gridspacing’ dx gezocht, d.w.z. het algoritme vervolgt met 1 Een NS-struktuurdiagram wordt gegeven in figuur 10.14.
61
----------------------------------------------------------------|* init. | | nw = 0 | | x = xstart | |-----------------------------------------------------------------| |* zoek naar de eerstvolgende tekenwisseling | | while ( x <= xend ) | | -------------------------------------------------------------| | | y = f(x) | | | x1 = x | | | y1 = y | | | x = x + dx | | | y = f(x) | | |------------------------------------------------------------| | | (F) y*y1 <= 0 (T) | | |------------------------------------------------------------| | | | * teken wisseling gevonden - itereer naar de wortel | | | | while ( |x - x1| > eps ) | | | | ----------------------------------------------------| | | | | xm = ( x + x1 )/2 | | | | | ym = f(xm) | | | | |---------------------------------------------------| | | | | (T) ym*y1 <= 0 (F) | | | | |---------------------------------------------------| | | | | * wortel links van xm | * wortel rechts van xm| | | | | x = xm | x1 = xm | | | | | y = ym | y1 = ym | | | |---|---------------------------------------------------| | | | * convergentie: sla de wortel op | | | | nw = nw + 1 | | | | w(nw) = (x + x1)/2 | | | | * def. start positie voor zoeken naar nieuwe tekenw. | | | | x = w(nw) + dx | -----------------------------------------------------------------
Figuur 10.14: N-S diagram voor het bisectie algoritme voor het bepalen van wortels van niet-lineaire vergelijking.
OPDRACHTEN 10-2 a) Breid de opdracht, om een vijfletterig woord verticaal te schrijven (H.7) uit naar een woord van willekeurige lengte. 10-2 b) Ga na, m.b.v. bovenstaande NS-diagram, hoe een eventuele Fortran tekst handig opgedeeld kan worden in verschillende procedures, en beschrijf de argumenten (parameters) van de verschillende procedures. Beschouw hierbij de functie f (x) als een willekeurige, nader te specificeren functie met de naam funaam. Bekijk hiervoor het voorbeeld van de trapezium regel (§.8.2.3).
62
Hoofdstuk 11
Ge¨ındiceerde variabelen (arrays) Het is dikwijls handig om bij het oplossen van problemen van ge¨ındiceerde variabelen gebruik te maken. Een verzameling ge¨ındiceerde variabelen wordt in Fortran voorgesteld door een array. Zo’n array heeft de volgende eigenschappen: • Een array is een samengestelde variabele waarvan de afzonderlijke elementen allen van hetzelfde data type zijn. • Arrays kunnen van dezelfde data typen zijn als behandeld in hoofdstuk 5 voor enkelvoudige variabelen. In hoofdstuk 12 worden arrays van variabelen met derived types behandeld. • Maximaal zeven indices kunnen worden gebruikt om meerdimensionale datastructuren voor te stellen met zogenaamde meer-dimensionale array’s.
11.1
Array declaratie
11.1.1
Array declaratie in Fortran 77
Arrays moeten (net als enkelvoudige variabelen) worden gedeclareerd in de programma eenheid waarin ze worden gebruikt, met de toevoeging van een dimension statement: syntax dimension arrayname (l1 , l2 , . . . , ln )
met arrayname de naam van de array variabele en li de (integer) bovengrenzen van de ie array index. De indices indexi , i = 1, . . . , n moeten dan bij het gebruik in een programma voldoen aan indexi = 1, 2, . . . , li VOORBEELD Een eenvoudig voorbeeld van een toepassing van een 1-D array is het volgende programma voor het sorteren van een rij getallen met het bubble sort algoritme:
100
program sortest implicit none real rij, temp dimension rij(100) integer i, j . . * defin. het aantal getallen nrij en vul het array rij . do 200 j =2,n i = j continue if ( rij(i) .lt. rij(i-1) .and. i.gt.1 ) then temp = rij(i-1) rij(i-1) = rij(i) rij(i) = temp
63
200
i = i - 1 go to 100 end if continue . . * print de gesorteerde lijst . end
De default ondergrens van het index-bereik is 1, maar hiervan kan worden afgeweken met de volgende uitgebreide declaratie: syntax dimension arrayname (b1 : e1 , b2 : e2 , . . . , bn : en )
met bi , ei onder- resp. bovenwaarden van de ie arrayindex. Index i ‘loopt’ dus van bi tot en met ei . Willen we bijvoorbeeld bij een 100 × 100 matrix rijen en kolommen vanaf 0 tellen, dan kan dit met de declaratie dimension matrix (0:99, 0:99)
Bij de bepaling van het data type van een array gelden de zelfde regels als voor enkelvoudige variabelen (zie §.5). Een expliciete declaratie is op twee manieren mogelijk. Ten eerste door een type declaratie na de betreffende dimension statement, bijvoorbeeld dimension matrix(100,100) real matrix of door een enkele statement, een tussenvorm van de type declaratie en de dimension statement, bijvoorbeeld real matrix(100,100)
11.1.2
Array declaratie in Fortran 90
In Fortran 90 worden arrays gedeclareerd door middel van toevoeging van het dimension attribute: syntax datatype, dimension (l1 , l2 , . . . , ln ) :: arrayname OPDRACHT 11-1
Schrijf bovenstaand voorbeeldprogramma sortest om naar Fortran 90. Gebruik de Fortran 90 declaraties en de do-loop met exit statement.
11.2
Geheugenopslag van arrays
Een array wordt in het intern geheugen lineair (1-dimensionaal) opgeslagen, o ´o ´k een meerdimensionaal array. Dit betekent, dat de plaats van een array element in het geheugen wordt bepaald door een startadres van het array en een ‘offset’. Wanneer in Fortran statements meerdimensionale arrays worden gebruikt dan worden de gebruikte indices eerst omgerekend naar een waarde van de genoemde offset. Deze omrekening gaat bijvoorbeeld voor een element a(i,j,k) van een 3-D array gedeclareerd met dimension a(n1 , n2 , n3 ) als volgt offsetijk = (k − 1) · (n1 · n2 ) + (j − 1) · n1 + i 64
OPDRACHT 11-2 Ga na aan de hand van bovenstaande uitdrukking voor de offset, dat bij de geheugen opslag ‘de eerste array index het snelst varieert’, daarna de tweede enz.
VOORBEELD Voor een 2-D array - corresponderend met een matrix - betekent dit dat de matrix kolommen achter elkaar in het geheugen worden opgeslagen. De elementen van een 2-D array corresponderend met een vierkante 2 × 2 matrix
a11 a21
a12 a22
zijn bijvoorbeeld opgeslagen in het geheugen in de volgorde a(1, 1), a(2, 1), a(1, 2), a(2, 2)
Het adresseren van meerdimensionale arrays kost extra rekenkundige operaties, omdat een conversie van de gebruikte meervoudige indices naar een 1-D adressering (offset) moet worden uitgevoerd. In toepassingen waarin intensief van meerdimensionale arrays gebruik gemaakt wordt kan men soms rekentijd besparen door een meerdimensionaal array te vervangen door meerdere eendimensionale. Heeft men bijvoorbeeld in een programma te maken met een matrix, waarin uitsluitend elementen op de hoofd diagonaal en de beide eerste neven diagonalen ongelijk nul zijn - een z.g.n tri-diagonale matrix - dan kan het, afhankelijke van de uit te voeren operaties, efficienter zijn de drie diagonalen van deze matrix in afzonderlijke arrays op te slaan in plaats van in een enkel 2-D array. VOORBEELD Een voorbeeld van het gebruik van 1-D en 2-D arrays wordt gegeven in het volgende programma voor een matrix-vector vermenigvuldiging. Het programma berekent het matrixvector product, b = ax, met vectoren x, b en matrix a: bi =
n X
aij xj
j=1
100 200
program mavmul implicit none real a(100,100), x(100), b(100) . . * statement block (definitie a,x,nrij,ncol) . do 200 i=1,nrij som = 0.0 do 100 j =1,ncol som = som + a(i,j)*x(j) continue b(i) = som continue ... end
Merk op dat de matrix elementen ‘rij-gewijs’ uit het geheugen worden gebruikt in de binnenste do-loop. Uit de bovenstaande beschrijving van de geheugen adressering voor meerdimensionale arrays volgt dat er tussen de achtereenvolgens opgehaalde matrix elementen een ‘afstand’ in het geheugen moet worden overbrugd ter grootte van een matrix kolom. Bij zeer grote matrices heeft dit nadelige gevolgen voor de effici¨entie van de programma executie, met name bij computers met een zogenaamd cache geheugen. 65
OPDRACHTEN 11-3 a) Ontwerp een algoritme voor de matrix-vector vermenigvuldiging, waarin de matrix elementen ‘koloms-gewijs’ worden gebruikt, (aanwijzing: bereken het m-v product als lineaire combinatie van matrix kolommen). 11-3 b) Ontwerp een Fortran routine waarmee een matrix-vector vermenigvuldiging voor een tridiagonale matrix wordt uitgevoerd. Sla de matrix op in drie 1-D arrays.
11.3
De juiste array lengten en geheugenallocatie
11.3.1
Statische geheugenallocatie in Fortran 77
In Fortran 77 moet de lengte van een array voorafgaand aan de programma compilatie worden vastgelegd in de array declaratie. Het is in het algemeen ongewenst op grotere ‘multi-user’ systemen veel meer array ruimte te declareren in een programma dan er werkelijk wordt gebruikt. Voor programma’s die met sterk wisselende hoeveelheden data werken kan het dus nuttig zijn de array declaraties per geval aan te passen. Hierbij is enige voorzichtigheid geboden, vooral ook omdat in Fortran programma’s (standaard) niet op array grens overschrijdingen wordt getest. Hierbij kan nuttig gebruik gemaakt worden van een definitie van arraygrenzen m.b.v. parameter declaratie:
syntax parameter( arraygrens = expressie)
In expressie mogen alleen constanten en eerder gedefinieerde parameters worden gebruikt. De hiermee gedefinieerde parameter arraygrens kan vervolgens in het programma worden gebruikt, zowel in de array declaratie als in eventuele controles op de beschikbare arrayruimte. VOORBEELD In het volgende voorbeeld wordt geschetst hoe men zelf eenvoudig array lengtes kan aanpassen en vervolgens kan testen in het programma afhankelijk van de benodigde ruimte voor data opslag. program par implicit none integer ndec, a parameter (ndec = 100) dimension a(ndec) read *, ndata if (ndata .gt. ndec) then print *, ’ERROR: ndata=’, ndata, ’ .gt. ndec=’, ndec stop end if lees ndata gegevens, sla ze op in array a en verwerk ze end
11.3.2
Automatic arrays
Vaak worden arrays slechts tijdelijk gebruikt in een programma. Het is dan ineffici¨ent om veel ongebruikte array ruimte permanent in een programma te declareren. Fortran 90 kent een voorziening waarmee ‘tijdelijke’ arrays, gebruikt in procedures, als zogenaamde automatic arrays kunnen worden gedeclareerd. Dit houd in, dat de geheugenruimte van een automatic array beschikbaar is vanaf het moment dat de betreffende procedure wordt uitgevoerd. De geheugenruimte wordt weer vrijgegeven bij de return uit de procedure. 66
VOORBEELD In het volgend voorbeeld wordt een automatic array gebruikt in de subroutine printgridval voor het verwerken van data in de knooppunten van een gestructureerd rechthoekig rooster. De coordinaten van het 2-D rooster zijn opgeslagen in afzonderlijke 1-D arrays, xcoor en ycoor, dummy parameters van de procedure printgridval. subroutine printgridval (nx,ny,xcoor,ycoor) implicit none integer nx, ny real, dimension(:) :: xcoor,ycoor ! dummy arrays real, dimension(nx, ny) :: gridval ! automatic array ! bereken de knooppuntwaarden call compgridval(nx,ny,xcoor,ycoor,gridval) ! print de knooppuntwaarden call printval(nx,ny,gridval) return end
OPDRACHT 11-4 Ga na waarin de hier gebruikte automatic array declaratie verschilt van een declaratie met een ‘assumed size’ array in Fortran 77.
11.3.3
Dynamische geheugen allocatie in Fortran 90
Fortran 90 beschikt over een uitgebreider array syntax. Een belangrijke uitbreiding is de introductie van dynamische geheugen allocatie van arrays. Dit houdt in dat tijdens de uitvoering van een programma een hoeveelheid geheugen door het programma kan worden ’aangevraagd’. Een dynamisch (allocatable) array, moet als zodanig worden gedeclareerd d.m.v. het allocatable ‘attribute’ in een type declaratie: syntax datatype, dimension (:[,:, . . . ,:]), allocatable :: arraynaam De array grenzen in het dimension-attribuut worden dus niet ingevuld. Deze declaratie moet plaatsvinden in dezelfde programma eenheid waarin de betreffende allocate wordt uitgevoerd. Het daadwerkelijk toewijzen van geheugen voor het dynamische array gebeurt d.m.v. een allocate statement: syntax allocate(arraynaam(lengte))
Deze hoeveelheid geheugen zal vervolgens - indien beschikbaar - door het besturingssysteem aan het programma worden toegewezen (gealloceerd). Na gebruik kan deze geheugenruimte eventueel worden vrijgegeven met een deallocate statement: syntax deallocate(arraynaam) Op deze manier kan de totale hoeveelheid op enig moment gebruikt geheugen tijdens de programma executie dynamisch, afhankelijk van de behoefte, worden aangepast. VOORBEELD Het volgende voorbeeld illustreert het gebruik van een 1-D en een 2-D allocatable array: program matalloc implicit none real, dimension (:,:), allocatable :: matrix
67
real, dimension (:), integer nrij,nkol
allocatable :: vector
read(5,*) nrij,nkol allocate(matrix(nrij,nkol)) allocate(vector(nkol)) array toepassingen deallocate(matrix) deallocate(vector) end
11.4
!* lees de matrix en vector grootte in !* geheugen allocatie ! !* geheugen weer vrijgeven !
Arrays als parameters van procedures
Arrays kunnen als parameters worden ‘doorgegeven’ aan Fortran procedures - functions en subroutines. In Fortran programma’s worden (standaard) geen tests op array grens overschrijding uitgevoerd. Wanneer een array optreedt als parameter van een subroutine wordt dan ook alleen het startadres van het array in het geheugen (en niet de gedeclareerde lengte) doorgegeven aan de subroutine. Voor een array dat als formele parameter aan een procedure wordt doorgegeven hoeft niet opnieuw geheugenruimte te worden vrijgemaakt. Er moet echter wel een array declaratie gedaan worden, om duidelijk te maken, dat de variabele ook in de aangeroepen procedure niet enkelvoudig (scalair) is. Hierbij kan van een zogenaamde variabele array declaratie gebruik worden gemaakt (assumed size arrays). Dit houdt in dat de arraygrenzen mogen worden weggelaten (zie onder) (in het 1-D geval) of worden opgegeven als variabelen, die als formele parameters of via common (zie §.12) aan de routine zijn doorgegeven (zie onderstaande voorbeelden). We hebben gezien dat bij het gebruik van M dimensionale arrays de indices worden omgerekend in een enkel geheugen adres. Hierbij worden de array grenzen in de eerste M − 1 dimensies gebruikt (de ‘leading dimensions’). Bij declaratie van een M dimensionaal array moeten dan ook de eerste M − 1 array grenzen ‘normaal’ worden gedeclareerd. Assumed size array grenzen worden genoteerd met een asterisk * teken. Het is in Fortran ook mogelijk om het aantal dimensies van een array in een programma eenheid te laten verschillen van het aantal dimensies van het zelfde array in een aangeroepen procedure - zie de onderstaande voorbeelden. Uiteraard moeten de assumed size arrays wel elders in het programma met een voldoende lengte zijn gedeclareerd. VOORBEELDEN • Een en ander wordt toegelicht in het volgend voorbeeld van een subroutine voor matrix-vector vermenigvuldiging
c
c c c c
program mvtst implicit none integer nrmax, ncomax, maxmat, nrij, ncol real a, b, c parameter (nrmax=100,ncomax=100,maxmat=nrmax*ncomax) dimension a(maxmat), b(ncomax), c(nrmax) definieer nrij,ncol en check array grenzen vul de matrix a en de input-vector call mvmult(a,b,c,nrij,ncol) ... end ===================================================================== subroutine mvmult(matrix,vecin,vecuit,nrij,ncol) implicit none -------------------------------------------------------------------| Matrix-Vector velmenigvuldiging --------------------------------------------------------------------> formele parameters: real matrix(nrij,*), vecin(*), vecuit(*) integer nrij, ncol
68
c
100 200
-> locals: integer i, j real som do 200 i=1,nrij som = 0.0 do 100 j=1,ncol som = som + matrix(i,j)*vecin(j) continue vecuit(i) = som continue return end
Het array met de matrix is hier als 1-D array gedeclareerd in het hoofdprogramma en als 2-D array in de subroutine. Merk op dat in de parameter statement in dit voorbeeld een rekenkundige expressie wordt gebruikt. • Het volgende voorbeeld laat zien hoe kolommen van een 2-D array in een subroutine als 1-D arrays worden gehanteerd. In het programma worden de kolommen van een matrix met een schaalfactor vermenigvuldigd. In dit voorbeeld worden de matrix kolommen als 1-D arrays doorgegeven aan de subroutine. In het hoofdprogramma wordt de matrix als 2-D array gebruikt. De kolom vectoren worden in de subroutine als 1-D array gedeclareerd. program matscal implicit none integer nrij, ncol, kol parameter(nrij=10,ncol=10) real matrix(nrij,ncol), scal(ncol) definieer arrays matrix en scal do 100 kol=1,ncol call vscal0(matrix(1,kol),nrij,scal(kol)) 100 continue end c =================================================== subroutine vscal0(vector,nvec,scal) implicit none real vector(*), scal do 100 i=1,nvec vector(i) = vector(i) * scal 100 continue return end Willen we rijen in plaats van kolommen van een matrix schalen en de schaling weer als een vector operatie uitvoeren in een subroutine, dan moet e.e.a worden aan gepast omdat de matrix niet rij-gewijs in het geheugen is opgeslagen. De subroutine kan hiertoe worden gegeneraliseerd met een ‘stride’ ongelijk aan ´e´en. Het bovenstaande voorbeeld verandert dan als volgt:
100
100
... do 100 irij =1,nrij call vscal1(matrix(irij,1),nrij,ncol,scal(irij)) continue ... end subroutine vscal1(vector,istride,nvec,scal) dimension vector(*) do 100 i=1,nvec,istride vector(i) = vector(i) * scal continue return end
69
OPDRACHTEN 11-5 a) Pas de bovenstaande voorbeeldprogramma’s aan voor gebruik van Fortran 90 allocatable arrays. 11-5 b) Schrijf een Fortran programma, waarin een matrix-vector vermenigvuldiging wordt uitgevoerd als een gewogen som van de matrix kolommen. Maak hierin gebruik van bovenstaande routine vscal0 voor het berekenen van geschaalde kolom vectoren. Hoe kan het programma worden gewijzigd, z.d.d. de algemene toepasbare routine vscal1 wordt gebruikt? 11-5 c) Schrijf een programma, dat een character array op alfabetische volgorde sorteert. Maak hierbij van het bubblesort algoritme aan het begin van dit hoofdstuk.
11.5
Uitgebreide array syntax in Fortran 90
Fortran 90 kent een veel uitgebreider array syntax dan Fortran 77. Het gebruik van ‘automatic arrays’ en ’allocatable arrays’ voor dynamisch geheugen gebruik in Fortran 90 codes is al eerder behandeld in sectie 11.3.3. Hier bespreken we de mogelijkheden om operaties met arrays op een compacter manier in Fortran te noteren. Deze kortere notatie kan resulteren in overzichtelijker programma code. Daarnaast leent programma code met deze compacte array syntax zich goed voor optimalisatie voor de gebruikte computer hardware.
11.5.1
Array shape en size
Met de shape van een array arrexmp wordt bedoeld een 1-D integer array met als lengte de rank (het aantal dimensies) van array arrexmp. De opeenvolgende elementen van dit shape-array bevatten de afmeting in de verschillende dimensies (extent) van array arrexmp. De Fortran 90 intrinsic shape kan worden gebruikt voor het bepalen van de shape van de array. Arrays worden comform genoemd als ze een dezelfde shape bezitten. Een verwante Fortran 90 intrinsieke functie - size - bepaald de totale (gedeclareerde) grootte van een array (default) danwel de afmeting in een van de afzonderlijke dimensies. VOORBEELD real, integer, integer ... ishape = isize = isize1 = isize2 = isize3 =
dimension(0:9,0:99,0:999) :: arrexmp dimension(3) :: ishape isize, isize1, isize2, isize3 shape(arrexmp) size(arrexmp) size(arrexmp,1) size(arrexmp,2) size(arrexmp,3)
Het array ishape krijgt hiermee de waarden (10,100,1000), de scalair isize krijgt de waarde 106 en isize1, ... isize3 worden resp. 10,100,1000.
11.5.2
Assumed-shape arrays en interface definities
We hebben eerder gezien hoe in Fortran 77 m.b.v. dummy arrays (arrays optredend als formele parameters) in procedures op een flexibele manier gebruik gemaakt kan worden van meerdimensionale arrays. De afmetingen in de verschillende dimensies kunnen hierbij m.b.v. programma variabelen worden gedefinieerd en dus afhankelijk worden gemaakt van de (wisselende) programma input. Deze ‘afmetingen’ moeten dan als formele parameters of via common (hoofdstuk 12) zijn gedefinieerd in de betreffende procedure. Met Fortran 90 zijn deze mogelijkheden voor flexibel gebruik van dummy arrays verder uitgebreid met zogenaamde ‘assumed-shape’ arrays. De verschillende afmetingen worden hiermee 70
niet meer expliciet - als formele parameter of via common - maar impliciet via de interface definitie bepaald. VOORBEELD Een voorbeeld van het gebruik van een assumed-shape array is het volgende: program exmpshape implicit none real, dimension (:,:,:), allocatable :: grid3d . . . interface fillgrid subroutine fillgrid(grid) real, dimension(:,:,:) :: grid end subroutine fillgrid end interface fillgrid . . . read (5,*) nx,ny,nz allocate ( grid3d(nx,ny,nz) ) call fillgrid(grid3d) . . . end program exmpshape subroutine fillgrid(grid) implicit none real, dimension(:,:,:) :: grid ! assumed shape array declaratie ... integer n1,n2,n3 ... n1 = size(grid,1) ! bepaal de array afmetingen n2 = size(grid,2) ! n3 = size(grid,3) ! do k=1,n3 ! vul het 3-D array do j=1,n2 ! m.b.v. function defgrid do i=1,n1 ! grid(i,j,k) = defgrid( ... ) ! end do ! end do ! end do ! return end subroutine fillgrid
Bij de compilatie van programma exmpshape wordt de interface definitie van de procedure fillgrid ‘meegecompileerd’ zodat de juiste gegevens van het actuele parameter array grid3d (de array ‘shape’) worden doorgegeven die nodig zijn voor de aanroep van de intrinsieke functie size. De interface definitie kan ook via een Fortran 90 module (hoofdstuk 12) worden gedeclareerd.
11.5.3
Elemental array operaties
In Fortran 90 kunnen operaties uitgevoerd op arrays met een compacte notatie worden weergegeven. Stel we willen een lineare combinatie berekenen van twee vectoren a en b met n elementen, in een derde vector c, als ci = αai + βbi + γ met i = 1, . . . , n en scalairen α, β en γ dan kan dit met een enkel statement worden aangegeven: c = alpha*a + beta*b + gamma 71
Hierin zijn c,a,b arrays. Deze syntax kan worden gebruikt voor verschillende dimensionaliteit van de arrays. D.w.z. het statement is ook bruikbaar wanneer de operatie moet worden uitgevoerd op matrices of algemeen meerdimensionale arrays. Voorwaarde hierbij is dat de drie betrokken arrays a,b,c conform (conformal) zijn d.w.z. dat ze een identieke shape bezitten. Een scalair wordt in dit verband geacht conform te zijn met ieder array. De naam elemental operatie geeft aan dat de bewerking elementsgewijs wordt uitgevoerd, d.w.z. de bewerkingen worden voor alle overeenkomstige elementen uitgevoerd. Een verwante voorziening in Fortran 90 hangt samen met array sections. Hiermee kunnen onderdelen (subsections) van een array worden aangeduid. VOORBEELDEN • In het volgende voorbeeld wordt de eerste kolom uit een 2-D matrix gecopieerd naar een 1-D array, met een enkel Fortran 90 statement, real, dimension(10,10) :: amat real, dimension(10) :: bvec integer kolom ... kolom = 1 bvec = amat(1:10,kolom) • Voorbeelden van assignment van array sections zijn (met X en Y 2-D arrays): X( 1:4, 1:6) = Y(3:6, 6:1:-1) X(10:1:-1, 1) = Y( :, 2)
! copieer een 4X6 matrix block ! copieer een 10X1 matrix kolom
Echter fout is: X(1:3, 1:3) = Y(1:4,1:3) vanwege ongelijke shapes van de beide array sections.
Zoals in bovenstaand voorbeeld al te zien is, maakt de algemene syntax voor de vorming van een array section gebruik van een zogenaamd subscript triplet, met ondergrens, bovengrens en stapgrootte (stride), analoog aan het gebruik bij do loops. Als scheider wordt in het subscript triplet de colon(:) gebruikt. De stride kan hierbij negatief zijn en de default waarde is 1. Wanneer de subscript triplet een lege verzameling aangeeft dan is de de betreffende operatie een no-op en wordt niet uitgevoerd. OPDRACHT 11-6 Ontwerp een procedure waarin een matrix-vector vermenigvuldiging b = Ax wordt berekend als een lineaire combinatie van de kolom vectoren van de matrix. De matrix kolommen worden hierbij als array secties behandeld. De initialisatie van de resultaat vector kan met een enkel statement, b = 0, worden uitgevoerd.
Intrinsieke functions kunnen ook gebruikt worden in elemental operaties zoals in: c = alpha*sin(a) + beta*cos(b) + gamma Deze toepassing is in Fortran 90 beperkt tot intrinsieke functies.
72
Hoofdstuk 12
Modulen en common block’s Goed gestructureerde computer programma’s bestaan i.h.a. uit een hoofdprogramma (main program) en verschillende procedures (subroutines en/of functions in Fortran). De verschillende procedures moeten dan in principe dezelfde gegevens, opgeslagen in het computer geheugen, kunnen bewerken. We hebben gezien in H.8 en H.11 dat variabelen aan procedures kunnen worden doorgegeven door ze als actuele parameters in de procedure aanroep op te nemen. Hieraan kleven echter enkele bezwaren. Vooral wanneer een programma een groot aantal lagen van procedures bevat, kunnen parameterlijsten hinderlijk lang worden. Bij de ontwikkeling van een programma, wanneer bijvoorbeeld parameters in lijsten worden toegevoegd of verwijderd, wordt de kans op het introduceren van fouten dan groter. Daarnaast kan de indirecte adressering van variabelen via meerdere lagen van parameterlijsten, afhankelijk van het type computer systeem, leiden tot een inefficiente en onoverzichtelijke programma code. In Fortran zijn twee alternatieven beschikbaar, namelijk modulen (Fortran 90) en common blocks, waarmee bovenstaande bezwaren zijn te ondervangen.
12.1
common block’s
Met behulp van een ‘common’ kan aan groepen variabelen een plaats in het geheugen (het common block) worden toegekend. Deze variabelen zijn dan vervolgens toegankelijk voor alle programma eenheden, waarin het betreffende common block is gedeclareerd. Common blocks worden gedeclareerd met een common statement in de programma eenheden, waarin ze worden gebruikt. Een common statement is van de vorm syntax common [ / blocknaam / ] varlist Hierin is:
• blocknaam de optionele (unieke) symbolische naam waaronder het common block in het programma bekend is. Wordt / block naam / weggelaten dan spreekt men van blank common, anders van named common of ook wel labeled common. • varlist een lijst van namen van variabelen. Voor de verschillende variabelen gelden dezelfde data type conventies als voor niet-common variabelen (zie H.5). Er geldt de restrictie dat character variabelen niet gemengd met andere variabelen in het zelfde common mogen voorkomen. De common declaratie moet worden vergezeld van volledige type declaratie van alle optredende variabelen. Hoewel dit niet verplicht is valt het sterk aan te bevelen om common declaraties en de corresponderende type declaraties bij elkaar te groeperen in de programma tekst. VOORBEELDEN • Een voorbeeld van het gebruik van common blocks is het volgende programma: program exmcom implicit none real matrix, vector integer ndata common /x/ matrix(10,5),vector(5),ndata
73
character*80 text common /y/ text ... executable statement block ... end • Het onderstaande voorbeeld resulteert in een syntax foutmelding, omdat een character variabele samen met een niet-character variabele voorkomt in ´e´en common-block: real numdata character*80 text common /y/ numdata,text
Wanneer numerieke variabelen van verschillend type real, double precision of integer in het zelfde common block voorkomen dan verdient het de voorkeur om de integer variabelen achteraan te plaatsen.
12.1.1
Common blocks in combinatie met formele of actuele parameters
Een syntax regel is dat de variabelen in een common block niet als formele parameters mogen voorkomen. De adressering van de doorgegeven parameters zou anders immers niet uniek zijn. Actuele parameters kunnen daarentegen wel in een common block voorkomen. VOORBEELDEN • Dit voorbeeld geeft een syntax foutmelding omdat de parameter ´en via het common block ´en als formele parameter naar de subroutine exmp wordt doorgegeven. subroutine exmp(x) implicit none real x common /a/ x ... end • Actuele parameters mogen wel in een common block voorkomen: subroutine exmp1 implicit none real x common /a/ x ... call exmp2(x) ... end c ===================================================================== subroutine exmp2(x) implicit none real x executable statement block waarin x wordt gebruikt end
12.1.2
Het gebruik van common blocks in include-files
Bij het gebruik van common blocks moet men bij de programma ontwikkeling extra voorzichtig zijn met het aanbrengen van wijzigingen in de indeling van de common blocks omdat deze globale 74
variabelen bevatten. Stel dat men een variabele toevoegt aan het begin van de parameterlijst van een common block en men vergeet vervolgens dezelfde wijzigingen aan te brengen in ´e´en van de procedures waarin het common block gebruikt wordt. Als nu in laatst genoemde routine waarden worden toegekend aan variabelen in het common block dan kunnen lastig op te sporen fouten optreden in ´e´en der overige procedures waarin het common block gebruikt wordt. Een beveiliging tegen dit probleem die de integriteit van gebruikte common blocks garandeert is het gebruikt van include statements: syntax include ’f ilenaam’ Hiermee zal de compiler de (unieke) brontekstregels voor de common block declaratie van de file met padnaam filenaam lezen. Hierin zijn de quotes om de padnaam verplicht. Het include statement brengt men nu aan in alle procedures waarin het common block wordt gebruikt. Eventuele wijzigingen in het common block worden nu aangebracht in de unieke versie in de file filenaam (zie tevens appendix C).
12.2
Fortran 90 modulen
Binnen de Fortran 90 standaard worden common blocks als verouderde taal elementen aangemerkt (Brainerd et al., 1990), hoewel ze nog wel volledig worden ondersteund. Dit betekent echter dat in de toekomstige versies van Fortran common blocks mogelijk niet langer zullen worden opgenomen in de taal. Fortran 90 beschikt over een goed alternatief voor common blocks met uitgebreider mogelijkheden: modulen. Een module in Fortran 90 is een programma eenheid waarin: 1. declaraties van variabelen 2. definities van programma procedures kunnen worden ondergebracht. We behandelen hier de eerste toepassing waarmee declaraties van datastructuren globaal kunnen worden gemaakt. Naast de declaraties zijn ook de waarden van deze variabelen in alle procedures bekend, waarin de module is opgenomen. Een module is dus een soort ’declaratie block en common block ineen’. Modulen hebben, zoals alle programma eenheden, een unieke naam, die wordt vastgelegd in de module declaratie:
syntax module modnaam declaraties en definities end module modnaam Een programma eenheid kan gebruik maken van een module m.b.v. een use statement aan het begin van de eenheid (voorafgaand aan implicit none): syntax use modnaam
Het verdient aanbeveling variabelen met betrekking tot een gemeenschappelijk onderwerp in ´e´en module onder te brengen. Een procedure hoeft dan slechts die modules te gebruiken, waarvan het onderwerp aan bod komt. Dit verhoogt de leesbaarheid van de programma code. VOORBEELD We behandelen een voorbeeld, waarin een 1-D buffer array als globale ’werkruimte’ wordt gedeclareerd in een module. In dit voorbeeld wordt in de integer variabele lenwork het aantal in het werkarray opgeslagen data bijgehouden. Het werk-array wordt met het attribuut allocatable gedeclareerd (zie §.11.3.3). Onderstaande routine illustreert het gebruik van modules: een hoeveelheid data uit een input array van een subroutine wordt gekopieerd in het werk array en wordt vervolgens vanuit het werkarray in het hoofdprogramma geprint: module work_module integer lenwork real, dimension(:), allocatable :: workarray end module work_module !----------------------------------------------------------program exmp11
75
use work_module implicit none integer index,lenarray parameter (lenarray=10) real, dimension(1:lenarray) :: arrayin allocate(workarray(1:lenarray)) !* alloceer geheugenruimte voor werk array do index=1,lenarray arrayin(index)=index**2 end do call workcopy(arrayin,lenarray) write(6,’(5e10.3)’) (workarray(index),index=1,lenwork) end !----------------------------------------------------------subroutine workcopy(arrayin,lenarray) use work_module implicit none real arrayin(*) integer lenarray integer index lenwork = lenarray do index=1,lenarray workarray(index) = arrayin(index) end do return end
VRAAG 12-1 Waarom is bij gebruik van modulen de eenduidigheid van de betreffende declaraties ’automatisch’ gegarandeerd en waarom is dit niet zo bij het common blocks die niet via een include file zijn gedeclareerd?
12.2.1
‘Derived types’ en ‘structures’
De standaard of ‘intrinsic’ data typen in Fortran zijn vermeld in hoofdstuk 4 van deze syllabus. Fortran 90 kent daarnaast voorzieningen voor (eigen) definitie van nieuwe, niet standaard datatypen, de zogenaamde derived types. Derived types worden gedeclareerd m.b.v. een type statement: syntax type typenaam datatypen varlist end type typenaam
Hierin is datatypen varlist een lijst van declaraties van de onderdelen (componenten van de derived type typenaam. Het verdient aanbeveling om derived types onder te brengen in modulen, o.a. om eenduidige definities op een eenvoudige manier te garanderen. Een variabele van zo’n derived type heet een structure. De declaratie van een structure strucnaam van het type typenaam is alsvolgt: syntax type(typenaam) strucnaam 76
Complexe datastructuren kunnen op een eenvoudige manier als structure onder ´e´en noemer worden bewerkt. VOORBEELDEN • In een textueel voorbeeld wordt een database met literatuur referenties in het ‘refer’ formaat bewerkt. In de UNIX omgeving zijn een aantal ‘utilities’ beschikbaar waarmee een eenvoudige database van bibliografische gegevens kan worden onderhouden. Zo kunnen met addbib nieuwe ‘referentie records’ aan een database worden toegevoegd. M.b.v. lookbib kan een bestaande database worden doorzocht op auteursnaam, trefwoorden etc. Voor een uitvoeriger beschrijving raadplege men de UNIX manual pages. De betreffende data base is een tekst file waarin de bibliografische gegevens per referentie in een aantal opeenvolgende regels voorkomen. Alle gegevens van een bepaalde referentie vormen samen een ‘record’ in de database. Opeenvolgende records worden gescheiden door een lege regel. Hieronder worden twee mogelijke records weergegeven van zo’n database, %A %A %D %T %I
M. Metcalf J. Reid 1996 FORTRAN 90/95 explained Oxford University Press
%A %A %A %D %T %I
W.S. Brainerd Ch. H. Goldberg J.C. Adams 1990 Fortran 90 Academic Service
We zien dat verschillende ‘velden’ binnen een record worden aangeduid met een letter code, %A voor auteurnamen, %D voor publicatiedatum, %T voor titel en %I voor de uitgever. In het voorbeeld programma dat hieronder is afgedrukt worden de records uit een database doorzocht op auteurnaam en alle records met de betreffende auteurnaam worden geprint. Het programma bevat hiervoor een routine readnextrecord waarmee het eerstvolgend record van de database wordt gelezen. Voor het afdrukken van het record wordt een format statement gebruikt, welke in H.13 worden behandeld. De definitie van het record type is ondergebracht in de volgende module: module bibldbs_module !* type def. database record type record_type integer numauthor character (len=80), dimension(1:10) :: author character (len=240) title character (len=4) date character (len=80) journal character (len=80) volume character (len=80) page end type record_type end module bibldbs_module
numauthor, author, title, etc. zijn de componenten van het type record_type. In dit voorbeeld is de eerste component het aantal auteurs van een publicatie. De overige componenten corresponderen met de verschillende velden van de database records. In een Fortran 90 programma kunnen de verschillende componenten afzonderlijk worden bereikt met het teken %, zoals in het volgende voorbeeld: program naam_in_bib !* zoek naam in bib en print bijbehorende record use bibldbs_module implicit none character (len=80) zoeknaam type (record_type) dbs_record
77
... lees zoeknaam ... do irecord = 1, aantal_records ! loop over records call nextrecord(dbs_record) ! lees volgende record do iauthor = 1, dbs_record%numauthor if (dbs_record%author(iauthor) .eq. zoeknaam) then call printrecord(dbs_record) end if end do end do ... end De hierin gebruikte routines nextrecord en printrecord worden hier niet nader uitgewerkt. • Een numeriek voorbeeld van het gebruik van ‘derived types’ en modulen is het volgende. In numerieke modelberekeningen wordt een ruimtelijk gebied (domein) vaak onderverdeeld (gediscretiseerd) in een rooster, opgebouwd uit rechthoekige cellen en de op het domein uit te voeren berekeningen worden vervolgens opgesplitst over de cellen binnen het domein. Hierbij kan bijvoorbeeld worden gedacht aan het voorbeeld met de berekening van het zwaartekrachtseffect van een dichtheidsanomalie in de ondergrond, behandeld in §.3.2.1 van deze syllabus. Belangrijker voorbeelden van deze discretisatie methoden zijn de eindige differentie methode en de eindige elementen methode, waarmee parti¨ele differentiaalvergelijkingen op een domein numeriek kunnen worden opgelost, wat een essentieel onderdeel vormt van veel methoden van numeriek modelleren in uiteenlopende technische en wetenschappelijke toepassingen. In het onderstaande voorbeeld wordt e.e.a. geillustreerd voor een rechthoekig rooster voor het ruimtelijk domein [0, 1] × [0, 1]. Het rooster bestaat uit nrij rijen en nkol kolommen van roosterpunten op onderling gelijke horizontale (dx) en verticale (dy) afstanden. Door de snijdende roosterlijnen wordt het domein gediscretiseerd in rechthoekige cellen. We willen nu een niet-standaard datatype introduceren aangeduid als cel_type. In de variabelen van dit type, aangeduid als ‘structures’ willen we de roosterknooppunt nummers en de x, y-coordinaten van de vier hoekpunten van een cel samenvoegen. Het volledige rooster wordt hiermee vervolgens gedefinieerd als een array van structures - de individuele roostercellen. In het programma kunnen bewerkingen op het hele domein worden opgesplitst over de roostercellen en dit is ge¨ımplementeerd door de bewerkingen in een loop over de elementen van het array van structures. De eenvoudige bewerking in het voorbeeld programma is het printen van de knooppuntnummers en x, y-coordinaten van de roostercellen. De hierin gebruikte format statements voor het formatteren van de programma output worden behandeld in hoofdstuk 13 van de syllabus. Merk op, dat alle type declaraties m.b.t. de ‘structure’ variabelen in de module zijn ondergebracht, resulterend in een beknopte programmacode voor de resterende programmaeenheden. Verder wordt het rooster array als ‘allocatable’ array gedeclareerd - zie hoofdstuk 11 - waardoor de programma code toepasbaar is geworden voor willekeurige roosterdimensies. module rooster_module !* type def. rooster cellen type cel_type integer, dimension(1:4) :: knoop_num real, dimension(1:4) :: knoop_x, knoop_y end type cel_type !* aantal rijen, resp. kolommen van roosterpunten integer nrij, nkol !* aantal roostercellen en roosterpunten integer ncel, nknoop ! compleet roosterarray van roostercellen type (cel_type), dimension(:), allocatable :: cellen_rooster end module rooster_module !----------------------------------------------------------------------
78
program exmrooster use rooster_module implicit none ! prompt voor rooster spec’s write(6,ADVANCE=’NO’,FMT=’(’’enter nrij,nkol >’’)’) read(5,*) nrij,nkol ! lees spec’s van stdin call defrooster ! bereken rooster data call prnrooster ! print rooster data end !---------------------------------------------------------------------subroutine defrooster use rooster_module implicit none integer cel,kol,rij real dx,dy ncel nknoop = dx dy
= (nrij-1) * (nkol-1) nrij * nkol = 1.0 / (nkol-1) = 1.0 / (nrij -1)
! def. rooster spec’s ! in rooster_module ! horiz. roosterafstand ! vert. roosterafstand
!* alloceer array ruimte voor het rooster array allocate (cellen_rooster(ncel)) !* loop over rooster kolommen do kol = 1, nkol-1 !* loop over rooster rijen do rij = 1, nrij-1 cel = (kol-1)*(nrij-1) + rij ! nummer punten anti-klok vanaf linksonder (zw) cellen_rooster(cel)%knoop_num(1) =(kol-1)*nrij + rij cellen_rooster(cel)%knoop_x (1) = dx * (kol-1) cellen_rooster(cel)%knoop_y (1) = dy * (rij-1)
!zw !zw !zw
cellen_rooster(cel)%knoop_num(2) =(kol )*nrij + rij cellen_rooster(cel)%knoop_x (2) = dx * (kol ) cellen_rooster(cel)%knoop_y (2) = dy * (rij-1)
!zo !zo !zo
cellen_rooster(cel)%knoop_num(3) =(kol )*nrij + rij+1 !no cellen_rooster(cel)%knoop_x (3) = dx * (kol ) !no cellen_rooster(cel)%knoop_y (3) = dy * (rij ) !no cellen_rooster(cel)%knoop_num(4) =(kol-1)*nrij + rij+1 !nw cellen_rooster(cel)%knoop_x (4) = dx * (kol-1) !nw cellen_rooster(cel)%knoop_y (4) = dy * (rij ) !nw end do end do return end !---------------------------------------------------------------------subroutine prnrooster use rooster_module implicit none integer cel,celknoopnum write(6,1000) ncel,nknoop,nrij,nkol !* loop over roostercellen
79
do cel=1,ncel write(6,2000) cel !* loop over vier knoopen do celknoopnum=1,4 write(6,3000) cellen_rooster(cel)%knoop_num(celknoopnum), cellen_rooster(cel)%knoop_x (celknoopnum), cellen_rooster(cel)%knoop_y (celknoopnum) end do enddo
1000
2000 3000
& & &
return format(//,’prnrooster - print rooster data:’,/, & &’ncel ........... ’,i5,/, & &’nknoop ......... ’,i5,/, & &’nrij, nkol ..... ’,2i5) format(/,’prnrooster - cell nummer:’,i5) format(’knoop_num =’,i5,’ knoop_x =’,e12.4,’ knoop_y =’,e12.4) end
De programma output van bovenstaand programma voor een run met nrij=3, nkol=3 is hieronder weergegeven. enter nrij,nkol > prnrooster - print rooster data: ncel ........... 4 nknoop ......... 9 nrij, nkol ..... 3 3 prnrooster - cell nummer: knoop_num = 1 knoop_x = knoop_num = 4 knoop_x = knoop_num = 5 knoop_x = knoop_num = 2 knoop_x =
1 0.0000E+00 0.5000E+00 0.5000E+00 0.0000E+00
knoop_y knoop_y knoop_y knoop_y
= = = =
0.0000E+00 0.0000E+00 0.5000E+00 0.5000E+00
prnrooster - cell nummer: knoop_num = 2 knoop_x = knoop_num = 5 knoop_x = knoop_num = 6 knoop_x = knoop_num = 3 knoop_x =
2 0.0000E+00 0.5000E+00 0.5000E+00 0.0000E+00
knoop_y knoop_y knoop_y knoop_y
= = = =
0.5000E+00 0.5000E+00 0.1000E+01 0.1000E+01
prnrooster - cell nummer: knoop_num = 4 knoop_x = knoop_num = 7 knoop_x = knoop_num = 8 knoop_x = knoop_num = 5 knoop_x =
3 0.5000E+00 0.1000E+01 0.1000E+01 0.5000E+00
knoop_y knoop_y knoop_y knoop_y
= = = =
0.0000E+00 0.0000E+00 0.5000E+00 0.5000E+00
prnrooster - cell nummer: knoop_num = 5 knoop_x = knoop_num = 8 knoop_x = knoop_num = 9 knoop_x = knoop_num = 6 knoop_x =
4 0.5000E+00 0.1000E+01 0.1000E+01 0.5000E+00
knoop_y knoop_y knoop_y knoop_y
= = = =
0.5000E+00 0.5000E+00 0.1000E+01 0.1000E+01
OPDRACHT 12-1 Ga na, dat het rooster is gedefinieerd met eenheidsafmetingen. Hoe zouden de afmetingen meer algemeen gemaakt kunnen worden.
80
Hoofdstuk 13
Input en output in Fortran programma’s Het doel van de meeste (Fortran) programma’s is het bewerken en berekenen van (vaak grote hoeveelheden) gegevens. Data moet hiervoor worden ingelezen (input) en worden weggeschreven (output). Dit hoofdstuk behandelt de verschillende vormen van input en output.
13.1
Het gebruik van input en output files
Tot nu toe hebben we in voorbeelden van programma’s uitsluitend input gezien van een toetsenbord en output naar een beeldscherm in de vorm read *, in te lezen variabelen print *, weg te schrijven variabelen & constanten Het input en output ’verkeer’ wordt kortweg I/O genoemd. Waar de input bij dit read statement vandaan komt en waar de output bij dit print statement naar toe gaat is systeem afhankelijk. In het UNIX en MS-DOS besturingssysteem komt de input van het zogenaamde standard input device (of kortweg stdin, normaliter het toetsenbord) en gaat de output naar het standard output device (of kortweg stdout, normaliter het beeldscherm). Van deze organisatie kan op twee manieren worden afgeweken: 1. met behulp van I/O redirection 2. door het gebruik van logical units
13.1.1
Input/output redirection van/naar een file
Met I/O redirection kan men data van en naar files ‘doorsluizen’: input redirection: de inhoud van een file wordt ‘doorgesluisd’ naar stdin output redirection: de inhoud van stdout wordt ‘doorgesluisd’ naar een file Dit gebeurt niet binnen het programma, maar via de command line van de programma-aanroep: prognam [< inputfile] [> outputfile] Input kan hiermee van tevoren worden aangemaakt (bij veel of routine-matige input) en output kan hiermee bewaard worden voor nadere inspectie.
13.1.2
Input/output van/naar een file met logical units
I/O redirection beperkt zich tot ´e´en input- en ´e´en output file. Wil men meerdere files vanuit een programma kunnen gebruiken voor input of output, dan moet de koppeling tussen programma en files binnen het programma tot stand gebracht worden. Dit moet voor iedere input- of outputfile apart worden uitgevoerd met een open statement die in een eenvoudig geval van de vorm is
81
syntax open ([UNIT=]lu, FILE=filespec) Hierin zijn de Fortran ‘keywords’: UNIT alleen optioneel als (eerste) keyword. Aan iedere file, die ‘geopend’ is voor een programma wordt op deze manier een uniek ‘logical unit number’ lu toegekend. Geldige waarden voor lu zijn systeem afhankelijk, maar meestal is lu ∈ [1, 100]. Bij de UNIX en de MS-DOS Fortran compilers zijn de unit numbers 5 en 6 gereserveerd voor respectievelijk stdin en stdout. FILE het (tweede) keyword: verplicht. Met f ilespec wordt de padnaam (zie ook §.2.4.1) van de betreffende file aangegeven als character constante, omgeven met quotes, of als character variabele. Na het openen van de betreffende files kan met de volgende statements respectievelijk worden gelezen van en geschreven naar de logical unit: syntax read ([UNIT=] lu, [FMT=] label ) inputlist write ([UNIT=] lu, [FMT=] label ) outputlist Hierin is:
UNIT zoals besproken bij bovenstaande syntax van het open statement FMT het format (vorm/layout) waarin gelezen of geschreven moet worden: optioneel Dit onderwerp wordt uitgebreid behandeld in sectie 13.4. Voorlopig volstaat de uitleg, dat de asterisk * free format betekent (zie §.13.4.2) label het statement labelnummer van een format statement (zie hieronder). VOORBEELDEN • De padnaam wordt in het open statement opgegeven met een character constante: open ( UNIT=10, FILE=’/subdir/data’) of character variabele: character*20 fnaam ... fnaam = ’/subdir/data’ open ( UNIT=10, FILE=fnaam ) • Het volgende voorbeeld bevat zowel I/O van files als stdin & stdout: program ftest character*20 inaam, onaam ... write(6,*) ’type de inputfile naam’ read(5,*) inaam open(UNIT=1, FILE=inaam) write(6,*) ’type de outputfile naam’ read(5,*) onaam open(UNIT=2, FILE=onaam) read(1,*) input list verwerk de input tot output write(2,*) output list end De file inaam kan hierin een met een text-editor aangemaakte tekst file zijn.
OPDRACHT 13-1 Schrijf een programma, dat een gegeven aantal integer getallen uit een file leest en daarvan de grootste en kleinste op het scherm afdrukt. De naam van de file moet van stdin gelezen worden.
82
13.2
Uitgebreider I/O syntax
De uitgebreide versie van het open statement is:
syntax open ( [UNIT=] lu, FILE= filespec + [,ACCESS= acc.type] [,RECL= len] [,FORM= formtype] + [,ERR= errlabel] [,IOSTAT= ios] ) De bijbehorende read en write statements hebben de volgende syntax:
syntax read ( [UNIT=] lu [,[FMT=] format] [,REC= irecnr] [,IOSTAT= ios] + [,ERR= errlabel] [,END= endlabel] ) inputlist write ( [UNIT=] lu [,[FMT=] format] [,REC= irecnr] + [,IOSTAT= ios] [,ERR= errlabel] ) outputlist
Hierin moeten of mogen de volgende keywords worden gedefinieerd: UNIT besproken in §.13.1.2 FILE besproken in §.13.1.2 ACCESS Fortran kent twee typen file toegangsvormen: sequenti¨eel en direct-toegankelijke. §.13.3 behandelt dit onderwerp. acc.type heeft ´e´en van de volgende waarden: • ’sequential’ (dit is de default) files van dit type kunnen uitsluitend van voor naar achter worden gelezen of geschreven. • ’direct’ records van files met dit type kunnen in willekeurige volgorde worden gelezen of geschreven. RECL Verplicht in het open statement bij gebruik van direct-access files (ACCESS=’direct’): geeft bij deze toegangsvorm de vaste record-lengte aan len (meestal in bytes, soms in woorden(systeem-afhankelijk)) gewerkt. (zie verder §.13.3) REC Verplicht in het read en write statement bij gebruik van direct-access files (ACCESS=’direct’): geeft het record nummer irecnr van het record aan waar vandaan gelezen of waar naartoe geschreven moet worden. FORM Bij het lezen/schrijven van data kan wel of geen bepaalde format (vorm/layout) worden meegegeven. Zonder format wordt de data binair opgeslagen (zie ook appendix B). §.13.4 behandelt dit onderwerp. De opties voor formtype zijn: • ’formatted’ (dit is de default) • ’unformatted’
FMT Alleen bij het gebruik van geformatteerde layout (FORM=’formatted’): format wordt opgegeven in de vorm van: • een asterisk voor z.g.n. list directed I/O (zie §.13.4.2)
• een format character constante (omgeven door quotes) (zie §. 13.4.3)
• een label van een format statement (zie §. 13.4.3)
ERR Optioneel: als tijdens het openen/lezen of schrijven een fout opstreedt, wordt naar het label errlabel gesprongen, waarvandaan het programma wordt voortgezet. END Optioneel: als tijdens het lezen het einde van de file bereikt wordt, wordt naar het label endlabel gesprongen, waarvandaan het programma wordt voortgezet. IOSTAT Optioneel: het type foutmelding wordt als (systeem-afhankelijke) integer-waarde in ios opgeslagen. Zonder fouten wordt ios = 0. De keywords UNIT en FMT mogen achterwege blijven, mits deze definities als respectievelijk eerste en tweede keyword definitie worden genoemd. 83
13.3
Verschillende file toegangsvormen in Fortran
Een datafile is in het algemeen onderverdeeld in kleinere eenheden, zogenaamde records. In Fortran worden file typen van verschillende datastruktuur en bijbehorende toegangsvorm onderscheiden. Dit type wordt gespecificeerd via het keyword ACCESS) opgegeven in het open statement, §13.2). De mogelijkheden zijn sequential toegankelijke en direct toegankelijke files.
13.3.1
Sequentieel toegankelijke files
De tot nu toe behandelde voorbeelden van files hebben allen betrekking op sequenti¨ele files. Deze toegangsvorm is default bij het openen van een file, maar kan ook expliciet vermeld worden met de keyword definitie ACCESS=’sequential’ in het open statement. De logische struktuur van zo’n file komt overeen met die van een magnetische tape: de file wordt sequentieel (van voor naar achter) doorlopen. Een ‘file pointer’ houdt de (begin)positie van het volgende record bij. Met een rewind statement kan de file ’teruggespoeld’ worden: syntax rewind lu waarna de file pointer weer naar het eerste record verwijst. Een voordeel van deze toegangsvorm is, dat de lengte van de record niet a priori bekend en niet contstant hoeft te zijn.
13.3.2
Direct toegankelijke files
Soms is het gewenst om de records niet sequentieel maar in een andere volgorde te lezen of te schrijven. In dit geval kan van de direct access toegangsvorm gebruik gemaakt worden. De logische struktuur van een direct access file komt overeen met een magnetische schijf, waarvanaf alle aanwezige datarecords in willekeurige volgorde kunnen worden gelezen of geschreven, door de leeskoppen te verplaatsen. Deze toegangsvorm moet expliciet worden vermeld bij het openen van een file met de keyword definitie ACCESS=’direct’ in het open statement evenals de bijbehorende vaste record lengte RECL=len. I/O met ´e´en bepaalde record irecnr in de geopende file is nu mogelijk is met de keyword definitie REC=irecnr in een read of write statement.
13.4
I/O formats
13.4.1
Ongeformatteerde I/O
I/O kan zonder format (ongeformatteerd) plaatsvinden met z.g.n. binaire files. Dit moet bij het openen van de betreffende file worden aangegeven met de keyword definitie FORM=’unformatted’. De format keyword definitie [FMT=]format wordt in dat geval in de read en write statements weggelaten. De data wordt dan binair gelezen of weggeschreven, d.w.z. gebruikmakend van de (systeemafhankelijke) machine representatie (zie appendix B). Dit is de snelste en meest efficiente manier van data wegschrijven, gemeten naar de resulterende file grootte uitgedrukt in bytes. Een beperking is, dat binaire files niet met een text-editor ‘leesbaar’ zijn, omdat text-editors ASCII-code verwachten en geen binaire code.
13.4.2
Free format of list directed I/O
Bij geformatteerde I/O kan een file worden geopend met de keyword definitie FORM=’formatted’. Het geheel weglaten van de keyword definitie heeft echter hetzelfde effect, aangezien formatted I/O default is. In het read of write statement wordt het format met een keyword definitie opgegeven: [FMT=]format. FMT= is hierin optioneel, format niet! Tot nu toe hebben we uitsluitend voorbeelden van list directed of ook wel free format I/O beschouwd (niet te verwarren met unformatted of binary I/O in §.13.4.1): er wordt wel een format gebruikt, maar het preciese formaat van de data wordt niet gespecificeerd. Dit type format wordt in het read of write statement aangeduid met de keyword definitie [FMT=] * , zie §.13.2. Bij free format input wordt opeenvolgende data (uit de ASCII dataset, maar van willekeurige format) gescheiden door komma’s en/of (´e´en of meerdere) spaties verwacht. Dit verhoogt dus de flexibiliteit van een programma m.b.t. het input formaat. Appendix B bespreekt de ASCII data set. Bij free format input van character variabelen moeten in te lezen strings omgeven worden door quotes (’ ’). 84
Bij free format output wordt er van een standaard output formaat gebruikt. Zo worden bij uitvoer van een real variabele een standaard aantal decimalen gebruikt en het aantal spaties tussen twee opeenvolgende uitgevoerde variabelen ligt eveneens vast. Free format output is eenvoudig in het gebruik, maar vooral bij het uitvoeren van grotere hoeveelheden geformatteerde data - bijvoorbeeld in tabelvorm - is list directed output minder geschikt dan het opgeven van exacte format specificatie (zie §.13.4.3).
13.4.3
I/O met format specificaties
Naast unformatted en list directed I/O kan het format ook expliciet gespecificeerd worden. Tabellen kunnen op deze manier bijvoorbeeld mooi worden vormgegeven. Het format kan rechtstreeks als character string in het read of write statement opgegeven worden: syntax read( [UNIT=]lu, [FMT=] ’format list’, [...] ) varlist of er kan naar een label met een format statement verwezen worden:
syntax read( [UNIT=] lu, [FMT=] label [,...] ) varlist label format format list met label een statement label en format list een lijst van zogenaamde edit descriptors, gescheiden door komma’s en omgeven door ronde haken. type edit descriptor opties
numeriek rfw.d rew.d rgw.d rdw.d riw.m rfw.d
character 0
raw c1 c2 . . . c0n it rx
format control : / $
Tabel 13.1: Voorkomende edit descriptors
Voorkomende edit descriptors zijn gegeven in tabel 13.1. Hierin is: • r een optionele herhalings operator (bv.: 3i2 ≡ i2,i2,i2) • w de totale breedte van het veld (inclusief eventuele decimale punt en exponent) • d het aantal decimalen • m een positieve constante, die het minimum aantal cijfers in de uitvoer van een integer getal aangeeft. Zonodig worden niet significante nullen links toegevoegd. • ci een character uit de ASCII character set. r, w, d en m zijn positieve integer constanten. We bespreken achtereenvolgens de in de overzichtstabel genoemde edit descriptors. Numerieke edit descriptors fw.d - fixed point descriptor voor de weergave van re¨ele getallen: variabelen worden weergegeven met een minteken (voor negatieve getallen), gevolgd door de decimalen van het gehele deel, de decimale punt en de decimalen van het breuk deel. Er wordt afgerond naar d decimalen achter de punt. Als een waarde het totale veld van w posities niet opvult, dan wordt er rechts opgelijnd. Als het aantal posities w te klein is, dan wordt het hele ’veld’ gevuld met asterisk tekens (*). Complexe variabelen moeten als twee re¨ele getallen worden aangegeven, corresponderend met een re¨eel en een imaginair deel. Er is geen aparte descriptor voor complexe variabelen. ew.d - floating point descriptor voor reals voor re¨ele getallen, voor een exponenti¨ele notatie. 85
gw.d - fixed/floating point - combinatie descriptor Afhankelijk van de waarde gelijk aan het fw.d of ew.d format. Er wordt een fixed point notatie met d cijfers gegeven, met uitzondering van twee gevallen, waarin een floating point format wordt gebruikt: • als het eerste cijfer achter de decimale punt nul is • als het aantal decimalen v` oo `r de decimale punt groter is dan d Een aantal voorbeelden: Format Interne Resulterend Output descriptor waarde format g12.4 .056321 e12.4 .5632E-01 g12.4 .563217 f8.4,4x .5632 g12.4 5.63217 f8.3,4x 5.632 g12.4 563.217 f8.1,4x 563.2 g12.4 56321.7 e12.4 .5632E+05 In deze voorbeelden hebben alle weergegeven getallen vier significante cijfers.
dw.d - floating point descriptor voor double precision getallen produceert dezelfde output als de e descriptor, maar met D in plaats van E. (bedoeld voor double precision variabelen iw.m - integer descriptor w is de breedte van het totale veld inclusief het min teken bij negatieve integers. m specificeert het minimaal aantal weergegeven cijfers - eventueel verkregen door niet significante nullen links toe te voegen. De .m extentie is optioneel. VOORBEELD Ter verduidelijking van de numerieke edit descriptor definities volgt hier een voorbeeld van een Fortran 77 code: en bijbehorende output: program descriptor1 implicit none c ---------------------------------------c | Illustraties van numerieke descriptors c ---------------------------------------real r1, r2, r3 integer i1, i2, i3
10 20 30 40
r1 = 3.14159 r2 = -273. r3 = 1.23456e5 write(6, 10) r1, write(6, 20) r1, write(6, 30) r1, i1 = 98 i2 = -76 i3 = 54321 write(6, 40) i1, format(3f10.3) format(3e10.3) format(3g10.3) format(3i4.3) end
r2, r3 r2, r3 r2, r3
i2, i3
86
3.142 -273.000123456.000 0.314E+01-0.273E+03 0.123E+06 3.14 -273. 0.123E+06 098-076****
Character edit descriptors
aw - character descriptor Als w afwezig is, wordt de gehele character string verwerkt. Als w kleiner is dan de lengte van de input/output string, worden alleen de meest linkse w characters genomen. Als w groter is dan de lengte van de string, dan wordt de string rechts opgelijnd (links opgevuld met spaties). 0
c1 c2 . . . c0n - apostrophe descriptor voor een constante tekst string: de string wordt omgeven door enkele aanhalingstekens (apostrophes). In het algemeen geldt, dat aanhalingstekens binnen een string verdubbeld moeten worden opgegeven (zoals bv. in ’programma’’s’). De apostrophe notatie wordt behalve voor het aangeven van output strings ook in input strings gebruikt. Wanneer een character string m.b.v. list-directed input wordt gelezen, dat moet deze omgeven zijn met apostrophes. De apostrophes worden zelf niet intern opgeslagen, maar dienen als beginen eindmarkering. Deze manier van list-directed input maakt het gebruik van character variabelen flexibeler.
x - spati¨erings descriptor Hiermee kunnen spaties in uitvoer-strings worden tussengevoegd. Met rx worden r spaties tussengevoegd.
VOORBEELD Ter verduidelijking van de character edit descriptor definities volgt hier een voorbeeld en bijbehorende output: van een Fortran 77 code: program descriptor2 c ---------------------------------------c | Illustraties van character descriptors c ---------------------------------------write(6,10) ’Tekst’, ’Tekst’, ’Teveel tekst’ write(6,20) ’3x’,’en’,’ ’’3 spaties’’ zijn’ 10 format(a5,’+’,a5,’=’a5) 20 format(a,3x,a,’ ’,a,’ gelijk’) end
Tekst+Tekst=Tevee 3x en ’3 spaties’ zijn gelijk
Format control : - colon descriptor Met de colon (:) descriptor kan de format control worden afgesloten als er geen verdere items in de output list over zijn.
VOORBEELD In onderstaand programma wordt met de colon descriptor voorkomen dat aan het einde van de output list nog eens de regel header resultaat2 = wordt geprint:
87
de Fortran code:
met bijbehorende output:
program formtst implicit none c ---------------------------------------c | Illustraties van de colon descriptor c ---------------------------------------real result1, result2 result1 = 101. result2 = 102. write(6,100) result1,result2 100 format(10(’resultaat1 = ’,f7.1,/)) write(6,200) result1,result2 200 format(10(’resultaat2 = ’,f7.1,:,/)) end
resultaat1 resultaat1 resultaat1 resultaat2 resultaat2
= = = = =
101.0 102.0 101.0 102.0
/ - slash descriptor Met de slash (/) descriptor kan de huidige regel worden afgebroken zowel bij input als bij output toepassingen. Bij output worden bij een slash naar de nieuwe regel gesprongen. Bij een input toepassing wordt dan het nog niet gelezen deel van de inputregel overgeslagen. VOORBEELD program inputtest implicit none integer inputdata(3)
100
read(5,100) inputdata format( 3(i2,/)) print*, inputdata(1), inputdata(2), inputdata(3) end
met als input:
geeft als output:
1 2 3 4 5 6 7 8 9 Dit is commentaar
1 4 7
$ - dollar descriptor Met de dollar ($) descriptor aan het eind van een output string wordt de normaliter automatisch gegenereerde ‘newline’ onderdrukt. De beeldscherm cursor blijft dan aan het einde van de output string staan. Dit wordt vooral toegepast bij programma ‘prompts’, waarmee een programma om input vraagt. Deze descriptor behoort niet tot de Fortran standaard maar is in verschillende taal extensies ge¨ımplementeerd. VOORBEELD De cursor blijft hier achter het > teken staan, totdat een getal is ingevoerd: write(6,’(’type een waarde voor x > ’,$)’) read(5,*) x
De Fortran 90/95 standaard bevat een voorziening waarmee hetzelfde kan worden bereikt, z.g.n. non-advancing I/O. Dit moet via een apart keyword ADVANCE in de write statement worden aangegeven zoals in onderstaand voorbeeld. 88
VOORBEELD De cursor blijft hier achter het > teken staan, totdat een getal is ingevoerd: write(6,ADVANCE=’NO’,FMT=’(’type een waarde voor x > ’)’) read(5,*) x
Merk op dat nu ook het keyword FMT moet worden aangegeven. VOORBEELDEN • Een voorbeeld van het gebruik van een wat gecompliceerder format specificatie is het volgende, waarin een matrix, opgeslagen in een 2-D array wordt geprint in een loop over de matrix rijen. De afzonderlijke matrix rijen worden geprint met een z.g.n. ‘implied do’ construct. Een implied do is een lijst met data specificaties die een index bevat, gevolgd door een index specificatie. De index specificatie komt overeen komt met die in een gewone Fortran do loop (zie H.10). ( data list, index = m1, m2 [, m3]) De matrix wordt in dit geval geprint in een (buitenste) loop over de matrix rijen. Iedere rij wordt voorafgegaan door het rijnummer. De rij elementen worden geprint in een (binnenste loop) over de matrix kolommen. Ieder rij element wordt voorafgegaan door het kolom nummer gevolgd door een ‘:’.
100 200
300 400
program matpr implicit none integer nrij, nkol, i, j real a parameter (nrij=10,nkol=10) dimension a(nrij,nkol) do 200 i=1,nrij do 100 j=1,nkol a(i,j) = i + 0.1*j continue continue do 300 i =1,nrij write(6,400) i,(j,a(i,j),j=1,nkol) continue format(100(i5,5(i5,’:’,f5.2),/, . 100(5x,5(i5,’:’,f5.2),/) )) end
Dit produceert de volgende output: 1
1: 1.10 6: 1.60
2: 1.20 7: 1.70
3: 1.30 8: 1.80
4: 1.40 9: 1.90
5: 1.50 10: 2.00
2
1: 2.10 6: 2.60
2: 2.20 7: 2.70
3: 2.30 8: 2.80
4: 2.40 9: 2.90
5: 2.50 10: 3.00
3
1: 3.10 6: 3.60
2: 3.20 7: 3.70
3: 3.30 8: 3.80
4: 3.40 9: 3.90
5: 3.50 10: 4.00
4
1: 4.10 6: 4.60
2: 4.20 7: 4.70
3: 4.30 8: 4.80
4: 4.40 9: 4.90
5: 4.50 10: 5.00
5
1: 5.10 6: 5.60
2: 5.20 7: 5.70
3: 5.30 8: 5.80
4: 5.40 9: 5.90
5: 5.50 10: 6.00
6
1: 6.10 6: 6.60
2: 6.20 7: 6.70
3: 6.30 8: 6.80
4: 6.40 9: 6.90
5: 6.50 10: 7.00
7
1: 7.10
2: 7.20
3: 7.30
4: 7.40
5: 7.50
89
6: 7.60
7: 7.70
8: 7.80
9: 7.90
10: 8.00
8
1: 8.10 6: 8.60
2: 8.20 7: 8.70
3: 8.30 8: 8.80
4: 8.40 9: 8.90
5: 8.50 10: 9.00
9
1: 9.10 6: 9.60
2: 9.20 7: 9.70
3: 9.30 8: 9.80
4: 9.40 9: 9.90
5: 9.50 10:10.00
10
1:10.10 6:10.60
2:10.20 7:10.70
3:10.30 8:10.80
4:10.40 9:10.90
5:10.50 10:11.00
• In het volgende voorbeeld wordt een deel van de ASCII-tekenset in tabel vorm geprint: program asciitabel implicit none c c c c c
* * drukt de ASCII-tekens in tabelvorm af * plaatsen 32 t/m 126 zijn leesbare ASCII-tekens * * Declaraties integer iteken character*1 char intrinsic char
c * Code write(6,100) (iteken, char(iteken), iteken=32,126) 100 format(100(7(i5,’:’,a1,3x),/)) end met als resultaat: 32: 39:’ 46:. 53:5 60:< 67:C 74:J 81:Q 88:X 95:_ 102:f 109:m 116:t 123:{
13.5
33:! 40:( 47:/ 54:6 61:= 68:D 75:K 82:R 89:Y 96:‘ 103:g 110:n 117:u 124:|
34:" 41:) 48:0 55:7 62:> 69:E 76:L 83:S 90:Z 97:a 104:h 111:o 118:v 125:}
35:# 42:* 49:1 56:8 63:? 70:F 77:M 84:T 91:[ 98:b 105:i 112:p 119:w 126:~
36:$ 43:+ 50:2 57:9 64:@ 71:G 78:N 85:U 92:\ 99:c 106:j 113:q 120:x
37:% 44:, 51:3 58:: 65:A 72:H 79:O 86:V 93:] 100:d 107:k 114:r 121:y
38:& 45:52:4 59:; 66:B 73:I 80:P 87:W 94:^ 101:e 108:l 115:s 122:z
Internal files
In plaats van I/O van en naar files in het achtergrond geheugen (disk) kan men ook met de gebruikelijke (read en write) statements data lezen en schrijven in het interne geheugen. In plaats van een unit nummer wordt dan in de read/write statement een character variabele gebruikt. Deze vorm is wel beperkt tot geformatteerde I/O. Een format-specificatie is in dit geval verplicht, d.w.z. list-directed input is niet toegestaan bij internal files. Dit geeft een uitbreiding van de mogelijkheden om in een programma met character strings te manipuleren. VOORBEELDEN • We geven eerst een voorbeeld van een internal write, waarin in een programma een serie filenamen data.001, ..., data.010 worden gedefinieerd en de corresponderende files vervolgens worden geopend. program write_char implicit none
90
100
integer iteller character*20 naam do 100 iteller =1,10 write(naam, ’(’’data.’’,i3.3)’) iteller open( UNIT=10+iteller, FILE=naam) print*,’Geopende file heeft naam:’, naam continue end
OPDRACHT 13-2 Ga na hoe dit voorbeeld kan worden aangepast wanneer we de ‘stam’ van de complete file naam in het voorbeeld data - willen inlezen en dus met een variabele lengte rekening moeten houden.
• Het is handig om bij interactieve programma’s (programma’s die input parameters van het toetsenbord lezen) voor standaard waarden van bepaalde input parameters te kunnen kiezen, door bijvoorbeeld in plaats van de waarde van de parameter afgesloten met een return teken alleen het return teken in te typen. We kiezen in dat laatste geval voor de zogenaamde default waarde van de betreffende parameter. In het volgende voorbeeld wordt gebruik gemaakt van de functie lnblnk, waarmee de positie van het laatste niet-spatie character in een character string kan worden bepaald (zie hoofdstuk 10):
c
c
c
13.6
character*20 antwoord ... * defin. een default waarde voor x xdefl = expressie write(6,’(’type een waarde voor x in (default’,e10.3,’ >’,$)’) xdefl read(5,’(a)’) antwoord if(lnblnk(antwoord).gt.0) then * lees x met een internal read uit het character buffer read(antwoord,’(f5.1)’) x else * lege input string: gebruik de default waarde x = xdefl end if ... end
Opslag van data - files en records
Data bestanden in de vorm van (external) files zijn fysiek aanwezig in het achtergrond geheugen (magnetic disk/tape). Ze worden onderverdeeld in eenvoudig hanteerbare eenheden, ‘records’ genoemd. Bijvoorbeeld bij een tekst file zijn de records de verschillende tekstregels, afgesloten door een ‘newline’ character (UNIX) of een ‘carriage return’ plus een ‘newline’ character (MSDOS). Men onderscheidt nog physical records en logical records: Physical records zijn de data eenheden, die de computer met het specifieke achtergrond geheugen uitwisselt. Een physical record - ook wel record block grootte - is voor I/O naar disk bijvoorbeeld 1024 bytes voor het UNIX systeem bij Geofysica en 512 bytes voor MS-DOS systemen. Bij het gebruik van magnetische tapes is het vaak mogelijk de record block grootte zelf te specificeren (met een systeem afhankelijk maximum). Het is dan effici¨ent grote tape blocks te gebruiken. Logical records worden bepaald door een programma, dat I/O uitvoert. De hoeveelheid data, die per read/write operatie in een programma wordt gelezen respectievelijk geschreven wordt een 91
logical record genoemd. In het eerder genoemde voorbeeld van een tekst file zijn de afzonderlijke tekst regels - door een programma (bijvoorbeeld een text-editor) naar file geschreven - de logical records.
92
Hoofdstuk 14
Het data type complex Fortran kent het data type complex, waarmee complexe getallen kunnen worden voorgesteld. Complexe variabelen worden gedeclareerd met de volgende type declaratie, syntax complex varlist Complexe getallen kunnen voorkomen in rekenkundige expressies. Hierbij kunnen dezelfde rekenkundige operatoren worden gebruikt als bij expressies met data van het type real: +, −, ∗, / en ∗∗. Wanneer een complexe grootheid in een expressie voorkomt samen met reals en integers, dan wordt de expressie als een complexe expressie ge¨evalueerd. Verschillende intrinsieke functies zijn beschikbaar voor complexe argumenten en resultaat waarden. Hierbij kan gebruik gemaakt worden van de generieke vorm van de functie danwel van de specifieke vorm (zie H.8). VOORBEELD Een complexe exponent kan bijvoorbeeld worden berekend met de generieke functie aanroep exp1 of met de specifieke aanroep cexp complex carg = cresl = of ook: cresl =
carg, cresl complexe expressie exp(carg)
! generic form
cexp(carg)
! type specific form
De volgende taalelementen zijn specifiek voor gebruik van complexe variabelen: 1. Conversie van twee real variabelen in ´e´en enkele complexe variabele door een waardetoekenning van een re¨eel en imaginair deel. Voor constante waarden van re¨eel en imaginair deel gaat dit alsvolgt: complex imagunit . . . imagunit = ( 0.0, 1.0 ) Voor een variabel re¨eel en imaginair deel moet de intrinsieke functie cmplx worden gebruikt: complex cvar real varre, varim . . . varre = real expressie varim = real expressie cvar = cmplx( varre, varim ) 1 Zie voor een volledige lijst van intrinsieke functies en bijbehorende generieke en specifieke vorm, het Fortran manual.
93
2. De omgekeerde operatie, conversie van complexe naar re¨ele variabelen gaat met de intrinsics real en imag, zoals bijvoorbeeld in complex cvar real varre, varim . . . cvar = complex expressie varre = real( cvar ) varim = imag( cvar ) Een andere speciale intrinsic is conjg voor het berekenen van de complex geconjugeerde van een complex argument. Een enkelvoudige complexe variabele wordt als twee real variabelen - corresponderend met re¨eel en imaginair deel - in twee opeenvolgende geheugen adressen opgeslagen. Een complex array c van lengte n wordt intern in het geheugen opgeslagen in de volgorde: Re{c(1) }, Im{c(1) }, Re{c(2) }, Im{c(2) },..., Re{c(n) }, Im{c(n) } OPDRACHT 14-1 Ga na, dat een 1-D complex array de struktuur heeft van een 2-D real array met leidende dimensie 2.
Bij I/O moet rekening worden gehouden, dat per complex getal twee re¨ele getallen (re¨eel en imaginair deel) worden verwerkt. Bij format specificaties moeten zo per complex getal twee real specificaties worden gebruikt. VOORBEELD program comtst implicit none real pi complex carg,ci,csin090,csin270 parameter (pi= 3.1415926532)
100
carg = (-1.,0.) ci = sqrt(carg) csin090 = exp( ci*pi/2) csin270 = exp(-ci*pi/2) write(6,100) carg,ci,csin090,csin270 format(’comtst - print complexe expressies:’,/, 1 ’carg ............. ’,2f5.0,/, 2 ’ci ............... ’,2f5.0,/, 3 ’csin090 .......... ’,2f5.0,/, 4 ’csin270 .......... ’,2f5.0) end
Dit programma produceert de output: comtst - print complexe expressies: carg ............. -1. 0. ci ............... 0. 1. csin090 .......... 0. 1. csin270 .......... 0. -1.
94
Bijlage A
Handleiding bij de unix screen editor vi A.1
Algemeen
In deze appendix worden enkele commando’s voor de UNIX full-screen editor vi besproken. Een vollediger beschrijving van deze tektst editor is te vinden in de UNIX tutorials 1 .
A.2
Programma aanroep
De editor vi wordt geactiveerd door in te typen vi fnaam, waarin fnaam de file naam is van een tekst bestand. Als de file met de ingetypte naam niet bestaat wordt er een nieuwe file aangemaakt. Als de file al bestaat, dan wordt deze door vi ‘geopend’.
A.3
Programma modes
De editor vi kan zich in twee verschillende ‘modes’ bevinden, de command mode en de input mode. De meest voorkomende fout bij het gebruik van de editor is, dat commando’s behorend bij een ‘verkeerde mode’ worden ingetypt. Command mode : Dit is de toestand waarin vi zich bevind direct na activering. In de command mode kan men de ‘cursor’ over het scherm bewegen, zonder iets aan de inhoud van de file te veranderen. In de command mode kan men dus door een file heen ‘bladeren’. Het postioneren van de cursor gaat het eenvoudigst m.b.v de toetsen met de pijlen rechts boven aan het toetsenbord. Vanuit de command mode kunnen ook stukken tekst worden verwijderd. Zo wordt het ‘character’ onder de cursor verwijderd door ‘x’ in te typen. De hele ‘huidige’ regel is te verwijderen met ‘dd’. Wil men tekst toevoegen aan een file, dan moet men vanuit de command mode naar de ‘input mode’ gaan, dit kan op verschillende manieren ,zoals: • met ‘a’ (append) kan men tekst toevoegen achter de huidige cursor positie. • met ‘i’ (insert) kan men tekst tussenvoegen op de huidige cursor positie.
Wanneer men aan het eind van een sessie met vi de tekst file wil opslaan typt men vanuit de command mode ‘:wq’. Input mode : In deze toestand voegt men tekst toe aan de file. Ieder ingetypt character (behalve Esc) wordt aan de tekst toegevoegd. Als men voldoende tekst heeft toe/tussen gevoegd en men wil de cursor verplaatsen naar een andere plaats in de file of men wil de gewijzigde file opslaan, dan moet men eerst vanuit de ‘input mode’ terug naar de ‘command mode’. Dit wordt bereikt door de Esc-toets in te drukken. Wanneer vanuit de ‘command mode’ 1 William
Joy ‘An Introduction to Display Editing with VI’, Unix Programmers Manual, vol. 2C part 1.
95
de Esc-toets wordt ingedrukt piept de terminal om aan te geven in welke ‘mode’ vi zich bevindt.
A.4
Lijst van veel gebruikte commando’s
Hieronder volgt een onvolledige lijst van veel gebruikte editor commando’s. Een vollediger opsomming is te vinden in de volgende sectie. Al deze commando’s worden ingetypt vanuit de command mode van vi. Men komt in de command mode door de Esc-toets (Esc) in te drukken. • vi fnaam activeer de editor met te bewerken file fnaam. De cusor staat nu aan het begin van de file. • :nn Hiermee verplaats men de cursor naar het begin van regel nummer ‘nn’. Een notatie voor het laatste regelnummer in de file is ‘:$’. Het commando ‘:$’ verplaatst de cursor naar het eind van de file. • :set nu Hiermee laat men regelnummers bij de tekst op het scherm verschijnen. Verwijderen gebeurt met ‘:set nonu’. • Ctrl f verplaatst de cursor een volle pagina (scherm) vooruit en laat de nieuwe pagina zien. • Ctrl b verplaatst de cursor een volle pagina (scherm) achteruit en laat de nieuwe pagina zien. • Ctrl r Na dit commando schrijft vi het huidige scherm opnieuw, er verandert niets aan de inhoud van de file. Dit commando is handig als het scherm vervuilt is geraakt - byvoorbeeld door een binnenkomend bericht. • r vervang het character onder de cursor door het eerst volgende ingetypte character. • x verwijder het character onder de cursor • dd verwijder de hele huidige regel • \string verplaats cursor naar het de eerstvolgende positie, waar de tekst string voorkomt hiermee zoekt men iets op in een tekst. • a ga naar ‘input mode’ (voeg tekst toe achter de cursor) • i ga naar ‘input mode’ (voeg tekst tussen op de cursor positie) • o ga naar ‘input mode’ (voeg tekst tussen op een nieuwe regel na de huidige) • O ga naar ‘input mode’ (voeg tekst tussen op een nieuwe regel voor de huidige) • :w sla de (bijgewerkte) file (tussentijds) op op disc. • :wq sla de (bijgewerkte) file op op disc en verlaat de editor • Esc (‘input mode’) Esc-toets vi gaat over in ‘command mode’ 96
• :n1,n2 m n3 Hiermee verplaatst men regels met num. ‘n1’ t/m ‘n2’ naar de positie direct achter regel num. ‘n3’. De spaties zijn optioneel. • :n1,n2 t n3 Hiermee copieert men regels met num. ‘n1’ t/m ‘n2’ naar de positie direct achter regel num. ‘n3’. De spaties zijn optioneel. • :n1,n2 w fnaam Hiermee copieert men regels met num. ‘n1’ t/m ‘n2’ naar de nieuwe file ‘fnaam’ De spaties zijn optioneel. • :n1,n2 d Hiermee verwijdert men regels met num. ‘n1’ t/m ‘n2’ uit de file. De spatie is optioneel.
A.5
Editor quick reference
Onderstaande lijst behoord tot de meegeleverde informatie van de PC versie van de screen editor vi. De lijst geeft een uitgebreid overzicht van vi commando’s en is bedoeld als naslag informatie bij het gebruik van de editor. PC / VI QUICK REFERENCE -------------------------
All help refers to VISUAL mode, unless a preceding colon (:) is specified. Refer to the PC/VI user’s manual for command mode commands. Copyright (C) 1985-1987 by Custom Software Systems All Rights Reserved. Note: This text is a summary intended to provide only a basic outline. ----Invocation---vi file vi file1 file2 vi [options] file
; Edit at first line of ’file’ ; Edit file1. Edit file2 via :n ; As above, but with Command Line Options
----Command Line Options---+n ; Edit at line n + ; Edit at last line +/pattern ; Edit at line containing ’pattern’ -R ; open file(s) in read-only mode -c ; Use line-oriented command mode -e ; ... restricted ’edit’ mode. -l ; Enable ’lisp’ and ’showmatch’ options -wn ; Specify window size of ’n’ lines -? ; Print a summary of all command line options -; End processing of command line options ----Exiting---ZZ :x :wq :w :w file :w>> file :n,mw file :q :q! :e!
; ; ; ; ; ; ; ; ; ;
Exit visual mode, saving changes Exit saving changes Write buffer, then quit. Write buffer to original file Write buffer to ’file’ Append to ’file’ Write lines ’n’ through ’m’ to ’file’ Quit out of editor Quit discarding changes Reedit file, discarding changes
----Setting Options---:set all ; Prints all option settings :set option ; Enables option ’option’
97
:set nooption :set option? :set
; Disables option ’option’ ; Prints current value of ’option’ ; Prints modified options
----Text Input---i ; Insert before cursor I ; Insert before first non-blank on line a ; Append after cursor A ; Append at end of line o ; Open and insert at line below O ; Open and insert at line above ; ESCape key terminates Input mode. ----Insert Mode Commands---Control-@ ; Repeat Last Insertion Control-W ; Erase last word entered Control-H ; Erase last character entered DELete or Backspace ; Same as Control-H Control-U ; Erase entire line Control-D ; Backtab over ’autoindent’ 0Control-D ; Kill all ’autoindent’ ^Control-D ; Kill ’autoindent’ on current line only Control-V ; Next character literal (to insert control codes) ----Screen Adjustment---Control-L ; Clear and redraw screen Control-R ; Redraw only if @ (deleted) lines zCR ; Redraw, current line at top of page z. ; Redraw, current line at center of page z; Redraw, current line at bottom of page /pattern/z; Redraw, line containing ’pattern’ at bottom of page zn. ; Redraw, set new window size of ’n’ lines Control-E ; Scroll down 1 line Control-Y ; Scroll up 1 line Control-F ; Scroll forward one screen Control-B ; Scroll backward one screen Control-D ; Scroll down half screen Control-U ; Scroll up half screen ----Cursor Motion---H ; Move cursor to top of page M ; ... to middle of page L ; ... to last line of page + ; ... to next line at first non-white character ; ... to previous line at first non-white down-arrow or j ; ... down one line, staying in same column up-arrow or k ; ... up one line, staying in same column left-arrow or h ; ... to the left right-arrow or l ; ... to the right 0 ; ... to beginning of line ^ ; ... to beginning of line (first non-white) $ ; ... to the end of line n| ; ... to column ’n’ Control-H ; Same as left-arrow ----File Motion---G nG /pattern ?pattern n N /pattern/+n ?pattern?-n )
; ; ; ; ; ; ; ; ;
Last line of file Line ’n’ of file Next line containing ’pattern’ Previous line containing ’pattern’ Next / or ? previous / or ? ’n’th line after line containing ’pattern’ ’n’th line before line containing ’pattern’ Next sentence
98
( } { ]] [[ ‘‘ ’’
; ; ; ; ; ; ;
Preceding sentence Next paragraph Preceding paragraph Next section/function Preceding section/function Previous context Previous context at first non-white in line
----Search Patterns---^ ; Beginning of line $ ; End of line . ; Any character * ; Zero or more of previous character [A-Z] ; Matches any character from A to Z [abc] ; Matches a, b, or c [^abc] ; Matches any character BUT a, b or c \ ; Esc character for literal: \ / $ . ^ [ ’ & * | ~ \< ; Beginning of word \> ; End of word ----Marking Position---mx ; Mark current position with locator ’x’ ‘x ; Return to position at locator ’x’ ’x ; ... at first non-white in line ----Yank and Put---yy or Y ; Yank line to buffer yw ; Yank word to buffer nyy or nY ; Yank ’n’ lines to buffer p ; Put back lines below cursor P ; Put back lines above cursor "xy ; Yank line to buffer ’x’ "xp ; Put from buffer ’x’ ----Substitution---stext ; Substitutes ’text’ for character until ESCape S ; Substitute for entire line until ESCape :s/X/Y/opt ; Substitute ’Y’ for first occurrence of ’X’ ; Options: g - Change every currence in line ; c - Confirm each change ; p - Print each change (Command mode only) & ; Repeat last :substitute command :g/X/s//Y/opt ; Globally find the first ’X’ on each line and ; substitute ’Y’ for the string ’X’ ----Deleting Text---x ; Delete character under cursor nx ; ... ’n’ characters X ; ... character before cursor dw ; ... word ndw ; ... ’n’ words dd ; ... entire line dtx ; ... to ’x’ in current line D ; ... to end of line d/pattern ; ... up to ’pattern’ d?pattern ; ... back to ’pattern’ d‘x ; ... to marked location ’x’ d’x ; ... current line and to line which contains marked location ’x’ d) ; ... to end of sentence d( ; ... to beginning of sentence d} ; ... to end of paragraph d{ ; ... to beginning of paragraph d]] ; ... to end of section d[[ ; ... to beginning of section
99
----Changing Text---cw ; Change word ncw ; ... ’n’ words cc ; ... entire line ncc ; ... ’n’ lines C ; ... to end of current line c/pattern ; ... up to ’pattern’ c?pattern ; ... back to ’pattern’ c‘x ; ... to marked location ’x’ c’x ; ... current line and to line which contains marked location ’x’ c) ; ... to end of sentence c( ; ... to beginning of sentence c} ; ... to end of paragraph c{ ; ... to beginning of paragraph c]] ; ... to end of section c[[ ; ... to beginning of section rx ; Replace character with ’x’ R ; Replace all characters until ESCape is entered ----Word and Within Line Motions---w ; Move cursor forward by one word W ; ... including any punctuation b ; Move cursor backward by one word B ; ... including any punctuation e ; Move cursor forward to end of word E ; ... including any punctuation fx ; Search current line (only) forward for ’x’ Fx ; Search current line (only) backward for ’x’ ; ; Repeat last ’f’ or ’F’ , ; Reverse of last ’f’ or ’F’ ----Undo---u U "np "n1pu.u.u.u.
; ; ; ;
Undo last change Restore current line Retrieve from buffer ’n’ (1-9 and a-z) Scan recent deletions
----Miscellaneous---. ; Repeat previous operation ~ ; Reverse case of letter and advance one position J ; Join lines together << ; Shift line left one tab position >> ; Shift line right one tab position :g/pattern/p ; Locate and print all occurrences of ’pattern’ within the file xp ; Reverse order of two letters dwwP ; Reverse order of two words ddp ; Reverse order of two lines
100
Bijlage B
Data representatie in computers In hoofdstuk 2 is een overzicht gegeven van de componenten in een computersysteem. Hierbij zijn de Central Processing Unit (CPU) en het Central Memory (CM) genoemd. In de CPU worden operaties op data uitgevoerd, in het geheugen worden data opgeslagen. Het geheugen is onderverdeeld in ‘concentrische’ eenheden, de kleinste eenheid is de bit (BInary digiT) die de waarden 0 en 1 kan representeren. De eerstvolgende eenheid is de byte een groep van acht opeenvolgende bits. De hieropvolgende eenheid bestaat uit een aantal bytes, 2,4 of 8 afhankelijk van de programmatuur en van het type computer. Wat oudere PC’s werkend met het MSDOS besturingssysteem werken met 16 bits (2 bytes) woorden, moderne PC’s en workstations met 32 bits (4 bytes) woorden en geavanceerde workstations en supercomputers met 64 bits (8 bytes) woorden. De gebruikte woordlengte hangt samen met de maximale precisie waarmee rekenkundige bewerkingen kunnen worden uitgevoerd. Aangezien de bits twee waarden kunnen aannemen, worden gegevens intern in het binaire (twee-tallige) getallenstelsel gecodeerd. De manier waarop de data intern worden voorgesteld (interne representatie) wordt in het volgende besproken. Hierbij wordt onderscheid gemaakt tussen alfanumerieke data (tekst) en numerieke data (getallen).
B.1
Alfanumerieke data
Alfanumerieke gegevens worden gecodeerd volgens een codetabel. Zo’n tabel voegt aan ieder gedefinieerd symbool een volgnummer toe, de zogenaamde ‘colating sequence’ van de betreffende code. Een hoeveelheid tekst (lijst van symbolen) wordt nu intern gerepresenteerd door een lijst van symbool nummers uit de codetabel. De meest gebruikte codetabel is de ASCII-tabel (American Standard Code for Information Interchange). Deze wordt o.a. gebruikt in UNIX en MS-DOS systemen. De ASCII-tabel bevat naast ‘leesbare’ symbolen, zoals het alfabet en, de cijfers 0 t/m 9, etc., ook ‘niet leesbare’ symbolen, die bijvoorbeeld als stuurcode bij data transmissie worden gebruikt. De ASCII-tabel bevat 28 = 256 codes, waarvan i.h.a. alleen de eerste 128 worden gebruikt. De 256 codes worden binair m.b.v. 8 bits (1 byte) weergegeven. Dit betekent, dat voor de representatie van een hoeveelheid tekst van N tekens er evenzoveel bytes nodig zijn. VOORBEELD Enkele voorbeelden van decimale en binaire ASCII codes worden in onderstaande tabel gegeven. decimaal 10 13
binair 0000 1010 0000 1101
tekstsymbool
ASCII LF (line feed) CR (carriage return)
65 66
0100 0001 0100 0010
A B
uppercase A uppercase B
90
0101 1010
Z
uppercase Z
101
In Fortran kan men beschikken over data van het type character om alfanumerieke gegevens weer te geven. Wanneer men data van het type character gebruikt, heeft men geen problemen bij de opslag van tekst data in computers die verschillende woordlengten gebruiken. De volledige ASCII-tabel - (output van het commando man ascii) is hieronder weergegeven, waarbij een octale nummering gebruikt is. | | | | | | | | | | | | | | | |
000 010 020 030 040 050 060 070 100 110 120 130 140 150 160 170
NUL BS DLE CAN SP ( 0 8 @ H P X ‘ h p x
B.2
| | | | | | | | | | | | | | | |
001 011 021 031 041 051 061 071 101 111 121 131 141 151 161 171
SOH HT DC1 EM ! ) 1 9 A I Q Y a i q y
| | | | | | | | | | | | | | | |
002 012 022 032 042 052 062 072 102 112 122 132 142 152 162 172
STX NL DC2 SUB " * 2 : B J R Z b j r z
| | | | | | | | | | | | | | | |
003 013 023 033 043 053 063 073 103 113 123 133 143 153 163 173
ETX VT DC3 ESC # + 3 ; C K S [ c k s {
| | | | | | | | | | | | | | | |
004 014 024 034 044 054 064 074 104 114 124 134 144 154 164 174
EOT NP DC4 FS $ , 4 < D L T \ d l t |
| | | | | | | | | | | | | | | |
005 015 025 035 045 055 065 075 105 115 125 135 145 155 165 175
ENQ CR NAK GS % 5 = E M U ] e m u }
| | | | | | | | | | | | | | | |
006 016 026 036 046 056 066 076 106 116 126 136 146 156 166 176
ACK SO SYN RS & . 6 > F N V ^ f n v ~
| | | | | | | | | | | | | | | |
007 017 027 037 047 057 067 077 107 117 127 137 147 157 167 177
BEL SI ETB US ’ / 7 ? G O W _ g o w DEL
| | | | | | | | | | | | | | | |
Numerieke data
Numerieke gegevens kunnen worden onderverdeeld in integers, reals en complexe getallen. Complexe data worden als tweetallen van reals behandeld (re¨ele en imaginaire deel).
B.2.1
Data van het type integer
Deze worden binair gerepresenteerd, meestal (default) in 32 bits. Via declaratie is ook een andere lengte mogelijk, wat van belang kan zijn indien er zuinig moet worden omgesprongen met geheugen ruimte. Declareer integer* n i voor een n-byte integer variabele i.
B.2.2
Data van het type real
Re¨ele getallen worden in drijvende komma (floating point) voorstelling gebruikt. Het getal x wordt gerepresenteerd als x = sign × M × 2e met sign =
−1 , x < 0 1 ,x ≥ 0
De mantisse M voldoet door normalisatie, via aanpassen van de exponent e, aan 1/2 ≤ M < 1 d.w.z., M=
m X
bj 2−j
j=1
waarin bj = 0 of 1, de waarden van de mantisse bits voorstelt en door normalisatie geldt b 1 = 1. Een gangbare interne representatie maakt gebruik van de volgende 32 bits indeling: 1 teken bit, 7 exponent bits en 24 mantisse bits. ±
exponent 102
mantisse
Machine precisie De maximale nauwkeurigheid waarmee men getallen intern kan voorstellen wordt bepaald door de lengte van de mantisse. Bij floating point getallen is er voor gegeven lengte van de mantisse een bovengrens voor de nauwkeurigheid, aangeduid als de relatieve afbreekfout, die onafhankelijk is van de getalwaarde. Voor de absolute afbreekfout δ geldt, bij een m bits mantisse M δ < 2−(m+1) × 2e = 2−(m+1)+e voor de relatieve fout geldt dan δ 2−(m+1) 2−m−1 || = = ≤ = 2−m x M 2−1
waarbij de ondergrens 1/2 van de genormaliseerde mantisse is ingevuld. De machine precisie wordt wel gedefinieerd als deze (maximale) relatieve afbreekfout = 2 −m . Voor een 24 bits mantisse vindt men zo 24 = 2−24 = 10−7.224 , waaruit volgt dat de afbreekfout optreedt in de achtste decimaal. Het heeft dus geen zin om resultaten met meer dan zeven decimalen te berekenen bij gebruik van een 24 bits mantisse (enkelvoudige precisie in Fortran). Als een grotere nauwkeurigheid nodig is dan zal men met een ‘32 bits’ computer in Fortran programmatuur double precision variabelen moeten gebruiken. Een double precision variabele wordt intern met 8 bytes gerepresenteerd. Het rekenen in dubbele precisie wordt bijvoorbeeld op grote schaal toegepast bij het numeriek modelleren van vloeistof stromingsproblemen. Het gegeven, dat de precisie van de getalrepresentatie eindig is, kan worden toegepast bij het stoppen van een numeriek iteratief proces wanneer de maximaal haalbare precisie (machine precisie) is bereikt. Stel bijvoorbeeld dat een rij iteranden wordt berekend m.b.v. s0 , s1 , . . . , sn door middel van sn+1 = sn + tn met |tn | een monotoon dalende rij. Noteren we de machinaal berekende iteranden als s¯n , dan zal vanwege de eindige precisie t.g.v. de afbreekfout gelden, |tn | < → s¯n+1 = s¯n |¯ sn | We kunnen de iteratie dus stoppen zodra de iterand s¯n+1 (in de machine representatie) niet meer verandert. Dan is de maximaal haalbare (machine) precisie bereikt. VOORBEELD stel m = 24, sn = 1, tn = 2−25 . We schrijven sn als produkt van een (genormaliseerde) mantisse en een schaalfactor sn = 2−1 × 21 . Bij het optellen van floating point getallen worden de exponenten in de schaalfactoren gelijk gemaakt door verschuiven van de mantisse. Dit geeft s¯n+1 = s¯n + t¯n = 2−1 × 21 + 2−26 × 21 = (2−1 + 2−26 ) × 21 Vanwege de beperkte lengte van de mantisse wordt het resultaat afgerond tot s¯n+1 = 2−1 × 21 = s¯n .
B.2.3
Effect van eindige machine precisie bij rekenkundige bewerkingen
We bekijken het effect van de machine precisie op de nauwkeurigheid van rekenkundige bewerkingen. Uit het voorgaande volgt dat we een benaderde waarde x ¯ van het exacte getal x opslaan in de computer, x ¯ = (1 + x )x 103
met x ≤ , de eerder genoemde machine precisie. We bekijken het effect van de benaderingen in de getal representaties op het resultaat van een optelling van twee getallen x en y. De absolute fout wordt gegeven door, δ+ = (1 + x )x + (1 + y )y − (x + y) en de relatieve fout door, + =
x y δ+ = x + y x+y x+y x+y
In het geval dat x en y bijna even grote absolute waarde hebben, maar van tegengesteld teken zijn, volgt dat de relatieve fout in de afzonderlijke getallen onbegrensd versterkt wordt in het eindresultaat. Het resultaat van de optelling is dus volledig onbetrouwbaar. Dit wordt aangeduid als ‘swamping’: het resultaat is weggezonken in het ‘fouten-moeras’. OPDRACHT B-1 Ga na dat de relatieve fout bij de vermenigvuldiging en deling eindig blijft door de volgende eerste orde uitdrukkingen af te leiden voor de relatieve fout: ∗ = x + y en / = x − y .
104
Bijlage C
Het installeren (compileren en linken) van programma’s C.1
Compilereren en linken
Om een Fortran programma te kunnen uitvoeren is er een executable file (kortweg executable) van het programma nodig. Om een executable te maken moeten i.h.a. diverse Fortan code files (source files) van verschillende programma eenheden, (main program, subroutines, etc.) m.b.v. een compiler naar machinetaal worden vertaald en vervolgens moeten alle de machinetaal versies van de verschillende programma eenheden aangevuld met ontbrekende bibliotheek routines, door het linker programma worden omgezet in een executable file. Het resultaat van het compileren van een Fortran source file, bijvoorbeeld prog.f, is een zogenaamde objectfile, prog.o. Dat is een file die nog niet uitgevoerd kan worden omdat er nog verwijzingen in staan naar bibliotheekroutines. Denk hierbij bijv. aan de routines voor invoer en uitvoer. Het is de taak van het ‘linker’ programma om de verschillende objectfiles waaruit een programma wordt opgebouwd te combineren met de libraries tot een executable (uitvoerbaar programma). Op UNIX systemen kun je met een enkel commando, (f77, f90 of f95, hier algemeen aangeduid met fxx) al deze verschillende stappen laten uitvoeren. Het programma fxx zorgt er voor dat voor alle gespecificeerde Fortran sources de compiler aangeroepen wordt. Bij het linken - met het linker programma ld dat eveneens door fxx wordt geactiveerd - worden automatisch de ondersteunende Fortran libraries (runtime libraries) meegelinkt. Een overzicht van de compiler opties voor fxx kan worden verkregen met de on-line manual commando’s man fxx. De belangrijkste compiler opties zijn: -c -g -O -o -C -S
Alleen compileren en niet linken; maakt objectfiles uit Fortran source files Compileer en link met debugger informatie Optimaliseer de resulterende machine code m.b.t rekentijd Specificeer een naam voor de executable file (default a.out) Array lengte overschrijdingen worden gecontrolleerd Produceer assembler source in een aparte file
C.1.1
Libraries en ar
Met het ‘archive’ programma ar is het mogelijk om op zichzelf staande objectfiles te combineren tot een bibliotheekfile of kortweg ‘library’. M.b.v. deze ‘libraries’ kunnen voldoende geteste programma eenheden die zich lenen voor algemeen gebruik op een efficiente manier toegankelijk worden gemaakt voor grotere groepen gebruikers. De standaard aanroep van ar is ar ru bibliotheek file objectfiles Bij het maken van bibliotheekroutines verdient het aanbeveling om van te voren nauwkeurig te plannen welke routines men combineert in de objectfiles. De linker combineert alle object modulen uit de libraries waaraan vanuit het programma (eventueel indirect) wordt gerefereerd. Het kan zo gebeuren de er veel overbodige code in het programma wordt opgenomen. Een veilige en flexibele oplossing is om alle functies en subroutines in aparte sourcefiles onder te brengen. Dit laatste maakt ook een efficient gebruik van de make utility - zie hieronder - mogelijk. 105
C.2
Make
Als er bij een project met meerdere programma eenheden (subroutines, functions, etc.) wordt gewerkt dan kan het volledig opnieuw compileren van alle eenheden onnodig lang duren. Het is dan veel efficienter om alleen die eenheden opnieuw te compileren waarvan de source veranderd is. Vergeet men hierbij echter ´e´en van de gewijzigde sourcefiles te compileren, wat in zo’n situatie niet denkbeeldig is, dan kan dat weer aanleiding zijn tot moeilijk traceerbare fouten. Gebruik van de make utility kan dit soort problemen voorkomen.
C.2.1
Een eenvoudige Makefile
We zullen in de navolgende voorbeelden de Fortran 77 compiler f77 gebruiken. Bij installatie van Fortran 90 programmatuur moet uiteraard de compiler aanroep worden aangepast in f90. VOORBEELD Stel dat we een programma hebben dat bestaat uit twee programma eenheden met afzonderlijke sourcefiles: prog.f en subr.f en dat we beide sourcefiles apart willen compileren om ze later te linken. We moeten dan de volgende serie commando’s uitvoeren: f77 -c prog.f; f77 -c subr.f; f77 -o prog prog.o subr.o Waarbij we een van de eerste twee commando’s achterwege mogen laten als de desbetreffende source niet gewijzigd is. We kunnen dit in een Makefile als volgt specificeren: # Alles vanaf een # teken tot het einde van de regel is commentaar prog: prog.o subr.o # Om prog te maken zijn prog.o en subr.o nodig. tab f77 -o prog prog.o subr.o # Is prog.o of subr.o ‘jonger’ dan prog, dan # wordt prog (opnieuw) gemaakt met dit commando. prog.o: prog.f # Om prog.o te maken is prog.f nodig tab f77 -c prog.f # Is prog.f ‘jonger’ dan prog.o, dan wordt # prog.o (opnieuw) gemaakt met dit commando. subr.o: subr.f # Om subr.o te maken is subr.f nodig. tab f77 -c subr.f # Is subr.f ‘jonger’ dan subr.o, dan wordt # subr.o (opnieuw) gemaakt met dit commando. Als we bovenstaande file Makefile noemen en vervolgens make prog of alleen make intypen dan worden de voor installatie van prog benodigde sourcefiles gecompileerd en gelinkt. Merk op, dat de tab karakters, vooraf gaand aan de eventueel uit te voeren commando’s, hierbij noodzakelijk zijn.
Aan de hand van de afhankelijkheden gedefinieerd in de Makefile maakt de make utility de targetfile aan, in dit voorbeeld de executable file prog. Hierbij hangt de targetfile af van een serie files die onderling ook weer afhankelijk zijn. Deze onderlinge relaties kunnen worden beschreven met een omgekeerde boomstructuur (dependency tree), waarbij de targetfile de plaats van de wortel inneemt. Het make programma doorloopt de verschillende takken van de boomstructuur en controleert de tijdstippen waarop de files voor het laatst zijn gewijzigd (de time stamps). Wanneer een file A in de boomstructuur jonger is dan de targetfile dan zullen alle files op de tak tussen A en de target file door make ‘up to date’ worden gemaakt. Concreet: wanneer in bovenstaande voorbeelden een source file wordt gewijzigd, dan zal make vaststellen dat de corresponderende object files niet meer up to date zijn en deze files opnieuw compileren. De resulterende nieuwe objectfiles maken dan de targetfile prog ‘outdated’ en daarom zal make tenslotte de targetfile opnieuw aanmaken door het programma prog opnieuw te linken m.b.v. o.a. de nieuwe objectfiles. Bij gebruik van include files (zie hoofdstuk 12, i.v.m. common blocks in include files) is het dus belangrijk om alle voorkomens van de include files expliciet in de dependency tree van de Makefile te vermelden. Dit is noodzakelijk om, bij wijzigingen van een include file alle hiervan afhankelijke sourcefiles en object files ‘up to date’ te maken. 106
VOORBEELD We willen onderstaand programma met ‘main program’ en een subroutine installeren m.b.v. make. program prog1 implicit none * common block include ’prog1_com.inc’
c
a=1.0 b=2.0 c=3.0 call subr1 end subroutine subr1 implicit none * common block include ’prog1_com.inc’
c
print*, a,b,c return end In beide programma eenheden wordt het volgende common block, waarvan de source text aanwezig is in de file prog1 com.inc, gebruikt via een include-regel, real a,b,c common /hulp1/ a,b,c De bijbehorende Makefile kan er alsvolgt uitzien: prog1: tab
prog1.o subr1.o # prog1 hangt af van prog1.o en subr1.o f77 -o prog1 prog1.o subr1.o
prog1.o: prog1.f tab f77 -c prog1.f
# prog1.o hangt af van prog.f
subr1.o: subr1.f tab f77 -c subr1.f prog1.f: prog1_com.inc tab f77 -c prog1.f subr1.f: prog1_com.inc tab f77 -c subr1.f
C.2.2
# beide source files hangen bovendien # af van de include file met het # het common block #
Macro’s
Het is mogelijk om in een Makefile variabelen te definieren en te gebruiken: een regel van de vorm FFLAGS = -g
is een variabele definitie, waarmee compiler opties (besproken in sectie C.1) aan een variabele worden toegekend. In de Makefile kan naar deze variabele worden verwezen door de naam $(FFLAGS). Het is ook mogelijk om vanuit de UNIX shell met het setenv commando zogenaamde environment variabelen te definieren - bijvoorbeeld setenv FFLAGS -g - en hiernaar vervolgens in Makefile te refereren. Toepassing van variabelen is handig omdat bijvoorbeeld het aanpassen van compilerparameters zoals -g (debug) of -O (optimalisatie) ) voor elke Makefile snel gedaan kan worden door simpelweg met setenv de betreffende parameter te veranderen. Een ander nuttig gebruik van environment variabelen betreft de naam van het compiler programma zelf. 107
VOORBEELD Definieer de Fortran compiler naam met, setenv FC f90, dan kan vervolgens de bovenstaande voorbeeld Makefile worden gewijzigd in, prog: tab prog.o: tab subr.o: tab
prog.o subr.o $(FC) -o prog prog.o subr.o prog.f $(FC) -c prog.f subr.f $(FC) -c subr.f
Dit illusteert dat men m.b.v. environment variabelen eenvoudig van verschillende compilers gebruik kan maken, zonder de Makefile te hoeven aanpassen. Een belangrijker toepassing hiervan is de mogelijkheid om de programma installatie files, Fortran source files en Makefiles op een eenvoudige manier op andere computer systemen met andere conventies voor compiler namen en compiler optie vlaggen, ongewijzigd te kunnen gebruiken. Hiermee verhoogd men de portability van de programmatuur tussen verschillende computer systemen. Een volgende toepassing van macro’s, die het werken met make minder omslachtig maakt is het gebruiken van een lijst met objectfile namen. Stel dat het programma prog uit bovenstaand voorbeeld ook nog procedures hulp1, ... , hulp4 bevat dan kan e.e.a. verkort worden gespecificeerd, zoals in onderstaand voorbeeld. OBJS = prog.o subr.o hulp.o hulp2.o hulp3.o hulp4.o prog: $(OBJS) tab f77 -o prog $(OBJS)
C.2.3
‘Make rules’ en ‘default rules’
Het is lastig om voor iedere sourcefile de volgende regels te moeten invoeren in de Makefile, hulp.o: hulp.f tab f77 -c hulp.f
We kunnen make echter ook eenmalig duidelijk maken hoe in het algemeen van een Fortran source een objectfile gemaakt moet worden m.b.v. een zogenaamde make rule. Dat kan met het .SUFFIXES commando in de Makefile. De make utility kent ook default rules, standaard aanwezige make rules. In de UNIX make is onder andere de volgende default rule ingebouwd: FC = f77 .SUFFIXES: .f .o .f.o: tab $(FC) $(FFLAGS) -c $<
Dit betekent dat voor iedere Fortran source file die niet meer ‘up to date’ is de f77 -c (met eventuele aanvullende opties gedefinieerd in de variabele $FFLAGS) aangeroepen moet worden, als er niets anders gespecificeerd is. De $< staat voor de sourcefile(s) die ‘jonger’ zijn dan de targetfile (waarvoor de macro $@ is gedefinieerd, niet genoemd in dit voorbeeld). De defaultregels worden alleen uitgevoerd als er geen expliciete commando’s voor het maken van een file in de Makefile zijn opgenomen. VOORBEELD Het gebruik van Fortran 90/95 modulen en de daarbij optredende file afhankelijkheden wordt hier ge¨ıllustreerd aan de hand van het in hoofdstuk 12 behandelde voorbeeldprogramma exmrooster. Alle programma eenheden zijn hierbij in afzonderlijke Fortran source files ondergebracht. De file Makefile:
TARGET=$(BINDIR)/exmrooster # spec. alle modulen eerst MODULES= rooster_module.o
108
OBJECTS=
defrooster.o
exmrooster.o
prnrooster.o
FC=f95 FFLAGS= -c -O2 include Mkrules.h TARGET: $(MODULES) $(OBJECTS) $(FC) -o $(TARGET) $(OBJECTS) $(MODULES) # module dependencies defrooster.o: rooster_module.f90 exmrooster.o: rooster_module.f90 prnrooster.o: rooster_module.f90 !-----------------------------------------------------------
De file Mkrules.h .SUFFIXES: .o .f .f90 .f.o: $(FC) $(FFLAGS) $< .f90.o: $(FC) $(FFLAGS) $<
OPDRACHTEN C-1 a) Ga na dat in het laatste voorbeeld in de voorgaande sectie blijkbaar van deze default regels gebruik gemaakt wordt. C-1 b) Een library lib.a wordt opgebouwd met de sources source.f, ..., source3.f. Deze drie sources gebruiken een file common.inc. Om de library te testen is er een programma met sourcefile test1.f die de library routines aanroept. Schrijf een Makefile voor het aanmaken van zowel lib.a als programma test1. Maak hierbij gebruik van de default regels.
109
110
Bijlage D
Intrinsieke Fortran functies
D.1
Intrinsieke functies in Fortran 77
De volgende tabellen geven alle intrinsieke procedures die in Fortran 77 opgenomen zijn 1 . De zogenaamde generieke naam kan altijd gebruikt worden tenzij er alleen een specifieke naam gegeven is. De generieke naam kan echter niet altijd als actuele parameter gebruikt worden. Het verdient hoe dan ook aanbeveling om geen (intrinsieke) functies als actuele parameters te gebruiken aangezien dit de leesbaarheid niet ten goede komt. Eventuele extra Fortran statements die nodig zijn om dit te voorkomen, worden doorgaans door de compiler optimalisatie als overbodig opgemerkt en verwijderd en gesubstitueerd. Dit is vanzelfsprekend compiler en daarmee systeem afhankelijk. Het gebruik van de specifieke namen kan dus beperkt blijven tot die functies waar geen generieke naam voor bestaat. De specifieke namen zijn opgenomen om ook oudere Fortran 77 code te kunnen begrijpen.
Naast de in de tabel gegeven intrinsieke functies bestaan er op elk systeem vele anderen. Deze zijn vanzelfsprekend systeem afhankelijk en dienen met grote omzichtigheid te worden gebruikt. Ze zijn te vinden in de Reference Manual van de betreffende compiler en doorgaans via de manual pages te achterhalen wat hun functie is en hoe ze aangeroepen dienen te worden. (Bijv. man sin). Enkele veel gebruikte functies en procedures die tot voorheen nog systeem afhankelijk waren (zoals het opvragen van de systeem clock time) zijn in Fortran 90 gestandaardiseerd hetgeen de overdraagbaarheid van de code verder vergroot. In de tabellen staat Dcomplex voor double complex en double voor double precision.
1 Overgenomen uit de Fortran 3.0.1 Reference Manual van SunSoft WorkShop Solaris/SPARC (1995) documentatie.
111
Intrinsic Function Truncation
Definition int(a) (Read Note 1)
Arithmetic Functions No. of Generic Args Name 1 AINT
Intrinsic Name AINT DINT
Type Argument real double
of Function real double
Nearest Whole Number
int(a + .5) if a ≥ 0 int(a + .5) if a < 0
1
ANINT
ANINT DNINT
real double
real double
Nearest Integer
int(a + .5) if a ≥ 0 int(a + .5) if a < 0
1
NINT
NINT IDINT
real double
integer integer
Absolute Value
|a| (Read Note 6) (ar2 + ai2 )1/2
1
ABS
IABS ABS DABS CABS
integer real double complex
integer real double real
Remainder
a1 −int(a1 /a2 ) ∗ a2 (Read Note 1)
2
MOD
MOD AMOD DMOD
integer real double
integer real double
Transfer of Sign
|a1 | if a2 ≥ 0 −|a1 | if a2 < 0
2
SIGN
ISIGN SIGN DSIGN
integer real double
integer real double
Positive Difference
a1 − a2 if a1 > a2 0 if a1 ≤ a2
2
DIM
IDIM DIM DDIM
integer real double
integer real double
Double Product
a 1 ∗ a2
2
(-)
DPROD
real
double
Choosing Largest Value
max(a1 , a2 , . . .)
MAX
MAX0 AMAX1 DMAX1 AMAX0 MAX1
integer real double integer real
integer real double real integer
MIN0 AMIN1 DMIN1 AMIN0 MIN1
integer real double integer real
integer real double real integer
≥2
(-) (-) Choosing Smallest Value
min(a1 , a2 , . . .)
≥2
MIN
(-) (-)
112
Trigonometric Functions No. of Generic Intrinsic Args Name Name 1 SIN SIN DSIN CSIN ZSIN
Intrinsic Function Sine
Definition
Cosine
cos(a)
1
COS
COS DCOS CCOS ZCOS
real double complex Dcomplex
real double complex Dcomplex
Tangent
tan(a)
1
TAN
TAN DTAN
real double
real double
Arcsine
arcsin(a)
1
ASIN
ASIN DASIN
real double
real double
Arccosine
arccos(a)
1
ACOS
ACOS DACOS
real double
real double
Arctangent
arctan(a)
1
ATAN
arctan(a1 /a2 )
2
ATAN2
ATAN DATAN ATAN2 DATAN2
real double real double
real double real double
Hyperbolic Sine
sinh(a)
1
SINH
SINH DSINH
real double
real double
Hyperbolic Cosine
cosh(a)
1
COSH
COSH DCOSH
real double
real double
Hyperbolic Tangent
tanh(a)
1
TANH
TANH DTANH
real double
real double
sin(a)
Type of Argument Function real real double double complex complex Dcomplex Dcomplex
Intrinsic Function Imaginary Part of a Complex
Other Math No. of Args ai 1 (Read Note 6)
Functions Generic Intrinsic Name Name AIMAG AIMAG
Type of Argument Function complex real
Conjugate of a Complex
(ar , −ai ) (Read Note 6)
1
CONJG
CONJG
complex
complex
Square Root
√ a
1
SQRT
SQRT DSQRT CSQRT
real double complex
real double complex
Exponential
ea
1
EXP
EXP DEXP CEXP
real double complex
real double complex
Natural Logarithm
log(a) (ln(a))
1
LOG
ALOG DLOG CLOG
real double complex
real double complex
Common Logarithm
log 10(a) (10 log(a))
1
LOG10
ALOG10 DLOG10
real double
real double
Definition
113
Intrinsic Function Integer (Read Note 1)
Real (Read Note 2)
Type Conversion Functions No. of Generic Intrinsic Type of Args Name Name Argument Function 1 INT (-) integer integer INT real integer IFIX real integer IDINT double integer (-) complex integer 1
REAL
REAL FLOAT (-) SNGL (-)
integer integer real double complex
real real real real real
Double (Read Note 3)
1
DBLE
DBLE DFLOAT (-) (-) (-)
integer integer real double complex
double double double double double
Complex (Read Note 4)
1 or 2
CMPLX
(-) (-) (-) (-)
integer real double complex
complex complex complex complex
DComplex
1 or 2
DCMPLX
(-) (-) (-) (-)
integer real double complex
Dcomplex Dcomplex Dcomplex Dcomplex
Integer (Read Note 5)
1
ICHAR
charcater
integer
Character (Read Note 5)
1
CHAR
integer
character
114
Character Functions No. of Generic Args Name Conversion to character 1
Intrinsic Name CHAR
Type of Argument Function integer character
Conversion (Read Note 5)
Conversion to integer
1
ICHAR
character
integer
Index of a Substring
Location of a substring a2 in String a1 (Read Note 7)
1
INDEX
character
integer
Length
Length of Character Entity (Read Note 8)
1
LEN
character
integer
Lexically Greater Than or Equal
a 1 ≥ a2 (Read Note 5)
2
LGE
character
logical
Lexically Greater Than
a 1 > a2 (Read Note 5)
2
LGT
character
logical
Lexically Less Than or Equal
a 1 ≤ a2 (Read Note 5)
2
LLE
character
logical
Lexically less Than
a 1 < a2 (Read Note 5)
2
LLT
character
logical
Intrinsic Function Conversion
Definition
115
Notes corresponding with the intrinsic tables above 1. If a is type integer, then INT(a) is a. If a is type real or double precision then: • if |a| < 1, then INT(a) is 0
• if |a| ≥ 1, then
INT(a) is the greatest integer that does not exceed the magnitude of a, and whose sign is the same as the sign of a. (Such a mathematical integer value may be too large to fit in the computer integer type) If a is type complex or double complex then • apply the above rule to the real part of a. If a is type real, then IFIX(a) is the same as INT(a).
2. If a is type real, then REAL(a) is a. If a is type real or double precision then: • REAL(a) is as much precision as the significant part of a as a real datum can contain. If a is type complex, then REAL(a) is the real part of a. If a is type double complex, then • REAL(a) is as much precision as the significant part of the real part a as a real datum can contain. 3. If a is type double precision, then DBLE(a) is a. If a is type integer, then DBLE(a) is • as much precision of the significant part of a as a double precision datum can contain. If a is type complex, then DBLE(a) is • as much precision of the significant part of the real part of a as a double precision datum can contain. 4. If a is type complex, then CMPLX(a) is a. If a is type integer, real or double precision, then • CMPLX(a) is REAL(a)+0 ∗ i If a1 and a2 are type integer, real or double precision, then • CMPLX(a) is REAL(a1 )+REAL(a2 )∗i If a is type double complex, then • CMPLX(a) is REAL( DBLE(a) )+REAL( DIMAG(a) )∗i If CMPLX has two arguments, then • they must be of the same type, and
• they may be one of integer, real, or double precision
If CMPLX has one argument, then • it may be one of integer, real, double precision, or complex. 5. ICHAR(a) is the position of a in the collating sequence. The first position is 0, the last is N − 1, 0 ≤ ICHAR(a) ≤ N − 1, where N is the number of characters in the collating sequence, and a is of type character of length one. CHAR and ICHAR are inverses in the following sense: • ICHAR( CHAR( i ) ) = i, for 0 ≤ i ≤ N − 1
• CHAR( ICHAR( c ) ) = c, for any character c capable of representation in the processor.
6. A Complex value is expressed as an ordered pair of reals, (ar , ai ), where ar is the real part and ai is the imaginary part.
116
7. INDEX(x, y) is the place in x where y starts. That is, it is the starting position within character string x of the first occurence of character string y If y does not occur in x, then INDEX(x, y) is 0. If LEN(x) < LEN(y), then INDEX(x, y) is 0.
8. The value of the argument of the LEN function need not be defined at the time the function reference is executed.
9. LGE(x, y) is TRUE if x = y or if x follows y in the collating sequence; otherwise it is FALSE. LGT(x, y) is TRUE if x follows y in the collating sequence; otherwise it is FALSE. LLE(x, y) is TRUE if x = y or if x precedes y in the collating sequence; otherwise it is FALSE. LLT(x, y) is TRUE if x precedes y in the collating sequence; otherwise it is FALSE. If the opperands for LGE, LGT, LLE, and LLT are of unequal length, the shorter operand is considered as if it were extended on the right with blanks.
D.2
Intrinsieke functies in Fortran 90
Naast de bovenstaande Fortran 77 functies welke alle tevens in Fortran 90 gebruikt mogen worden, bevat Fortran 90 standaard een aantal krachtige nieuwe intrinsieke functies (en subroutines). De hieronder gegeven tabellen2 geven slechts een greep uit de nieuwe functies en enkele ervan o werden ook al door veel Fortran 77 compilers ondersteund.
Intrinsic Function Circular Shift
Array Manipulation Functions No. of Name Args Perform circular shift 1-3 CSHIFT(array,shift[,dim])
End-off-Shift
End off shift
3-4
EOSHIFT(array,shift[,boundary][,dim])
Transpose
Matrix transpose
3-4
TRANSPOSE(matrix)
Definition
2 De hier gegeven lijsten vormen een compilatie gebaseerd op 1) Fortran 90 Brainerd, Goldberg & Adams Academic Service (1991) 2) Fortran 90 explained Metcalf & Reid Oxford University Press (1990) 3) SPARCompiler Fortran 90 1.0 User’s Guide van SunSoft WorkShop Solaris/SPARC (1995) documentatie.
117
Character Functions Intrinsic Function Left Adjusting
Definition Adjust left, removing leading blanks and inserting trailing blanks
No. of Args 1
Name ADJUSTL(string)
ADJUSTL(string)
Right Adjusting
Adjust right, removing trailing blanks and inserting leading blanks
1
Scan a String
Index of left-most (right-most if [back] is used) character of string that belongs to set; zero if none belong
2-3
Remove Blanks
Remove trailing blanks from a a single string
1
TRIM(string)
Concatenate
Concatenates ncopies of string
2
REPEAT(string,ncopies)
Verify
Zero if all characters of string belong to set or index of left-most (right-most if [back] TRUE) that does not
2
VERIFY(string,set[,back])
Intrinsic Function Significant digits
SCAN(string,set[,back])
Numerical Inquiry Functions Definition Number of significant digits in the model for x
No. of Args 1
Name DIGITS(x)
Accuracy (Small)
Number that is almost negligible compared with one in the model for numbers like x
1
EPSILON(x)
Accuracy (Large)
Largest number in the model for numbers like x
1
HUGE(x)
Exponent (Maximum)
Maximum exponent in the model for numbers like x
1
MAXEXPONENT(x)
Exponent (Minimum)
Minimum exponent in the model for numbers like x
1
MINEXPONENT(x)
Exponent (Range)
Decimal exponent range in the model for x
1
RANGE(x)
Precision (Decimal)
Decimal Precision in the model for x
1
DECIMAL(x)
Precision (Positive Number)
Smallest Positive number in the model for numbers like x
1
TINY(x)
Radix
Base of the model for numbers like x
1
RADIX(x)
118
Intrinsic Function Exponent (Part of)
Floating Point Manipulation Functions Definition No. of Args Exponent part for the model of x 1
EXPONENT(x)
Fractional
Fractional part for the model of x
1
FRACTION(x)
Nearest machine number
Nearest different machine number in the direction given by the sign of s
2
NEAREST(x,s)
Reciprocal
Reciprocal of the relative spacing of model numbers near x
1
RESPACING(x)
Real Scaling
x × bi , where b =RADIX(x)
2
SCALE(x,i)
Exponent (Set)
Model number whose sign and fractional part are those of x and whose exponent part is i
2
SET EXPONENT(x,i)
Absolute Spacing
Absolute spacing of model numbers near x
1
SPACING(x)
Intrinsic Function Dotproduct
Name
Vector and Matrix Multiplication Functions Definition No. of Name Args (~va , ~vb ) 2 DOT PRODUCT(vector a,vector b)
Multiplication
~·B ~ A
2
MATMUL(matrix a,matrix b)
Intrinsic Functions Boolean ’all’
Array Reduction Functions No. of Name Args TRUE if all elements are TRUE 1-2 ALL(mask[,dim])
Boolean ’any’
TRUE if any element is TRUE
1-2
ANY(mask[,dim])
Count Booleans
TRUE if any element is TRUE
1-2
COUNT(mask[,dim])
Maximum value
Value of maximum array element
1-3
MAXVAL(array[,dim][,mask])
Minimum value
Value of minimum array element
1-3
MAXVAL(array[,dim][,mask])
Product
Product of array elements
1-3
PRODUCT(array[,dim][,mask])
Sum
Summation of array elements
1-3
SUM(array[,dim][,mask])
Definition
119
Array Inquiry Functions Intrinsic Functions Allocation
Definition
No. of Args 1
Deallocation
deallocates memory corresponding to pointer
Array Bound (Lower)
array lower bound
1-2
LBOUND(array[,dim])
Array Bound (Upper)
array upper bound
1-2
UBOUND(array[,dim])
Array Shape
Array (or scalar) shape
1
SHAPE(source)
Array Size
Array size
2
SIZE(array,[dim])
TRUE if array allocated
1
Name ALLOCATE(array) DEALLOCATE(pointer)
Array Construction Functions No. of Name Args tsource when mask is TRUE and 3 MERGE(tsource,fsource,mask) f source otherwise
Intrinsic Functions Merge
Definition
Pack
Pack elements corresponding to true elements of mask into rank-one result
2-3
PACK(array,mask[,vector])
Unpack
Unpack elements corresponding to true elements of mask
3
UNPACK(array,mask,field)
Spread
ncopies copies of source forming an array of rank one greater
3
SPREAD(source,dim,ncopies)
Other Functions Intrinsic Functions Location (Maximum)
Definition Location of maximum array element
No. of Args 1-2
MAXLOC(array[,mask])
Location (Minimum)
Location of minimum array element
1-2
MINLOC(array[,mask])
Pointer
TRUE if pointer is associated with target
1-2
ASSOCIATED(pointer[,target])
Truncation
Least integer greater than or equal to its argument
1
CEILING(argument)
Truncation
Greatest integer less than or equal to its argument
1
FLOOR(argument)
120
Name
Subroutines Intrinsic Subroutine Date and Time
Definition Real-time clock reading date and time
System Clock
Integer data from real-time clock
Random Number
Random numbers in range 0 ≥ x < 1
1
Random Seed
Initialize or restart random number generator
0-3
121
No. of Args 2-10
0-3
Name DATE AND TIME([all][,count] [,msecond][,second][,minute] [,hour,]day,month[,year][,zone]) SYSTEM CLOCK([count][,countrate] [,countmax]) RANDOM NUMBER(harvest) RANDOM SEED([size,][put,][get])
Index $ (edit descriptor), 88 ’ (quote), 36 ** (machtsverheffen), 29 .and., 33 .false., 27 .not., 33 .or., 33 .true., 27 / (edit descriptor), 88 // (concatenatie), 30 : als edit descriptor, 87 als scheider in array, 72 % (in structure), 78
call, 44 carriage return, 91 case selector, 57 case construct, 56 m.b.v. if then else construct, 57 central processing unit (CPU), 9 character gedeclareerde lengte, 31 string, 30 substring expressie, 31 character, 27 character, 30 character expressie, 30 character operator concatenatie (//), 30 codering, 17 commentaar tekst, 23 common block, 73 compiler, 15, 17 complex, 27, 93 complexe intrinsieke functies, 93 concatenatie (//), 30 construct case, 56 if then else if, 56 if then else, 55 block if, 55 implied do, 89 constructs, 55 continuerings veld, 23 continueringsregel, 24 control statement, 22 control structures, 55 current directory, 12
a (edit descriptor), 87 ACCESS, 83, 84 actuele parameter, 41, 42, 44, 111 addbib, 77 afbreken van een programma, 13 allocatable, 67, 70, 78 allocate, 67 .and., 33 arithmetic and logical unit (ALU), 9 array, 63 dimension, 63 array allocatable, 67 als parameter, 68 assumed size, 68 assumed-shape, 70 conform, 71 declaratie, 63 dynamisch, 67 geheugen opslag, 64 index, 63 offset, 64 section, 72 shape, 70 size, 70 startadres, 64 array operaties elemental, 71 ASCII collating sequence, 32 assembler taal, 15 assignment statement, 22 automatic array, 66
d (edit descriptor), 86 data opslag, 91 data, 25 data statement, 25 data type, 27 derived, 21 derived type, 76 intrinsic, 21 intrinsiek, 27, 76 niet numerieke character, 27 logical, 27 numerieke, 27 complex, 27, 93 double complex, 27
besturingssysteem, 10 binary, 11 blank common, 73 122
double precision, 27 integer, 27 real, 27 data type conventie, 73 dbx, 19 deallocate, 67 debugger, 19 debugging dbx, 19 debugger, 19 declaratie, 27 derived types, 27 dimension, 63 direct access file, 84 directory, 10 home, 11 root, 11 sub-, 11 working, 12 do implied, 89 do while construct, 59 do loop, 58 double complex, 27 double precision, 27
FMT, 83 FORM, 83 format fixed point, 85 floating point, 85 free, 82 format, 84 formatted, 84 formele parameter, 42 Fortran, 21 Fortran 77, 21 Fortran 90, 21 free source format, 23 ftnchek, 18 function, 40 external, 41 dummy parameters, 42 formele parameters, 42 in Fortran 77 intrinsieke, 111 in Fortran 90 intrinsieke, 117 statement, 43 g (edit descriptor), 86 geheugen allocatie dynamische, 67 statische, 66 ge¨ındiceerde variabele, 63 generic functions, 41 genest block if construct, 56 geneste procedure, 46 gestructureerd programmeren, 40 go to, 59
e (edit descriptor), 85 edit descriptor, 85 apostrophe, 87 character, 87 format control, 87 numerieke, 85 spati¨ering, 87 END, 83 end, 22, 35, 41, 44 end do, 58 ERR, 83 executable, 12, 18 executable statement, 22 executie, 13 expressie, 29 character, 30 logische, 33 prioriteit, 33 rekenkundige, 29 relationele, 32 external function, 41
haakjes, 29 hierarchisch file systeem, 10 hogere programmeertaal, 15 i (edit descriptor), 86 I/O, 22, 81 direct access file, 84 files, 81 free format, 84 geformatteerd, 85 list directed, 84 logical units, 81 ongeformatteerd, 84 redirection, 81 sequential file, 84 if then else construct, 55, 60 if then else if construct, 56 implicit, 27 implicit none, 28, 35 implied do construct, 89 include, 74 index, 63 indirecte adressering, 73 input/output (I/O), 9, 35 integer, 27 intentie, 44 intention, 44
f (edit descriptor), 85 f90 als syntax-checker, 19 FILE, 82, 83 file, 10, 91 direct access, 84 internal, 90 sequential, 84 file pointer, 84 fixed point format, 85 fixed source format, 23 floating point format, 85 123
interface, 49 interface definitie, 49 intern geheugen, 9 internal file, 90 internal write, 90 inti¨ele regel, 24 intrinsic function, 41 intrinsieke data type, 27 intrinsieke functie in Fortran 77, 111 generieke naam, 111 intrinsieke functie in Fortran 90, 117 IOSTAT, 83
formele, 42 physical record, 91 pointer, 12 primitieven, 51 print, 81 prioriteit van operatoren, 29 procedure, 39, 73 function, 40 external, 41 intrinsic, 41 statement, 43 subroutine, 43 als parameter, 45 geneste, 46 standaard procedure, 41 program, 22, 35 program counter, 12 programma struktuur, 22 programmeerfouten opsporen, 18 debug software, 18 syntax-checker, 18 werkwijze, 18 voorkomen, 18 programmeertalen, 15
label veld, 23 labeled common, 73 leading dimension, 68 len, 31 LIFO mechanisme, 46 linken, 40 Linux, 10 lnblnk, 60 local area network (LAN), 9 logical, 27 logical record, 91 logical unit I/O, 81 lookbib, 77
quote (’), 36
machine taal, 15 machtsverheffen (**), 29 meer-dimensionale array, 63 modulaire programmeerstijl, 39 module, 73 module, 75
randapparatuur, 9 read, 81, 83 real, 27 RECL, 83 record, 77, 84, 91 logical, 91 physical, 91 redirection of I/O, 81 return, 41, 44
named common, 73 Nassi-Schneiderman diagram, 51 nested block if construct, 56 newline character, 91 nint, 30 no-op, 72 non-executable statements, 21 .not., 33 notatie, 7
save, 25 sequential file, 84 shape, 70 size, 70 source format fixed, 23 free, 23 spatie, 36 standaard Fortran type conventie, 28 standard input device (stdin), 81 standard output device (stdout), 81 statement, 21 assignment, 22 control, 22 data, 25 executable, 21, 22 I/O, 22 non-executable, 21 volgorde, 24 statement function, 43 statement veld, 23 stride, 69
open, 81, 83 operand, 29 operating system, 10 operator character concatenatie (//), 30 rekenkundige, 29 machtsverheffen (**), 29 prioriteit, 29 .or., 33 padnaam absoluut, 11 relatief, 12 parameter, 73 actuele, 41, 42, 44, 111 array, 68 124
string, 30 substring assignment, 31 substring expressie, 31 substring mechanisme, 31 structure, 27, 76 struktuur programma-, 22 struktuurdiagram, 51 case box, 52 decision box, 51 iteration box, 52 process box, 51 subroutine, 43 subscript triplet, 72 syntax, 17 syntax-checker f90 als, 19 ftnchek, 18 top down design strategy, 40 trapezium regel, 45 truncatie bij type conversie, 30 type, 76 type conventie in Fortran, 28 type conversie, 30 truncatie, 30 type declaratie expliciet, 27 impliciet, 27 type identifiers, 27 uitvoering van een programma, 18 unformatted, 84 UNIT, 82, 83 Unix, 10 use, 75 vierde generatie talen (4GL), 15 working directory, 12 write, 83 write internal, 90 x (edit descriptor), 87
125