Einführung in die Informationstechnik für Ingenieure – Fortran95–Skript – Herrmann–Föttinger–Institut für Strömungsmechanik Technische Universität Berlin W. Baumann, C. Böhning, T. Schmidt 31. März 2003 Inhaltsverzeichnis 4 Programmiersprachen, FORTRAN 4.1 4.2 Allgemeines zu Programmiersprachen 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 4.1.1 Wahl einer Programmiersprache . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 4.1.2 Sprachniveaus . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 4.1.3 Höhere Programmiersprachen: Besonderheiten, Übersicht und Historisches . . . . . 3 FORTRAN . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 4.2.1 Einfache FORTRAN–Programmbeispiele . . . . . . . . . . . . . . . . . . . . . . . . 8 4.2.2 Sprachelemente von Fortran95 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 4.2.2.1 Zeichensatz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 4.2.2.2 FORTRAN–Quelltextzeile . . . . . . . . . . . . . . . . . . . . . . . . . . 10 4.2.2.3 Symbolische Namen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 4.2.2.4 Konstanten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 4.2.2.5 Variablen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 4.2.2.6 Anordnungsreihenfolge von Anweisungen . . . . . . . . . . . . . . . . . . 12 Vereinbarungsanweisungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 4.2.3.1 Typvereinbarungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 4.2.3.2 Explizite Typvereinbarungen . . . . . . . . . . . . . . . . . . . . . . . . . 15 4.2.3.3 Felder . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 4.2.3.4 Speicherung von Feldern . . . . . . . . . . . . . . . . . . . . . . . . . . . 19 4.2.3.5 Dynamische Felder . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19 4.2.3.6 Zeiger und Ziele . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20 4.2.4 Initialisierungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21 4.2.5 Arithmetische Ausdrücke und Anweisungen . . . . . . . . . . . . . . . . . . . . . . 22 4.2.5.1 Arithmetische Ausdrücke . . . . . . . . . . . . . . . . . . . . . . . . . . . 22 4.2.5.2 Arithmetische Anweisungen . . . . . . . . . . . . . . . . . . . . . . . . . . 24 4.2.3 ii INHALTSVERZEICHNIS 4.2.6 iii Logische Ausdrücke und Anweisungen . . . . . . . . . . . . . . . . . . . . . . . . . 25 4.2.6.1 Logische Ausdrücke . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25 4.2.6.2 Logische Anweisungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27 4.2.7 Zeichenausdrücke und -anweisungen . . . . . . . . . . . . . . . . . . . . . . . . . . 27 4.2.8 Ausdrücke und Anweisungen mit Feldern und Teilfeldern . . . . . . . . . . . . . . 28 4.2.8.1 Ausdrücke mit Feldern und Teilfeldern . . . . . . . . . . . . . . . . . . . 28 4.2.8.2 Anweisungen mit Feldern und Teilfeldern . . . . . . . . . . . . . . . . . . 29 Steueranweisungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30 4.2.10 Verzweigungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30 4.2.10.1 Block–IF–Strukturen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31 4.2.10.2 CASE–Verzweigung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35 4.2.11 Schleifen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35 4.2.11.1 Zählschleifen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36 4.2.11.2 Schleifen mit Eintrittsbedingung . . . . . . . . . . . . . . . . . . . . . . . 38 4.2.11.3 Schleifen mit Austrittsbedingung . . . . . . . . . . . . . . . . . . . . . . . 39 4.2.12 CONTAINS– und END–Anweisung . . . . . . . . . . . . . . . . . . . . . . . . . . . 40 4.2.13 Ein-/Ausgabe (Standard-Ein-/Ausgabe) . . . . . . . . . . . . . . . . . . . . . . . . 41 4.2.14 Erweiterte Ein-/Ausgabe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42 4.2.14.1 Formatierte Ein-/Ausgabe . . . . . . . . . . . . . . . . . . . . . . . . . . 42 4.2.14.2 Externe Ein-/Ausgabe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47 4.2.14.3 Interne Ein-/Ausgabe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51 4.2.15 Programmeinheiten und Anweisungen . . . . . . . . . . . . . . . . . . . . . . . . . 51 4.2.16 Parameterlisten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52 4.2.17 Vordefinierte Standardfunktionen . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53 4.2.18 Benutzerdefinierte Funktionen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54 4.2.19 Benutzerdefinierte Unterprogramme – Subroutinen . . . . . . . . . . . . . . . . . . 57 4.2.20 Speicherplatz-Zuordnung und Einbettung von Programmzeilen . . . . . . . . . . . 58 4.2.21 Kommunikation zwischen Programmeinheiten . . . . . . . . . . . . . . . . . . . . . 60 4.3 Weitere Sprachmittel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62 4.4 Tips zum (nicht nur FORTRAN–) Programmieren . . . . . . . . . . . . . . . . . . . . . . 64 4.5 FORTRAN–Knigge . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68 4.5.1 68 4.2.9 Vorbemerkung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . iv INHALTSVERZEICHNIS 4.5.2 Allgemeines . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68 4.5.3 Projektorganisation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69 4.5.4 Programme und Unterprogramme . . . . . . . . . . . . . . . . . . . . . . . . . . . 69 4.5.5 Format des Quelltextes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69 4.5.6 Labels . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69 4.5.7 Groß- und Kleinschreibung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69 4.5.8 Verwendung von Leerzeichen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70 4.5.9 Wahl von Bezeichnern . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70 4.5.10 Datentypen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70 4.5.11 Logische Operatoren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71 4.5.12 Arithmetische Ausdrücke . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71 4.5.13 Felder . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71 4.5.14 Kontrollstrukturen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71 4.5.15 Parameter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72 4.5.16 COMMON–Blöcke . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72 4.5.17 Ein- und Ausgabe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72 4.5.18 Abschlußbemerkung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72 4.6 Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73 4.7 Fragen und Übungen zu Kapitel 4 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74 4.8 Antworten zu den Fragen und Übungen zu Kapitel 4 . . . . . . . . . . . . . . . . . . . . . 76 Literatur 79 Kapitel 4 Programmiersprachen, FORTRAN 4.1 4.1.1 Allgemeines zu Programmiersprachen Wahl einer Programmiersprache Im Kapitel Programmiertechnik“ haben wir Techniken gelernt, wie man ausgehend von einer Aufga” benstellung schrittweise ein Programm entwickelt. Wir haben uns dazu einer grafischen Entwurfssprache, der Struktogrammtechnik bedient. Der nächste Schritt in der Programmentwicklung ist die sprachliche Formulierung (Codierung) in einer Programmiersprache. Der Ausdruck Codierung weist darauf hin, daß es sich vor allem um eine Umsetzung handelt, und die gedankliche Hauptarbeit bereits vorher geleistet wurde. Eine Programmiersprache ist eine künstliche Sprache, die so entworfen ist, daß einerseits Menschen darin Programme formulieren können und andererseits ein Rechner diese Programme ausführen kann. Die verwendete Programmiersprache muß über Ausdrucksmittel verfügen, die eine entsprechende, problemnahe Beschreibung des Problems gestatten, z.B. mathematische Formeln direkt in Formelsprache umzusetzen. Sie sollte außerdem den Prinzipien der strukturierten Programmierung entsprechen. Die Wahl der Programmiersprache beeinflußt stark die Portabilität (Übertragbarkeit auf andere Rechnersysteme) und damit die Verbreitung eines Programms. Beschränkung auf (genormte) Standards begünstigt die Portabilität, kann aber auch den Verzicht auf attraktive, effiziente bzw. elegante Spracherweiterungen bedeuten. Die Auswahl der Programmiersprache ist jedoch oft nicht durch logische sondern durch pragmatische Gründe bestimmt und an unterschiedliche Randbedingungen geknüpft wie • bestehende Programmier- und Nutzungsgewohnheiten im jeweiligen Anwendungsbereich • Festlegung auf Sprachen innerhalb bestimmter Institutionen • Portabilität auf andere Rechnerfamilien • Verfügbarkeit und Effizienz der Compiler (prinzipiell sind alle gängigen Programmiersprachen auf den Standard–Betriebssystemen implementiert) • Einschränkungen z.B. bezüglich Speicherplatz, Compilations- und Rechenzeiten. 4.1.2 Sprachniveaus Für die Codierung von Programmen existiert eine große Zahl von Programmiersprachen, die man nach ihren stark unterschiedlichen Ausdrucksmöglichkeiten entsprechenden Sprachniveaus zuordnet, Abb. 4.1. 2 KAPITEL 4. PROGRAMMIERSPRACHEN, FORTRAN Zu den sogenannten maschinennahen Sprachen zählen die Assemblersprachen. Sie bilden die Befehlsstruktur der Hardware ab und sind daher an einen bestimmten Rechner oder an eine Rechnerfamilie gebunden. Assemblerprogrammierung setzt Kenntnisse über den Aufbau und die Wirkungsweise des jeweils zugrundeliegenden Prozessors voraus. Dem Programmierer wird nur ein geringer Programmierkomfort geboten. Zwar wird ihm dadurch die Arbeit erleichtert, daß die binär codierten Maschinen–Befehle durch eingängige Abkürzungen (mnemonics) ersetzt werden, der Programmierer muß die Aufgabe jedoch selbst in maschinennahe Befehle zerlegen, was zeitaufwendig und fehleranfällig ist. Dies führt oft zu langen Testzeiten. Die mögliche hohe Effizienz bei der Ausführung von Assemblerprogrammen erreichen tatsächlich nur geübte Programmierer. Da zudem das Problem des Speicherplatzbedarfs weitgehend entschärft ist, und auch höhere Programmiersprachen die Möglichkeit bieten, hardwarenahe Funktionen zu programmieren (z.B. die Sprache C), sollten Anwenderprogramme heute nicht mehr in Assembler geschrieben werden. Eine Ausnahme bilden zeitkritische Abschnitte, die eine so optimal wie irgend möglich programmiert werden müssen. Dann bietet sich auch eine Lösung in Assembler an. anwendungsorientierte Sprachen verfahrensorientierte Sprachen Assemblersprachen Mikroprogrammiersprachen @ ¡@ maschinenunabhängige (höhere) Programmiersprachen ¡ @ ¡@ maschinenabhängige Programmiersprachen ¡ Hardware Abbildung 4.1: Sprachniveaus Die höheren Programmiersprachen gehören zur Ebene der problemnahen Sprachen. Sie ermöglichen eine problemangepaßte, für den Anwender durchschaubare Darstellung, die der Forderung nach Verständlichkeit und Anwenderfreundlichkeit entgegenkommt. Ihre Befehlswörter sind meist der englischen Sprache entnommen, und Ausdrücke für Berechnungen entsprechen der mathematischen Schreibweise. Die maschinelle Übersetzung der problemnahe formulierten Anweisungen in Maschinenbefehle (sogenannter Objektcode) durch den Compiler entlastet den Programmierer von dieser mühsamen Aufgabe. Der vom Compiler erzeugte Maschinencode ist in der Regel weniger effizient als ein gutes Assemblerprogramm. Dies liegt an den allgemeingültig gehaltenen Übersetzungsalgorithmen. Aufgrund des stetigen Fortschritts beim Bau“ moderner hochoptimierender Compiler wird sich diese Effizienzlücke jedoch weiter schließen. So exi” stieren bereits leistungsfähige Compilationssysteme insbesondere für Parallel- und Vektorrechner, die zunächst Datenabhängigkeiten sowohl innerhalb von Programmodulen als auch global analysieren. Die Berücksichtigung der Analyse gestattet dann eine Optimierung des Maschinencodes auch über Module hinweg. Die Verwendung höherer Programmiersprachen hat also folgende Vorteile: • deutliche Verkürzung der Programmier- und Testzeiten, 4.1. ALLGEMEINES ZU PROGRAMMIERSPRACHEN 3 • geringere Fehleranfälligkeit und bessere Übersichtlichkeit durch problemnahe Formulierung (Selbstdokumentation), • weitgehende Portabilität auf andere Rechnertypen durch maschinenunabhängige Darstellung der Algorithmen. 4.1.3 Höhere Programmiersprachen: Besonderheiten, Übersicht und Historisches Seit Ende der siebziger Jahre haben sich auch im Bereich der Mikrorechner höhere Programmiersprachen aufgrund der im vorstehenden Abschnitt angeführten Vorzüge und der ständig wachsenden Leistungsfähigkeit der Rechner durchgesetzt. Voraussetzung für die Implementierung sind ausreichende Hardware–Ressourcen, d.h. ein genügend großer Arbeitsspeicher und externe Massenspeicher (Festplatte, Diskette), sowie ein leistungsfähiges Betriebssystem. Ein großer Teil der höheren Programmiersprachen wurde von den größeren Rechenanlagen übernommen, z.B. FORTRAN, Pascal, COBOL und C. Diese Sprachen sind damit auf fast allen Rechnerfamilien verfügbar. Es gibt verschiedene Möglichkeiten Programmiersprachen in Kategorien einzuteilen. Eine häufig benutzte Einordnung hat vier Klassen: • imperative Programmiersprachen Dazu gehören z.B. Pascal, Modula, C, Fortran. • funktionale Programmiersprachen Dazu gehören z.B. Opal, Gopher. • logische Programmiersprachen • objekt–orientierte Programmiersprachen Objektorientierung ist eine relativ neue Entwicklung. Zu diesen Sprachen gehören: C++, Smalltalk, Java. Betrachtet man Programmiersprachen nach formalen Gesichtspunkten, so sind ihre Definition sowie ihre Sprachelemente wichtig. Eine Programmiersprache wird definiert durch folgende Informationen: • Die Syntax: sie wird entweder nur verbal beschrieben (FORTRAN) oder auch formal angegeben (z.B. Syntaxdiagramme (Backus–Naur–Form) für Pascal), • Das Vokabular: reservierte Schlüsselworte wie DO und SUBROUTINE sowie benutzerdefinierte symbolische Namen, die in der Sprache nach bestimmten Regeln gebildet werden, z.B. FORTRAN– Variablennamen, • Der Zeichensatz: z.B. FORTRAN: 26 alphabetische, 10 numerische, 22 Sonderzeichen. Die Leistungsfähigkeit einer Programmiersprache wird bestimmt durch die Mächtigkeit ihres Befehlssatzes und die Flexibilität ihrer elementaren Bestandteile (Konstrukte). Programmiersprachen setzen sich aus folgenden Elementen zusammen [c’t 1/89]. • Kontrollstrukturen – Entscheidungskonstrukte für Fallunterscheidungen wie die bedingte Anweisung IF–THEN oder die Auswahlanweisung CASE. – Schleifen wie die DO–, WHILE–, REPEAT– oder FOR–Anweisungen. 4 KAPITEL 4. PROGRAMMIERSPRACHEN, FORTRAN – Blöcke im Rahmen von Schleifen und Auswahlanweisungen oder als abgeschlossene Unterprogramme. – Rekursionen, wenn Blöcke sich wechselseitig oder selbst aufrufen. – Sprünge • Datentypen – Felder (sequentielle Folgen von Elementen eines Typs, die über einen Index direkt adressiert werden können). – Mengen (in Programmiersprachen nur selten vorhanden) – Zeiger, zum Aufbau dynamischer, sich während des Programmlaufs verändernder Datenstrukturen, dienen als symbolische Bezeichnung der Speicheradresse eines Objekts. – Initialisierung von Datenobjekten innerhalb einer Deklaration zur Vermeidung von Fehlern beim Arbeiten mit nichtdefinierten Daten. – Erweiterbarkeit der vorgegebenen Typen durch selbstdefinierte Typen. • Parameter Parameter vermeiden Seiteneffekte in Unterprogrammen. Programmeinheiten sollen möglichst nur über ihre eigenen lokalen Datenstrukturen und die als Parameter übergebenen Daten oder Algorithmen verfügen. Anzustreben ist die Definition von Eingangs-, Ausgangs-, sowie Ein-/Ausgangsparametern. Eingangsparameter reichen nur einen Wert in die Routine hinein, Ausgangsparameter sind Funktionsresultate. Ein-/Ausgangsparameter reichen einen Wert in die Routine hinein und erscheinen dort als lokale Variable, deren letzter Wert wieder zurückgegeben wird. • Module Module sind ganz allgemein Sammlungen von Datenobjekten, Datentypen, Routinen, Prozessen und Anweisungen und stellen in der Regel die Lösung einer Teilaufgabe innerhalb des Gesamtprogramms dar. Module dienen damit der Strukturierung und Abstraktion des globalen Programmflusses. Für häufig auftretende Standardprobleme gibt es meist fertige, in Modulen zusammengefaßte Lösungen (Bibliotheken), so daß man als Programmierer nicht jedesmal das Rad neu erfinden muß“. ” • Fehlerbehandlung Als Ausnahme (engl. exception) wird die Behandlung eines Fehlers, eines unerwarteten Ereignisses oder Zustandes bezeichnet. Auslöser können sowohl Hardware- als auch Softwarefehler sein. Nach diesen mehr formalen Gesichtspunkten wollen wir nun einen kurzen Blick auf die Historie der gängigsten höheren Programmiersprachen werfen und eine kurze Charakterisierung geben [2][1/89], [10]. FORTRAN Die älteste höhere Programmiersprache ist FORTRAN (FORmula TRANslator), eine Compilersprache. Sie wurde 1953 bis 1956 entwickelt für die numerische Behandlung naturwissenschaftlicher und technischer Probleme in herkömmlicher Formelsprache. Die einfache Sprachgestaltung, effiziente Übersetzbarkeit der Programme und hohe Rechengeschwindigkeit des Objektcodes sorgten für die rasche Verbreitung der Sprache, und es entstand ein immenser Fundus an leistungsfähigen Programmen vor allem im technisch– wissenschaftlichen Bereich. FORTRAN hat sich in weiterentwickelten standardisierten (genormten) Versionen (Fortran90 und Fortran95) bis zum heutigen Tag auch in der Mikrocomputertechnik behauptet. Darüberhinaus existiert ein High Performance Fortran (HPF), welches insbesondere auf Hochleistungscomputer wie Vielprozessorrechner zugeschnitten ist und die massiv parallele Programmverarbeitung durch rechnerunabhängige Sprachelemente hierfür unterstützen soll. ALGOL 4.1. ALLGEMEINES ZU PROGRAMMIERSPRACHEN 5 Für das gleiche Anwendungsgebiet wie FORTRAN wurde 1958 bis 1960 die Sprache ALGOL (ALGOrithmic Language), ebenfalls eine Compilersprache, konstruiert. Mit ALGOL wurde erstmals eine Programmiersprache nach informationstheoretischen Gesichtspunkten auf der Basis einer formalen Grammatik entwickelt. ALGOL verfügte gegenüber FORTRAN schon damals über erweiterte Ausdrucksmittel, wie dynamische Speicherverwaltung, Elemente der strukturierten Programmierung und Blockstruktur der Programme. Die Sprache zeichnet sich durch sprachliche Geschlossenheit und Transparenz aus und hat die weitere Sprachentwicklung, vor allem PL/1 und Pascal, beeinflußt, und wurde schließlich von diesen Sprachen verdrängt. Pascal Die Sprache Pascal (benannt nach dem Mathematiker Blaise Pascal) wurde 1970 von N. Wirth als Lehrsprache zur Beschreibung und Programmierung von Algorithmen entwickelt. Die Compilersprache Pascal besitzt eine genau definierte Struktur, unterstützt konsequent die strukturierte Programmierung und erlaubt die Definition auch von nicht-numerischen Datentypen und Mengen und deren Zusammenfassung zu noch komplexeren Daten-Strukturen. Das Zeigerkonzept (symbolische Bezeichnung von Speicheradressen) erlaubt sogar die dynamische Erzeugung von Datenstrukturen zur Laufzeit. Die Sprache hat vor allem auf Klein- und Mikrorechnern (CP/M– und MS–DOS–Rechner) Anwendung gefunden und liegt neben einer genormten Standardversion ISO 7185–1982 in einer Vielzahl von Dialekten vor (z.B. der de–facto– Standard Turbo–Pascal). Pascal hat maßgeblich die Sprachen MODULA–2 und ADA beeinflußt. COBOL COBOL (COmmon Business Oriented Language) wurde Ende der fünfziger Jahre im Auftrag des USVerteidigungsministeriums entwickelt. Im Jahr 1960 wurde vom Committee of the Conference on Data Systems Languages (CODASYL) die erste Version veröffentlicht. Die Compilersprache COBOL ist auf die Anforderungen im kaufmännischen Bereich zugeschnitten. Sie verfügt über eine Dezimalarithmetik (BCD, siehe Kapitel 5) und unterstützt den Umgang mit großen Datenmengen. Ihre ausführliche Syntax liest sich fast wie Klartext. Ab Anfang der sechziger Jahre wurde COBOL zunehmend in der kaufmännisch/kommerziellen Datenverarbeitung verwendet und ist dort heute die meistbenutzte Sprache. Sie ist auch auf Mikrorechnern implementiert. BASIC Die gegenwärtig auf Klein- und Mikrorechnern am weitesten verbreitete Interpretersprache ist BASIC (Beginners All–Purpose Symbolic Instruction Code), von der es auch Compilerversionen gibt. BASIC ist eine sehr einfache Sprache und wurde 1964 von John Kemeny und Thomas Kurtz als Lehrsprache für Anfänger auf Kleinrechnern entwickelt. Sie ist wegen des beschränkten Befehlsumfangs leicht erlernbar und eignet sich sowohl für kaufmännische Verwaltungsaufgaben als auch für technisch–wissenschaftliche Anwendungen. Zur Behebung seiner ursprünglichen Beschränkungen ist BASIC wie FORTRAN auch ständig weiterentwickelt worden. Zusammen mit den preiswerten Homecomputern verbreitete sich BASIC in einer Fülle von Dialekten, die jedoch im Gegensatz zu FORTRAN stark vom verwendeten Rechner abhängen. C Modern und in zunehmender Verbreitung begriffen ist die Programmiersprache C, die 1972 im Hinblick auf das Betriebssystem UNIX entwickelt und 1973 erstmals implementiert worden ist. Ken Thompson entwickelte unter Anlehnung an die Programmiersprache BCPL (1967 von Richards zum Compilerbau entwickelt) und mit der Systementwicklern eigenen Vorliebe für kurze, knappe Bezeichner zunächst die Sprache B. Da B kein Typenkonzept hatte, erwies sie sich als zu wenig mächtig. Dennis Ritchie modifizierte deshalb diese Programmiersprache, und als Weiterentwicklung entstand C. Heute sind UNIX selbst (zu ca. 90%) und viele seiner Dienstprogramme in C geschrieben. Stärke und Schwäche zugleich ist die Maschinennähe, die dem Programmierer sehr viele Freiheiten läßt und C in die Nähe von Assemblersprachen rückt. Dank standardisierter Bibliotheken sind C–Programme gut portierbar. Die Sprache liegt in zwei Standards vor: dem von den meisten C–Compilern unterstützten Pseudo-Standard, wie er ursprünglich von Kernighan und Ritchie in der ersten Ausgabe von The C Programming Language 1977 veröffentlicht 6 KAPITEL 4. PROGRAMMIERSPRACHEN, FORTRAN wurde, und dem 1989 veröffentlichten ANSI–Standard X3.159-1989. Die Syntax ist sehr kompakt und für den Ungeübten nicht leicht nachvollziehbar (siehe z.B. der Obfuscated C Contest). C übernahm von Pascal die definierbaren Datentypen, die lokalen Variablen und alle Möglichkeiten zur strukturierten Programmierung. Unterprogramme können jedoch nicht geschachtelt werden (!). Die Möglichkeit, Variablen bestimmten CPU–Registern fest zuweisen zu können und die maschinennahen Operatoren, wie etwa zum Inkrementieren und Dekrementieren von Variablen machen C-Programme vergleichsweise schnell in der Ausführung. C wurde im Hinblick auf objektorientierte Programmierung zu C++ weiterentwickelt. LISP Nahezu gleichzeitig mit FORTRAN entwickelte John Mc Carthy am MIT (Massachusetts Institute of Technology) die Grundidee von LISP (LISt Processer). 1959 gelang es, LISP auf einem Rechner zu implementieren. LISP ist eine Sprache zur Lösung nichtnumerischer Probleme. Sie eignet sich hervorragend zum Be- und Verarbeiten von Zeichenketten. Gleichzeitig ist LISP eine offene Sprache, d.h. ihr Sprachumfang läßt sich ständig erweitern, was die Entwicklung zahlreicher unterschiedlicher Dialekte förderte und Standardisierungsversuche lange Zeit scheitern ließ. Erst 1992 gelang die Fertigstellung des ANSI–Standards Common LISP. LISP gehört zu den Sprachen der Künstlichen Intelligenz (KI), als deren typische Eigenschaft gilt, einzelne Objekte weitgehend abstrakt beschreiben zu können. Dadurch lassen sich diese in Struktur und Bezeichnung den ’realen’ Objekten aus der Aufgabe und dem Einsatzgebiet der Software nachempfinden. LISP ist eine listenverarbeitende Sprache. Listen, zu denen auch die LISP– Programme selbst gehören, stellen eine Aufzählung von Atomen (Zahlen oder Zeichenfolgen) dar, mit der beliebige komplexe Datenstrukturen erzeugt werden können. Die Programme bestehen nicht, wie bei prozeduralen Sprachen, aus einer Aneinanderreihung von Befehlen, sondern aus Funktionen, die auf Argumente (Atome, Funktionsausdrücke oder andere Listen) angewendet werden. Diese Funktionsapplikation wertet der LISP-Interpreter aus und gibt das Ergebnis zurück. Dieses Sprachkonzept eignet sich insbesondere für rekursive Programmierung. Wegen seiner Andersartigkeit und dem häufigen Einsatz in der KI genießt LISP ein hohes Ansehen. 4.2. FORTRAN 7 FORTRAN BASIC ALGOL 60 COBOL ALGOL 68 PL/1 LOGO Pascal BCPL SIMULA PROLOG ADA PEARL C SmallTalk APL Modula-2 LISP C++ OBERON Z E I T E ¦ E¦ E¦ Abbildung 4.2: Der Stammbaum der Programmiersprachen [c’t 1/89] Betrachtet man den Stammbaum der Programmiersprachen, Abb. 4.2, so kann man erkennen, daß sich mehrere Entwicklungslinien herausgebildet haben, innerhalb derer eine Gruppierung in eine erste (bis 1970) und eine zweite Sprachgeneration (nach 1970) vorgenommen werden kann. Während die erste Generation, wie FORTRAN66 und BASIC, noch durch zeilenorientierte Anweisungen, einfache Datentypen (und Felder), arithmetische und logische Operationen gekennzeichnet sind, verfügt die zweite Sprachgeneration u.a. über strukturierte Datentypen und entsprechende Operatoren sowie Kontrollanweisungen, die den Prinzipien der strukturierten Programmierung entsprechen. Dies trifft insbesondere auf Pascal und seine Abkömmlinge zu. Typisch für die Entwicklung nach 1975 sind die Bemühungen, die Sprachen der ersten Generation auf das Niveau der zweiten Sprachgeneration zu bringen, z.B. mit der Entwicklung von FORTRAN77 bis zum neuen Standard Fortran95. Wir werden uns im folgenden der Sprache FORTRAN widmen, da sie im Ingenieurbereich (Aerodynamik, Thermofluiddynamik, Mechanik, usw.) die am weitesten verbreitete Sprache ist. Für viele (nicht nur Ingenieurs-) Probleme liegen teils umfangreiche FORTRAN–Programmsysteme und Unterprogrammsammlungen zur Lösung vor, die nicht etwa eingemottet sind, sondern die benutzt und gepflegt werden. FORTRAN ist eine lebendige Sprache, die sich ständig weiterentwickelt; sie bietet zur Zeit die beste Unterstützung zur effizienten Ausnutzung spezieller Hardwareeigenschaften von Supercomputern (z.B. Vektor– und Pipeline–Rechnern). 4.2 FORTRAN Von der Mitte der fünfziger Jahre entwickelten ersten höheren Programmiersprache FORTRAN war 1958 eine überarbeitete und erweiterte Version FORTRAN II verfügbar, die u.a. auch die Unterprogrammtechnik zuließ. FORTRAN IV erschien 1962 als Quasi-Standard und blieb über ein Jahrzehnt die am weitesten verbreitete FORTRAN–Version. Zur Eindämmung der Entwicklung unterschiedlichster Ver- 8 KAPITEL 4. PROGRAMMIERSPRACHEN, FORTRAN sionen wurde 1966 der Sprachstandard FORTRAN66 verabschiedet, der weitgehend mit FORTRAN IV übereinstimmt. FORTRAN77 besaß gegenüber seinem Vorläufer bereits Erweiterungen zur Unterstützung strukturierten Programmierens sowie Verbesserungen in der Dateiverwaltung und bei der Verarbeitung von Zeichenketten. Nach FORTRAN77 und dem schon wesentlich weiterentwickelten Fortran90, liegt nun mit Fortran95 ein neuer Fortran–Standard vor, der unter der DIN–Norm ISO/IEC 1539-1 bekannt ist. Die Entwicklung der Programmiersprache FORTRAN wird damit aber sicher noch nicht am Ende angelangt sein, da bereits Fortran2000 in den Startlöcher steht und um weitere Fähigkeiten, wie z.B. die Objektorientierte–Programmierung erweitert sein wird. Die im folgenden erläuterten Sprachelemente sind alle Bestandteil des Standards Fortran95. Dieses Skript soll nur einen Überblick über die wichtigsten Elemente des Sprachstandards liefern und Hinweise zur sprachlichen Umsetzung der Struktogrammelemente liefern; es kann damit ein vollständiges FORTRAN– Handbuch oder Lehrbuch nicht ersetzen. Zur erschöpfenden Beschreibung des vollen Sprachstandards sei daher auf die zahlreiche Literatur zu FORTRAN verwiesen (z.B. [RRZN, . . . ]). 4.2.1 Einfache FORTRAN–Programmbeispiele Die folgenden Programmbeispiele sind gedacht als erstes Anschauungsmaterial. Die Funktion der Programmzeilen wird nur grob erläutert, für die genaue Beschreibung der einzelnen Anweisungen wird auf die folgenden Abschnitte dieses Kapitels verwiesen. Zum Einstieg sehen wir uns unser erstes, sehr einfaches FORTRAN–Programm an. Es ist die FORTRAN– Version des bekannten Hello world“–Programms: ” ! ! Mein erstes FORTRAN-Programm ! PROGRAM HALLO PRINT *, ’Hallo, Du da!’ END Nach drei als Kommentar markierten Zeilen (erkennbar am Zeichen ’!’), die für die Funktion des Programms keinerlei Bedeutung haben, folgen die eigentlichen Programmanweisungen: Vereinbarung eines Programmnamens (HALLO), Ausgabe der Zeichenkette Hallo, Du da!“ auf dem Bildschirm und schließlich ” die Programmende-Anweisung. Die genaue Form eines FORTRAN–Programms ist festgelegt; Zitat DIN: Ein Programm ist eine zur ” Lösung einer Aufgabe vollständige Anweisung mit allen erforderlichen Vereinbarungen.“ Ein FORTRAN– Programm besteht also aus einer Folge von ausführbaren und nichtausführbaren Anweisungen. Ausführbare Anweisungen bewirken Aktionen des Programms. Die Vereinbarungen sind nichtausführbare Anweisungen, sie enthalten Angaben für den Compiler oder das Laufzeitsystem, z.B. über die Eigenschaften oder die Art der Konvertierung von Daten. Ein weiteres einfaches Beispiel soll dies genauer zeigen. Das Programm soll zwei über die Tastatur einzugebende Zahlen lesen, deren Summe berechnen und auf dem Bildschirm ausgeben. ! ! Ein einfaches FORTRAN-Programm ! PROGRAM addier INTEGER :: zahl1, zahl2, summe READ *, zahl1, zahl2 summe = zahl1 + zahl2 PRINT *, summe END 4.2. FORTRAN 9 Nach wiederum drei Kommentarzeilen erfolgt die Vereinbarung eines Programmnamens (addier), dann werden die Namen der Variablen zahl1, zahl2 und summe vereinbart und die Variablen werden als vom Typ Ganze Zahl definiert. Die READ–Anweiung liest die beiden Zahlen von der Tastatur ein, dann wird deren Summe berechnet, auf dem Bildschirm ausgegeben und das Programm beendet. Abschließend hier nun ein weiteres Programm–Beispiel, das einerseits durch Kommentare und sinnvolle Variablennamen recht gut selbstdokumentierend ist, andererseits durch Bildschirm-Meldungen gut mit dem Benutzer kommuniziert. Es soll aus einem über die Tastatur einzugebenden Wert des Radius die Fläche des entsprechenden Kreises berechnen. Dieses Beispiel demonstriert auch gut das EVA“–Schema ” (Eingabe, Verarbeitung, Ausgabe). ! ! Ein FORTRAN-Programm mit allen ! Grundelementen ! -- PROGRAM-Anweisung PROGRAM circle ! Vereinbarungsteil REAL :: radius, flaech REAL, PARAMETER :: PI = 3.14159 INTEGER :: wert, n ! Ende des Vereinbarungsteils ! Meldung auf Bildschirm, Einlesen Radius PRINT *, ’Programm zur Kreisflaechenberechnung’ PRINT *, ’Bitte den Radius eingeben!’ READ *, radius ! Ende des Einlesens ! Es folgen die Anweisungen und Berechnungen n = 2 flaech = PI * radius ** n wert = PI * radius ** n ! Ausgabe der Ergebnisse PRINT *, ’Radius =’, radius PRINT *, ’Flaeche =’, flaech PRINT *, ’WERT =’, wert ! -- Programm-Ende END In der ersten Anweisung nach den einleitenden Kommentarzeilen wird wieder der Programmname circle vereinbart. Im Vereinbarungsteil werden die Variablen radius, flaech und PI als reelle Variablen, wert und n als ganzzahlige Variablen festgelegt. Das zusätzliche Attribut PARAMETER vereinbart PI als Konstante (Unveränderliche). Bei der Deklaration von PI findet hier auch gleichzeitig eine Initialisierung (Belegung mit einem Anfangswert) statt, indem man ihr mit Hilfe des Zeichens ’=’ den Wert 3.14159 zuweist. Im ausführbaren Teil wird nach einer Bildschirm–Meldung der Benutzer aufgefordert, den Radius des Keises einzugeben, der dann von der Tastatur eingelesen wird. Im Berechnungsteil erhält n den Wert 2, danach wird die Kreis–Fläche πr2 berechnet und das Ergebnis den Variablen flaech und wert zugewiesen. Im Ausgabeteil werden der Radius und die beiden Werte für die Fläche zusammen mit einer Erläuterung auf dem Bildschirm ausgegeben. Schließlich wird das Programm beendet. Eine Ergebnisausgabe könnte beispielsweise so aussehen: 10 KAPITEL 4. PROGRAMMIERSPRACHEN, FORTRAN Radius = 1.000000 Flaeche = 3.141590 WERT = 3 Die Erklärung für die unterschiedlichen Werte von flaech und wert wird im Abschnitt 4.2.5 Arithme” tische Ausdrücke und Anweisungen“ gegeben. 4.2.2 Sprachelemente von Fortran95 Für ein korrektes FORTRAN–Programm müssen alle Anweisungen einer Reihe von eindeutig definierten Regeln (Syntax) genügen. Zunächst beschreiben wir die Grundelemente der Sprache, aus denen einzelne FORTRAN–Anweisungen zusammengesetzt werden können. 4.2.2.1 Zeichensatz Anweisungen werden aus den Zeichen des FORTRAN–Zeichensatzes gebildet: • Alphanumerische Zeichen (Buchstaben A bis Z und Ziffern 0 bis 9) • Sonderzeichen + - : = * / ( ) , . ’ ! " % & ; < > ? $ _ und das Leerzeichen Zwischen Groß- und Kleinschreibung wird nicht unterschieden. 4.2.2.2 FORTRAN–Quelltextzeile Eine FORTRAN–Zeile kann maximal 132 Zeichen lang sein. Innerhalb dieser 132 Zeichen dürfen Kommentare, Anweisungen und Leerzeichen an beliebiger Position stehen. Kommentare dienen nur zum besseren Verständnis eines Programmes und haben keinen Einfluß auf das Programm. Ein Kommentar wird mit dem Ausrufezeichen ! eingeleitet, wobei diese auch hinter den eigentlichen Anweisungen beginnen können. Leerzeichen können und sollten ebenso zur besseren Lesbarkeit eingefügt werden, haben aber auch keinerlei Auswirkungen auf das Programm. Bei mehr als einer Anweisung pro Zeile müssen diese durch ein Semikolon voneinander getrennt werden. Aus Übersichtlichkeitgründen sollte man dieses aber auf kurze Anweisungen beschränken oder gleich ganz vermeiden. a = 1. ; b = 2. ! 2 Anweisungen Anweisungen können auch in der nachfolgenden Zeile fortgesetzt werden. Dazu wird das &–Zeichen verwendet, welches man an das Ende der zu verlängernden Zeile anhängt. Dadurch wird die aktuelle und die nachfolgende Zeile als eine Befehlszeile interpretiert. Eine Anweisung darf sich aber maximal über 40 Anweisungzeilen erstrecken. kreisflaeche = radius * radius & * 3.14159 Verwendet man das Fortsetzungszeichen auch am Anfang der Folgezeile, wird die Zeile erst ab dem zweiten &–Zeichen fortgesetzt. PRINT *, ’Hello& & World’ ! Liefert bei der Ausgabe: ! ’Hello World’ 4.2. FORTRAN 11 Anweisungsmarken (Label) dürfen nur zu Beginn einer Anweisungszeile vor der eigentlichen Anweisung stehen. Anweisungsmarken sind ein- bis fünfstellige positive Zahlen und dienen dazu, um auf diese Anweisung Bezug nehmen zu können, wie es beispielsweise für die FORMAT–Anweisung wichtig ist. Sie können innerhalb einer Programmeinheit nur einmal vergeben werden, ihre numerische Reihenfolge ist dabei beliebig und sie sind außerhalb dieser Programmeinheit unbekannt. 1430 FORMAT(A) 4.2.2.3 ! Die FORMAT-Anweisung wird spaeter erklaert. Symbolische Namen Symbolische Namen für Programme, Unterprogramme, Variablen, Felder, Funktionen etc. bestehen aus bis zu 31 Zeichen, wie Buchstaben, Ziffern und/oder dem Unterstrich ”_” , von denen das erste ein Buchstabe sein muß. Bei der Wahl eines symbolischen Namens muß jedoch darauf geachtet werden, daß es sich dabei nicht bereits um ein reserviertes Schlüsselwort handelt, wie zum Besipiel PROGRAM, END o.ä. (Achtung! Verwechselungsgefahr: O und 0 (Buchstabe O und die Null), sowie I, l und 1 (Buchstaben großes I“, kleines L“ und die Eins“)!) ” ” ” 4.2.2.4 Konstanten Ein Datenelement mit festem Wert, das während der Programmausführung nicht geändert werden kann, ist eine Kostante. Eine symbolische Konstante (Konstante mit Namen) wird mit Hilfe des PARAMETER– Attributs (Abschnitt 4.2.3) definiert. Konstanten können vom Typ ganzzahlig (INTEGER), reell (REAL), doppelt genau (DOUBLEPRECISION), komplex (COMPLEX), logisch (LOGICAL) oder Zeichenreihe (CHARACTER) sein. Der Typ einer symbolischen Konstanten wird explizit, implizit oder per Konvention festgelegt, siehe Abschnitt 4.2.3 Vereinbarungsanweisungen“. ” Typen von Konstanten: • ganzzahlig: eine Folge von Ziffern mit Vorzeichen, ohne Dezimalpunkt. Beispiele: 4711 (auch: +4711) -131072 • reell: eine Folge von Ziffern mit Dezimalpunkt und optional mit ganzzahligem Exponent zur Basis 10, gekennzeichnet durch E. Diese einfach genauen Werte werden in einem Speicherwort (Mikrocomputer: 4 Byte, 6 bis 7 signifikante Dezimalstellen genau) gespeichert. Beispiele: 3.14159 -17.04 .5 12.E1 -.0094E+4 3.141E3 -256.E-2 Wert: Wert: Wert: Wert: 12. ∗ 101 = 120. −0.0094 ∗ 104 = −94. 3.141 ∗ 103 = 3141. −256. ∗ 10−2 = −2.56 • doppelt genau: wie reell, der Exponent wird mit D gekennzeichnet. Doppelt genaue Werte werden intern in zwei Speicherworten gespeichert (Mikrocomputer: 8 Byte, 13 bis 14 signifikante Dezimalstellen genau), wodurch sich eine höhere Genauigkeit ergibt. Beispiele: 12.D1 -256.D-2 Wert: 12. ∗ 101 = 120. Wert: −256. ∗ 10−2 = −2.56 12 KAPITEL 4. PROGRAMMIERSPRACHEN, FORTRAN • komplex: ein Paar reeller oder ganzzahliger Konstanten, durch Komma getrennt und in Klammern eingeschlossen. Komplexe Werte werden intern in zwei aufeinanderfolgenden Speicherworten als reelle Zahlenwerte abgespeichert. Beispiele: (3, 9.9) (-6.4E-1, 2) Wert: 3. + 9.9i (i2 = −1) Wert: −0.64 + 2.0i • logisch: es gibt die zwei Konstanten .TRUE. (wahr) und .FALSE. (falsch). Die Punkte sind zwingender Bestandteil. Beispiele: .TRUE. .FALSE. • Zeichenreihe: eine Reihe von Zeichen (mindestens eines), auch CHARACTER–String genannt, in Apostrophe eingeschlossen. Leerzeichen sind signifikant. Die Länge der Zeichenreihe ist die Anzahl der Zeichen zwischen den Apostrophen. Sie ist wichtig für Operationen mit der Zeichenreihe. Beispiele: 4.2.2.5 ’HFI’ ’NULL8FUFF10’ ’NOCH EIN BEISPIEL’ Länge: 3 Länge: 11 Länge: 17 Variablen Eine Variable bezeichnet eine Größe, die im Lauf der Rechnung verschiedene Werte annehmen kann. Die Variable wird durch ihren symbolischen Namen identifiziert, ihr wird vom Compiler ein Speicherplatz im Arbeitsspeicher zugeordnet. Bei Benutzung der Variablen in einer Anweisung verfügt man über den Inhalt ihres Speicherplatzes. Vor der ersten Verwendung muß die Variable definiert (initialisiert) werden. Der Typ einer Variablen wird analog zum Typ von Konstanten explizit, implizit oder per Konvention festgelegt, siehe Abschnitt 4.2.3 Vereinbarungsanweisungen“. Ansonsten gilt das bei Konstanten Gesagte. ” 4.2.2.6 Anordnungsreihenfolge von Anweisungen Die zulässige Anordnung von FORTRAN–Anweisungen innerhalb eines Programms sei an dieser Stelle schon angeführt, Abb. 4.3, auch wenn die meisten FORTRAN–Anweisungen erst später erklärt werden. Ein FORTRAN–Programm besteht aus einem Hauptprogramm und einem oder mehreren (Funktions–) Unterprogrammen. Die PROGRAM–Anweisung darf nur als erste Anweisung in einem Hauptprogramm stehen. Die erste Anweisung einer Subroutine oder einer Funktion ist entsprechend eine SUBROUTINE– oder eine FUNCTION–Anweisung. Zwischen der CONTAINS– und der END–Anweisung können für sich abgeschlossene interne Unterprogramme eingefügt werden. Fehlen interne Unterprogramme, wird die CONTAINS–Anweisung selbst auch weggelassen. Die END-Anweisung ist die letzte Anweisung jeder Programmeinheit. Weiteres hierzu im Abschnitt 4.2.15 Programmeinheiten und Anweisungen“. ” Der Aufbau eines FORTRAN–Moduls ist in Abb.4.3 beispielhaft angegeben. Die Reihenfolge von nebeneinanderstehenden Anweisungen ist beliebig. Durch horizontale Linien getrennte Anweisungen dürfen nicht miteinander gemischt werden. 4.2.3 Vereinbarungsanweisungen Vereinbarungen gehören zu den nichtausführbaren Anweisungen und dienen dazu, die Eigenschaften der im Programm verwendeten Namen (z.B. von Variablen, Unterprogrammen) festzulegen. Vereinbarungen 4.2. FORTRAN 13 PROGRAM–, SUBROUTINE–, FUNCTION–Anweisung IMPLICIT NONE–Anweisung IMPLICIT–Anweisungen INCLUDE– Zeilen und Compiler– Anweisungen Kommentar– und Leerzeile FORMAT– Anweisungen PARAMETER– Anweisungen Typvereinbarungen, wie REAL INTEGER CHARACTER TYPE–Anweisungen Formelfunktions–Definitionen ausführbare Anweisungen, wie Zuweisungen READ WRITE OPEN DO IF () THEN, ... CONTAINS–Anweisung interne Unterprogramme END–Anweisung Abbildung 4.3: Anordnungsreihenfolge von FORTRAN–Anweisungen 14 KAPITEL 4. PROGRAMMIERSPRACHEN, FORTRAN SUBROUTINE low2UP (ZKette) !====================================================================== !*Vers: 06.10.1989/WWB HFI-TUB !---------------------------------------------------------------------!*Zweck: Diese Routine wandelt in der Zeichenkette ZKette enthaltene ! Kleinbuchstaben in Grossbuchstaben um. !---------------------------------------------------------------------!*Vars: In/Out: ZKette (Char) - Umzuwandelnde Zeichenkette ! ! Lokal: LStr (Int) - Laenge der Zeichenkette ! NChar (Int) - Positionsnummer des Zeichens in der ! ASCII-Tabelle !---------------------------------------------------------------------!*Tip: Zeichenweise Umwandlung gemaess ASCII-Tabelle (Kap. 5); ! Kleinbuchstaben liegen zwischen 97 und 122, die entsprechenden ! Grossbuchstaben liegen um 32 Positionen darunter. !---------------------------------------------------------------------!*Mods: 30.10.90/WWB Version fuer Info-Tech-VL ! 18.12.01/TFS Umsetzung in Fortran 95 ! ====================================================================== !*Vereinbarungsteil !---------------------------------------------------------------------CHARACTER (LEN=*) :: ZKette INTEGER :: LStr, NChar, I !====================================================================== !*Prozedurrumpf !---------------------------------------------------------------------! --- Ermittlung der Laenge von ZKette LStr = LEN (ZKette) !---------------------------------------------------------------------! --- Zeichenweises Absuchen von ZKette nach Kleinbuchstaben DO I=1,LStr NChar = IChar( ZKette(I:I) ) IF (NChar .GE. 97 .AND. NChar .LE. 122) & ZKette(I:I) = CHAR(NChar-32) ENDDO !====================================================================== END SUBROUTINE Abbildung 4.4: Beispiel für den Aufbau eines FORTRAN–Moduls. 4.2. FORTRAN 15 müssen vor allen DATA–Anweisungen, Formelfunktionsdefinitionen und vor allen ausführbaren Anweisungen einer Programmeinheit stehen. Es gibt folgende Vereinbarungsanweisungen: • Typ (INTEGER, REAL, DOUBLE PRECISION, COMPLEX, LOGICAL, CHARACTER) • IMPLICIT • COMMON • EXTERNAL • INTRINSIC • SAVE • [ EQUIVALENCE ] Hier wird zunächst nur die Typ– sowie eingeschränkt die IMPLICIT–Anweisung behandelt. Die anderen Vereinbarungsanweisungen folgen später. 4.2.3.1 Typvereinbarungen Typenvereinbarungen sind notwendig wegen der unterschiedlichen rechnerinternen Darstellungsform beispielsweise von ganzen und reellen Zahlen (binäre Darstellung im Arbeitsspeicher). Der Datentyp von Variablen und Konstanten wird mit Typ- und IMPLICIT–Anweisungen bestimmt. Wenn weder Typ– noch IMPLICIT–Anweisungen verwendet werden, findet eine Standard–Typenzuordnung nach der Typenkonvention statt: tritt der Name nicht in einer expliziten oder impliziten Typanweisung auf, dann ist die Variable vom ganzzahligen Typ (INTEGER), wenn der Name mit einem der Buchstaben I, J, K, L, M, N beginnt, andernfalls ist sie von Typ reell (REAL). Alle anderen Typen müssen also explizit per Vereinbarung definiert werden. Die explizite Vereinbarung übersteuert dabei die implizite, die ihrerseits die Vereinbarung nach Konvention übersteuert. Aus Gründen der Eindeutigkeit, Klarheit und Übersichtlichkeit und zum Schutz vor unliebsamen Fehlern wird dringend empfohlen, alle Größen explizit per Typanweisung zu definieren. Für die Übungsaufgaben wird diese Empfehlung zur Pflicht. In diesem Zusammenhang findet sich auch die einzige sinnvolle Verwendung der IMPLICIT–Anweisung: durch die Deklaration IMPLICIT NONE in jeder Programmeinheit wird die implizite Variablendeklaration untersagt, und alle Variablen müssen explizit deklariert werden. Die IMPLICIT–Anweisung wird hier nicht weiter besprochen. 4.2.3.2 Explizite Typvereinbarungen Bei der expliziten Typanweisung wird der Name in eine Typanweisung aufgenommen. Sie hat die Form typ [, attribut]... :: v_1 , ..., v_n . Dabei ist typ die Typbezeichnungen, und v_1, ..., v_n sind die Namen der Datenobjekte, deren Typ festgelegt werden soll. Mit attribut kann man zusätzlich optionale Angaben zu deren Eigenschaften machen, wie schon z.B. im Programm circle mit Parameter PI gezeigt. Typen von Datenobjekten: 16 KAPITEL 4. PROGRAMMIERSPRACHEN, FORTRAN • ganzzahlig: INTEGER • reell: REAL • doppelt genau: DOUBLE PRECISION • komplex: COMPLEX Beispiele: INTEGER :: freddy, bud, alf REAL :: ha, haha, hihi, komik DOUBLE PRECISION :: viel, genauere, zahlen COMPLEX :: z0, phase Diese vier Typanweisungen dienen zur Deklaration von arithmetischen Datenobjekten. So legt z.B. REAL fest, daß die Namen ha, haha, hihi und komik vom Typ reell sind, sich dahinter also reelle Zahlen verbergen. • logisch: Beispiel: LOGICAL LOGICAL :: wahl, korrekt, ablauf LOGICAL–Variablen können dagegen nur die Wahrheitswerte beiden .TRUE. und .FALSE. enthalten. • Zeichenreihen: Beispiele: CHARACTER CHARACTER (LEN=20) :: zeichenkette, dateiname CHARACTER *25 :: buchstaben, text*15, zeile Bei der Typ–Vereinbarung CHARACTER muß zusätzlich die Länge der Zeichenkette vereinbart werden. Dies geschieht seit der Fortran95–Norm mit der (LEN=..)–Angabe. Diese bezieht sich dann auf alle in der Anweisung angegebenen Variablen und legt in unserem Beispiel für die Variablen zeichenkette und dateiname eine Länge von jeweils 20 Zeichen fest. Eine ältere Form der Längen– Angabe wird mittels *laenge vorgenommen. Dabei wird hier mit *15 hinter text dessen Länge mit 15 Zeichen vereinbart. Für alle anderen in der Anweisung aufgeführten Namen ( buchstaben und zeile) gilt die Länge, die unmittelbar hinter dem Schlüsselwort CHARACTER steht (hier 25 Zeichen). Prinzipiell sind beide Formen in der Fortran95–Norm enthalten. Die zweite soll jedoch in nachfolgenden Fortran–Normen durch die Angabe mit (LEN=..) abgelöst werden. Macht man jedoch bzgl. der Länge gar keine Angaben, so wird vom Compiler die Länge 1 festgelegt! Attribute von Datenobjekten: • Konstanten: PARAMETER Mit der Vergabe des PARAMETER–Attributs kann man für eine Konstante einen symbolischen Namen vereinbaren ( symbolische Konstante“): ” < typ >, PARAMETER :: p_1 = e_1, p_2 = e_2, ... Darin sind pi symbolische Namen, und ei sind Konstanten oder Konstantenausdrücke, die ihrerseits nur Konstanten und bereits vorher definierte symbolische Konstanten (also insbesondere keine Funktionsaufrufe) enthalten dürfen. Der Wert einer Konstanten kann im gesamten Programm nicht mehr verändert werden. 4.2. FORTRAN 17 Beispiel: INTEGER, PARAMETER :: G = 11, G2 = 2*G, G5 = 5*G+1 REAL :: FELD1 (G,G2), FELD2 (G5) : X1 = DELTAX/G Man kann die Vereinbarung einer Konstante aber auch als eigenständige Anweisung schreiben: PARAMETER (p_1 = e_1 , p_2 = e_2 , ...) In diesem Falle darf diese aber erst nach der Vereinbarung der symbolischen Namen pi , – sonst aber an beliebiger Stelle in den Vereinbarungs–Anweisungen stehen (Abb.4.3). • Felder: DIMENSION Das DIMENSION–Attribut wird verwendet, um eine Variable als Feld (indizierte Variable, array) zu vereinbaren. Ein Feld ist eine spezielle Datenstruktur, bei der alle Elemente der Struktur durch einen einzigen symbolischen Namen gekennzeichnet, und diese Elemente alle vom gleichen Typ sind. Neben dem DIMENSION–Attributes gibt es noch zwei weitere Möglichkeiten solch ein Feld zu deklarieren. Die eine ist die, der direkten Angabe und eine weitere, die der Verwendung der DIMENSION–Anweisung: Die Beispiele mit DIMENSION–Attribut: INTEGER, DIMENSION (3) REAL, DIMENSION (4:8) :: a :: xfeld (eindimensional, 3 Elemente) (eindimensional, 5 Elemente) oder mit direkter Angabe: REAL :: werte (-10:10, 0:20) COMPLEX :: s17u4 (100, -17:4, 3) (zweidimensional, 21*21 Elemente) (dreidimensional, 100*22*3 Elemente) oder mittels DIMENSION–Anweisung: INTEGER :: n DIMENSION :: n (16,2,10:15) (dreidimensional, 16*2*6 Elemente) zeigen die Syntax der Feldvereinbarungen. Die erste der beispielhaft gezeigten Feldvereinbarungen richtet drei Speicherplätze für ganze Zahlen ein, die mit a(1), a(2) und a(3) angesprochen werden können. Die untere und die obere Grenze einer Dimension (eines Indexes) können beliebige ganzzahlige Ausdrücke sein und werden durch einen Doppelpunkt getrennt. Fehlt die untere Grenze, wird der Wert 1 angenommen. Die obere Grenze muß größer gleich der unteren Grenze sein. Wir werden später sehen (Abschnitt 4.2.19), daß in Unterprogrammen auch Felder vereinbart werden können, deren Dimensionen durch Variablen gegeben sind oder diese auch direkt vom aufrufenden Unterprogramm übernommen werden. Es sind maximal sieben Dimensionen möglich. Die Länge eines Feldes ist gleich der Anzahl seiner Elemente. Weitere Informationen über Felder, insbesondere zu Dimensionierung, Form der Indizes und Speicherung, werden im Abschnitt 4.2.3.3 Felder“ gegeben. ” • dynamische Felder: ALLOCATABLE Dynamische Felder sind Felder, deren Element–Anzahl erst im Laufe des Programmes festgelegt werden können. Das ALLOCATABLE–Attribut kann also nur zusammen mit z.B. dem DIMENSION– Attribut auftreten. Dabei muß die Anzahl der Dimensionen bereits bei der Deklaration vorgegeben werden. Wir werden darauf genauer im Kapitel 4.2.3.5 eingehen. 18 KAPITEL 4. PROGRAMMIERSPRACHEN, FORTRAN • gesicherte Variablen: SAVE Das Attribut SAVE ist nur bei der Verwendung von Unterprogrammen interessant. Der FORTRAN– Standard besagt, daß beim Rücksprung in das rufende Programm alle lokalen Größen nicht mehr definiert sind. Das SAVE–Attribut sorgt dafür, daß der Wert der Variablen erhalten bleibt, wenn das Unterprogramm bei Ausführung der END–Anweisung wieder verlassen wird. Dieses sollte jedoch mit Vorsicht behandelt werden! • Zeiger: POINTER Mit dem POINTER–Attribut wird eine ganz besondere Art von Variablen deklariert. Diese dienen nicht – wie Variablen normalerweise – dazu, in ihnen Daten für zum Beispiel Berechnungen abzulegen, sondern bieten dem Programmierer die Möglichkeit, Adressen von anderen Variablen zu speichern. Mit dem Beispiel: INTEGER, POINTER :: zeigtauf haben wir einen Zeiger zeigtauf deklariert, der die Adresse einer Integervariablen speichern kann. Man sagt dabei: auf eine Integervariable zeigen kann. Genauer wird dies im Abschnitt 4.2.3.6 erläutert. • Zeigerziel: TARGET Das TARGET–Attribut kann man einer Variablen geben, wenn man diese als Ziel eines Zeigers benutzen möchte. Auch dieses wird genauer im Abschnitt4.2.3.6 erläutert. 4.2.3.3 Felder Ein Feld (indizierte Variable, Array) ist, wie in Abschnitt 4.2.3.2 bereits angesprochen, eine spezielle Datenstruktur, bei der alle Elemente der Struktur durch einen einzigen symbolischen Namen gekennzeichnet sind. Ein einzelnes Feldelement wird durch Angabe dieses Namens und eines Indexes angesprochen. Alle Feldelemente sind von gleichen Datentyp wie das Feld. Er wird wie bei Konstanten und Variablen über den Namen bestimmt. Feldname und Felddimension müssen in einer Typ– oder COMMON–Anweisung entweder direkt, mit Hilfe des DIMENSION–Attributes oder der DIMENSION–Anweisung vereinbart werden. Jedes Feld darf nur einmal in jeder Programmeinheit vereinbart werden. Die Dimensionierung von Feldern in einer expliziten Typvereinbarung hat die allgemeine Form < typ >, DIMENSION(d1_1, d1_2, ...) :: array1 bzw. < typ >:: array1(d1_1, d1_2, ...), array2(d1_2, d2_2, ...) Darin ist arrayj der Name des j–ten Feldes und dji gibt die Grenzen der i–ten Dimension des j–ten Feldes an, die in der Form li : ui geschrieben werden kann. Dabei ist ui die obere Grenze der i–ten Dimension und li ist deren untere Grenze. Wird li weggelassen, gilt li = 1. Neben den in 4.2.3.2 bespielhaft gezeigten Dimensionsvereinbarungen sind auch andere ganzzahlige konstante Ausdrücke1 für die Indexgrenzen erlaubt, z.B. (siehe auch Abschnitt 4.2.2): INTEGER, PARAMETER :: N = 11, N2 = 2*N, N5P1 = 5*N + 1 REAL :: XFELD(N2, (N+1)/2), YFELD(N2+3:N5P1) 1 Ausdrücke werden in Abschnitt 4.2.5 erläutert 4.2. FORTRAN 19 Zur flexiblen Verwendung von Unterprogrammen können übergebene Felder dort auch mit variablen Dimensionen deklariert werden, sofern die Dimension als Variable übergeben wurde. Man achte dabei darauf, daß Felder in allen Unterprogrammen stets gleich dimensioniert sind, da sonst insbesondere bei mehrdimensionalen Feldern schwer zu findende Fehler durch Zugriffe auf falsche Speicherplätze auftreten können (siehe Abschnitt 4.2.21 Kommunikation zwischen Programmeinheiten“). ” 4.2.3.4 Speicherung von Feldern Bei der Verwendung von Feldern ist es wichtig, Kenntnis über die Speicherung zu besitzen. Ein Feld wird in einem kontinuierlichen Block des Arbeitsspeichers abgespeichert. Das erste Feldelement kommt in das erste Speicherwort. Die Feldelemente werden spaltenweise in aufsteigender Reihenfolge gespeichert, d.h. der erste Indexwert wird am schnellsten erhöht und der letzte am langsamsten. Beispiel: Das zweidimensionale Feld A(3,4) wird in der unten angegebenen Weise im Speicher abgelegt: A(1,1); A(2,1); A(3,1); A(1,2); A(2,2); . . . ; A(2,4); A(3,4). Man kann auf das komplette Feld zugreifen durch Angabe des Feldnamens; der Zugriff auf einzelne Feldelemente erfordert zusätzlich die Angabe des Indexes (siehe obiges Beispiel). ? A(1,1) ? A(2,1) ? A(1,2) 6 ? A(3,1) ? A(2,2) ? A(3,2) ? A(1,3) 6 ? A(2,3) ? A(1,4) 6 ? A(3,3) ? A(2,4) ? A(3,4) ? Beim Zugriff auf Elemente kann der Indexausdruck Funktionsaufrufe und Zugriffe auf Feldelemente enthalten. Der Wert eines Indexausdrucks darf die aktuelle Größe der Dimension nicht überschreiten. Eine Überprüfung auf Zugriffe außerhalb der Indexgrenzen findet in der Regel nicht statt, kann aber durch Compileroptionen erzwungen werden. Feldzugriffe mit ungültigem Index bewirken nicht vorhersehbare Reaktionen und Ergebnisse des Programms! 4.2.3.5 Dynamische Felder Wie bereits unter 4.2.3.2 erwähnt, ist ein dynamisches Feld ein Feld, deren Feldweiten erst im Laufe eines Programmes festgelegt zu werden brauchen. Das ALLOCATABLE–Attribut hat also nur Sinn bei einer Variablen, die zugleich (z.B. mit dem DIMENSION–Attribut) die Eigenschaften eines Feldes erhält. Dabei muß die Anzahl der Dimensionen bereits bei der Deklaration vorgegeben werden. Eine Deklaration könnte z.B. wie folgt aussehen: REAL, ALLOCATABLE, DIMENSION (:, :) :: dynfeld1, dynfeld2(:, :, :) Für dynfeld1 ist durch das DIMENSION–Attribut also die Dimension 2 vorgegeben, wohingegen für das dynamische Feld dynfeld2 3 Dimensionen mittels direkter Angabe festgelegt wurden. Die Zuweisung, wieviel Elemente die einzelnen Dimensionen besitzen sollen, braucht erst im Laufe des Programmes erfolgen. Dazu wid die ALLOCATE–Anweisung verwendet: 20 KAPITEL 4. PROGRAMMIERSPRACHEN, FORTRAN ALLOCATE (feld1(dim,[dim]...), feld2(dim,[dim]...)[, STAT = statusvariable]) Der Parameter STAT dient dazu, um die korrekte Abbarbeitung der Anweisung zu überprüfen. Im Fehlerfall wird der angegebenen INTEGER–Variablen (hier: statusvariable) ein Wert ungleich Null zugewiesen; bei korrekter Ausführung dagegen gleich Null. Mit ALLOCATE (dynfeld1(5,20), dynfeld2(-2:10,3,9), STAT = fehler) zum Beispiel werden dynfeld1 5×20 Elemente zugewiesen und dynfeld2 erhält in seiner ersten Dimension 13 Elemente, wobei der unterste Index bei −2 beginnt, und in seiner zweiten und dritten je 3 und 9. Möchte man ein dynamisches Feld erneut zuweisen, muß es zuvor wieder freigegeben werden. Dazu dient die DEALLOCATE–Anweisung: DEALLOCATE (feld1, feld2, ...[, STAT = statusvariable]) Um den Status eines dynamischen Feldes zu erfragen existiert die vordefinierte Funktion ALLOCATED(feld), die TRUE als Rückgabewert liefert, falls das Feld definiert ist. Anderenfalls liefert sie den Wert FALSE. 4.2.3.6 Zeiger und Ziele Zeiger - also, eine Variable, die mit dem POINTER–Attribut versehen wurde - sind, wie bereits erwähnt, keine Variablen im eigentlichen Sinne. Sie dienen nicht zur Aufnahme von Rechendaten, sondern bieten dem Programmierer die Möglichkeit, Adressen im Arbeitsspeicher anzusprechen. Man sagt, sie zeigen darauf. Ist das Ziel eines Zeigers gesetzt worden, kann mit dem zugeordneten Speicherbereich gearbeitet werden, wie bei allen anderen Variablen auch. Es können Werte abgelegt, verändert und ausgelesen werden. Unter Fortran95 gibt es für einen Zeiger zwei Möglichkeiten eine Speicheradresse festgelegt zu bekommen. Zum einen kann ein Zeiger auf eine Adresse zeigen, die bereits einer anderen Variablen zugeordnet ist, zum anderen kann ihm aber auch ein eigener (und somit zuvor freier) Speicherplatz zugewiesen werden. Zeiger auf eine Variable Ein Zeiger kann nur auf eine andere Variable zeigen, wenn für diese das TARGET–Attribut gesetzt, sie also als Zeigerziel deklariert wurde. Die Zuweisung der Variable als Ziel erfolgt dann mit Hilfe einer Zeigerzuweisungs–Anweisung ("=>"). Beispiel: REAL, TARGET :: zahl REAL, POINTER :: zeigaufzahl ! ein REAL-Variable, die Zeigerziel sein kann ! ein Zeiger auf eine Variable vom Typ REAL zahl = 0. zeigaufzahl => zahl ! ’zeigaufzahl’ soll ’zahl’ als Ziel haben PRINT*, zeigaufzahl zeigaufzahl = 7.3 PRINT*, zahl ! gibt ’0.’ auf dem Bildschirm aus ! gibt ’7.3’ auf dem Bildschirm aus In unserem Beispiel haben wir eine REAL–Größe namens zahl deklariert, die auch das TARGET–Attribut besitzt. Somit kann zahl auch als Ziel eines Zeigers benutzt werden. Der Zeiger zeigaufzahl ist auch 4.2. FORTRAN 21 vom Typ REAL, und kann somit als Zeiger auf REAL-Größen verwendet werden. Mit "=>" haben wir zahl als Ziel von zeigaufzahl festgelegt. Somit zeigt zeigaufzahl auf zahl. Als wir den Wert von zeigaufzahl auf 7.3 gesetzt haben, hat sich dadurch auch der Wert von zahl auf 7.3 geändert, da beide symbolische Namen die gleiche Adresse im Speicher ansprechen. Um den Zeiger von seinem Zeigerziel wieder zu trennen, kann die Anweisung NULLIFY (zeiger1, zeiger2, ...) verwendet werden. Jedoch würde jede Neuzuordnung eines Zeigers zu einem Ziel bereits implizieren, daß die vorangegangene Zuordnung damit aufgehoben ist. Zeiger auf eigenen Speicherplatz Ähnlich wie bei dynamischen Feldern kann die Vergabe von Speicher an Zeiger gesehen werden. Daher ist es auch nicht weiter verwunderlich, daß hierbei auch nahezu die gleichen Anweisungen verwendet werden. Mit ALLOCATE (zeiger1, zeiger2, ...[, STAT = statusvariable]) kann einem Zeiger (oder auch mehreren gleichzeitig) Speicherplatz zugewiesen werden. Dabei wird dem Zeiger abhängig von seinem Typ Speicher zugeteilt und kann anschließend wie eine gewöhnliche Variable dieses Typs behandelt werden. Die korrekte Abbarbeitung der Anweisung kann wie bei den dynamischen Felder unter 4.2.3.5 abgefragt werden. Um den Speicherplatz eines Zeigers wieder freizugeben verwendet man auch hier die Anweisung DEALLOCATE (zeiger1, zeiger2, ...[, STAT = statusvariable]). Um den Zuordungsstatus eines Zeigers zu erfragen existiert die vordefinierte Funktion ASSOCIATED(zeiger), die TRUE als Rückgabewert liefert, falls dem Zeiger ein Ziel zugewiesen ist. Allgemein sollte aber noch gesagt sein, daß niemals ein Zeiger verwendet werden sollte, der nicht bereits ein Ziel zugewiesen bekommen hat. Um etwaige Fehler innerhalb eines Programmablaufs auszzuschließen empfiehlt es sich, jeden Zeiger mit dem Rückgabewert der Funktion NULL() zu belegen zeiger => NULL() ,um diesen als nicht–zugewiesen festzulegen! 4.2.4 Initialisierungen Mit Initialisierung bezeichnet man die Vergabe eines Anfangswertes an ein Datenobjektes. Die Initialisierung von Variablen in einem Programm ist bei den meisten Programmiersprachen wichtig, da Variablen oft nach der Deklaration keinen festgelegten Wert haben. Somit können Berechnungen, die von einem Startwert abhängen, leicht falsche Ergebnisse liefern, ohne daß der Fehler im Programm ersichtlich ist. In Fortran95 gibt es nun folgende Möglichkeiten der Initialisierung: • Initialisierung bei der Deklaration Die Initialisierung kann direkt hinter der Deklaration erfolgen. 22 KAPITEL 4. PROGRAMMIERSPRACHEN, FORTRAN INTEGER :: zaehler = 1, summe = 0 REAL :: feld1(100) = 1.5 , feld2(50) = / 1., 2., 3., 4., 5.,(i+5.,i=1,45)/ Den beiden INTEGER–Größen zaehler und summe werden hier die Anfangswerte 1 bzw. 0 zugewiesen. Bei dem REAL–Feld feld1 werden alle 100 Elemente auf einmal mit dem Wert 1.5 belegt. Beim feld2 sollen die einzelnen Elemente mit verschiedenen Werten versehen werden. Dies kann mit einer Werte–Liste erreicht werden, in der man in / .. / eingeschlossen alle Werte einzeln oder mittels einer impliziten Zählschleife für mehrere Elemente angibt. Dabei muß jedoch nicht unbedingt mit einem konstanten Wert initialisiert werden, sondern dieser kann sich auch durch einen arithmetischen Ausdruck ergeben. • DATA–Anweisung Die Initialisierung kann aber auch in einer seperaten Anweisung erfolgen, der DATA–Anweisung: DATA nlist1 /wlist1 /, nlist2 /wlist2 /, . . . Die DATA–Anweisung ist nicht ausführbar und muß nach dem Vereinbarungsteil auftreten (siehe Bild 4.3). nlisti ist eine Liste von Namen, denen Anfangswerte zugewiesen werden sollen, und wlisti ist eine Liste von (symbolischen) Konstanten, die die eigentlichen Anfangswerte sind. Die nlisti kann unterschiedlichste Elemente sowie implizite Schleifen enthalten. Die Liste der Anfangswerte wlisti kann vor den Konstanten Wiederholungsfaktoren enthalten. Für jedes Element aus nlisti muß ein Wert in wlisti vorhanden sein. Das erste Element der Konstantenliste entspricht dem ersten Namen der Namenliste, das zweite der Konstantenliste dem zweiten der Namenliste usw. Beispiel 1: INTEGER :: array (200), feld (100), i DATA (array (i), i = 1,111), feld /111*0, 60*1, 40*2/ Die ersten 111 Elmente des Feldes array werden über eine implizite Zählschleife mit Null initialisiert, die ersten 60 Elemente von feld werden mit Eins vorbesetzt und die restlichen 40 mit Zwei. Man beachte, daß nicht alle Elemente von array initialisiert sind, und daß durch die Angabe des Feldnamens feld ohne Indexausdruck alle Elemente dieses Feldes vorbesetzt werden müssen. Beispiel 2: REAL z(400) DATA z(1:399:2), z(2:400:2) / 200*1., 200*-1./ Hier wird jedes Element von von Z abwechselnd mit dem Wert 1. und -1. belegt. Der Umgang mit Feldern und Teilfeldern wird in einem späteren Abschnitt genauer besprochen. Gleichgültig auf welche Art und Weise man ein Datenelement initialisiert, darf dieses nur ein einziges Mal im gesamten Programm geschehen. Bezüglich weiterer Einschränkungen sei auf die weiterführende FORTRAN–Literatur verwiesen. 4.2.5 Arithmetische Ausdrücke und Anweisungen 4.2.5.1 Arithmetische Ausdrücke Ein arithmetischer Ausdruck besteht aus einer Folge von arithmetischen Operanden (Konstanten, Variablen, Feldelementen und Funktionsaufrufen), die durch (arithmetische) Operatoren und Klammern voneinander getrennt (oder miteinander verknüpft) sind. 4.2. FORTRAN 23 Zum Beispiel ist -(3.-X)*F + H/D**4 ein gültiger arithmetischer Ausdruck. Arithmetische Operanden stellen Werte vom Typ INTEGER, REAL, DOUBLEPRECISION und COMPLEX dar. Arithmetische Operationen sind: ** * / + - Exponentiation Multiplikation Division Addition Subtraktion Enthält der Ausdruck mehrere Operatoren, so gilt für die Auswertung die Reihenfolge: 1. ** 2. * und / 3. + und - . Durch Klammern kann die Auswertungsreihenfolge beeinflußt werden. Beispiele: -Z**2 (-Z)**2 ist gleichbedeutend mit −(Z 2 ) ergibt (−Z)2 = Z 2 Aufeinanderfolgende Operationen von gleichem Rang (* und / bzw. + und -) werden von links nach rechts bearbeitet: A/B*C ist gleichbedeutend mit (A/B)*C (und nicht A/(B*C)). Aber: Aufeinanderfolgende Exponentiationen werden von rechts nach links zusammengefaßt: 2 2**3**2 ist gleichbedeutend mit 2**(3**2), also mit 2(3 ) . Mehrere aufeinanderfolgende Operatoren wie A**-B oder C+-D sind verboten. Durch Verwendung von Klammern wie A**(-B) oder C+(-D) werden diese Ausdrücke statthaft. Überhaupt empfiehlt sich die fleißige Verwendung von Klammern auch an solchen Stellen, wo sie eigentlich nicht zwingend notwendig sind. Man schafft dadurch Klarheit und Übersichtlichkeit in den Anweisungen und beugt Zweifeln bei der Auswertungsreihenfolge vor. Der Datentyp eines arithmetischen Ausdrucks ergibt sich aus dem Datentyp der Operanden nach der folgenden Tabelle: 24 KAPITEL 4. PROGRAMMIERSPRACHEN, FORTRAN DOUBLE INTEGER REAL COMPLEX INTEGER I R C D REAL R R C D COMPLEX C C C — D D — D DOUBLE PRECISION PRECISION ¾ Exponent bei ** 6 Basis bei ** Tabelle 4.1. Ergebnistypen arithmetischer Ausdrücke. (I = INTEGER, R = REAL, C = COMPLEX, D = DOUBLEPRECISION, — = nicht erlaubt) Bei der Verknüpfung von Operanden unterschiedlichen Typs wird der vom Ergebnistyp abweichende Operand zunächst in den Ergebnistyp umgewandelt. (Ausnahme: Exponentiation mit INTEGER–Exponent). Der Ausdruck 2.*(2/3) hat beispielsweise den Wert 0.0, aber (2.*2)/3 hat den Wert 1.333333.... . Bei der Division von INTEGER–Größen ist das Ergebnis wieder eine INTEGER–Größe, die durch Abschneiden der Nachkommastellen im mathematischen Quotienten entsteht: 12/8*2 ergibt 2, 12*2/8 ergibt 3. Aus dem selben Grund ist 8/3*3.0 gleich 6.0 und nicht 8.0. Generell muß vor der Verwendung von sogenannten mixed mode–Ausdrücken (arithmetische Ausdrücke mit Größen unterschiedlicher Typen) gewarnt werden, da sie eine beliebte Fehlerquelle darstellen (siehe obige Beispiele). Für die Typ–Umwandlung gibt es in FORTRAN Standardfunktionen, z.B. INT, FLOAT, usw.. Für die in arithmetischen Ausdrücken ebenfalls möglichen Funktionsaufrufe (z.B. Wurzel: SQRT, Sinus: SIN usw.) gelten die gleichen Regeln wie oben. Funktionen werden später erläutert (Abschnitt 4.2.17, 4.2.18). 4.2.5.2 Arithmetische Anweisungen Arithmetische Anweisungen gehören zu den ausführbaren Anweisungen und haben die allgemeine Form v = expr Darin ist v eine (einfache oder indizierte) Variable und expr ein beliebiger arithmetischer Ausdruck. Das Zeichen ’=’ muß dabei interpretiert werden als ergibt“ oder als Zuweisungszeichen: der Wert von ” 4.2. FORTRAN 25 expr wird der Variablen v zugewiesen, d.h. er wird auf dem der Variablen v zugeordneten Speicherplatz gespeichert. Arithmetische Anweisungen sind keine Gleichungen im mathematischen Sinn, wie das Beispiel N = N + 1 zeigt: der Wert von N wird um 1 erhöht und das Ergebnis wird auf den Speicherplatz von N zurückgeschrieben. Ist der Typ der Variablen vom Typ des arithmetischen Ausdrucks verschieden, so wird der Ausdruck zuerst seinem Typ entsprechend berechnet; dann wird der Typ des Ergebnisses in den Typ der Variablen umgewandelt. Beispiel (LX und I3 sind vom Typ INTEGER): I3 = LX/3. Der Wert von LX wird in eine reele Größe umgewandelt und im reelen Modus durch 3. dividiert. Das Ergebnis wird durch Abschneiden der Nachkommastellen in eine ganze Zahl umgewandelt und in I3 gespeichert. Mit dem oben Gesagten erklären sich auch die unterschiedlichen Ergebnisse des dritten Programmbeispiels in Abschnitt 4.2.1 Einfache FORTRAN–Programmbeispiele“. Die Auswertung der Kreisflächen” formel πr2 für r = 1 ergibt einen Wert von 3.14159. . . , der durch die Zuweisung an die REAL–Variable flaech auch richtig ausgegeben wird. Bei der Zuweisung an die INTEGER–Variable wert findet jedoch eine Umwandlung des Ergebnisses in eine ganze Zahl statt, die mit dem Abschneiden der Nachkommastellen verbunden ist. Daher sind im FORTRAN–Sinne beide Ergebnisse richtig. 4.2.6 Logische Ausdrücke und Anweisungen Die Ausführungsreihenfolge der Programmanweisungen kann abhängen von Größen, die sich während der Rechnung ergeben. Die Steuerung solcher Verzweigungen geschieht über Steueranweisungen (siehe Abschnitt 4.2.9 Steueranweisungen“), die logische Ausdrücke auswerten. ” 4.2.6.1 Logische Ausdrücke Logische Berechnungen werden mit logischen Ausdrücken formuliert, bei deren Auswertung sich ein Ergebnis vom logischen Typ mit dem Wert wahr oder f alsch ergibt. Jede logische Konstante, Variable, jeder Vergleichsausdruck und jeder Aufruf einer logischen Funktion ist ein einfacher logischer Ausdruck. Kompliziertere logische Ausdrücke werden gebildet durch Verknüpfung logischer Operanden mit logischen Operatoren und Klammern. Logische Operatoren sind Operator .NOT. .AND. .OR. .EQV. .NEQV. mit der folgenden Wertetabelle: Name Negation Konjunktion Disjunktion Äquivalenz Antivalenz Gebrauch .NOT.X X1.AND.X2 X1.OR.X2 X1.EQV.X2 X1.NEQV.X2 26 KAPITEL 4. PROGRAMMIERSPRACHEN, FORTRAN X1 .TRUE. .TRUE. .FALSE. .FALSE. X2 .TRUE. .FALSE. .TRUE. .FALSE. .NOT.X2 .FALSE. .TRUE. .FALSE. .TRUE. X1.AND.X2 .TRUE. .FALSE. .FALSE. .FALSE. X1.OR.X2 .TRUE. .TRUE. .TRUE. .FALSE. X1.EQV.X2 .TRUE. .FALSE. .FALSE. .TRUE. X1.NEQV.X2 .FALSE. .TRUE. .TRUE. .FALSE. In einem Vergleichsausdruck können die Werte zweier arithmetischer, zweier logischer oder zweier Zeichenausdrücke verglichen werden, nicht jedoch z.B. der Wert eines arithmetischen mit dem eines Zeichenausdrucks. Für Vergleichsoperationen mit REAL-Größen sei auf Abschnitt 4.4 hingewiesen. Vergleichsausdrücke treten nur in logischen Ausdrücken auf, ihr Wert ist wahr oder falsch. Es gibt folgende Vergleichsoperatoren: Operator .LT. < .LE. <= .EQ. == .NE. /= .GT. > .GE. >= Name less than less or equal equal not equal greater than greater or equal Gebrauch X1.LT.X2 X1 < X2 X1.LE.X2 X1 <= X2 X1.EQ.X2 X1 == X2 X1.NE.X2 X1 /= X2 X1.GT.X2 X1 > X2 X1.GE.X2 X1 >= X2 Bedeutung Ist X1 kleiner als X2? Ist X1 kleiner oder gleich X2? Ist X1 gleich X2? Ist X1 ungleich X2? Ist X1 größer als X2? Ist X1 größer oder gleich X2? Die Punkte bei den logischen und Vergleichsoperatoren sind zwingender Bestandteil. Die oben tabellierten Vergleichsoperatoren gelten nur für numerische Typen. Logische Typen und Zeichenketten lassen sich nur auf (Un-)Gleichheit überprüfen. Weitere, auf Zeichenketten beschränkte Vergleichsoperatoren sind im Abschnitt 4.2.7 erwähnt. Die Reihenfolge der Auswertung von (hier numerischen) Ausdrücken wird normalerweise durch Klammern vorgegeben. Generell gilt folgende Rangfolge: 0. 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. Klammern Funktionsaufruf ** Vorzeichenoperationen * und / + und Vergleichsoperationen .NOT. .AND. .OR. .EQV. und .NEQV. Stehen gleichrangige Operationen nebeneinander, so wird von links nach rechts interpretiert (Ausnahme: Exponentiation!). Danach ist der Ausdruck (mit REAL P,G und LOGICAL L,K) -P**2.GE.G.AND.L.OR..NOT.K gleichbedeutend mit 4.2. FORTRAN 27 (((-(P**2)).GE.G).AND.L).OR.(.NOT.K) Es soll hier nochmals betont werden, daß die Verwendung von Klammern, auch wenn sie überflüssig sind, die Verständlichkeit und Lesbarkeit von Ausdrücken wesentlich verbessert, siehe obiges Beispiel. 4.2.6.2 Logische Anweisungen Logische Anweisungen haben die Form v = expr wobei v eine (einfache oder indizierte) logische Variable und expr ein logischer Ausdruck mit dem Wert .TRUE. oder .FALSE. ist. 4.2.7 Zeichenausdrücke und -anweisungen Ein Zeichenausdruck beschreibt eine Zeichenfolge (einen Text). Seine Auswertung liefert ein Ergebnis vom Typ Zeichen. Zeichenausdrücke bestehen aus Zeichenkonstanten, Namen von Zeichenkonstanten, Zeichenvariablen, Zeichenfeldelementen, Teilzeichenreihen und Aufrufe von Zeichenfunktionen, die durch den Verkettungsoperator // (zwei Schrägstriche) und Klammern verbunden sein können. Beispiel: ’AB’ // ’CDE’ ergibt die Zeichenfolge ’ABCDE’. Zeichenanweisungen haben die Form Zeich = expr wobei Zeich der Name einer Zeichenvariablen, eines Zeichenfeldelementes oder einer Teilzeichenreihe ist, und expr ist ein Zeichenausdruck. Keine der Zeichenpositionen von Zeich darf in expr auftreten (siehe unten). Die Längen von Zeich und expr können unterschiedlich sein. Ist Zeich länger als expr, so wird von rechts bis zur Länge von Zeich mit Leerzeichen aufgefüllt. Wenn Zeich kürzer ist als expr, wird expr entsprechend der Länge von Zeich von rechts verkürzt. Beispiel: CHARACTER :: mo*3, da*3, date*12 date = da//mo//’1993’ Die Zeichenvariablen mo und da werden mit der Zeichenfolge 1993 zu einer 12 Zeichen langen Zeichenreihe verkettet, die der Variablen date zugewiesen wird. date wird dabei rechts mit 2 Leerzeichen aufgefüllt. Eine definierte und initialisierte Zeichenreihe (CHARACTER–String) erlaubt den Zugriff auf die komplette Reihe oder auf Teile davon (Teilzeichenreihe, Substring). Syntax: char( first : last ) Dabei ist char der Name der Zeichenreihe, first die Position des ersten Zeichens des Substrings und last die des letzten Zeichens des Substrings. Fehlt first, wird der Wert 1 genommen, fehlt last, wird die Länge len des Strings genommen, wobei allgemein gilt: 1 ≤ f irst ≤ last ≤ len. Beispiel: CHARACTER (LEN=6) :: chara1 chara1 = ’STRING’ 28 KAPITEL 4. PROGRAMMIERSPRACHEN, FORTRAN Hier ist die Länge des Strings len = 6. Mögliche Teilzeichenreihenzugriffe sind dann beispielsweise: chara1(1:3) Wert: ’STR’ chara1(3:4) Wert: ’RI’ chara1(4: ) Wert: ’ING’ chara1( :4) Wert: ’STRI’ chara1( : ) Wert: ’STRING’ (also die ganze Zeichenreihe) Nach dem oben Gesagten ist eine Zuweisung zeich(1:5) = zeich(3:7) unzulässig, da die beiden Teilzeichenreihen sich in zeich(3:5) überlappen. Bei Substring–Zugriffen auf Zeichenfelder wird erst das Feldelement (also dessen Index/Indices) und dann der Substring spezifiziert: CHARACTER :: cfeld(6)*8, name*4 name = cfeld(2)(3:6) Der Zugriff bezieht sich auf das 3. bis 6. Zeichen des 2. Elementes des Feldes cfeld. Im Zusammenhang mit Zeichen soll noch auf die damit zusammenhängenden Standardfunktionen CHAR, ICHAR, (Typumwandlung Ganzzahl in Zeichen bzw. Zeichen in Ganzzahl), LEN (Länge einer Zeichenreihe), INDEX (Ort der Zeichenreihe in einer anderen) sowie LGE, LGT, LLE, LLT (lexikalische Anordnung) hingewiesen werden. Genaueres über die Standardfunktionen entnehme man der FORTRAN–Literatur (z.B. [11] ). 4.2.8 Ausdrücke und Anweisungen mit Feldern und Teilfeldern Alle der hier vorgestellten Operationen zwischen Datenobjekten gleichen Typs sind auch mit Feldern bzw. Teilfeldern durchzuführen. 4.2.8.1 Ausdrücke mit Feldern und Teilfeldern Wie man auf einzelne Elemente eines Feldes durch Angabe des jeweiligen Indexes zugreifen kann, ist bereits im Abschnitt 4.2.3.2 erläutert worden. Es besteht allerdings auch die Möglichkeit, ausgewählte Teile eines Feldes durch die Angabe eines Indextripels anzusprechen: [ indexanfang ] : [ indexende ] [ : indexabstand ] Dabei sind indexanfang und indexende ganzzahlige Indexausdrücke, die den ersten und den letzten Wert der Indexfolge angeben. Fehlt indexanfang wird als erster Index die untere Indexgrenze des Feldes angenommen. Läßt man indexende weg, wird als obere Grenze der letzte Index der Dimension des Ausgangsfeldes gewählt. Auch die Angabe von indexabstand ist optional. Sie dient dazu den Abstand zwischen den Werten in der Indexfolge festzulegen. indexabstand kann auch negativ sein, wenn indexanfang größer ist als indexende. Fehlt die Angabe von indexabstand wird der Wert als 1 angenommen. Beispiele: 4.2. FORTRAN 29 DIMENSION :: feld(25,30), i(5) PARAMETER (i = (/ 3, 6, 9, 12, 15 /) ) feld(1,:) spricht alle 30 Elemente der ersten Zeile von feld an. feld(7,2:30:2) liefert ein eindimensionales Teilfeld von feld, welches nur jedes zweite Element der siebten Zeile von feld enthält. feld(4:6,2:30:2) kann als ein zweidimensionales Teilfeld mit 3*15 Elementen verstanden werden, welches jedes zweite Element der vierten, fünften und sechsten Zeile von feld enthält. feld(23,20:9:-4) ist ein eindimensionales Teilfeld von feld, das die Elemente (23,20),(23,16) und (23,12) von feld enthält. feld(19,i) entspricht einem eindimensionales Teilfeld, das die Elemente (19,3), (19,6), (19,9), (19,12) und (19,15) enthält. 4.2.8.2 Anweisungen mit Feldern und Teilfeldern Anweisungen sehen bei Feldern bzw. Teilfeldern allgemein wie folgt aus: f eld = expr expr kann dabei, je nach Datentyp von f eld, ein beliebiger Ausdruck sein, wie wir es in den voranstehenden Kapiteln gesehen haben. Bei der Rechnung mit Teilfeldern ist aber im Besonderen zu beachten, daß beide Seiten der Zuweisung die gleichen Feldgrößen haben müssen, genauer die gleiche Anzahl an Elementen. Taucht in der Berechnung von expr ein skalarer Wert auf, so wird er als Feld mit gleicher Größe wie das eigentliche Teilfeld angenommen. Beispiele: REAL :: r1(7),r2(9),r3(6) r1(1:3) = r2(3:9:3) - r3(2:6:2) Es ergibt sich somit r1(1) = r2(3) - r3(2), r1(2) = r2(6) - r3(4) und r1(3) = r2(9) - r3(6) r1 = r1 + 3. erhöht den Wert jedes Elementes von r1 um 3. : r1(1) = r1(1) + 3., r1(2) = r1(2) + 3., usw... CHARACTER (LEN=5) :: c1(3)*17,c2(3),c3(3) c2 = (/’Anton’,’Berta’,’Chris’/) c3 = (/’Alice’,’Bernd’,’Carla’/) c1(1:3) = c2(1:3)//’ liebt ’//c3(3:1:-1) LOGICAL l2(1) = l3(1) = l4(1) = liefert für c1(1) : ’Anton liebt Carla’ c1(2) : ’Berta liebt Bernd’, c1(3) : ’Chris liebt Alice’ :: l1(2),l2(2),l3(2),l4(2) .TRUE. ; l2(2) = .TRUE. .FALSE. ; l3(2) = .TRUE. .FALSE. ; l4(2) = .FALSE. l1 = (.NOT.(l2.AND.l3(2:1:-1))).EQV.l4 liefert für l1(1) : .TRUE. und für l1(2) : .FALSE. 30 KAPITEL 4. PROGRAMMIERSPRACHEN, FORTRAN Die Auswertung solcher Anweisungen erfolgt also offensichtlich elementweise, wobei das jeweils das erste Element eines Teilfeldes von expr genommen wird um das erste Element von f eld zu bestimmen. Ebenso geschieht dies mit dem zweiten, dritten, usw., bis das gesamte (Teil–)Feld f eld mit neuen Werten belegt ist. 4.2.9 Steueranweisungen Im Kapitel Programmiertechnik“ haben wir die Möglichkeiten kennengelernt, mit denen ein Programm ” vom linearen Verlauf abweichen kann: Verzweigungen und Schleifen. Mit Steueranweisungen wird die Abarbeitungsreihenfolge der Programmanweisungen beeinflußt, die Ausführung des Programms kann unterbrochen oder beendet werden. Mögliche Steueranweisungen sind unter anderem: Logisches IF Block IF ELSEIF ELSE ENDIF END ( DO ) ( CALL ) [ Arithmetisches IF ] [ STOP ] [ RETURN ] Die mit (...) versehenen Anweisungen werden in den folgenden Abschnitten behandelt: die DO–Anweisung wird im Abschnitt 4.2.11 behandelt; CALL wird im Zusammenhang mit Unterprogrammen, Abschnitte 4.2.17, 4.2.18 und 4.2.19 beschrieben. Die mit [....] versehenen Anweisungen sind nur der Vollständigkeit halber aufgeführt. Von ihrer Verwendung wird im Allgemeinen abgeraten, da sie entweder der strukturierten Programmierung widersprechen, sie überholt sind, oder es bessere Ersatzkonstrukte in FORTRAN gibt. 4.2.10 Verzweigungen In diesem Abschnitt beginnen wir, die in Kapitel Programmiertechnik“ angeführten Struktogramm–Ele” mente in entsprechende FORTRAN–Anweisungen umzusetzen. Verzweigungen stellen Fälle dar, in denen je nach Ergebnis der Auswertung von Bedingungen vom linearen Programmablauf abgewichen wird oder einer von mehreren alternativen Programmpfaden durchlaufen wird. Die bedingte Anweisung stellt den einfachsten Fall dar. Soll in Abhängigkeit von einer Bedingung eine einzelne Anweisung ausgeführt werden oder nicht, so liegt eine einfache Verzweigung mit leerem False–Pfad vor: XXX T XXX XXX XX stat » »»» » » »» expr XXX »»» » » » XXX »» » F 4.2. FORTRAN 31 Hierfür bietet FORTRAN das logische IF: IF (expr) stat Es ermöglicht die bedingte Ausführung der Anweisung stat, wenn der logische Ausdruck expr wahr ist. Hat expr den Wert falsch, wird mit der dem IF folgenden Anweisung fortgesetzt. Die Anweisung stat darf keine DO–, Block-IF oder logische IF–Anweisung sein. Ein Beispiel: Berechnung der Wurzel aus einer Zahl X, sofern diese positiv ist, XX XXX XXX T »» XXX X≥0 XXX »» »»» »» XXX » »»» »» » » F Berechne Wurzel wird umgesetzt zu IF (x .GE. 0.0) wurz = SQRT(X) 4.2.10.1 Block–IF–Strukturen Sollen in Abhängigkeit von einer Bedingung mehrere Anweisungen ausgeführt werden, so verwenden wir das Block–IF. XX X XX XXX T »»» »» » » expr »» XX »» XXX » » XXX F »» X»» Anweisungen wenn expr den Wert wahr hat Eine Block–IF–Struktur beginnt mit einer Block–IF–Anweisung und endet mit einer ENDIF–Anweisung. Dazwischen steht ein Block von ausführbaren Anweisungen: IF (expr) THEN Anweisungen wenn expr den W ert wahr hat ENDIF Darin ist expr ein logischer Ausdruck. Ein Beispiel für die einfachste Form einer Block–IF–Struktur: wir erweitern das obige Beispiel der Wurzelberechnung um die Ausgabe des Wertes der Wurzel auf dem Bildschirm. 32 KAPITEL 4. PROGRAMMIERSPRACHEN, FORTRAN XX »»» »» » » XXX » x≥0 XXX »»» » » F XXX » X »»» XX XXX T Berechne Wurzel(x) A(B): Wurzel(x) Dies läßt sich wie folgt umsetzen: IF (x .GE. 0.0) THEN wurz = SQRT (x) PRINT*, ’Die Wurzel ist:’, wurz ENDIF Wenn die Bedingung x ≥ 0 erfüllt ist, dann wird die Wurzel berechnet, und das Ergebnis wird zusammen mit einem Kommentar ausgedruckt. Anschließend wird die auf die ENDIF–Anweisung folgende Anweisung ausgeführt. Ist der logische Ausdruck falsch, also x < 0, dann wird sofort mit der auf das ENDIF folgenden Anweisung fortgefahren. Um zwei alternative Anweisungsblöcke programmieren zu können, verwendet man die ELSE–Anweisung in der Block–IF–Struktur. XX XXX T »»» XX XXX »»» »»» Bedingung »»» » XXX » X »»» Anweisungen A1 Anweisungen A2 ... ... ... ... XXX F Die Umsetzung in FORTRAN lautet allgemein: IF (Bedingung) THEN Anweisungen A1 wenn Bedingung erf üllt ist ELSE Anweisungen A2 wenn Bedingung nicht erf üllt ist ENDIF Bedingung ist ein logischer Ausdruck. Ist sein Wert wahr, wird der Block mit den Anweisungen A1 ausgeführt, ist sein Wert f alsch, dann wird der Block mit den Anweisungen A2 ausgeführt. Beispiel: 4.2. FORTRAN 33 XX »»» »» » » XXX » x≥0 XXX »»» » » XXX » F X »»» XX XXX T Berechne Wurzel(x) A(B): Fehlermeldung A(B): Ergebnis Das Struktogramm kann wie folgt umgesetzt werden: IF (x .GE. 0.0) THEN wurz = SQRT (x) PRINT*, ’Die Wurzel ist:’, wurz ELSE PRINT*, ’Fehler: Der Radikand ist negativ!’ ENDIF Je nachdem ob x ≥ 0 wahr oder falsch ist, wird entweder die Wurzel berechnet und ausgedruckt, oder es wird eine Fehlermeldung ausgegeben. Wird beim Abarbeiten eines Blockes ELSE oder ENDIF angetroffen, ist damit der betreffende Block abgeschlossen, und es wird mit der auf ENDIF folgenden Anweisung fortgesetzt. Für den Fall der allgemeinen Auswahlanweisung lassen sich mehr als zwei alternative Anweisungsblöcke mit einer oder mehreren ELSEIF–Anweisungen realisieren. PP PP PP PP PP PP PP PP Auswahlkriterium PP PP B1 B2 B3 A1 A2 A3 Dieses Konstrukt wird wie folgt umgesetzt: IF (Bedingung B1) THEN Anweisungen A1 wenn B1 erf üllt ist ELSEIF (Bedingung B2) THEN Anweisungen A2 wenn B2 erf üllt ist ELSEIF (Bedingung B3) THEN Anweisungen A3 wenn B3 erf üllt ist ELSEIF (Bedingung B4) THEN PP P B4 PPP ¿ P¿ A4 ¿ ¿ ¿ ¿ ¿ sonst Asonst ¿ ¿ ¿ 34 KAPITEL 4. PROGRAMMIERSPRACHEN, FORTRAN Anweisungen A4 wenn B4 erf üllt ist ELSE Anweisungen Asonst wenn keine der anderen Bedingungen erf üllt sind ENDIF Von den hier fünf IF–Blöcken wird höchstens einer ausgeführt. Die logischen Ausdrücke Bedingung Bn werden der Reihe nach ausgewertet, bis einer mit dem Wert wahr gefunden wird. Dann wird mit der ersten Anweisung des zugehörigen IF–Blocks fortgefahren. Wenn dieser IF–Block abgearbeitet ist (oder der zugehörige Block leer sein sollte), wird mit der dem ENDIF folgenden Anweisung weitergemacht. Um alle möglichen Fälle in der Auswahlanweisung zu erfassen, kann man dies mit der zusätzlichen ELSE–Anweisung in der Block–IF–Struktur erreichen. Sie muß dem letzten ELSEIF folgen. Ist keiner der logischen Ausdrücke Bn wahr, wird mit dem auf ELSE folgenden Block fortgefahren. Als Beispiel diene wieder die Wurzelberechnung von oben, diesmal mit noch mehr Ausgabekomfort. Als Struktogramm könnte das so aussehen: PP P PP ¡ PP ¡ PP PP x>0 WURZ=SQRT(X) PP ¡ ¡ PP PP ¡ Auswahlkriterium PP PP PP PP ¡ ¡ ¡ PP ¡ PP x<0 PP¡ ¡ x=0 A(B): A(B): ’Wurzel = 0’ ’Radikand negativ’ sonst A(B): ’Dieser Fall kann gar A(B): WURZ nicht auftreten Die Umsetzung in FORTRAN könnte dann so erfolgen: IF (x .GT. 0.0) THEN wurz = SQRT (x) PRINT*, ’Die Wurzel ist’, wurz ELSEIF (x .EQ. 0.0) THEN PRINT*, ’Die Wurzel ist: 0.0’ ELSEIF (x .LT. 0.0) THEN PRINT*, ’Fehler: Der Radikand ist negativ!’ ELSE PRINT*, ’Dieser Fall kann gar nicht auftreten!’ ENDIF Hier wird nacheinander überprüft, ob die Zahl größer, gleich, oder kleiner Null ist. Trifft einer dieser Fälle zu, wird eine Meldung und gegebenenfalls das Ergebnis ausgegeben. In allen anderen Fällen (solche kann es eigentlich gar nicht geben) wird eine Meldung mit Hinweis auf die Unmöglichkeit des Ereignisses ausgegeben. 4.2. FORTRAN 4.2.10.2 35 CASE–Verzweigung Die CASE–Verzweigung ist der Block–IF–Verzweigung sehr ähnlich und soll hier insbesondere aus Vollständigkeitsgründen aufgeführt werden. Im Gegensatz zu der Block–IF–Verzweigung wird hier der auszuführende Block nur nach Inhalt einer Varialen bestimmt. Die Konstruktion der CASE–Verzweigung sieht allgemein aus: SELECT CASE (varbl) CASE (selectlist1) ... CASE (selectlist2) ... CASE DEFAULT ... END SELECT varbl ist eine Variable eines beliebigen Datentyps, die bei der Abbarbeitung der CASE–Anweisung ausgewertet werden soll. selectlist ist eine Liste von skalaren Werten gleichen Typs, wie varbl. Stimmt der Inhalt der Variablen varbl mit einem Element der selectlist überein, wird der entsprechende Block – und nur dieser Block – abgearbeitet. selectlist kann wie folgt zusammengesetzt sein: w1, w2, w3, .. w1:w2 w1: :w2 Wert w1 oder Wert w2 oder ... zwischen Wert w1 und Wert w2 größer als Wert w1 kleiner als Wert w2 Beispiel: ! ! ! ! 4.2.11 CHARACTER (LEN=5) :: Farbe SELECT CASE (Farbe) CASE (’rot’, ’ROT’) ..... CASE (’gruen’, ’GRUEN’) ..... CASE (’blau’, ’BLAU’) ..... CASE DEFAULT ..... END SELECT ! Farbe ist rot ! Farbe ist gruen ! Farbe ist blau ! Alle anderen Farben Schleifen Ein wichtiger Bestandteil von Programmen sind Schleifen. Das sind Gruppen von Anweisungen (Wiederholungsbereich), die im allgemeinen mehrfach ausgeführt werden. Wir haben im Kapitel Programmier” technik“ Schleifen mit Eintritts– bzw. Austritts–Bedingung sowie Zählschleifen kennengelernt. 36 KAPITEL 4. PROGRAMMIERSPRACHEN, FORTRAN 4.2.11.1 Zählschleifen Zählschleifen sind Schleifen, bei denen zu Beginn der Ausführung festgestellt wird, wie oft die Schleife durchlaufen wird; die Anzahl der Schleifendurchläufe steht also zu Beginn fest und wird während der Ausführung nicht mehr verändert. Das Struktogrammsymbol für Zählschleifen lautet: v = e1 , e2 [, e3 ] Schleifenanweisungen ... Für die Programmierung von Zählschleifen verwendet man unter Fortran95 die DO–Schleifen mit Laufvariable. Die Anweisung dann lautet DO v = e1 , e2 [, e3 ] Schleifenanweisungen ENDDO Die Laufvariable ist v, und der von ihr zu durchlaufende Wertebereich geht von e1 (Startwert) bis e2 (Endwert). Die Laufvariable wird in jedem Schleifendurchlauf um das Inkrement e3 erhöht. Wird das (optionale) Inkrement weggelassen, wird e3 = 1 angenommen. Die Größen e1 , e2 und e3 sind nichtkomplexe arithmetische Ausdrücke, und v muß eine ganzzahlige Variable sein. Zähl–Schleifen sind eigentlich spezielle Schleifen mit Eintrittsbedingung. Die Schleife wird je nach Vorzeichen des Inkrements e3 aufwärts (e3 > 0) oder abwärts (e3 < 0) durchlaufen bis v > e2 bzw. v < e2 ist. Die Schleife wird keinmal durchlaufen, wenn e1 > e2 bei e3 > 0 oder wenn e1 < e2 bei e3 < 0 ist. Die einzelnen Schritte bei der Ausführung einer Zählschleife sind wie folgt: 1. Die Laufvariable erhält den Startwert. 2. Aus Startwert, Endwert und Inkrement wird die Zahl der erforderlichen Schleifendurchläufe ermittelt nach der Formel: Durchlauf zahl = (Endwert − Startwert + Inkrement)/Inkrement 3. Ist die Durchlaufzahl kleiner oder gleich Null, wird die Schleife nicht ausgeführt (sie wird übersprungen). 4. Am Ende jedes Schleifendurchlaufs wird die Laufvariable um den Wert des Inkrements erhöht. Beispiele: Das Programmstück 4.2. FORTRAN 37 SU M = 0 i = 1, 3 SU M = SU M + Ai Bi ... j = 10, 0, −3 A(B): j sollte in FORTRAN folgendermaßen aussehen (i und j sind ganzzahlig, sum, a und b sind reell): ! --- ZaehlSchleife mit Vorwaertszaehlung ! --sum = 0.0 DO i = 1, 3 sum = sum + a(i)*b(i) ENDDO : : ! --- ZaehlSchleife mit Rueckwaertszaehlung ! --DO j = 10, 0, -3 PRINT*, j ENDDO Die letzte Schleife würde die Ausgabe 10, 7, 4, 1 erzeugen. Zählschleifen können wie IF–Blöcke geschachtelt werden. Dabei muß die innere Schleife vollständig im Schleifenbereich der äußeren enthalten sein. Zählschleifen können, wenn es sein muß, durch einen Sprung aus dem Bereich verlassen werden. Sprünge in einen Schleifenbereich hinein sind verboten. Beispiel für zulässige Schachtelung: 38 KAPITEL 4. PROGRAMMIERSPRACHEN, FORTRAN i = 1, 4 j = 1, 4 k = 1, 5 A(k, j, i) = 0 B(i, j) = 0 Die Umsetzung in FORTRAN sollte wie folgt aussehen: INTEGER :: i, j, k REAL :: a(5,4,4), b(4,4) DO i = 1, 4 DO j = 1, 4 DO k = 1, 5 a(k,j,i) = 0.0 ENDDO b(j,i) = 0.0 ENDDO ENDDO In dieser Schleifenkonstruktion werden die beiden Felder a und b auf Null gesetzt. Innerhalb des Wiederholungsbereichs einer Zählschleife dürfen weder die Laufvariable noch die Parameter e1 , e2 und e3 geändert werden. Sie können aber sonst in beliebiger Weise, z.B. als Operanden in arithmetischen Ausdrücken, verwendet werden. Nach dem Ende der Schleife hat die Laufvariable den Wert Endwert + Inkrement. 4.2.11.2 Schleifen mit Eintrittsbedingung Eine Schleife mit Eintrittsbedingung (auch kopfgesteuerte oder abweisende Schleife) wird solange ausgeführt, wie die Eintrittsbedingung erfüllt ist. Ist diese Bedingung von Anfang an nicht erfüllt, wird die Schleife nicht ausgeführt. Das Struktogrammsymbol Eintrittsbedingung Schleifenanweisungen ... 4.2. FORTRAN 39 wird in der folgenden Form umgesetzt: DO WHILE (Eintrittsbedingung) Schleifenanweisungen ENDDO Ein Beispiel: Finden des ersten von Null verschiedenen Elements eines Feldes mit N Elementen. Zur Realisierung bietet sich eine Schleife mit Eintrittsbedingung an, da die notwendige Anzahl der Schleifendurchläufe nicht von vorneherein feststeht: i=1 A(i) = 0 und i < N i=i+1 Es kann wie folgt umgesetzt werden: INTEGER :: i, N PARAMETER (N=99) REAL :: a(N) ... i = 1 DO WHILE ((a(i).EQ.0.00) .AND. (i.LT.N)) i = i+1 ENDDO 4.2.11.3 Schleifen mit Austrittsbedingung Bei Schleifen mit Austrittsbedingung (auch fußgesteuerte Schleife) werden die Anweisungen im Schleifenkörper so lange wiederholt, bis die Austritts– oder Abbruch–Bedingung erfüllt ist. Die Überprüfung dieser Bedingung erfolgt immer am Ende eines Schleifendurchlaufs. Da es in Fortran95 leider keine direkte Umsetzung der Schleife mit Austrittsbedingung gibt, behelfen wir uns mit einer Endlos–Schleife (DO ohne Fortsetzungsbedingung) und der EXIT–Anweisung, mit deren Hilfe wir eine Schleife wieder verlassen können. Die Umsetzung des Struktogrammsymbols Schleifenanweisungen ... Abbruchbedingung 40 KAPITEL 4. PROGRAMMIERSPRACHEN, FORTRAN erfolgt dann in der Form: DO Schleifenanweisungen IF (Austrittsbedingung) EXIT ENDDO Für die korrekte Umsetzung des Struktogrammsymbols sei noch erwähnt, daß sich zwischen der EXIT– und ENDDO–Anweisung keine weitere Anweisung, gleich welcher Art, befinden darf. Anderenfalls handelt es sich nicht mehr um eine strukturierte Programmierung! Ein kleines Beispiel für eine Schleife mit Austrittsbedingung: Positive Meßwerte von Tastatur einlesen; Abbruch durch Eingabe einer negativen Zahl. Drucke Eingabeauf forderung Lies Meßwert ein Meßwert < 0 Die Umsetzung des Struktogramms kann beispielsweise so aussehen: DO PRINT *, ’Bitte Messwert eingeben’ READ *, MessW IF (MessW .LT. 0) EXIT ENDDO 4.2.12 CONTAINS– und END–Anweisung Der allgemeine Aufbau eines Programmmoduls sieht wie folgt aus: PROGRAM, SUBROUTINE bzw. FUNCTION upname : CONTAINS : END [PROGRAM, SUBROUTINE bzw. FUNCTION] Die CONTAINS–Anweisung ist eine nicht–ausführbare Anweisung und wird nur benötigt, wenn innerhalb eines Programmmoduls interne Funktionen oder Unterroutinen definiert werden sollen. Diese interne Funktionen oder Unterroutinen dürfen nur zwischen CONTAINS und END stehen und außer diesen Definitionen dürfen dort auch keine weiteren (selbständigen) Anweisungen untergebracht werden. Fehlt die Definition interner Unterprogramme, so muß auch die CONTAINS–Anweisung weggelassen werden. Die END–Anweisung zeigt dem Compiler das Ende der Programmeinheit an. Daher muß jede Programmeinheit mit einer END–Anweisung enden. Die END–Anweisung kann auch optional mit der Angabe der hier zu beendenen Programmeinheit erfolgen. Handelt es sich jedoch um den Abschluß einer internen Funktion 4.2. FORTRAN 41 oder Unterroutine, muß die END–Anweisung um die Angabe FUNCTION bzw. SUBROUTINE erweitert werden. Das Antreffen einer END–Anweisung bedeutet auch das logische Ende der betreffenden Programmeinheit und bewirkt bei Unterprogrammen den Rücksprung ins rufende Programm, bei Hauptprogrammen die Beendigung der Programmausführung. 4.2.13 Ein-/Ausgabe (Standard-Ein-/Ausgabe) Bei der Eingabe von Daten, auch Lesen“ genannt, werden Daten an das Programm übergeben. Bei ” der Ausgabe, auch Schreiben“ genannt, werden Daten vom Programm an eine Datei übergeben. Bei ” den hier zunächst behandelten Standard-Ein-/Ausgabemöglichkeiten bedeutet Datei“ die Standard– ” Ein-/Ausgabeeinheit. Das meint im Allgemeinen die Tastatur und den Bildschirm. Die erweiterten Ein-/Ausgabemöglichkeiten werden im Abschnitt 4.2.14 Erweiterte Ein-/Ausgabe“ behandelt. ” Die einfachste Ein-/Ausgabe ist das listengesteuerte Lesen bzw. Schreiben. Dabei werden codierte (d.h. lesbare) Datensätze ohne Angaben zur Formatierung verarbeitet. Jeder Datensatz besteht aus einer Liste von Werten in freier Form; man spricht deshalb auch von freiem Format“ (nicht zu verwechseln mit ” formatfrei“). ” Die Ein-/Ausgabeanweisungen lauten in diesem einfachsten Fall READ *, iolist (Lesen, Eingabe von der Tastatur) PRINT *, iolist (Schreiben, Ausgabe auf dem Bildschirm) Die Ein-/Ausgabeliste iolist kann Variablennamen, Feldnamen, Namen von Feldelementen, Namen von Teilzeichenketten und Listen mit impliziter Schleife enthalten. Die Elemente der Liste werden durch Komma getrennt. Ein Feldname ohne Indexangabe bezeichnet das gesamte Feld in der der internen Speicherbelegung entsprechenden Reihenfolge; es wird also das ganze Feld gelesen oder geschrieben. Beim Lesen werden Daten von der Eingabeeinheit nach Umwandlung in die rechnerinterne Darstellung (siehe Kapitel 5) an die in der Eingabeliste iolist symbolisch aufgeführten Speicherplätze (i.a. Variablen) übertragen. Beispiel: READ *, zahl1, zahl2, zahl3 Die einzelnen Eingabedaten der Eingabeeinheit (hier: Tastatur) müssen dabei durch Trennzeichen (Komma oder Leerzeichen) voneinander getrennt werden. Korrekte Eingaben für die obige Leseanweisung sind beispielsweise: 3.14159,-17.400,4.567890 oder 2.9, 9.8765 -7.182e-4 Jede READ–Anweisung liest einen neuen Datensatz (d.h. eine neue Zeile). Die Eingabeliste darf keine Konstanten, Ausdrücke oder Zeichenketten enthalten. Beim Schreiben werden Daten von den in der Ausgabeliste iolist symbolisch aufgeführten Speicherplätzen nach Umwandlung von der rechnerinternen Darstellung in die externe Darstellung (siehe Kapitel 5) an die Ausgabeeinheit übertragen. Beispiel: PRINT *, ’DIE WURZEL IST:’, wurzel 42 KAPITEL 4. PROGRAMMIERSPRACHEN, FORTRAN Das Beispiel zeigt, daß in der Ausgabeliste neben symbolischen Namen auch Zeichenausdrücke vorkommen können. Außerdem sind auch Konstanten sowie arithmetische und logische Ausdrücke zugelassen. Eine implizite Schleife in einer Ein-/Ausgabeliste hat die Form (dlist, v = e1 , e2 , e3 ) Die Elemente v, e1 , e2 , e3 haben dieselbe Bedeutung wie bei DO–Schleifen, und dlist ist eine Ein-/Ausgabeliste. Beispiel: READ *, max, (number(i), i = 1, max) Einzelne Elemente von dlist können ihrerseits wieder implizite Schleifen enthalten. Beispiel: PRINT *, ((hfi(i,j), i = 1,4), j = 1,5) Die Elemente werden dabei in der folgenden Reihenfolge ausgegeben (der erste Index läuft als erster): hfi(1,1), hfi(2,1), hfi(3,1), hfi(4,1), hfi(1,2), hfi(2,2), hfi(3,2), hfi(4,2), hfi(1,3), hfi(2,3), ... Jede READ– oder PRINT–Anweisung beginnt einen neuen Datensatz. Die Reihenfolge der zu übertragenden Daten wird durch die Liste bestimmt. Daher bezeichnet man diese Art der Ein-/Ausgabe auch als listengesteuert“. Die Anzahl der eingelesenen Daten wird nur durch die Liste bestimmt, nicht durch ” die tatsächlich vorhandenen Daten. Enthält ein Datensatz mehr Daten als Elemente in der Liste sind, werden die überzähligen Daten ignoriert. Sind in der Liste mehr Elemente angegeben als ein Datensatz enthält, so werden mehrere Datensätze gelesen. Erfolgen die Eingaben nicht Typengerecht, so wird das Programm beendet. Möglichkeiten, dies zu verhindern, werden in Abschnitt 4.2.14 beschrieben. 4.2.14 Erweiterte Ein-/Ausgabe Die Ein-/Ausgabe, wie wir sie bisher kennengelernt haben war listengesteuert im sogenannten freien Format. Für die allgemeine Ein- und Ausgabe von Daten gibt es verschiedene Möglichkeiten. Diese unterscheiden sich hinsichtlich der Art des Zugriffs auf die Daten, der Art der beteiligten Speichermedien, der Art der externen Darstellung der Daten und deren Art der Beschreibung. So soll beispielsweise die Eingabe nicht nur von Tastatur sondern auch aus einer Datei erfolgen, und die Ausgabe sollte auch in eine Datei auf Diskette oder Platte oder auf den Drucker gemacht werden können. Neben den Anweisungen für die Art der Ein-/Ausgabe (READ, PRINT, WRITE) gibt es Anweisungen, die den Zustand einer Datei abfragen oder verändern (OPEN, INQUIRE, CLOSE) sowie Anweisungen zur Positionierung einer Datei (REWIND, BACKSPACE, ENDFILE). Wir werden nicht auf alle Anweisungen detailliert eingehen, da dies den Rahmen einer Einführung sprengt. 4.2.14.1 Formatierte Ein-/Ausgabe Neben der bisher vorgestellten Ein-/Ausgabe im freien Format (listengesteuert) gibt es die formatierte oder formatgesteuerte Ein-/Ausgabe. Dabei wird bei Abarbeitung der Ein-/Ausgabeliste jedem Element der Liste die zugehörige Datenfeldbeschreibung aus der Formatangabe (z.B. FORMAT–Anweisung, s.u.) zugeordnet und das Element entsprechend ausgegeben. Beispiel (seien i, j, k ganzzahlig): 4.2. FORTRAN 43 PRINT 999, i, j, k 999 FORMAT (I4, I2, I5) Die Integer–Variablen i, j und k werden entsprechend der Formatangabe mit der Marke 999 als 4–, 2– bzw. 5–stellige ganze Zahlen hintereinander in einer Zeile ausgegeben. Dabei ist zu beachten, daß die Formatangaben und die auszugebenden Daten vom Typ her stets zusammenpassen (im obigen Fall: ganzzahlig). Die Formatbeschreibung wird bis zum Ende der Ein-/Ausgabeliste abgearbeitet. Reicht die Formatbeschreibung nicht aus, wird das Format wiederholt, und dabei wird eine neue Zeile begonnen. Bei der formatierten Ein-/Ausgabe werden die Daten entsprechend den Formatangaben gelesen und in die interne binäre Darstellung (rechnerabhängig) konvertiert bzw. aus dieser internen Darstellung in Zeichenfolgen umgewandelt. Formatangaben sind in runde Klammern eingeschlossene Zeichenfolgen, die alternativ unter anderem angegeben werden können durch 1. die Anweisungsmarke einer FORMAT–Anweisung (siehe obiges Beispiel) 2. den Namen eines Zeichenfeldes 3. einen beliebigen Zeichenausdruck. Wir werden hier nur auf die erste Möglichkeit eingehen. Es können sich mehrere Ein-/Ausgabeanweisungen auf die gleiche Formatangabe beziehen. Die Format–Anweisung labl FORMAT( f list ) ist eine nichtausführbare Anweisung, die die Konvertierung und Formatierung der zu übertragenden Daten festlegt. Dabei ist labl eine Anweisungsmarke und f list eine Liste von durch Komma getrennten Elementen der Form r ed, ned, r(f list) ed und ned sind wiederholbare bzw. nichtwiederholbare Datenfeldbeschreibungen und die Wiederholungszahl r ist eine ganzzahlige Konstante > 0. Fehlt r, wird der Wert 1 angenommen. In den Tabellen 4.2 und 4.3 geben Großbuchstaben die Konvertierungsart an, und kleine Buchstaben bezeichnen vom Programmierer hinzuzufügende Informationen: w Von Null verschiedene, vorzeichenlose, ganzzahlige Konstante, die die Datenfeldweite der externen Darstellung, d.h. die Anzahl der Zeichen einschließlich führender Leerzeichen, Vorzeichen, Dezimalpunkt und Exponenten angibt. d Vorzeichenlose, ganzzahlige Konstante, die die Anzahl der Ziffern rechts vom Dezimalpunkt angibt. Bei der Ausgabe werden Zahlen gegebenenfalls gerundet. e Von Null verschiedene, vorzeichenlose, ganzzahlige Konstante, die die Anzahl der Ziffern im Exponenten angibt. m Vorzeichenlose, ganzzahlige Konstante, die angibt, wieviele Ziffern mindestens ausgegeben werden sollen. k Skalierfaktor (genauer: Exponent der Zehnerpotenz), ganzzahlige Konstante. n Positive ganzzahlige Konstante ungleich Null. 44 KAPITEL 4. PROGRAMMIERSPRACHEN, FORTRAN Form A Aw Dw.d Ew.d Ew.dEe Typ Zeichen Zeichen Numerisch Numerisch Numerisch Fw.d Gw.d Gw.dEe Numerisch Numerisch Numerisch Iw Iw.m Lw Numerisch Numerisch Logisch Bedeutung Zeichen mit datenabhängiger Länge Zeichen mit vereinbarter Länge Doppelt genaue Gleitpunktzahl mit Exponent Einfach genaue Gleitpunktzahl mit Exponent Einfach genaue Gleitpunktzahl mit explizit vereinbarter Länge des Exponenten Einfach genaue Gleitpunktzahl ohne Exponent Einfach genaue Gleitpunktzahl mit oder ohne Exponent Einfach genaue Gleitpunktzahl ohne Exponent oder mit Exponent (mit explizit vereinbarter Länge des möglichen Exponenten) Ganze Dezimalzahl Ganze Dezimalzahl mit mindestens m Ziffern Logischer Wert Tabelle 4.1: Wiederholbare Datenfeldbeschreibungen Form BN BZ kP nX SP SS S Tn TRn TLn : / $ Typ Steuerung der numerischen Eingabe Skalierfaktor Steuerung der numerischen Ausgabe E/A-Tabulator Formatkontrolle Ende des Datensatzes Bedeutung Leerzeichen werden ignoriert Leerzeichen werden als Nullen aufgefaßt Skalierung für numerische Konvertierung Setze um n Positionen vor Plus–Zeichen werden vorangestellt Plus–Zeichen werden unterdrückt Plus–Zeichen werden unterdrückt Setze auf Position n Setze um n Positionen vor Setze um n Positionen zurück Beendet die Formatabarbeitung Zeigt das Ende des gegenwärtigen Ein- oder Ausgabedatensatzes an Verhindert den Zeilenumbruch Tabelle 4.2: Nichtwiederholbare Datenfeldbeschreibungen Das Ausgabefeld wird bei allen Umwandlungen rechtsbündig aufgefüllt. Es folgt eine kurze Beschreibung der wichtigsten Datenfeldbeschreibungen (auch: Formatspezifikationen). Das A-Format dient zur Ein-/Ausgabe von Zeichengrößen und hat die Form A oder Aw Ist w kleiner als die Länge der Zeichengröße, dann werden die ersten w Zeichen ausgegeben bzw. die am weitesten rechtsstehenden Zeichen eingelesen, und der Rest wird jeweils ignoriert. Falls w größer als die Länge der Zeichengröße ist, dann wird linksbündig in den Speicher eingelesen bzw. rechtsbündig ausgegeben, und der Rest wird mit Leerzeichen aufgefüllt. Wird keine Datenfeldweite angegeben, wird die Länge der Zeichengröße benutzt. Innerhalb der Formatangabe auszugebende Zeichenfolgen werden im Apostroph–Format angegeben, d.h. in Apostrophe eingeschlossen. Beispiel: 4.2. FORTRAN 45 CHARACTER :: what*5 what = ’ALLES’ PRINT 120, what 120 FORMAT (’ Ich will’, A7) ergibt die Bildschirm–Ausgabe: Spalte: 1234567890123456 Zeichen: Ich will ALLES Zur Ein-/Ausgabe von reellen Zahlen gibt es u.a. das F–Format und das E–Format. Die einfachste Form dieser Formate ist Fw.d bzw. Ew.d Bei der Ausgabe im E–Format wird ein Exponent gedruckt; die Verwendung des F–Formats führt zur Ausgabe einer reellen Zahl ohne Exponent. Bei der Eingabe werden genau w Zeichen eingelesen. Es können auch Zahlen im jeweils anderen Format eingelesen werden, z.B. kann man mit dem F–Format auch Zahlen lesen, die im E–Format auf dem Datensatz stehen. Reelle Zahlen sollten im Eingabebereich stets mit Dezimalpunkt geschrieben werden. Bei der Ausgabe im E–Format in der oben angegebenen Form hat der Exponent stets zwei Stellen plus ein Vorzeichen, d.h. zur Darstellung des Exponenten werden von der gesamten Feldweite w vier Stellen verwendet. Die Mantisse ist stets: 0.1 ≤ Mantisse < 1.0 (Ausnahme: Die Zahl Null), und der Exponent wird entsprechend angeglichen. Allgemein muß die Feldweite so groß sein, daß alle Zeichen auch ausgegeben werden können. Dies gilt insbesondere für das F-Format. Falls w zu klein gewählt wird, werden w Sterne gedruckt. Falls w zu groß ist, wird rechtsbündig ausgegeben und mit Leerzeichen aufgefüllt. Beispiel: REAL :: alfa alfa = -3.98E+03 PRINT 910 ,(alfa, i = 1,5) 910 FORMAT (’ ’, F8.2, F5.0, E10.3, E8.1, E6.1) erzeugt die Ausgabe (auf einer Datei): Spalten: Zeichen: 12345678901234567890123456789012345678 -3980.00***** -.398E+04 -.4E+04****** Das erste Zeichen der Ausgabezeile ist ein sogenanntes Vorschubsteuerzeichen für den Drucker. Wir werden darauf später eingehen. Die beiden ersten Formatspezifikationen geben alfa ohne Exponent aus, die anderen drei bewirken eine Ausgabe mit Exponent. Die Feldweiten des 2. und 5. Formats sind zu klein um die notwendigen Zeichen aufzunehmen, daher werden Sterne gedruckt. Das vierte Format verwendet nur eine Stelle für die Mantisse, die daher gerundet wird. Die Ausgabe ganzzahliger Variabler mit E– oder F–Format produziert nicht vorhersehbare Ergebnisse, da ganze und reelle Zahlen intern unterschiedlich dargestellt werden (siehe Kapitel 5). Zur Ein-/Ausgabe ganzer Zahlen dient das I–Format: Iw 46 KAPITEL 4. PROGRAMMIERSPRACHEN, FORTRAN Bei der Eingabe werden genau w Zeichen eingelesen. Die Zahl sollte rechtsbündig geschrieben werden, da abhängig von der Voreinstellung des Compilers Leerzeichen als Nullen interpretiert werden können. Standard ist, daß Leerzeichen ignoriert werden. Ein vollständig leeres Feld wird jedoch als Null interpretiert. Dezimalpunkte sind verboten. Die Ausgabe erfolgt ohne Dezimalpunkt. Für negative Zahlen wird das Vorzeichen unmittelbar vor die erste Ziffer gedruckt. Beispiel: INTEGER :: izahl1, izahl2 izahl1 = -123 izahl2 = +45678 PRINT 117, izahl1, izahl2 117 FORMAT (’ ’, 2I4) erzeugt die Ausgabe Spalten: Zeichen: 1234567890 -123**** Die zweite Datenfeldbeschreibung ist zu klein, es werden vier Sterne ausgegeben. Das L–Format wird benutzt, um logische Daten ein- oder auszugeben: Lw Bei der Eingabe wird dem logischen Listenelement der Wert .TRUE. oder .FALSE. zugeordnet, wenn die ersten von Leerzeichen verschiedenen Zeichen T oder .T bzw. F oder .F sind. Bei der Ausgabe werden .TRUE. und .FALSE. rechtsbündig mit w − 1 führenden Leerzeichen als T bzw. F ausgegeben. Beispiel: LOGICAL :: ok, ko ok = .TRUE. ko = .FALSE. PRINT 135, ok, ko 135 FORMAT (’ ’, 2L3) erzeugt die Ausgabe: Spalten: Zeichen: 1234567890 T F Der Schrägstrich / ( Slash“) innerhalb einer Formatangabe zeigt das Ende des letzten und den Beginn ” eines neuen Datensatzes an, es wird also eine neue Zeile begonnen. Das X–Format wird verwendet, um Zeichen zu überspringen. Seine Form ist nX und n ist eine ganze Zahl > 0, die die Anzahl der zu überspringenden Zeichenpositionen angibt, ausgehend von der augenblicklichen Position. Bei der Ausgabe werden übersprungene noch nicht gefüllte Positionen des Datensatzes mit Leerzeichen gefüllt. Das X–Format sollte besser durch das allgemeinere Tabulatorformat T, insbesondere den Rechtstabulator TRn ersetzt werden. 4.2. FORTRAN 47 Einzelne Formatspezifikationen oder ganze Gruppen können durch Angabe eines Wiederholungsfaktors wiederholt werden. Gruppen werden dabei in Klammern eingeschlossen. Beispiel: 102 FORMAT (3I4, 2(1X, 2F6.1)) entspricht: 102 FORMAT (I4, I4, I4, 1X, F6.1, F6.1, 1X, F6.1, F6.1) Das erste Zeichen eines jeden Datensatzes wird bei der Ausgabe auf manchen Druckern nicht ausgedruckt sondern zur Verschubsteuerung benutzt. Bei anderen Ausgabemedien gehört es zu den Daten. Es gibt drei Zeichen, die besondere Aktionen des Druckers bewirken können: 0 1 + eine Leerzeile vor dem Drucken Vorschub auf den Anfang einer neuen Seite kein Vorschub, in der gleichen Zeile weiterdrucken. Um einer eventuellen Fehlinterpretation der Daten in der ersten Spalte jedes Datensatzes beim Fortlassen der Vorschubzeichen aus dem Weg zu gehen,sollte bei der Ausgabe das erste Zeichen jeder Zeile ein Leerzeichen sein. Dieses Leerzeichen sollte explizit angegeben werden und nicht in einer Formatspezifikation versteckt“ werden. Also: ” 100 FORMAT (1X, I4, 2F5.2) und nicht 100 FORMAT (I5, 2F5.2) 4.2.14.2 Externe Ein-/Ausgabe Wir haben bisher die Ein-/Ausgabe über die spezielle Standard– Datei“ Konsole (also Tastatur und ” Bildschirm) kennengelernt. Die allgemeine Ein-/Ausgabe erfolgt über sogenannte externe Dateien. Das sind Dateien, die auf einem beliebigen Speichermedium (Platte, Diskette, Band etc.) vorliegen. Diese Dateien haben einen Namen; er dient der Identifikation dieser Datei durch das FORTRAN–Programm einerseits und das Betriebssystem andererseits. Vor dem ersten Zugriff auf eine (neue oder bereits existierende) Datei muß die Datei geöffnet werden, wobei sie für die Ein-/Ausgabe eingerichtet und mit gewissen Dateiattributen versehen wird. Wir werden hier nur sequentielle Dateien behandeln. Sequentielle Dateien kann man sich vorstellen als Dateien mit Zeilenstruktur. Jede Zeile enthält Daten in Form eines Datensatzes (record). Records können formatiert oder unformatiert sein. Auf die Records kann nur sequentiell (d.h. der Reihe nach) zugegriffen werden: auf eine bestimmte Zeile kann nur zugegriffen werden, wenn alle davorliegenden Zeilen bereits gelesen oder übersprungen wurden. Bei jeder Anweisung zum Zugriff auf Dateien kann abgefragt werden, ob ein Fehler aufgetreten ist, z.B. durch Fehlen der Datei oder Erreichen des Dateiendes. Werden diese Abfragen nicht durchgeführt, bricht das Programm im Fehlerfall ab. Auf Dateien wird in E/A–Anweisungen über die E/A–Einheit (auch: E/A–Kanal) Bezug genommen. Zum Beispiel spezifiziert die Anweisung READ (2, 100) X, Y 48 KAPITEL 4. PROGRAMMIERSPRACHEN, FORTRAN die E/A–Einheit 2. Die Leseoperation wird auf der Datei durchgeführt, die der Einheit 2 zugeordnet ist. Die Zuordnung von Einheiten zu Dateien geschieht mit dem UNIT–Parameter in der OPEN–Anweisung (siehe unten). In den Ein-/Ausgabeanweisungen können Parameter verwendet werden. Die wichtigsten E/A–Parameter werden im folgenden kurz erläutert. UNIT=u spezifiziert die E/A–Einheit für den Zugriff auf eine externe Datei oder spezifiziert die interne Datei (siehe interne Ein-/Ausgabe“). Für u gibt es folgende Möglichkeiten: ” - ein * bedeutet die Standardeinheit (in der Regel die Konsole), - ein ganzzahliger Ausdruck mit 0 ≤ u ≤ 999, - der Name einer Zeichengröße, die eine interne Datei definiert. UNIT=“ kann entfallen, wenn u der erste Parameter ist. ” Standard–E/A–Einheiten sind meist UNIT=5 für die Eingabe, UNIT=6 für die Ausgabe, d.h. diese Angaben wirken wie UNIT=*. FMT=fn Formatangabe für formatierte Ein-/Ausgabe. Für fn gibt es unter anderem folgende Möglichkeiten: - die Anweisungsmarke einer FORMAT–Anweisung in der selben Programmeinheit, - eine Zeichengröße, deren Wert die Formatangabe enthält, - ein Stern * für listengesteuerte E/A. Wenn UNIT=“ fehlt, darf auch FMT=“ fehlen, und die Formatangabe muß an zweiter Stelle ” ” in der Liste stehen. IOSTAT=ios Mit dem optionalen IOSTAT–Parameter spezifiziert man eine ganzzahlige Variable ios, der, je nach Korrektheit der Abbarbeitung der Anweisung, einer der folgenden Werte zugewiesen wird: ios = 0 : die Abbarbeitung verlief fehlerfrei! ios > 0 : eine Fehlerbedingung ist aufgetreten! ios < 0 : das Dateiende wurde (beim Lesen) erreicht! END=labl Marke einer ausführbaren Anweisung, zu der bei Erreichen des Dateiendes beim Lesen verzweigt wird. (Optionaler Parameter) ERR=labl Marke einer ausführbaren Anweisung, zu der im Fehlerfall verzweigt wird. (Optionaler Parameter) iolist E/A-Liste. Weitere Parameter können in einem FORTRAN–Handbuch nachgelesen werden. Bei formatgebundener Ein-/Ausgabe muß eine Formatangabe spezifiziert werden. Die E/A-Liste darf fehlen. Jede formatgebundene E/A–Anweisung überträgt einen oder mehrere Datensätze. Formatgebundenes Lesen erfolgt mit der Anweisung READ (UNIT = u, FMT = f n, IOSTAT = ios, END = labl, ERR = labl) iolist oder kürzer READ (u, f n, IOSTAT = ios, END = labl, ERR = labl) iolist Formatgebundenes Schreiben erfolgt mit der Anweisung 4.2. FORTRAN 49 WRITE (UNIT = u, FMT = f n, IOSTAT = ios, ERR = labl) iolist oder kürzer WRITE (u, f n, IOSTAT = ios, ERR = labl) iolist oder auf die Standardausgabeeinheit mit PRINT f n, iolist Diese Anweisung wirkt wie WRITE(*,f n) iolist . Es gelten die bei den E/A- Parametern gemachten Bemerkungen. Beispiel (die OPEN–Anweisung wird unten erklärt): ! --- Beispiel fuer Ein-/Ausgabe PROGRAM IOTest IMPLICIT NONE REAL :: a, b INTEGER :: io1, io2, io3 ! --- vorhandene Datei oeffnen (Zuordnung Kanal 2): OPEN (2, FILE=’Datei.2’, IOSTAT=io1, STATUS=’OLD’) IF (io1 == 0) THEN ! --- Formatiert von Kanal 2 einlesen: READ (2, 100, IOSTAT = io2) a, b IF (io2 == 0) THEN ! --- Formatiert auf Console ausgeben: WRITE (*, 110, IOSTAT = io3) a, b ! --- Ausgabe bei Schreibfehler: IF (io3 /= 0) PRINT *, ’ E/A-Fehler!’ ELSEIF (io2 < 0) THEN ! --- Ausgabe bei Dateiende: PRINT *, ’ Dateiende!’ ELSE ! --- Ausgabe bei Lesefehler: WRITE (*,*) ’ E/A-Fehler!’ ENDIF ELSE ! --- Ausgabe falls die Datei nicht existiert: PRINT *, ’ Fehler beim Oeffnen!’ ENDIF ! --- Formatangaben: 100 FORMAT (2F10.4) 110 FORMAT (1X, 2(F10.4, 2X) ) END Falls während des Öffnens der Datei ein Fehler auftritt, weil die Datei nicht existiert, wird der INTEGER– Variablen io1 ein Wert größer als Null zugewiesen. Der IF-Block wählt nun den Pfad für die zugehörige Fehlerausgabe. Sonst werden entsprechend der FORMAT–Anweisung 100 zwei Werte gelesen. Beim Lesen 50 KAPITEL 4. PROGRAMMIERSPRACHEN, FORTRAN oder dem anschließenden Schreiben gemäß FORMAT–Anweisung 110 verzweigt das Programm wieder in Abhängikeit davon, ob io2 und io3 größer, kleiner oder gleich Null sind, und gibt im Fehlerfall eine passende Meldung auf dem Bildschirm aus. Die Möglichkeit der formatfreien (unformatierten) Ein-/Ausgabe soll hier auch noch erwähnt werden. Dabei werden die Daten nicht konvertiert, sondern in binärer Form übertragen. Diese E/A-Methode wird benutzt, um Daten schnell zwischenzuspeichern, um sie z.B. in einem nachfolgenden Programmlauf weiterzuverarbeiten. Näheres entnehme man der FORTAN–Literatur. Die Anweisungen für listengesteuerte Ein-/Ausgabe entsprechen formal denen für formatierte E/A, mit der Einschränkung, daß für die Formatangabe ein Stern gesetzt wird: FMT=*. Wir haben den Spezialfall der listengesteuerten E/A über Standard-E/A-Einheiten schon in Abschnitt 4.2.13 kennengelernt. Mit Ein-/Ausgabe–Status–Anweisungen werden Eigenschaften einer Datei festgelegt, geprüft oder geändert. Hier sollen nur die wichtigsten Parameter der Anweisungen angegeben werden. Mit der OPEN–Anweisung OPEN (UNIT = u, FILE = f il, STATUS = stat, IOSTAT = ios, ERR = labl) wird die dem FORTRAN–Programm bekannte E/A-Einheit u der Datei mit dem Namen f il (auf Betriebssystemebene) zugeordnet. f il ist dabei ein Zeichenausdruck. Das Programm nimmt also auf die Datei f il Bezug durch die E/A-Einheit mit der Nummer u. stat ist ebenfalls ein Zeichenausdruck, der den erwarteten Status der zu öffnenden Datei angibt. Dabei kann stat folgende Werte entahlten ’OLD’ ’NEW’ : : ’UNKNOWN’ : die Datei soll bereits existieren die Datei darf noch nicht existieren, sondern wird erst mit dieser Anweisung angelegt das ist die Standardeinstellung; existiert sie noch nicht, wird sie angelegt, ansonsten wird die vorhandene Datei geöffnet Im Fehlerfall wird der Variablen ios ein Wert ungleich Null zugewiesen und/oder zur Anweisung mit der Marke labl verzweigt. Die Angabe der E/A-Einheit u ist unbedingt erforderlich, die anderen Parameter können gegebenenfalls fehlen. Beispiel: OPEN (6, FILE = ’OUTPUT’) Neben der Zuordnung lassen sich mit der OPEN–Anweisung auch Details des Zugriffs auf die Datei (Dateiattribute) festlegen. Wir werden hierauf nicht weiter eingehen und verweisen auf weiterführende FORTRAN–Literatur. Mit der CLOSE–Anweisung CLOSE (UNIT = u) wird eine Datei von der E/A-Einheit u getrennt. Danach kann z.B. die E/A-Einheit u mit einer anderen Datei verbunden werden. Die abgetrennte Datei kann z.B. zurückgegeben werden. Bei Programmende (END, STOP) findet ein selbständiges CLOSE statt. Zur Abfrage des Zustands einer E/A-Einheit oder einer Datei dient die INQUIRE–Anweisung. Sie soll hier nicht weiter besprochen werden; hierfür wird auf die FORTRAN–Literatur (z.B. [11] ) verwiesen. Zur Positionierung von sequentiellen Dateien gibt es drei Anweisungen. Die REWIND–Anweisung REWIND (UNIT = u, ERR = labl) 4.2. FORTRAN 51 setzt eine Datei auf den Anfang zurück, so daß der nächste in einer E/A–Anweisung angesprochene Datensatz der erste Datensatz dieser Datei ist. Die BACKSPACE–Anweisung BACKSPACE (UNIT = u, ERR = labl) setzt die E/A-Einheit u um einen Datensatz (d.h. um eine Zeile) zurück. Die ENDFILE–Anweisung ENDFILE (UNIT = u, ERR = labl) schreibt einen speziellen Datensatz, den sogenannten Dateiendesatz auf die E/A-Einheit u. Diese Dateiendesätze können beim Lesen z.B. mit dem E/A–Parameter END=“ erkannt werden. Das Dateiende ” wird aber auch erkannt wenn kein Dateiendesatz vorhanden ist. In allen drei Positionierungsanweisungen ist die Angabe von u erforderlich; das Schlüsselwort UNIT=“ ” kann entfallen, wenn u der erste Parameter ist. 4.2.14.3 Interne Ein-/Ausgabe Die interne Ein-/Ausgabe ermöglicht den Datentransport von einem Bereich des Arbeitsspeichers zu einem anderen, ohne Benutzung von E/A-Geräten oder externer Speichermedien und erlaubt gleichzeitig die Änderung der Darstellung, z.B. Umwandlung numerischer Daten in Zeichendaten und umgekehrt. Interne Ein-/Ausgabe wird nur mit formatgebundenen READ- und WRITE–Anweisungen vorgenommen und verwendet Variablen vom Typ CHARACTER als interne Dateien“. Diese enthalten einen einzigen Datensatz; ” Positionierungs- und Statusanweisungen sind daher für interne Dateien sinnlos. Bei der Ausgabe werden die Daten der Formatangabe entsprechend in Zeichen umgewandelt (wie auch bei externen formatierten Dateien) und in der Zeichengröße gespeichert. Bei der Eingabe werden die Zeichen der internen Datei der Formatangabe entsprechend konvertiert und an die in der Eingabeliste stehenden Größen übergeben. Beispiel: CHARACTER (LEN=20) :: IntDat INTEGER :: NR, Neu IntDat = ’Dies sind 20 Zeichen’ READ (IntDat, 100) NR WRITE (*,100) NR 100 FORMAT (10X, I2) Neu = 99 WRITE (IntDat(11:12), 102) Neu 102 FORMAT (I2) PRINT*, IntDat erzeugt die Ausgabe Spalten: 12345678901234567890 20 Dies sind 99 Zeichen 4.2.15 Programmeinheiten und Anweisungen Zur Lösung eines komplexen Problems ist es sinnvoll, das Gesamtproblem in mehrere Teilprobleme zu zerlegen. Durch diese Unterteilung lassen sich Anschaulichkeit und Übersichtlichkeit bewahren. FORT- 52 KAPITEL 4. PROGRAMMIERSPRACHEN, FORTRAN RAN unterstützt dies durch Unterprogramme, die für sich selbständige Teilprogramme darstellen. Die Verwendung von Unterprogrammen hat einige Vorteile: • modularer Programmaufbau; Unterprogramme können einzeln programmiert und ausgetestet werden. Das erleichtert die Fehlersuche, da die Übersichtlichkeit gewahrt bleibt. • wiederholt zu durchlaufende Programmteile brauchen nur einmal programmiert werden und werden als Unterprogramm nur immer wieder aufgerufen. • Unterprogramme zur Lösung von Teilproblemen können in anderen Programmen verwendet werden, siehe insbesondere numerische Standardverfahren, z.B. für Interpolation, Lösung von Gleichungssystemen etc. Außerdem existieren für etliche Problemstellungen (Berechnung mathematischer Funktionen) bereits fertige Unterprogrammbibliotheken, die durch entsprechende Aufrufe in beliebigen Programmeinheiten verwendet werden können. Ein fertiges Programm besteht somit mindestens aus einem Hauptprogramm, das mit einer PROGRAM– Anweisung beginnt, und gegebenenfalls einem oder mehreren Unterprogrammen. Jedes dieser Unterprogramme stellt eine Programmeinheit dar, die aus einer Reihe von FORTRAN–Anweisungen mit wahlweise eingestreuten Kommentaren besteht, und die mit einer END–Anweisung abgeschlossen werden muß, siehe auch Abschnitt 4.2.2. Unterprogramme können nur zusammen mit einem Hauptprogramm ausgeführt werden. Ein Vereinbarungsunterprogramm enthält keine ausführbaren Anweisungen und wird daher als nichtausführbares Programm bezeichnet. Es beginnt mit einer BLOCKDATA–Anweisung, und in ihm kann man alle möglichen Vereinbarungen treffen (Typdefinitionen, COMMON–Blöcke, Feldvereinbarungen) und z.B. Größen in benannten gemeinsamen Speicherbereichen (COMMON–Blöcke) Anfangswerte zuweisen. Ausführbare Unterprogramme sind Subroutinen und (benutzerdefinierte) Funktionen, die mit einer SUBROUTINE– bzw. FUNCTION–Anweisung beginnen und mit einer END–Anweisung enden. Ein Unterprogramm kann Größen von der rufenden Programmeinheit über eine Parameterliste und/oder über gemeinsame Speicherbereiche übernehmen, verarbeiten und auch an die rufende Programmeinheit zurückgeben. Eine Subroutine stellt die Lösung einer abgeschlossenen Teilaufgabe dar. Ihr Aufruf erfolgt über eine besondere Anweisung. Eine Funktion wird ausschließlich in (arithmetischen, logischen oder Zeichen–) Ausdrücken verwendet; sie liefert über ihren Namen einen Funktionswert als Operanden für den Ausdruck. Die vier Arten von Funktionen sind benutzereigene interne und externe Funktionsunterprogramme (FUNCTION), Formelfunktionen (Statement function), sowie vordefinierte Funktionen, die von FORTRAN fix und fertig zur Verfügung gestellt werden. Prinzipiell ist Fortran95 zur Rekursion, also der Möglichkeit eines Unterprogrammes sich selbst direkt oder indirekt aufzurufen, fähig. Es wird jedoch kein Bestandteil dieses Skriptes sein und sollte bei Bedarf in weiterführender Literatur nachgeschlagen werden. Erwähnt werden soll nur noch, daß sie mit Vorsicht genossen werden sollte, da die Gefahr der Endlos–Rekursion besteht! 4.2.16 Parameterlisten Bei den Parameterlisten unterscheidet man Aktual- und Formalparameter. Aktualparameter erscheinen in der Parameterliste in der aufrufenden Programmeinheit. Die aufrufende Programmeinheit übergibt diese Aktualparameter über die Liste an das aufgerufene Unterprogramm. Das Unterprogramm übernimmt die Werte der entsprechenden Aktualparameter und liefert die gegebenenfalls neu berechneten Werte über die Liste wieder an die aufrufende Programmeinheit zurück. Aktualparameter können neben Konstanten auch symbolische Konstanten, Namen von Variablen, Feldern und Funktionen sein. 4.2. FORTRAN 53 Formalparameter erscheinen in der Parameterliste des aufgerufenen Unterprogramms. Bei der Ausführung des Unterprogramms werden die Werte (genauer: die Speicheradressen dieser Werte, call by ” reference“ ) der Aktualparameter den entsprechenden Formalparametern des Unterprogramms zugeordnet und ersetzen diese. Die Formalparameter sind notwendig, um Typ, Art und Anzahl der Aktualparameter festlegen zu können. Die Aktualparameter müssen hinsichtlich Anzahl, Typ und Reihenfolge mit den Formalparametern übereinstimmen. Nach ihrer Funktion unterscheidet man drei Arten von Parametern: • Eingangs- oder Eingabe–Parameter, • Ausgangs- oder Resultat–Parameter und • Durchgangs–Parameter. Eingabe–Parameter (auch IN–Parameter) werden von rufenden Programm an das Unterprogramm übergeben, ihre Werte werden dort ohne Änderung benutzt. Ausgabe–Parameter (auch OUT–Parameter) sind Ergebnisse von Unterprogrammen, deren Werte an das rufende Programm zurückgeben werden. Durchgangs–Parameter (auch IN/OUT–Parameter) sind Größen, die vom rufenden Programm an das Unterprogramm übergeben werden, dort meist mit anderen Werten versehen werden, und dann an das rufende Programm zurückgeliefert werden. In Fortran95 kann man die Funktion eines Formalparameters mit dem INTENT–Attribut festlegen. Die drei Parameter–Arten sind dabei mittels Angabe von INTENT(IN), INTENT(OUT) oder INTENT(INOUT) zu bestimmen. 4.2.17 Vordefinierte Standardfunktionen Eine Reihe von Funktionen wie sin, cos, tan, log, Wurzel usw. werden in Anwendungen häufig benötigt. Solche und andere Funktionen, z.B. zur Typkonvertierung werden von FORTRAN fertig zur Verfügung gestellt. Sie sind im Compiler bereits definiert und liefern jeweils einen einzelnen Wert. Diesen erhält man durch Aufruf der Funktionen unter ihrem Namen mit dem gewünschten Argument, also durch einen Funktionsaufruf in einem Ausdruck. Zum Beispiel liefert LOG(x) den natürlichen Logarithmus des Arguments x. Der Typ des Arguments x kann reell, doppelt genau oder komplex sein. Der Typ des Ergebnisses eines Funktionsaufrufs ist mit Ausnahme z.B. der Typenumwandlungen gleich dem Typ des Arguments. Das Beispiel zeigt die Verwendung einer generischen, d.h. für Parameter verschiedenen Typs anwendbaren Funktion. Andere interne Funktionen sind spezifisch, d.h. nur für genau einen Parametertyp anwendbar. So berechnen die spezifischen Funktionen ALOG, DLOG und CLOG ebenfalls den natürlichen Logarithmus für ein reelles, doppelt genaues bzw. komplexes Argument und liefern ein reelles, doppelt genaues bzw. komplexes Ergebnis. Allgemein sollten die generischen Funktionsnamen verwendet werden. Soll jedoch der Name einer internen Funktion als Aktualparameter an ein Unterprogramm übergeben werden, muß ein geeigneter spezifischer Name verwendet werden. 54 KAPITEL 4. PROGRAMMIERSPRACHEN, FORTRAN 4.2.18 Benutzerdefinierte Funktionen Zu den benutzereigenen Unterprogrammen mit Funktionscharakter gehören die in diesem Abschnitt zu besprechenden Formelfunktionen, die internen und die externen Funktionen. Eine Formelfunktion (statement function) wird definiert durch eine einzige, vom Programmierer in der aufrufenden Programmeinheit anzugebende nichtausführbare Anweisung. Sie ähnelt einer Zuweisung. Die Definition einer Formelfunktion muß vor der ersten ausführbaren Anweisung und hinter den Vereinbarungsanweisungen vorgenommen werden. Die allgemeine Definitionsanweisung lautet: Stfun (p1 , p2 , p3 ,. . . ) = expr Der Name der Funktion ist Stfun und pi sind Formalparameter (Variablennamen). Der Wert des Ausdrucks expr ist der Wert der Formelfunktion nach deren Ausführung. Die Formelfunktion wird über ihren Namen aufgerufen: Stfun (a1 , a2 , a3 , . . . ) Die Aktualparameter ai sind Ausdrücke. Wird der Name der Formelfunktion in einem Ausdruck angetroffen, wird die Formelfunktion ausgewertet. Dazu werden die Werte der Aktualparameter berechnet und anstelle der Formalparameter bei der Auswertung des Ausdrucks expr verwendet. Eine Formelfunktion kann nur in der sie definierenden Programmeinheit aufgerufen werden. Ihr Name ist lokal, d.h. dieser Programmeinheit zugeordnet und darf nicht mit irgendeinem anderen Namen dieser Programmeinheit übereinstimmen. Beispiel für Definition und Verwendung einer Formelfunktion: PROGRAM demo ! --- Polynomauswertung nach HORNER-Schema IMPLICIT NONE REAL :: XAnf, XEnd, XI, DX, FXI, A0, A1, A2, A3 REAL :: A, B, C, D, X ! --- Die Formelfunktion HORNY muss selbst auch deklariert werden REAL :: HORNY INTEGER :: I, NX ! --- Definition des Hornerschemas als Statementfunction HORNY (A, B, C, D, X) = D + X*(C + X*(B + A*X)) ! --- Eingabe PolynomKoeffizienten und Wertebereich READ*, A0, A1, A2, A3 READ*, XAnf, XEnd, NX ! --- Berechnung und Ausgabe Funktionstabelle DX = (XEnd - XAnf) / (NX - 1) PRINT 100, ’WerteTabelle’ PRINT 100, ’X’,’F(X)’ DO I = 1, NX XI = Xanf + (I - 1)*DX FXI = HORNY (A0, A1, A2, A3, XI) PRINT 110, XI, FXI ENDDO 100 FORMAT(2A15) 110 FORMAT(2F15.5) END Die Verwendung von Formelfunktionen ist allerdings nicht sehr zukunftsweisend, da sie in nachfolgenden FORTRAN-Versionen nicht mehr unterstützt werden wird. Da die Formelfunktion ohnehin nur lokal, d.h. in einem Unterprogramm, Gültigkeit besitzt, ist sie durch die interne Funktion abgelöst worden. 4.2. FORTRAN 55 Die interne ist wie die externe Funktion ein benutzereigenes Unterprogramm, das eine Reihe von Anweisungen ausführt und einen Wert über seinen Namen zurückliefert. Sie werden über die FUNCTION– Anweisung typ FUNCTION fun (d1 , d2 , . . . ) definiert. typ ist der Typ der Funktion, welcher nicht nur jeder beliebige skalare Datentyp sein kann, sondern auch ein Feld beliebigen Typs, und f un ist ihr Funktionsname. (Die Zuweisung eines Typs zum Funktionnamen darf sogar auch erst innerhalb der Funktion selbst erfolgen.) Die Formalparameter di können Variablennamen, Feldnamen oder Namen anderer Unterprogramme sein. Der Name der Funktion ist innerhalb der Funktion selbst ein Variablenname, der nur auf der linken Seite einer Zuweisung stehen darf. Diese Variable muß während der Ausführung der Funktion definiert d.h. mit einem Wert versehen werden. Der Wert der Funktion beim Rücksprung (Antreffen von END FUNCTION) ins rufende Programm ist dann der Wert dieser Variablen. Prinzipielle Struktur einer Funktion: typ FUNCTION name (d1 , d2 , ...) : name = wert : END FUNCTION Der Aufruf einer internen oder externen Funktion geschieht über ihren Funktionsnamen. Die Funktion wird ausgeführt, wenn ihr Name als Operand innerhalb eines Ausdrucks erscheint. Die Funktion übernimmt Werte von der aufrufenden Programmeinheit über eine Parameterliste oder über gemeinsame Speicherbereiche; sie liefert einen Wert über ihren Namen zurück (oder auch mehrere Werte über die Parameterliste oder gemeinsame Speicherbereiche). Der Typ des Ergebnisses ist indirekt durch die Deklaration des Funktionsnamen festgelegt. Die Aktualparameter müssen hinsichtlich Anzahl, Typ und Reihenfolge mit den Formalparametern übereinstimmen, ihre Namen können unterschiedlich sein. Zwischen internen oder externen Funktion gibt es nun folgende Unterschiede: Die interne Funktion Die interne Funktion wird innerhalb einer Programmeinheit definiert, welche das Hauptprogramm, eine Unterroutine, aber auch eine andere Funktion sein kann. Die Definition der Funktion findet zwischen der CONTAINS– Anweisung und der END–Anweisung der umgebenen Programmeinheit statt: PROGRAM main : CONTAINS typ FUNCTION name (d1 , d2 , ...) : name = wert : END FUNCTION 56 KAPITEL 4. PROGRAMMIERSPRACHEN, FORTRAN END Die interne Funktion ist nur lokal in der umgebenen Programmeinheit bekannt, muß aber bei expliziter Deklaration noch mittels Deklaration des Funktionsnamens innerhalb der Einheit bekannt gemacht werden. Außerdem kann eine interne Funktion, ebenso wie Formelfunktionen, auf alle Variablen des umgebenen Unterprogramms zurückgreifen, und das auch ohne Kommunikation über die Parameterliste. Die externe Funktion Externe Funktionen sind gegenüber den internen global verfügbar. Ihr Name ist überall im Programm bekannt und darf daher im gesamten Programm nur ein einziges Mal auftauchen. Ihre Definition findet äuquivalent zum Hauptprogramm als eigenständiger Programmteil (also außerhalb jedes anderen Unterprogramms) statt und sieht wie folgt aus: typ FUNCTION name (d1 , d2 , ...) : name = wert : END FUNCTION Da die externe Funktion eigenständig definiert wird, sind in ihr nur die Werte bekannt, die ihr mittels Parameterliste, COMMON-Block, o.ä. zugänglich gemacht werden. Der Vorteil einer externen Funktion liegt offensichtlich in ihrer globalen Natur, da sie innerhalb des gesamten Programms aufgerufen werden kann. Im Prinzip hängt die Entscheidung für einen der Funktionsarten natürlich immer vom jeweiligen Verwendungszweck ab. Beispiel: PROGRAM vektor IMPLICIT NONE INTEGER :: i REAL :: a(3), b(3), sp, SKALP READ*, (a(i), b(i), i = 1,3) sp = SKALP (a, b, 3) PRINT*, ’Das Skalarprodukt ist:’, sp PRINT*, ’Aus der Vektoraddition ergibt sich: ’,VEKTADD(3) CONTAINS ! --- Funktion zur Addition zweier Vektoren FUNCTION VEKTADD (dimen) IMPLICIT NONE INTEGER :: k, dimen REAL, DIMENSION(dimen) :: VEKTADD DO k=1, dimen ! --- a() und b() sind aus dem umgebenen Programm bekannt VEKTADD (k) = a(k) + b(k) ENDDO END FUNCTION END ! --- Funktion zur Berechnung des Skalarprodukts zweier Vektoren REAL FUNCTION SKALP (v1, v2, n) 4.2. FORTRAN 57 IMPLICIT NONE INTEGER :: n, ik REAL :: v1(n), v2(n) SKALP = 0.0 DO ik = 1, n SKALP = SKALP + v1(ik)*v2(ik) ENDDO END FUNCTION Im Zusammenhang mit Funktionen soll noch kurz auf den Unterschied zwischen lokalen und globalen Größen eingegangen werden. Alle in Programmeinheiten auftretenden Namen und Anweisungsnummern haben lokale Bedeutung, d.h. sie sind nur in der jeweiligen Programmeinheit bekannt und dürfen außerhalb, gegebenenfalls mit anderer Bedeutung, wieder verwendet werden. Der Name einer externen Funktion ist dagegen eine globale Größe; er darf weder mit anderen globalen Namen (z.B. Name der rufenden Programmeinheit) noch mit lokalen Namen der betreffenden Programmeinheit identisch sein (Ausnahme: gleichnamige Variable in der FUNCTION selbst). Globale Größen sind für das gesamte Programm von Wichtigkeit. Weitere globale Größen sind z.B. die Namen gemeinsamer Speicherbereiche (benannte COMMON–Blöcke) oder benutzerdefinierte Typen (TYPE). 4.2.19 Benutzerdefinierte Unterprogramme – Subroutinen Während Funktionsunterprogramme bei jedem Aufruf einen einzigen Wert — den Funktionswert — berechnen, können in FORTRAN mit Subroutinen–Unterprogrammen mehrere Werte bei einem Aufruf berechnet und der rufenden Programmeinheit zur Verfügung gestellt werden. Eine Subroutine beginnt stets mit der SUBROUTINE–Anweisung SUBROUTINE sname (d1 , d2 , ...) Der Name sname dient ausschließlich zur Identifikation der Subroutine. Er besitzt keinen Datentyp und kann nicht zur Werteübergabe an das rufende Programm benutzt werden. Die Parameterübergabe zwischen rufendem Programm und Subroutine findet über die Parameterliste statt, wobei di die formalen Parameter sind. Daneben ist noch eine Parameterübergabe über gemeinsame Speicherbereiche (siehe unten) möglich. Jede Subroutine muß mit einer END SUBROUTINE–Anweisung abgeschlossen werden und kann eine RETURN–Anweisung enthalten, die den vorzeitigen Rücksprung ins rufende Programm bewirkt. Es gibt auch bei Subroutinen interne und externe Subroutinen. Ihre Unterscheidung bezüglich Gültigkeit und Definition entsprechen denen der internen und externen Funktionen, und sollen deshalb hier nicht erneut aufgeführt werden. Subroutinen stellen selbständige Programmeinheiten dar, weshalb auch alle erforderlichen Spezifikationen (z.B. Typdeklarationen) angegeben werden müssen. Der Aufruf einer Subroutine geschieht mit der CALL–Anweisung CALL sname (a1 , a2 , ...) Der Name der gerufenen Subroutine ist sname und ai sind die Aktualparameter. Diese müssen, wie auch bei Funktionen, bezüglich Anzahl, Typ und Reihenfolge mit den Formalparametern übereinstimmen. Die Parameter der Liste können Eingabe- oder Ausgabeparameter sein oder auch beide Funktionen gleichzeitig erfüllen. Aus Gründen der Übersichtlichkeit sollte man jedoch für Eingabe-, Ausgabe- und Durchgangs– Parameter jeweils unterschiedliche Parameter verwenden. Beispiel: PROGRAM Extrem 58 KAPITEL 4. PROGRAMMIERSPRACHEN, FORTRAN IMPLICIT NONE INTEGER :: i INTEGER, PARAMETER :: NDIM = 10 REAL :: x(NDIM), xmin, xmax PRINT*, ’Bitte die 10 Feldwerte eingeben:’ READ*, (x(i), i = 1,10) CALL MinMax (x, NDIM, xmin, xmax) PRINT*, ’Das Minimum betraegt:’, xmin PRINT*, ’Das Maximum betraegt:’, xmax END ! --- Subroutine zur Berechnung von Minimum und Maximum eines Feldes SUBROUTINE MinMax (f, n, fmi, fma) IMPLICIT NONE INTEGER :: n, i REAL :: f(n), fmI, fmA fmI = f(1) fmA = f(1) DO i = 1, n fmi = MIN (fmi, f(i)) fma = MAX (fma, f(i)) ENDDO END SUBROUTINE Im Hauptprogramm werden 10 Werte über die Tastatur eingegeben. Zur Ermittlung der Extremwerte wird das Feld x mit den 10 Werten an das Unterprogramm übergeben. Ein weiterer Eingabeparameter ist die aktuelle Dimension NDIM des Feldes. Die beiden letzten Parameter der Liste xmin und xmax sind Ausgabeparameter, die gesuchten Extremwerte. Das Unterprogramm MinMax benutzt variable Dimensionierung, d.h. es ist für allgemeine, beliebig große Felder geschrieben. Die jeweilige aktuelle Dimensionierung (hier: n) muß dann als Parameter vom rufenden Programm übergeben werden. Beim Aufruf des Unterprogramms werden die Werte der aktuellen Parameter (im rufenden Programm) den formalen Parametern (im Unterprogramm) zugeordnet, und zwar in der Reihenfolge ihres Auftretens in der Parameterliste: x→f, NDIM→n, xmin→fmi und xmax→fma. Der Algorithmus zur Extremwertbestimmung ist wie folgt: Das Unterprogramm setzt zunächst Minimum und Maximum auf den Wert des ersten Feldelements. In der Schleife werden dann durch Vergleich der bisher gefundenen Extremwerte mit dem laufenden Feldelement die globalen Extremwerte ermittelt. Der FORTRAN–Standard besagt, daß beim Rücksprung in das rufende Programm alle lokalen Größen nicht mehr definiert sind. Möchte man dem entgegenwirken muß für die betreffenden Variablen das SAVE–Attribut gesetzt werden. 4.2.20 Speicherplatz–Zuordnung (COMMON) und Einbettung von Programmzeilen (INLUDE) Bei der Übersetzung (Compilierung) bzw. beim Laden eines Programms werden den vorkommenden Größen Speicherplätze (Adressen) zugeordnet. Diese Zuordnung kann durch die im folgenden besprochenen Anweisungen beeinflußt werden. Die COMMON–Anweisung ordnet Variablen verschiedener Programmeinheiten gleiche Plätze im Arbeitsspeicher zu und erlaubt daher, neben der Parameterliste, die Übertragung von Werten zwischen Programmeinheiten. Formalparametern eines Unterprogramms, also allen Parametern der Parameterliste, werden keine Spei- 4.2. FORTRAN 59 cherplätze zugewiesen. Vielmehr werden beim Aufruf dem gerufenen Unterprogramm die Speicheradressen der aktuellen Parameter im rufenden Programm übermittelt. Daraus ergibt sich die folgende Regel: Formalparameter von Unterprogrammen dürfen nicht in einer COMMON–, SAVE–, DATA– oder EQUIVALENCE2 –Anweisung stehen. Umgekehrt dürfen aber Größen, die in einer solchen Anweisung auftreten als Aktualparameter in Unterprogrammaufrufen verwendet werden. Normalerweise werden Programme und Daten für jede Programmeinheit getrennt gespeichert, so daß beispielsweise eine Größe, die in einem Unterprogramm gespeichert ist und nicht in der Parameterliste oder einem Unterprogrammaufruf steht, von keiner anderen Programmeinheit aus erreicht werden kann (lokale Größe). Demgegenüber gibt es noch den allen Programmeinheiten gemeinsamen Speicherbereich (COMMON–Bereich), der über die COMMON–Anweisung definiert wird COMMON /name1 / liste1 , /name2 / liste2 , ... namei ist der Name eines gemeinsamen Speicherbereichs, der einen benannten COMMON–Bereich identifiziert und dessen Elemente in listei aufgeführt sind. Die Elemente von listei können Namen von einfachen und indizierten Variablen (ggfs. einschließlich der Dimensionen) sein, die durch Kommata getrennt werden. Die Verwendung von COMMON–Bereichen wird im Abschnitt 4.2.21 Kommunikation zwischen Pro” grammeinheiten“ beispielhaft erläutert. Wird der Name des COMMON–Blocks weggelassen, so ist der gemeinsame Speicherbereich ein unbenannter COMMON–Block. Die Zuordnung der Namen von Komponenten eines COMMON–Blocks zu den zugehörigen Speicherplätzen (Adressen) ergibt sich unabhängig von der Programmeinheit allein aus der Stellung der Komponenten innerhalb der Liste listei . Innerhalb einer Programmeinheit darf ein symbolischer Name nur einmal in einer COMMON–Anweisung auftreten, aber gemeinsame Speicherbereiche dürfen mehrmals vereinbart werden. Dabei werden durch listei , die dem Namen des COMMON–Blocks folgt, dem COMMON–Block weitere Elemente angehängt. Beispiel: COMMON /ALL/ MARS, VENUS COMMON /ALL/ ERDE, MOND ist gleichbedeutend mit: COMMON /ALL/ MARS, VENUS, ERDE, MOND Die Länge eines COMMON–Blocks ist die Gesamtzahl der Speicherplätze, die die Elemente des COMMON– Blocks belegen. Die Länge eines benannten COMMON–Blocks muß in allen Programmeinheiten gleich sein, der unbenannte COMMON–Block kann dagegen in verschiedenen Programmeinheiten verschiedene Längen haben. Das Auftreten einer Zeichengröße in einem COMMON–Block bedeutet, daß alle anderen Größen in diesem Block ebenfalls vom Typ Zeichen sein müssen. Elemente in benannten COMMON–Blöcken können mittels DATA–Anweisungen in Blockdaten–Unterprogrammen (BLOCKDATA, hier nicht weiter besprochen) mit Anfangswerten versehen (initialisiert) werden. Elemente im unbenannten COMMON–Block können nicht auf diese Weise initialisiert werden. Vor der Verwendung von COMMON–Blöcken sei aber gewarnt, denn sie erfordert äußerste Sorgfalt beim Programmieren wegen der möglichen Speicherplatzverschiebung bei unkorrekten Listen. Eine mögliche Lösung dieses Problems könnte dier Verwendung des INCLUDE–Befehls sein. 2 EQUIVALENCE sollte nicht verwendet werden und wird daher hier nicht weiter besprochen 60 KAPITEL 4. PROGRAMMIERSPRACHEN, FORTRAN Die INCLUDE–Anweisung erlaubt es, Quellcodeteile, die sich in einer seperaten (externen) Datei befinden, in den eigentlichen Quelltext einzufügen. INCLUDE "f ilename" Dabei wird die INCLUDE–Anweisung durch den kompletten Inhalt der angegebenen Datei f ilename ersetzt. Um sich dieses nun bei der Verwendung der COMMON–Blöcke zunutze zu machen, lagert man die Definition des COMMON–Blockes mitsamt den Deklarationen der darin enthaltenen Variablen in ein Datei beliebigen Namens aus und "included " sie in allen Unterprogrammen, in denen der COMMON–Block verwendet werden soll: PROGRAM Main INCLUDE "comm.inc" : CALL Subb (...) : END SUBROUTINE Subb (...) INCLUDE "comm.inc" : END comm.inc: INTEGER :: I, J REAL :: X(100) COMMON / COM / I, J, X 4.2.21 Kommunikation zwischen Programmeinheiten Der Informationsaustausch zwischen Programmeinheiten kann über gemeinsame Speicherbereiche (COMMON–Blöcke) und/oder über Parameterlisten stattfinden. COMMON–Blöcke können jedoch nicht zur Übertragung von Werten an interne und Formel-Funktionen benutzt werden; dies geht nur über Parameterlisten. Namen von Unterprogrammen können ebenfalls nur über Parameterlisten übergeben werden. Die Parameterübergabe über Parameterlisten ist bereits in Kapitel 4.2.20 Speicherplatzzuordnung und ” Einbettung von Programmzeilen“ beschrieben worden. Zur Übergabe von Feldern durch Angabe des Feldnamens ist noch zu bemerken, daß dabei lediglich die Speicheradresse des ersten Feldelements übergeben wird. Die Informationen für Zugriffe auf beliebige Feldelemente werden durch die Feldvereinbarung (Typ–, COMMON– oder DIMENSION–Anweisung) geliefert. Felder als Formalparameter in Unterprogrammen können mit variablen Dimensionen vereinbart werden, wenn die Elementanzahl einfach über die Parameterliste übergeben wird und man die Deklaration im Unterprogramm in Abhängigkeit dieser Größe durchführt: SUBROUTINE Subb (X, N) INTEGER :: N REAL :: X(N) : END Allgemein wird die Speicheradresse eines Feldelements anhand des Indexes dieses Elements relativ zum ersten Feldelement ermittelt, im allgemeinen ohne daß Feldgrenzenüberprüfung stattfindet. Dies 4.2. FORTRAN 61 wird gerne für trickreiche Programmierung verwendet. Außerdem birgt dies die schon in Abschnitt 4.2.3.3 Felder“ erwähnte Gefahr des Zugriffs auf falsche Speicherplätze bei der Übergabe mehrdimensionaler ” Felder, die nicht genau wie im rufenden Programm definiert sind. In 4.2.3.4 wurde schon beispielhaft die Speicherung eines Feldes A(3,4) erläutert. Die Reihenfolge im Speicher ist dann A(1,1); A(2,1); A(3,1); A(1,2); A(2,2); A(3,2)...A(2,4); A(3,4), und das Element A(2,2) befindet sich auf dem 5. Speicherplatz. Wird das Feld im gerufenen Unterprogramm mit A(4,3) dimensioniert, so ist die dort lokal wirksame Speicherplatzzuordnung A(1,1); A(2,1); A(3,1); A(4,1); A(1,2); A(2,2)...A(3,3); A(4,3), und für das Element A(2,2) wird jetzt auf den 6. Speicherplatz zugegriffen, der aber das Element A(3,2) des übergebenen Feldes enthält. Felder sollten daher stets genau wie im rufenden Programm dimensioniert werden. Neben Variablen können auch Namen von Unterprogrammen als Parameter über die Parameterliste übergeben werden. Dazu muß in der rufenden Programmeinheit der Name einer externen (benutzereigenen) Subroutine oder Funktion mit der EXTERNAL–Anweisung EXTERNAL f unnam und der Name einer internen (eingebauten) Funktion mit der INTRINSIC–Anweisung INTRINSIC f unnam deklariert werden, damit die Subroutine oder Funktion f unnam als Aktualparameter übergeben werden kann. Von den eingebauten Funktionen dürfen nicht als Aktualparameter verwendet werden: die Funktionen für Typkonvertierung und für lexikalische Vergleiche sowie die Minimum-/Maximumfunktionen. Von den übrigen dürfen nur die spezifischen, nicht die generischen Namen verwendet werden (z.B. ALOG statt LOG für natürlichen Logarithmus von reellen Zahlen). Beispiel: SUBROUTINE What REAL :: Wert1, Wert2, Wert3, Wert4 REAL :: Ergeb1, Ergeb2, Ergeb3, Ergeb4 INTRINSIC ALOG, SQRT EXTERNAL Quad, Halb : CALL Who (ALOG, Wert1, Ergeb1) CALL Who (Quad, Wert2, Ergeb2) CALL Who (SQRT, Wert3, Ergeb3) CALL Who (Halb, Wert4, Ergeb4) : END SUBROUTINE SUBROUTINE Who (Func, Argum, Result) REAL :: Argum, Result : Result = Func (Argum) : END SUBROUTINE REAL FUNCTION Quad (X) REAL :: X Quad = X*X 62 KAPITEL 4. PROGRAMMIERSPRACHEN, FORTRAN END FUNCTION REAL FUNCTION Halb (X) REAL :: X Halb = X / 2. END FUNCTION Die EXTERNAL– und INTRINSIC–Anweisungen können aber auch verwendet werden, wenn man eine benutzerdefienierte Funktion oder Unterroutine schreiben möchte, die den gleichen Namen besitzt, wie eine vom Compiler vordefinierte. In diesem Fall kann der Compiler nicht entscheiden, ob das eigene (EXTERNAL) oder das vorgegebene (INTRINSIC) Unterprogramm benutzt werden soll. Dieses muß in dem aufrufendem Modul explizit angegeben werden: SUBROUTINE vordefiniert INTRINSIC SQRT : zahl1 = SQRT(wert1) : END SUBROUTINE SUBROUTINE eigene EXTERNAL SQRT : zahl2 = SQRT(wert2) : END SUBROUTINE REAL FUNCTION sqrt(wert) : END FUNCTION ! Hier wird die Compilerfkt. verwendet!! ! Hier wird die eigene Fkt. verwendet!! ! Definition der eigenen Funktion ! zur Wurzel-Berechnung Die Ausführung einer END– oder RETURN–Anweisung bewirkt normalerweise, daß alle lokalen Datenelemente eines Unterprogramms undefiniert werden. Ausgenommen sind die Fälle, in denen Datenelemente • mit SAVE–Attribut deklariert werden, • im unbenannten COMMON–Block stehen, • (mittels DATA–Anweisung) initialisiert und nicht verändert worden sind, • in benannten COMMON–Blöcken stehen, welche sowohl im Unterprogramm als auch in einer direkt oder indirekt übergeordneten Programmeinheit auftreten. Insbesondere bleiben die Werte aller im Hauptprogramm aufgeführten COMMON–Blöcke definiert. 4.3 Weitere Sprachmittel In diesem Abschnitt sollen lediglich aus Gründen einer besseren Vollständigkeit noch einige Elemente des Fortran95–Sprachstandards erwähnt werden. Zum einen sollen dem Interessierten kurze Anregungen gegeben werden, zum anderen soll die Möglichkeit geschaffen werden, bereits geschriebene Programme besser zu verstehen. 4.3. WEITERE SPRACHMITTEL 63 • Spezifizierung der Genauigkeit: Fortran95 ermöglicht mit dem Attribut KIND den Wertebereich und die Genauigkeit von REAL- und INTEGER–Variablen ein, z.B. INTEGER, PARAMETER :: GENAU=SELECTED REAL KIND(10,100) REAL(KIND=GENAU), DIMENSION(30) :: X Hiermit ist X als Feld mit 30 Elementen, einer Genauigkeit von mindestens 10 Dezimalstellen und einem Exponentenbereich von mindestens 100 vereinbart. Zur Laufzeit des Programms hat man über Funktionen Zugriff auf Typcharakteristika. • Datentypen Fortran95 bietet neben der expliziten Genauigkeitsangabe bei numerischen Typen auch Ganzzahlkonstanten zu verschiedenen Basen (z.B. binär, oktal, hexadezimal), Verbunde (records), sowie die Möglichkeit, zusätzliche, sogenannte abgeleitete Datentypen zu definieren, z.B. TYPE FAHRER CHARACTER (LEN=20) :: VORNAME, FAMILIENNAME INTEGER :: SCHICHT, BUS END TYPE Die Struktur beschreibt einen Busfahrer durch seinen Vor- und Familiennamen, die Nummer der Schicht, der er angehört, und durch die Nummer des Busses, den er fährt. Die Betriebsabteilung bestehe aus 20 Fahrern, so daß eine weitere Vereinbarung folgen kann: TYPE (FAHRER), DIMENSION (20) :: FUHRBETRIEB Ein Busfahrer kann mithilfe des Prozentsymbols durch FUHRBETRIEB (I) % FAMILIENNAME beschrieben werden. Daneben können Operatoren und Funktionen auf diesen Datenobjekten definiert werden. Auf benutzerdefinierten Datentypen können sogar vordefinierte Operatoren umdefiniert (überladen) werden. • Die STOP–Anweisung STOP n (mit n kann eine Bildschirmausgabe bewirkt werden) bildet das logische Ende des Programms und bewirkt das Beenden der Ausführung des Programms. Beispiel: STOP ’Programmlauf OK’ Ein Programm kann mehrere STOP–Anweisungen enthalten. • Der Rücksprung aus einem Unterprogramm ins rufende Programm wird normalerweise durch die END–Anweisung veranlaßt. Als Alternative dazu kann der Rücksprung auch durch eine RETURN– Anweisung erfolgen: RETURN Die RETURN–Anweisung bildet damit analog zur STOP–Anweisung das logische Ende eines Unterprogramms. Zur Beendigung der Abarbeitung einer Subroutine gibt es neben dem einfachen Rücksprung auch die Möglichkeit des mehrfachen Rücksprungs (mehrere RETURN–Anweisungen) und des alternativen Rücksprungs. Wir gehen darauf ebensowenig ein wie auf die Möglichkeit der mehrfachen Eingangspunkte (ENTRY–Anweisung) und verweisen hier auf die FORTRAN–Literatur. 64 KAPITEL 4. PROGRAMMIERSPRACHEN, FORTRAN • Bei der impliziten Typvereinbarung wird der Typ einer Grösse durch ihren Anfangsbuchstaben festgelegt. Hierfür gibt es die IMPLICIT–Anweisung. so wird zum Beispiel mit IMPLICIT INTEGER (I-K,M,N), REAL (A-B,D-H,O-Z), LOGICAL(L) festgelegt, daß alle Variablen, deren Namen mit einem der Buchstaben I bis K, M oder N beginnt vom Typ INTEGER sind, solche mit Anfangsbuchstaben A, B, D bis H, oder O bis Z sind vom Typ REAL und Größen mit Anfangsbuchstaben L sind logische Größen. Von der impliziten Typvereinbarung wird jedoch abgeraten, alle Größen sollen explizit vereinbart werden. Auf die einzig sinnvolle Anwendung der impliziten Typvereinbarung ist bereits in Abschnitt 4.2.3 hingewiesen worden. 4.4 Tips zum (nicht nur FORTRAN–) Programmieren Hier sollen die in Abschnitt Programmierstil — wie schreibt man ein gutes Programm?“ gebrachten ” sprachunabhängigen Hinweise nochmals in Kurzfassung angegeben und um weitere, mehr FORTRAN– spezifische Tips ergänzt werden. Im anschließenden Abschnitt wird ergänzend ein kurzgefaßter FORT” RAN–Knigge“ gebracht. Ziel ist ein klar strukturiertes und gut lesbares Programm. Struktur erreicht man durch Gliederung und Zerlegung in nicht zu große Module mit definierten Schnittstellen. Die einzelnen Module sollen ebenfalls im formalen Aufbau und im Ablauf strukturiert sein. Dabei unterstützt uns die Verwendung von Konstrukten zur strukturierten Programmierung. Lesbarkeit und Selbst-Dokumentation erreicht man durch Gliederung in Programmkopf (Kommentarteil und Vereinbarungsteil) und Prozedurrumpf. Der Kommentarteil umfaßt die Zweckangabe und die Parameterbeschreibung und enthält weitere Hinweise sowie eine Modifikationsgeschichte. Der Vereinbarungsteil umfaßt Definitionen, Vereinbarungen und Initialisierungen. Für die Programmanweisungen im Prozedurrumpf sollen aussagekräftige Namen verwendet werden. Übersichtlichkeit erreicht man durch optische Trennung mit Leerzeichen und -Zeilen. Zusammengehörige Blöcke im Wiederholungsbereich von Schleifen und in IF–Blöcken werden eingerückt. Kleinere Anweisungsblöcke werden mit beschreibenden Kommentaren versehen, ebenso wie die Eingabeanforderungen und Ausgaben des Programms. Die folgenden Tips sind zum Teil ebenfalls schon an anderer Stelle im Kapitel 4.2 näher besprochen worden. Geschachtelte Schleifen: • Optimierende Compiler (z.B. auf Vektorrechnern) können (zumeist) nur die innerste Schleife optimieren/vektorisieren. Bei kurzen inneren Schleifen mit fester Durchlaufzahl überprüfe man, ob diese Schleife nicht abgerollt werden kann (Unrolling). Beispiel: DO i = 1, 64 DO j = 1, 4 A(i) = B(i,j)*C(i,j) ENDDO ENDDO läßt sich abrollen zu DO i = 1, 64 A(i) = B(i,1)*C(i,1) + B(i,2)*C(i,2) + B(i,3)*C(i,3) + B(i,4)*C(i,4) ENDDO 4.4. TIPS ZUM (NICHT NUR FORTRAN–) PROGRAMMIEREN 65 Die untere Codeversion bewirkt auch auf nicht-vektorisierenden Rechnern eine schnellere Ausführung: der Overhead für die innere Schleife entfällt, und der Rechner kann Zwischenresultate statt im Arbeitsspeicher in den schnelleren Registern halten. Überdies kann jetzt die größere Schleife vektorisiert werden. Vergleiche mit REAL-Größen sollten wegen der endlichen internen Darstellungsgenauigkeit nicht auf gleich“ durchgeführt werden, sondern stets auf einen Toleranzbereich (ein Epsilon ε); ” also nicht IF (A-B .EQ. 0.00) THEN sondern IF (ABS(A-B) .LT. 1.E-6) THEN Bei Iterationsdifferenzen (Unterschied zweier aufeinanderfolgender iterierter Werte) sollte eine Abfrage auf relative Genauigkeit statt auf absolute gemacht werden, da nur dann gewährleistet ist, daß die Rechnung auf eine bestimmte Anzahl von signifikanten Stellen genau ist. Also nicht absolut: IF (ABS(X(i) - X(i-1)) .LE. Eps) THEN sondern relativ: IF (ABS(X(i) - X(i-1)) .LE. Eps*ABS(X(i)) THEN oder äquivalent dazu und gleichzeitig effizienter: IF (ABS(X(i-1)/X(i)-1.) .LE. Eps) THEN Für den Fall Eps=0.01 ergibt sich dann für ∆X=X(i)-X(i-1) und jeweils absolute und relative Genauigkeitsabfrage bei X(i) = 100.2 X(i-1) = 100.1 : ∆X = 1.E-1, X(i) = 0.1002 X(i-1) = 0.1001 : ∆X = 1.E-4, X(i) = 0.009 X(i-1) = 0.001 : ∆X = 8.E-3, absolut nicht erfüllt relativ erfüllt absolut erfüllt relativ erfüllt absolut erfüllt relativ nicht erfüllt Obwohl in den ersten beiden Fällen beide Werte in drei signifikanten Stellen übereinstimmen, ist die absolute Genauigkeitsabfrage einmal erfüllt, einmal nicht. Im letzten Fall ist die absolute Abfrage erfüllt, obwohl beide Werte in keiner signifikanten Stelle übereinstimmen. Die Verwendung von Klammern (auch redundanten) verbessert die Lesbarkeit von Anweisungen erheblich und schließt Zweifel bei der Interpretation und Auswertungsreihenfolge von komplizierten Ausdrücken aus. Vorschubsteuerzeichen für den Drucker sollten zur Vermeidung unerwünschter Effekte nicht in anderen FORMAT–Spezifikationen versteckt werden, sondern explizit angegeben werden. Also nicht: 100 FORMAT (I5, F6.2) sondern 100 FORMAT (’ ’, I4, F6.2) Alle Größen im Programm sollten grundsätzlich initialisiert, d.h. mit Anfangswerten versehen werden. Man überlasse diese Arbeit nicht dem rechner- und installationsabhängigen Betriebssystem/Compiler. 66 KAPITEL 4. PROGRAMMIERSPRACHEN, FORTRAN Schon oft haben Programme mit vom Betriebssystem zu Null initialisierten Größen gerechnet und dabei vernünftig aussehende, jedoch falsche Ergebnisse produziert. Alle Größen im Programm sollten in expliziten Typ–Anweisungen definiert werden, d.h. man sollte auf Typzuweisung nach Konvention durch den Anfangsbuchstaben der Variablen und auf die DIMENSION– Anweisung (ohne eine damit verbundene IMPLICIT–Anweisung) verzichten. Parameterübergabe an Unterprogramme sollte im Hinblick auf klare Schnittstellen durch Parameterlisten erfolgen. Die Verwendung von COMMON–Blöcken läßt zwar effizientere Programme zu, birgt aber auch einige Risiken in sich (siehe 4.2.20 Speicherplatz-Zuordnung [...]“). ” Generell läßt sich der Widerspruch zwischen effizientem und stilvollem Programmieren nicht auflösen. Effizienz und Stil schließen sich meist gegenseitig aus. Aus Gründen des Selbstschutzes sollte man dem Motto Stil immer da, wo möglich — Effizienz dort, wo unbedingt nötig“ folgen. ” Zum Testen von Programmen, die im Entstehen oder in der Veränderung begriffen sind, noch einige Hinweise. Zunächst sollen die wichtigsten Fehlerarten erläutert werden, die während der Programmentwicklung auftreten können: Syntax-, Compilations-, Link-, logische und Laufzeitfehler. Die Schwere der Fehler wird oftmals noch unterschieden nach: Hinweis (Comment), Vorsicht (Caution), Warnung (Warning) und Fatal. Fatale Fehler müssen auf jeden Fall beseitigt werden, da sie so schwerwiegend sind, daß sie ein lauffähiges Programm verhindern. Warnungen deuten meist darauf hin, daß trotz formal korrektem Programm ein Fehler wahrscheinlich ist. Generell sollten alle Meldungen beachtet werden, auch wenn es sich dabei nur“ um Hinweise handelt. ” Syntaxfehler sind Verletzungen der FORTRAN–Syntax auf der Ebene einzelner Wörter (lexikalisch) und einzelner Anweisungen (syntaktische Struktur). Beispiele hierfür sind • falsch geschriebene Schlüsselwörter ( FUNKTION“ statt FUNCTION“) ” ” • unzulässige Variablennamen • unzulässige Reihenfolge von Anweisungen, z.B. eine Vereinbarungsanweisung nach der ersten ausführbaren Anweisung • nicht paarweise auftretende Klammern • REAL-Zahlen mit Komma statt Dezimalpunkt • Zeichensetzungsfehler, z.B. falsche Trennzeichen (Punkt oder Semikolon statt eines Komma), fehlendes Komma in einer Liste, Zeichenkette nicht durch Hochkomma abgeschlossen • Verwendung von Feldnamen mit mehr oder weniger Indices als Dimensionen vereinbart wurden • falsch geschachtelte IF–Blöcke oder DO–Schleifen • fehlendes ENDIF • mehrfach definierte Labels • mehrfach verwendetet Namen (z.B. Variable und Unterprogramm haben den selben Namen) • fehlendes END im Haupt- oder Unterprogramm; der dann folgende Vereinbarungsteil wird oft recht seltsam interpretiert • Unzulässige Mischung von numerischen und nicht–numerischen Operanden in Ausdrücken 4.4. TIPS ZUM (NICHT NUR FORTRAN–) PROGRAMMIEREN 67 Diese Fehler werden im ersten Übersetzungsdurchgang vom Compiler erkannt und als Syntaxfehler ausgeworfen. Von Programmen mit Syntaxfehlern werden keine Objektdateien erzeugt, es kann somit kein lauffähiges Programm erhalten werden. Compilationsfehler treten auf, wenn das Programm zwar syntaktisch richtig ist, aber dennoch kein lauffähiger Objektcode erzeugt werden kann. Meist werden compilerinterne Grenzen (heap, stack, table sizes) oder Systemgrenzen (segment size) überschritten. Neben diesen sind Beispiele für Compilationsfehler • Zu groß dimensionierte Felder • Auswertung von Konstantenausdrücken zur Übersetzungszeit führt auf Überschreiten des darstellbaren Wertebereichs (engl. overflow) Linkfehler treten in der letzten Phase der Erzeugung eines lauffähigen Programms auf. Neben den Fehlern aufgrund der Überschreitung linkerinterner Grenzen machen sich sich beispielsweise folgende Fehler bemerkbar: • Aufrufe von nicht vorhandenen Unterprogrammen; meist hervorgerufen durch Tippfehler beim Aufruf eines Unterprogramms oder durch Verwendung von Elementen nicht–dimensionierter Felder in Ausdrücken (der Compiler sucht dann eine Funktion gleichen Namens). • Programm ist zu groß (braucht zuviel Speicher); tritt häufig bei DOS–Programmen auf, selten unter UNIX. Ursache sind meist zu groß deklarierte Felder. Laufzeitfehler ergeben sich erst nach dem Start eines ausführbaren Programms, das fehlerfrei übersetzt und gelinkt werden konnte. Häufig vorkommende Fehler und Ursachen sind: • Zahlenbereichsüberschreitung in arithmetischen Ausdrücken durch Verwendung von betragsmäßig zu großen oder zu kleinen Zahlen (Overflow, Underflow); Ursache ist oft Division durch Null. • Zugriff auf Feldelemente, die außerhalb des deklarierten Indexbereichs liegen. • Verwendung ungültiger Operanden; z.B. bei Funktionsaufrufen (Logarithmus einer negativen Zahl) • Ein-/Ausgabe–Fehler, z.B. Dateiende erreicht; Fehlerhafte Eingabedaten (REAL-Zahl als INTEGER einlesen); Falsche Formatspezifikation (REAL als INTEGER ausgeben). • Inkorrekte Anzahl oder Typenkonflikt von Parametern beim Aufruf von Unterprogrammen. Logische Fehler entstehen bei ungenügend durchdachter Umsetzung einer Aufgabenstellung in einen Programmentwurf. Strikte Verwendung von Struktogrammen vor der Codierung sollte solche Fehler weitestgehend vermeiden helfen. • Falsche Anzahl von Schleifendurchläufen (oft einer zuviel oder zuwenig) • Endlosschleifen. Schleifenabbruchbedingungen werden nicht überprüft oder sind falsch formuliert. • Falsch formulierte Bedingungen in Verzweigungen. Ein Programm sollte nach jeder Änderung getestet werden; niemals mehrere Veränderungen gleichzeitig durchführen. Tests mit vorher festgelegten Testdaten durchführen und neben den eigentlichen Ergebnissen auch Zwischenergebnisse ausdrucken lassen; so lassen sich oft logische Fehler frühzeitig erkennen. Bei der Fehlersuche (Laufzeitfehler, logische Fehler) läßt man sich verdächtige“ Größen durch ” ins Programm gestreute WRITE– oder PRINT–Anweisungen ausdrucken. Verdächtige Programmbereiche 68 KAPITEL 4. PROGRAMMIERSPRACHEN, FORTRAN lassen sich so sukzessive eingrenzen. Für spätere Fehlersuchzwecke kann man diese Anweisungen statt sie zu entfernen einfach Kommentarisieren, d.h. ein ! davor setzen. Die Technik des Kommentarisierens empfiehlt sich auch bei Änderungen im Programmcode, wenn Anweisungsblöcke durch andere ersetzt werden. Dies kann allerdings ungewollte logische Fehler zur Folge haben: nicht alle unnötigen Anweisungen sind kommentarisiert oder notwendige Anweisungen sind aus Versehen noch kommentarisiert. Bei der Fehlersuche macht sich auch modularisierte Programmierung bezahlt. Compilations- und Laufzeitfehler lassen sich besser lokalisieren und damit schneller beseitigen. Korrekte Programmeinheiten brauchen nicht jedesmal neu übersetzt werden. Laufzeitfehler sind oft auch auf falsche Variablennamen aufgrund von Tippfehlern zurückzuführen. Solche lassen sich leicht lokalisieren, wenn man im Programm mit IMPLICIT NONE die implizite Typvereinbarung nach der Namenskonvention unwirksam macht. Damit müssen alle Variablen explizit deklariert werden, was ohnehin dringend angeraten wird. 4.5 FORTRAN–Knigge Dieser Abschnitt basiert noch auf den FORTRAN77 Coding Guidelines“ von David Levine. Diese sind ” in großen Teilen auch auf Fortran95 übertragbar und sollen hier einen Eindruck vermitteln, was einen guten Programmierstil ausmacht. 4.5.1 Vorbemerkung Viele der hier checklistenartig zusammengestellten Regeln sind bereits im Abschnitt 4.4 oder an anderer Stelle erwähnt worden. Die Regeln sollen dazu ermuntern, einen einheitlichen, programmierer- und projektübergreifenden Programmierstil zu pflegen. Viele tiefgreifende Entscheidungen werden in der Codierphase getroffen. Die meisten davon haben zwar keinen Einfluß auf den Maschinencode, sie beeinflussen aber das Erscheinungsbild des Programms. Konsistente Vorgehensweisen verbessern die Produktivität, die Transparenz und die Wiederverwendbarkeit. Projektbezogene Erfordernisse haben, sofern sie zutreffen, Vorrang. Die Ziele der Programmierregeln sind mit abnehmender Bedeutung: 1. Verständlichkeit — vermittelt dem Leser den Zweck der Berechnungen. 2. Portabilität — zwischen Compilern auf unterschiedlichen modernen Betriebssystemen. 3. Wartungsfähigkeit — wird sofort verbessert. 4. Effizienz — Ausführungsgeschwindigkeit. 4.5.2 Allgemeines 1. Der Standardzeichensatz von Fortran95 umfaßt + - : = _ [0--9] [A--Z] sowie [a-z] und das Leerzeichen. * / ( ) , . ’ ! " % & ; < > ? $ 4.5. FORTRAN–KNIGGE 4.5.3 69 Projektorganisation 1. Verwende einen einheitlich gestalteten Kommentarblock am Anfang jedes Unterprogramms (s. Beispiel in Abschnitt 4.2.2). 2. Fasse Unterprogramme, die zum selben Problembereich gehören, in einem Modul zusammen. Dieses Modul soll mit einem eindeutigen Namen versehen und in einer oder mehreren Dateien in einem eigenen Unterverzeichnis gespeichert werden. 3. Innerhalb eines Moduls sollen die Namen der Unterprogramme so gewählt werden, daß die ersten zwei Buchstaben mit dem Modulnamen zusammenhängen, die restlichen vier Buchstaben bezeichnen das Unterprogramm selbst. 4.5.4 Programme und Unterprogramme 1. Beginne jedes Haupt–Programm mit der PROGRAM–Anweisung 2. Wenn an ein Unterprogramm mehr als ein Parameter übergeben wird, ordne die Parameter in folgender Reihenfolge an: Eingangsparameter, Resultatparameter, Steuerparameter, externe Namen. 3. Eine FUNCTION darf keine Seiteneffekte haben. 4. Verwende in einem Unterprogramm nie mehrere RETURN–Anweisungen. Jedes Unterprogramm soll nur einen Eingang und einen Ausgang besitzen. Am besten benutze kein RETURN. 4.5.5 Format des Quelltextes 1. Trenne keine Bezeichner am Zeilenende. 2. Verwende nie mehr als eine Anweisung pro Zeile. 4.5.6 Labels 1. Verwende Labels nur wenn es nötig ist. 2. Verwende Labels in aufsteigender Folge. 3. Verwende für die FORMAT–Anweisungen, die am Ende eines Programms oder Unterprogramms zusammengefaßt sind, eine andere Label–Serie. 4. Schreibe Labels rechtsbündig. 4.5.7 Groß- und Kleinschreibung 1. Schreibe alle FORTRAN–Schlüsselworte mit großen Buchstaben. 2. Schreibe alle symbolischen Namen von Konstanten mit kleinen Buchstaben. 3. Schreibe alle anderen Bezeichner mit großem Anfangsbuchstaben und sonst mit kleinen Buchstaben (wenn es die Lesbarkeit erleichtert, verwende auch Großbuchstaben innerhalb des Namens). 4. Behalte innerhalb eines Programms eine einheitliche Schreibweise bei. 70 4.5.8 KAPITEL 4. PROGRAMMIERSPRACHEN, FORTRAN Verwendung von Leerzeichen 1. Verwende keine Tabulatoren (Tabs). 2. Schreibe FORTRAN–Schlüsselworte immer zusammen (ENDIF statt END IF), setze davor und dahinter ein Leerzeichen. 3. Schreibe kein Leerzeichen zwischen den Namen eines Feldes und seinen Index (z. B. Feld(I)). 4. Setze ein Leerzeichen zwischen den Namen eines Unterprogramms und seine Parameterliste (z. B. Uprog (Par1, Par2)). 5. Setze ein Leerzeichen hinter jede öffnende und vor jede schließende Klammer, Ausnahmen: Parameter- und Indexlisten. 6. Setze hinter jedes Komma ein Leerzeichen. 7. Verwende Leerzeichen in arithmetischen Ausdrücken, um die Operatoren hervorzuheben und den Vorrang der Operationen zu verdeutlichen. 8. Verwende Einrückungen, um die Programmstruktur zu verdeutlichen. Jede Einrückungsstufe sollte drei Leerzeichen tief sein. 9. Verwende Leerzeilen, um die Lesbarkeit zu verbessern. 4.5.9 Wahl von Bezeichnern 1. Beschränke die Länge von Namen auf 31 Zeichen, verwende die ersten 6 zu Unterscheidung. 2. Verwende sprechende“ Namen. ” 3. Erkläre die Bedeutung jeder Variable und jedes Feldes in einem Kommentar. 4. Kürze .TRUE. und .FALSE. nicht ab. 5. Schließe Zeichenketten in einfache Anführungszeichen (’) ein. 4.5.10 Datentypen 1. Deklariere alle Variablen. Verwende die Anweisung IMPLICIT NONE, um nicht deklarierte Variablen aufzuspüren. 2. Ordne die Bezeichner bei der Variablendeklaration in einer sinnvollen Reihenfolge an, die durch die Größen gegeben ist, die die Variablen repräsentieren. Bei Unterprogrammen können die Deklarationen auch in der Reihenfolge der Parameterliste erfolgen. Wenn es keine sich anbietende Ordnungsfolge gibt, ordne alphabetisch. 3. Verwende bei Funktionsaufrufen immer den generischen Namen (z. B. SQRT statt DSQRT). Ausnahme: Der Funktionswert wird als Argument an ein anderes Unterprogramm übergeben. 4. Schreibe Zahlen, die nicht ausdrücklich Integer sein sollen mit Dezimalpunkt (z.B. 7. statt 7). 4.5. FORTRAN–KNIGGE 4.5.11 71 Logische Operatoren 1. Verwende niemals .EQ. (==) oder .NE. (/ =), um Fließkommazahlen zu vergleichen. 2. Verwende .GE. (>=) oder .LE. (<=) anstelle von .EQ. (==), wenn überprüft werden soll, ob ein bestimmter Schwellenwert erreicht wurde. 3. Verwende nur die logischen Operatoren .AND., .OR., .EQV., .NEQV. und .NOT. und wende sie nur auf LOGICAL–Operanden an. 4. Vermeide .NE. (/ =) in einer IF–Konstruktion, die einen ELSE-Zweig besitzt. Verwende stattdessen .EQ. (==) und vertausche die beiden Zweige, so daß der ELSE-Zweig die logische Negation behandelt. 4.5.12 Arithmetische Ausdrücke 1. Setze vor und hinter einen Operator mit niedriger Priorität ein Leerzeichen. 2. Erstreckt sich ein arithmetischer Ausdruck über mehr als eine Zeile, so trenne ihn hinter einem Operator. 3. Rücke Fortsetzungszeilen ein. 4. Beachte die Typen der Operanden und ihre Auswirkung auf das Resultat einer Rechnung (z.B. 8 / 3 * 3.0 ergibt 6.0 und nicht 8.0). 5. Vorsicht, wenn einer INTEGER–Variablen ein vermeintlich exaktes Rechenergebnis einer Fließkommarechnung zugewiesen wird. Beispielsweise kann die Zuweisung von 30.0 / 0.1 an eine INTEGER– Variable den Wert 299 zum Ergebnis haben, da nicht unbedingt gerundet wird. 4.5.13 Felder 1. Lege die Dimension eines Feldes in der Variablendeklaration fest, vermeide eine extra DIMENSION– Anweisung. 2. Verwende beim Zugriff auf einzelne Feldelemente nur Indizes vom Typ INTEGER. 3. Versuche die Bearbeitung von mehrdimensionalen Feldern so durchzuführen, daß der erste Feldindex derjenige ist, der sich am schnellsten ändert, und der letzte der, der sich am langsamsten ändert. (s. Abschnitt 4.2.3.4) 4. Beim Zugriff auf Feldelemente müssen immer alle Indizes angegeben werden. 5. Überschreite nie die vorgegebenen Feldgrenzen. 4.5.14 Kontrollstrukturen 1. Springe nie in einen Schleifenkörper oder einen Zweig einer IF–Konstruktion. 2. Vermeide es, aus Schleifen herauszuspringen. Eine Schleife soll nur durch ihr eigenes Abbruchkriterium verlassen werden können. 3. Benutze die STOP–Anweisung nur, wenn besondere Umstände (z. B. Fehler) einen Programmabbruch erfordern und eine Fortsetzung unmöglich oder sinnlos ist. Gib mit der STOP–Anweisung eine Meldung aus, die den Grund des Abbruchs erklärt. 72 4.5.15 KAPITEL 4. PROGRAMMIERSPRACHEN, FORTRAN Parameter 1. Die Argumente eines Unterprogramms sollten beim Aufruf sowohl in der Anzahl als auch im Datentyp mit der Deklaration übereinstimmen. 2. Benutze keinen Parameter in einem Aufruf doppelt. 3. Wenn eine intrinsic function mehrere Argumente erfordert, müssen diese alle den selben Datentyp besitzten (z. B. bei MIN oder MAX). 4. Übergib nie eine Konstante als Parameter an ein Unterprogramm, es sei denn es ist sicher, daß es sich nur um einen Eingangsparameter handelt. 4.5.16 COMMON–Blöcke 1. Benutze COMMON–Blöcke nur wo es notwendig ist, und fasse dann auch nur die Variablen dort zusammen, für die es erforderlich ist. 2. Schreibe die Definition eines COMMON–Blocks in eine eigene Datei und lade diese beim Schreiben des Programms an den entsprechenden Stellen zum Quelltext hinzu (INCLUDE–Anweisung). So werden Fehler durch falsches Abtippen vermieden. 3. Beachte in einem COMMON–Block die Reihenfolge der Datentypen. Aus Geschwindigkeitsgründen kann es sinnvoll sein, die Variablen in einem COMMON–Block nach ihrem Datentyp zu sortieren, wobei folgende Reihenfolge eingehalten werden sollte: DOUBLEPRECISION, COMPLEX, REAL, INTEGER, LOGICAL, CHARACTER. 4. Übergib an ein Unterprogramm nie eine Variable als Parameter, die gleichzeitig in einem COMMON– Block verwendet wird. 5. Initialisiere COMMON–Blöcke nur mit der BLOCKDATA–Anweisung. 4.5.17 Ein- und Ausgabe 1. Verwende die Möglichkeiten zur Fehlerbehandlung mit END=, ERR= und IOSTAT=, und behandle alle möglichen Fälle sorgfältig. 2. Wenn eine formatierte Ein- oder Ausgabe erfolgt, schreibe die dazugehörige FORMAT–Anweisung in die darauffolgende Zeile. Ausgenommen hiervon sind FORMAT-Anweisungen, die von mehreren Ein/Ausgabe–Anweisungen verwendet werden, diese werden an das Ende der jeweiligen Programmeinheit gestellt. 3. Aus Geschwindigkeitsgründen sollten implizite Schleifen gegenüber expliziten DO–Schleifen bevorzugt werden. 4. Gib beim Öffnen von Dateien mit der OPEN–Anweisung, wenn nichts anderes gefordert ist, immer STATUS = ’UNKNOWN’ an. 4.5.18 Abschlußbemerkung Es sei noch bemerkt, daß ein Programmier–Knigge von Natur aus recht subjektiv ist, obwohl die meisten der Regeln weithin akzeptiert und für gut befunden sind. Mit den Regeln soll hauptsächlich die Aufmerksamkeit auf einige Kompliziertheiten bei der Programmierung gelenkt werden, mit dem Ziel, zur Konsistenz zu ermutigen. 4.6. ZUSAMMENFASSUNG 4.6 73 Zusammenfassung Dieses Kapitel informiert über Programmiersprachen allgemein und FORTRAN im Besonderen. Programmiersprachen gestatten die Codierung eines Programms so, daß der Rechner es ausführen kann. Maschinennahe Programmiersprachen erlauben die gezielte Ausnutzung von speziellen Rechnereigenschaften, sind aber hardwareabhängig. Höhere Programmiersprachen sind problemnahe Sprachen und meist rechnerunabhängig. Sie ermöglichen einfachere, schnellere und weniger fehleranfällige Programmierung, die Programme sind portabel. Die Übersetzung in Maschinencode geschieht mit einem Compiler. Für nahezu alle Zwecke gibt es abhängig von den jeweiligen Sprachelementen Sprachen, die mehr oder weniger spezialisiert sind. Von den prozeduralen Sprachen sind im Ingenieurbereich vor allem algorithmische Sprachen wie FORTRAN, C, Pascal oder BASIC verbreitet. Daneben gibt es nicht–prozedurale Sprachen für u.a. künstliche Intelligenz und Datenbanken. In diesem Kapitel wurde die älteste und am weitesten verbreitete Programmiersprache FORTRAN in der Standard–Version Fortran95 besprochen. FORTRAN eignet sich besonders für die numerische Behandlung naturwissenschaftlicher und technischer Probleme in herkömmlicher Formelsprache. FORTRAN verfügt neben den üblichen Datentypen wie INTEGER, REAL und LOGICAL auch über Zeichenketten und insbesondere komplexe Größen. Die Mehrheit der Sprachelemente dient der Arithmetik und der Ablaufsteuerung. Strukturiertes, modularisiertes Programmieren wird unterstützt, genauso wie die Verarbeitung von Zeichenketten und die Verwaltung von Dateien. Hierfür bietet FORTRAN eine große Zahl von vordefinierten Standardfunktionen. Es wurde gezeigt, wie die grundlegenden Struktogrammelemente mit Fortran umgesetzt werden können. 74 4.7 KAPITEL 4. PROGRAMMIERSPRACHEN, FORTRAN Fragen und Übungen zu Kapitel 4 F 4.1 Welche Vorteile hat die Verwendung einer höheren Programmiersprache wie z.B. Fortran gegenüber der Maschinensprache? Woraus bestimmt F 4.2 (Fortgeschritten!) (Fortran–)Programmes (Lademodul)? sich die Arbeitsspeicheranforderung eines F 4.3 Welche Möglichkeiten gibt es, Arbeitsspeicher zu sparen? F 4.4 Was ist eine Formelfunktion (statement function)? F 4.5 Wo stehen Vereinbarungsanweisungen in einer Programmeinheit? F 4.6 Welche der folgenden Zeichen gehören nicht zum Fortran Zeichensatz? u U ( Ä : ? ; F 4.7 Wieviel Elemente enthält ein mit INTEGER, DIMENSION (10, -15:5, 2:3) :: S dimensioniertes Feld? F 4.8 Wozu verwendet man die PARAMETER–Anweisung? F 4.9 Welchen Wert haben die Fortran–Ausdrücke 3**2+12/8 3**(2+12/8) (3**2+12)/8 F 4.10 Welchen Output erzeugt das folgende Programmstück? IMPLICIT LOGICAL (L) DATA LA, LB, LC, LD, LE & .FALSE., .FALSE., .TRUE., .FALSE., .FALSE./ LZ= LA .AND. .not.LB .OR. .not.LC .AND. LD .EQV. LE LY= .not.LA .AND. .not.LB .OR. LC .AND. .not.LD .NEQV. .not.LZ LX= .not.LA .AND. LB .OR. LC .AND. LD .NEQV. .not.LY LW= LA .or. LB .OR. LC .AND. .not.LD .EQV. LX Print*,la, lb, lc, ld, le, LZ, LY, LX, LW F 4.11 (Fortgeschritten!) Beim Zeichnen von Achsenkreuzen mit beschrifteten Achsen ergibt sich oft das Problem, daß der Text zentriert ausgegeben werden soll. Zur Lösung benötigt man ein Unterprogramm, das die Länge eines Zeichenstrings ermittelt. Dieses Unterprogramm ist zu schreiben. Das UP bekommt den String übergeben und soll die Anzahl der führenden Leerstellen und die Länge des Strings ermitteln. Unter Länge soll die Anzahl der Zeichen verstanden werden, die zwischen dem ersten und dem letzten vom Leerzeichen verschiedenen Zeichen liegen. Leerzeichen am Anfang und am Ende des Strings sollen bei der Längenermittlung ignoriert werden. Hinweis: Man suche den String zeichenweise nach Leerzeichen ab. Beispiel: 12345678901234567890 Test auf Laenge sollte eine Länge von 15 und 3 führende Leerzeichen ergeben. F 4.12 Was versteht man unter geschachtelten DO–Schleifen und welche Sprünge sind bei ihnen erlaubt? 4.7. FRAGEN UND ÜBUNGEN ZU KAPITEL 4 75 F 4.13 Wie können zwei Unterprogramme (SUBROUTINEn) Daten austauschen? F 4.14 Welchen Unterschied im Datenaustausch gibt es zwischen SUBROUTINE und FUNCTION ? F 4.15 Welche Möglichkeiten gibt es, das Lesen von einer Datei variabler Länge zu organisieren? F 4.16 (Fortgeschritten!) Wie setzt man einen Bildschirmpunkt in Fortran? F 4.17 Setze Frage F2.5 zuerst in ein Struktogramm um, erstelle danach ein FORTRAN–Programm für die Aufgabe. F 4.18 Welchen Output erzeugt folgendes Programm? PROGRAM test INTEGER, DIMENSION (2,2) :: k DO i=1,2 DO j=1,2 k(i,j)=i*2+j ENDDO ENDDO CALL sub1(k) END SUBROUTINE sub1(kfeld) INTEGER,DIMENSION(4) :: kfeld PRINT*,(kfeld(i),i=1,4) END SUBROUTINE F 4.19 Welchen Output erzeugt folgendes Programm auf dem Bildschirm ? print*,’12345678901234567890’ print 10,10,10,10 10 format(i4,i2) end F 4.20 Die Datei TEST.DAT enthalte folgende Daten: 10.3 -7.373 1.03E+01 Schreibe ein Programm, das diese Daten einliest und den Mittelwert ausgibt. 76 4.8 KAPITEL 4. PROGRAMMIERSPRACHEN, FORTRAN Antworten zu den Fragen und Übungen zu Kapitel 4 A 4.1 bessere Dokumentationsmöglichkeiten ; problemorientiert ; maschinenunabhängig, daher portabler Code ; leichtere Programmierung A 4.2 Aus der Größe des Objektcodes (Codesegment) plus der Größe des Datensegmentes. In das Codesegment gehen sowohl die übersetzten Anweisungen des selbstgeschriebenen Programms als auch der Code der Routinen aus der Laufzeitbibliothek (z.B. für Ein-/Ausgabe) ein. Vergrößerungen des Datenbereiches, z.B. durch größere Dimensionen von Feldern, gehen nicht notwendig linear in die Größe des Lademoduls ein. A 4.3 Mehrfachbenutzung von Variablen (-namen). Mehrfachnutzung von COMMON–Bereichen. Mehrfachnutzung von Speicherbereichen mit EQUIVALENCE. Datenauslagerung auf Massenspeicher (Platte) statt Halten im Arbeitsspeicher. Sinnvolle, problemangepasste Dimensionierung von Feldern. A 4.4 Eine statement function ist eine besondere Art einer Funktion. Sie besteht aus einer einzigen Anweisung und wird in einer Programmeinheit vor den ausführbaren Anweisungen vereinbart. Sie ist nur lokal gültig (wie die interne Funktion und im Gegensatz zur externen Funktion). A 4.5 Vor allen DATA–Anweisungen, Formelfunktionsdefinitionen und vor allen ausführbaren Anweisungen. A 4.6 Ä ? A 4.7 Es enthält 10 · 21 · 2 = 420 Elemente A 4.8 Zum Besetzen von Konstanten, wobei der Compiler überwacht,daß sie nicht verändert werden, sowie zum eleganten Dimensionieren von Feldern. A 4.9 10, 27, 2 A 4.10 F F F T F F T T F F A 4.11 Die Standardfunktion LEN alleine reicht nicht aus, da sie nur die in der CHARACTER–Anweisung definierte Länge des Strings ermittelt. Eine mögliche Lösung lautet: SUBROUTINE StrLen (String, NCLead, NCStr) !---- -------------------------------------!* Zweck: Subroutine zur Ermittlung der Anzahl fuehrender Leer ! zeichen und der Laenge einer Zeichenkette !* Vars: String --- zu untersuchende Zeichenkette (IN) ! NCLead --- Anzahl fuehrender Leerzeichen (OUT) ! NCStr --- Laenge der Zeichenkette ohne fuehrende und ! nachfolgende Leerzeichen (OUT) ! LenStr --- Laenge des uebergebenen Strings (LOC) ! i --- Laufvariable (LOC) !* Mods: Version 7.11.90/WWB f"ur InfoTech VL !* Version 7.03.02/TFS f"ur InfoTech VL !---- -------------------------------------!* Deklarationen und Initialisierungen CHARACTER :: String*(*) INTEGER :: LenStr, NCLead, NCStr NCLead = 0 NCStr = 0 !* Ausfuehrbarer Teil des Unterprogramms !---- Uebergegebene Laenge ermitteln LenStr = LEN (String) 4.8. ANTWORTEN ZU DEN FRAGEN UND ÜBUNGEN ZU KAPITEL 4 77 !---- Fuehrende Leerstellen ermitteln i = 1 DO WHILE ((i<=LenStr) .AND. (String (i:i) /= ’ ’)) NCLead = i i = i + 1 ENDDO !---- Echte Stringlaenge ermitteln durch Absuchen des Strings !---- nach Leerstellen vom Ende her NCStr = LenStr - NCLead i = LenStr DO WHILE (i <= NCLead +1 .AND. (String (i:i) .NE. ’ ’)) NCStr = i - NCLead - 1 i = i - 1 ENDDO !------------------------------------------END SUBROUTINE A 4.12 DO–Schleifen sind geschachtelt, wenn sich im Wiederholungsbereich einer Schleife eine weitere DO– Schleife befindet. Sprünge sind nur von der inneren in die äußere erlaubt. Einzige Ausnahme ist ein Sprung aus der äußeren an den Beginn der inneren Schleife. Jedoch sollten Sprünge vollkommen vermieden werden!! A 4.13 Direkt über Parameterliste Direkt und indirekt über COMMON–Blöcke Indirekt über externe Dateien A 4.14 Die FUNCTION gibt ihr Ergebnis über den Function–Namen zurück, die SUBROUTINE über die Parameterliste. A 4.15 - Man schreibt die Anzahl der records (Zeilen) an den Anfang der Datei. - Man vereinbart ein spezielles Dateiendezeichen. - Man benutzt den END–Parameter der READ–Anweisung. A 4.16 In Standard-FORTRAN gibt es diese Möglichkeit nicht. Falls es mit einem bestimmten Compiler möglich ist, handelt es sich um eine nichtstandardisierte Spracherweiterung. A 4.17 Das FORTRAN–Programm könnte so aussehen: i=0 j=0 k=0 DO IF (j > k) THEN k=k+2 ELSE k=k+1 j=j+k ENDIF IF (j > 10) EXIT ENDDO DO i = 1, 10 j=j+i ENDDO PRINT*, i, j, k END 78 KAPITEL 4. PROGRAMMIERSPRACHEN, FORTRAN A 4.18 3 5 4 6 A 4.19 12345678901234567890 1010 10 A 4.20 Das FORTRAN–Programm könnte so aussehen: OPEN (1,file=’TEST.DAT’) i=0 xm=0. DO READ (1,*, IOSTAT=ios) x IF (ios = 0) THEN i=i+1 xm=xm+x ENDIF IF (ios /= 0) EXIT ENDDO PRINT *,’ Der Mittelwert ist ’,xm/i END Der Mittelwert ist 4.409 Literaturverzeichnis [1] Byte. McGraw–Hill, New York, monatlich erscheinende Computerzeitschrift [2] c’t — Computerfachzeitschrift, Heise–Verlag, Hannover [3] Duden ’Informatik’. Dudenverlag Mannheim/Wien/Zürich, 1988 [4] Buchdruck auf dem PC, S. Demmig, Juli 1989 [5] Textverarbeitung von Rang — acht prominente Programme, A. Fourier, S. Pfeiffer, Mai 1990 [6] Informatik für Ingenieure, Prof. Dr. Dr. E. Hering, Dipl.–Phys. Dr. U. Dyllong, Dipl.–Ing. J. Gutekunst, VDI–Verlag GmbH, Düsseldorf 1995 [7] The TEXbook, D. E. Knuth, Addison–Wesley, 1986 [8] LATEX— Eine Einführung, H. Kopka, Addison–Wesley, 1994 [9] LATEX— A Document Preparation System, L. Lamport, Addison–Wesley, 1988 [10] Mikroprozessoren und Mikrorechner, W. Meiling und Ruprecht Fülle, Akademie–Verlag Berlin, 1988 [11] Fortran95 Sprachumfang, Regionales Rechenzentrun für Niedersachsen (RRZN), 2. Auflage, RRZN Hannover, 1997 (erhältlich im Sekretariat der ZRZ, TUB) [12] Beiträge zur Rechnerarithmetik RRZN-Bericht 38, 2. rev. Auflage, Regionales Rechenzentrum für Niedersachsen, 1988. [13] Computer Graphics: System and Concepts, R. Salmon und M. Slater, Addison–Wesley, Massachusetts, 1987 [14] Einführung in TEX, Schwarz, Addison–Wesley, 1988 [15] Computer Networks, A.S. Tanenbaum, Prentice–Hall, 1996 [16] VDI–Nachrichten,Wochenzeitung des VDI, VDI–Verlag, Düsseldorf [17] PostScript Language, Reference Manual (1986); Language Tutorial and Cook Book (1986); Language Design (1988); alle Bände: Addison–Wesley, Reading, Massachusetts. [18] http://www.mssi.com/s1672pic.htm [19] http://www.intel.com [20] http://www.intel.com/pressroom/archive/releases/dp010897.htm