Einführung in die Programmiersprache MUMPS [2. neubearb. Aufl. 1989. Reprint 2011] 9783110851861, 9783110115987


197 87 8MB

German Pages 274 [276] Year 1988

Report DMCA / Copyright

DOWNLOAD PDF FILE

Table of contents :
1 Grundlagen der Programmierung in MUMPS
1.1 Einführung
1.2 Benutzerbereiche und das Einloggen
1.3 Einfache syntaktische Regeln
1.4 Beispiele einfacher Sprachelemente
1.5 Zahlen und numerische Operationen
1.6 Zeichenvorrat und Zeichenketten
1.7 Datum und Zeit — $HOROLOG
1.8 Die Syntax einer Befehlszeile
2 Programme und Programmstrukturen
2.1 Programme in MUMPS
2.2 Lokaler und globaler Aufruf
2.3 Wertubergabe beim Unterprogrammaufruf
2.4 Benutzerdefinierte Funktionen und spezielle Variablen
2.5 Sichtbarkeit von lokalen Variablen — der NEW-Befehl
2.6 Programmverzweigung mit GOTO
2.7 Zeichenketten als Programmzeilen — der XECUTE-Befehl
2.8 Über das Editieren von Programmen
3 Steuerung des Programmflusses
3.1 Vergleichsoperatoren und ihre Anwendung
3.2 Logische Operatoren und die logische Interpretation
3.3 Bedingte Programmausführung — der IF-Befehl
3.4 IF, ELSE und $TEST
3.5 Bedingte Ausführung von Befehlen
3.6 Schleifen — der FOR-Befehl
3.7 Rekursive Programmierung
3.8 Programmblocke mit der Punktsyntax
3.9 Die Auswahlfunktion $SELECT
3.10 Die Indirektion
3.11 Fehlersuche mit BREAK
4 Kommunikation mit angeschlossenen Geräten
4.1 Bildschirmsteuerung im Rollmodus, $X und $Y
4.2 Zeichendarstellung
4.3 Prinzipien der Bildschirmsteuerung
4.4 Methodik der Bildschirmsteuerung
4.5 Die Benutzung externer Geräte
4.6 Der Sprachstandard und die Portabilität von Programmen
5 Datenhaltung und Datenmanagement
5.1 Indizierte Variablen
5.2 Datenhaltung in MUMPS — Globale Variablen
5.3 Sortierung und $ORDER
5.4 Existenz von Datensätzen — $DATA und $GET
5.5 Der implizite Bezug auf einen Global
5.6 Design von Datenstrukturen — Überlegungen und Beispiele
6 Analyse und Synthese von Zeichenketten
6.1 Allgemeines Modell und Anforderungen
6.2 Allgemeine Verarbeitung
6.3 Spezielle Feldverarbeitung
6.4 Datenprüfung mit Textoperatoren
6.5 Formatierung der Ausgabe
6.6 Ein einfacher Menütreiber mit $TEXT
7 Mehrbenutzersysteme
7.1 Begriffsklärung
7.2 Sperren im Mehrbenutzerbetrieb — der LOCK-Befehl
7.3 Hintergrundprozesse
A Übersicht über die Sprache
A.1 Darstellungsweise
A.2 Struktur eines Programms
A.3 Häufig benötigte nicht-terminale Symbole
A.4 Übersicht über Befehle
A.5 Übersicht über Funktionen
A.6 Ubersicht über Systemvariablen
A.7 Ubersicht über Operatoren
B Überblick über den Standard von 1989
C Verzeichnis der ASCII-Zeichen
Literaturverzeichnis
Sachverzeichnis
Recommend Papers

Einführung in die Programmiersprache MUMPS [2. neubearb. Aufl. 1989. Reprint 2011]
 9783110851861, 9783110115987

  • 0 0 0
  • Like this paper and download? You can publish your own PDF file online for free in a few minutes! Sign Up
File loading please wait...
Citation preview

de Gruyter Lehrbuch Hesse/Kirsten · Einführung in die Programmiersprache MUMPS

Stephan Hesse · Wolfgang Kirsten

Einführung in die Programmiersprache MUMPS 2., völlig neubearbeitete Auflage

W DE

Walter de Gruyter · Berlin · New York 1989

Dipl. inform. Stephan Hesse Mitarbeiter der Firma Telenet Kommunikationssysteme, Geschäftsstelle Rhein-Main, Darmstadt Dr. rer. med. Wolfgang Kirsten Mitarbeiter der Abteilung für Dokumentation und Datenverarbeitung am Zentrum der Medizinischen Informatik des Klinikums der Johann Wolfgang Goethe-Universität, Frankfurt

CIP-Titelaufnahme der Deutschen Bibliothek Hesse, Stephan: Einführung in die Programmiersprache MUMPS / Stephan Hesse ; Wolfgang Kirsten. - 2., neubearb. Aufl. - Berlin ; New York : de Gruyter, 1989 (De-Gruyter-Lehrbuch) ISBN 3-11-011598-0 NE: Kirsten, Wolfgang:

© Copyright 1988 by Walter de Gruyter & Co., 1000 Berlin 30. Alle Rechte, insbesondere das Recht der Vervielfältigung und Verbreitung sowie der Übersetzung, vorbehalten. Kein Teil des Werkes darf in irgendeiner Form (durch Photokopie, Mikrofilm oder ein anderes Verfahren) ohne schriftliche Genehmigung des Verlages reproduziert oder unter Verwendung elektronischer Systeme verarbeitet, vervielfältigt oder verbreitet werden. - Printed in Germany. - Druck: Bosch-Druck GmbH, 8300 Landshut/Ergolding. - Bindearbeiten: Dieter Mikolai, 1000 Berlin 10.

Vorwort MUMPS ist eine der wenigen von der amerikanischen Norxnierungsbehörde ANSI standardisierten Programmiersprachen. Die Abkürzung MUMPS steht für „Massachusetts General Hospital's Utility Multiprogramming System" und kennzeichnet so die Herkunft, jedoch in keiner Weise das ausschließliche Einsatzgebiet der Sprache. Das vorliegende Buch stellt die zweite Auflage der 1983 erschienenen einführenden Darstellung in MUMPS dar. Seit dieser Zeit ist in weiten Fachkreisen eine zunehmende Kenntnis dieser in mancher Hinsicht ungewöhnlichen Programmiersprache zu verzeichnen, die sich auch in einer Verbreitung sowohl im traditionellen medizinischen Segment als auch — und hier vor allem — in den kommerziellen Anwendungen niedergeschlagen hat. Die Entscheidung bedeutender europäischer und amerikanischer Hersteller, MUMPS neben den traditionellen Programmiersprachen auf ihren Betriebssystemen anzubieten, hat und wird die weitere Verbreitung fordern. Zu beobachten ist ferner eine zunehmende Öffnung von MUMPS zu anderen Standards wie GKS oder SQL. Sowohl die erste wie auch die zweite Auflage sind aus einfuhrenden Vorlesungen zu MUMPS am Klinikum der J.W.Goethe-Universität in Frankfurt hervorgegangen. Hörer dort sind Studenten der Medizin und der Informatik. Hieraus leitet sich auch die Zielgruppe dieses Lehrbuchs ab: informierte Laien, die auf MUMPS basierende Anwendungen beruflich oder privat nutzen, sowie Programmierer anderer Programmiersprachen, die MUMPS als weitere Programmiersprache kennenlernen wollen. Gegenüber der ersten Auflage wurde die zweite Auflage vollständig überarbeitet und erweitert. Insbesondere das in MUMPS zentrale Konzept der Dateibehandlung als integraler Bestandteil der Sprache wurde der Bedeutung gemäß beträchtlich erweitert und durch eine praxisbezogene Studie über das Design von globalen Variablen ergänzt. Die konzeptionelle Überarbeitung hat auch zu einer neuen Gliederung geführt. Grundsatz des Stoffinhalts der einzelnen Kapitel ist die vollständige Einführung aller Sprachelemente, sowie die Darstellung ihrer Verwendungsart am Beispiel typischer Programmsegmente. Diese Programme können als Teil einer Gesamtanwendung aufgefaßt werden, so daß der Leser in die Lage versetzt wird, die wesentlichen Teile einer dialogorientierten Anwendung zu verstehen und in MUMPS zu realisieren. Wie schon 1983, als die damalige Überarbeitung des Standards komplett berücksichtigt wurde, ist auch in der zweiten Auflage der zu erwartende neue Standard von 1989 vollständig eingearbeitet.

vi

Vorwort

Da diese Spracherweiterungen zum Teil gravierender Natur sind, ist dieses Buch auch erfahrenen Programmierern von Nutzen, die sich über diese Änderungen informieren mochten. Der Anhang enthält neben der Zusammenfassung der Sprachelemente eine Ubersicht über die Änderungen und Erweiterungen des neuen Standards und kann daher als Nachschlagewerk benutzt werden. Zusätzlich aufgenommen wurden die formalen Definitionen von Befehlen, Funktionen und speziellen Variablen in der Backus-Naur-Form. Interessierten Lesern kann von den Autoren eine preiswerte Implementierung von MUMPS für den Einbenutzerbetrieb vermittelt werden. Die Adresse findet sich im Anschluß an das Literaturverzeichnis. Bei der Neufassung des Lehrbuches haben uns viele Kollegen, Mitarbeiter und Freunde durch fortgesetzten Rat geholfen. Allen möchten wir unseren herzlichen Dank aussprechen und die Bitte äußern, uns auch weiterhin zu unterstützen. Frau Birgit Graf hat größere Teile des Manuskripts geschrieben und Frau Dr. Andrea Kilian hat die mühevolle Aufgabe der Korrektur des Textes übernommen. Auch Ihnen sei vielmals gedankt. Die Textverarbeitung dieses Buches wurde bei der Telenet GmbH, Geschäftsstelle Rhein-Main in Darmstadt besorgt. Für die Überlassung von Rechenleistung und die Unterstützung beim Ausdruck möchten wir uns bei Geschäftsstellenleiter Dr. Ian Nicholls ausdrücklich bedanken. Besonderer Dank gilt Herrn Dr. Günter Schuller vom Rechenzentrum der Universität Würzburg, der das Manuskript sorgfältig und kritisch gelesen hat. Seine Vorschläge insbesondere zur Darstellung des Konzepts der globalen Variablen wurden dankbar übernommen. Im Zusammenhang mit der Entstehungsgeschichte dieses Buches gilt ein besonderes Wort des Dankes Herrn Prof. Dr. Wolfgang Giere, Leiter der Abteilung für Dokumentation und Datenverarbeitung am Klinikum der J.W. Goethe-Universität in Frankfurt, der die Vorlesungsreihe über MUMPS bereits Ende der siebziger Jahre als festen Bestandteil des Lehrangebots im Bereich der Medizinischen Informatik etablierte. Wir danken ihm insbesondere auch für die aktive Unterstützung, für den immerwährenden Meinungsaustausch und die detaillierte Durchsicht des Manuskripts mit vielen wertvollen Hinweisen. Ferner danken wir dem Verlag de Gruyter für die fortgesetzte angenehme Zusammenarb eit. Frankfurt, im Oktober 1988

Stephan Hesse Wolfgang Kirsten

Inhalt

1

Grundlagen der Programmierung in M U M P S

1

1.1

1

Einführung 1.1.1

Programmiersprachen in der historischen Entwicklung — ein Überblick

1

1.1.2

Die Entwicklung von MUMPS

3

1.1.3

Ein Kaleidoskop der Sprache — MUMPS im Uberblick

4

1.2

Benutzerbereiche und das Einloggen

6

1.3

Einfache syntaktische Regeln

8

1.4

Beispiele einfacher Sprachelemente

10

1.4.1

Der WRITE-Befehl

10

1.4.2

Variablen in MUMPS und der SET-Befehl

12

1.4.3

Löschen von Variablen mit dem KILL-Befehl

15

1.4.4

Wertzuweisung im Dialog mit dem READ-Befehl . . .

17

1.4.5

SLENGTH als Beispiel einer einfachen Funktion

20

...

1.5

Zahlen und numerische Operationen

21

1.6

Zeichenvorrat und Zeichenketten

26

1.6.1

Zeichensatz und Stringliterale

26

1.6.2

Der Datentyp in MUMPS und die numerische Interpretation

2

28

1.7

Datum und Zeit — SHOROLOG

31

1.8

Die Syntax einer Befehlszeile

33

P r o g r a m m e und Programmstrukturen

35

2.1

Programme in MUMPS

35

2.1.1

Aufbau eines Programms

35

2.1.2

Aufruf eines Programms — der DO-Befehl

37

2.2

Lokaler und globaler Aufruf

39

2.3

Wertübergabe beim Unterprogrammaufruf

43

2.4

Benutzerdefinierte Funktionen und spezielle Variablen . . . .

46

2.5

Sichtbarkeit von lokalen Variablen — der NEW-Befehl . . . .

49

2.6

Programmverzweigung mit GOTO

52

viii

3

Inhalt

2.7

Zeichenketten als Programmzeilen — der XECUTE-Befehl. .

54

2.8

Uber das Editieren von Programmen

56

Steuerung des Programmflusses

59

3.1

Vergleichsoperatoren und ihre Anwendung

59

3.2

Logische Operatoren und die logische Interpretation

62

3.3

Bedingte Programmausführung — der IF-Befehl

65

3.4

IF, ELSE und $TEST

68

3.5

Bedingte Ausführung von Befehlen

72

3.5.1

Nachbedingung auf Befehle

72

3.5.2

Die Nachbedingung auf Argumente eines Befehls . . .

73

3.6

Schleifen — der FOR-Befehl

76

3.7

Rekursive Programmierung

81

3.8

Programmblocke mit der Punktsyntax

83

3.9

Die Auswahlfunktion $SELECT

86

3.10 Die Indirektion

4

88

3.10.1 Die Namensindirektion

88

3.10.2 Die Argumentindirektion

90

3.11 Fehlersuche mit BREAK

92

Kommunikation mit angeschlossenen Geräten

97

4.1

Bildschirmsteuerung im Rollmodus, $X und $Y

97

4.2

Zeichendarstellung

100

4.2.1

Der ASCII-Zeichensatz und Erweiterungen

100

4.2.2

Umwandlung eines Codes in ein Zeichen — SCHAR

4.2.3

Umwandlung eines Zeichens in seinen Code — $ASCII 102

4.2.4

Die Sternsyntax

. 101 103

4.3

Prinzipien der Bildschirmsteuerung

106

4.4

Methodik der Bildschirmsteuerung

109

4.4.1

Bildschinnsteuerung in der Praxis

109

4.4.2

Prinzipien tabellengesteuerter Masken

111

4.5

Die Benutzung externer Geräte

115

4.5.1

Prinzipien der Kommunikation mit externen Geräten . 115

4.5.2

Die Reservierung von Geräten mit OPEN

4.5.3

Auswahl eines Geräts und Beenden der Kommunikation — USE, $10 und CLOSE

116 118

ix

Inhalt

4.6 5

Datenhaltung und Datenmanagement

123

5.1

Indizierte Variablen

123

5.1.1

Zahlen als Indizes

123

5.1.2

Löschen und Wertübergabe von (Teil-) Bäumen . . . .

126

5.1.3

Zeichenketten als Indizes

126

5.2

5.3

Datenhaltung in MUMPS — Globale Variablen

130

5.2.1

Grundprinzipien globaler Variablen

130

5.2.2

Lesen, Schreiben und Löschen von globalen Variablen

132

5.2.3

Dienstprogramme für Globais

134

Sortierung und $ORDER

135

5.3.1

Die Sortierreihenfolge

135

5.3.2

Das Navigieren in Datenstrukturen — SORDER

5.3.3

Analyse der Baumstruktur — $QUERY

140

5.3.4

Indirektion auf Indizes

142

...

137

5.4

Existenz von Datensätzen — $DATA und $GET

145

5.5

Der implizite Bezug auf einen Global

150

5.5.1

Die Naked Reference

150

5.5.2

Die Tücken des SET-Befehls

152

5.6

6

Der Sprachstandard und die Portabilität von Programmen . . 120

Design von Datenstrukturen — Überlegungen und Beispiele . 154 5.6.1

Einführung und Beschreibung des Modells

154

5.6.2

Abbildung in Globalstrukturen

156

5.6.3

Aspekte eindeutiger Personenidentifizierung

159

5.6.4

Die Einbeziehung leistungsbezogener Daten

162

Analyse und Synthese von Zeichenketten

167

6.1

Allgemeines Modell und Anforderungen

167

6.2

Allgemeine Verarbeitung

169

6.3

6.2.1

Länge und Extraktion — $LENGTH und $ E X T R A C T 169

6.2.2

Suchen von Teilzeichenketten — $FIND

170

6.2.3

Ersetzen von Zeichen — STRANSLATE

172

Spezielle Feld Verarbeitung 6.3.1 6.3.2

175

Anzahl und Extraktion von Feldern — SLENGTH und SPIECE

175

Zuweisung von Feldern — S E T SPIECE

179

x

Inhalt

6.4

6.5

6.6 7

Datenprüfung mit Textoperatoren 6.4.1 Fehlerarten bei der Eingabe 6.4.2 Der Mustervergleich 6.4.3 Elemente eines Datumprüfprogramms 6.4.4 Der Folgt- und Enthält-Operator Formatierung der Ausgabe 6.5.1 Rechtsbündige Ausgabe — $JUSTIFY 6.5.2 Spezielle Formatierung mit $FNUMBER Ein einfacher Menütreiber mit $TEXT

Mehrbenutzersysteme 7.1 Begriffsklärung 7.2 Sperren im Mehrbenutzerbetrieb — der LOCK-Befehl . . . . 7.2.1 Einführung in die Problematik 7.2.2 Der LOCK-Befehl 7.2.3 Zusätzliche Sperren 7.3 Hintergrundprozesse 7.3.1 Der JOB-Befehl 7.3.2 Wertübergabe an Hintergrundprozesse 7.3.3 Verzögerung der Ausführung mit HANG

Α Übersicht über die Sprache A.l Darstellungsweise A.2 Struktur eines Programms A.3 Häufig benötigte nicht-terminale Symbole A.3.1 Namen A.3.2 Ausdrücke A.3.3 Zeilenreferenzen und Programmaufruf A.3.4 Ein / Ausgabe A.4 Übersicht über Befehle A.4.1 BREAK A.4.2 CLOSE A.4.3 DO A.4.4 ELSE A.4.5 FOR A.4.6 GOTO

183 183 184 188 190 193 193 195 197 201 201 203 203 204 206 208 208 209 210 213 213 214 215 215 216 217 218 219 219 219 220 221 221 222

Inhalt

xi

A.4.7 HALT A.4.8 HANG A.4.9 IF A.4.10 JOB A.4.11 KILL A.4.12 LOCK A.4.13 NEW A.4.14 OPEN A.4.15 QUIT A.4.16 READ A.4.17 SET A.4.18 USE A.4.19 VIEW A.4.20 WRITE A.4.21 XECUTE A.4.22 Z-Befehle A.5 Ubersicht über Funktionen A.5.1 SASCII A.5.2 SCHAR

222 223 223 224 224 225 226 226 227 227 228 228 229 229 229 230 230 230 230

A.5.3 A.5.4 A.5.5 A.5.6 A.5.7 A.5.8 A.5.9 A.5.10 A.5.11 A.5.12 A.5.13 A.5.14

SDATA SEXTRACT SFIND SFNUMBER $GET SJUSTIFY SLENGTH SNEXT SORDER SPIECE SQUERY SRANDOM

231 231 232 232 233 233 234 234 234 235 235 236

A.5.15 A.5.16 A.5.17 A.5.18

SSELECT STEXT $TRANSLATE $VIEW

236 237 237 238

xii

Inhalt

A.5.19 $ Ζ-Funktionen Α.6 Übersicht über Systemvariablen A.6.1 SHOROLOG A.6.2 $10 A.6.3 $JOB A.6.4 $STORAGE A.6.5 $TEST A.6.6 $X A.6.7 $Y A.6.8 $Z-Variablen A.7 Ubersicht über Operatoren A.7.1 Allgemeines A.7.2 Ubersicht über die einzelnen Operatoren

238 238 238 239 239 239 239 240 240 240 241 241 242

Β Überblick über den Standard von 1989

247

C Verzeichnis der ASCII-Zeichen

251

Literaturverzeichnis

253

Sachverzeichnis

256

Kapitel 1 Grundlagen der Programmierung in MUMPS

1.1

Einführung

1.1.1

Programmiersprachen in der historischen Entwicklung — ein Überblick

Mit dem Aufkommen der programmgesteuerten Rechner, deren erste experimentelle Ausführungen Ende der dreißiger, Anfang der vierziger Jahre zur Verfügung standen, war den Menschen ein Mittel an die Hand gegeben, komplizierte, numerische Berechnungen schneller als mit den bisher eingesetzten manuell zu bedienenden Rechenmaschinen durchzuführen. Es bereitete jedoch unendliche Mühe, zur Losung eines mathematischen Problems den Rechner zu instruieren. Hierfür mußten lange binäre oder oktale Zahlenreihen eingegeben werden. Die Anweisungen, zwei Variablen a und b zu multiplizieren und das Ergebnis einer dritten Variablen ζ zuzuweisen, hätte etwa wie folgt aussehen können: 021 057 314 062 216 105 142 273 041

Bereits wenig später wurden jedoch die Grundlagen von Programmiersprachen im heutigen Sinne geschaffen, in dem man — Eselsbrücken gleich — Symbole für häufig gebrauchte Operationen einführte und anstatt numerischer Adressen im Rechner symbolische Namen benutzte. Damit wäre das Beispiel etwa wie folgt dargestellt worden: LOAD A

MPY Β STORE Ζ

Die in dieser Form geschriebenen Programme wurden mittels eines eigenen Programms, das man Assembler nannte, wieder in einen maschinenlesbaren Code übersetzt. Diese Form der Notation nannte man daher eine Assemblersprache.

2

Kapitel 1. Grundlagen der Programmierung in MUMPS

Obwohl diese Art der „Programmierung" eines Rechners eine deutlich höhere Produktivität ermöglichte, war es immer noch sehr schwierig, Losungsverfahren für komplizierte Probleme in einer auch für die Maschine verständlichen Form darzustellen. Ein weiterer Entwicklungssprung fand Mitte der fünfziger Jahre statt, als eine Gruppe von Computerspezialisten unter der Leitung von John Backus ein Projekt zur automatischen Übersetzung von mathematischen Formeln in maschinenlesbare Instruktionen begann. Das Resultat dieser Bemühungen war FORTRAN (FORmular TRANslator), die erste höhere Programmiersprache in der Geschichte der EDV. In FORTRAN wurde das oben angeführte Beispiel als Z=A*B dargestellt. Man mußte dabei nicht mehr auf maschineninterne Register und Speicherzellen Bezug nehmen, sondern konnte beliebige Variablen mit symbolischen Namen ansprechen. FORTRAN ist das bekannteste Beispiel von höheren Programmiersprachen, die Mitte der fünfziger Jahre entwickelt wurden. Gemeinsam ist ihnen allen, daß man sie zur Lösung von numerischen Problemen benutzte und daß sie in erster Linie im Hinblick auf leichte Ubersetzbarkeit und nicht auf leichte Lesbarkeit entworfen wurden. Dies änderte sich in den darauffolgenden Jahren, als mit der Entwicklung einer neuen Programmiersprache begonnen wurde, die das ursprüngliche Einsatzfeld der Datenverarbeitung beträchtlich erweiterte. Der. Name der Programmiersprache ist COBOL (COmmon Business Oriented Language) und sie zielte auf kaufmännische Anwendungen, also die Verarbeitung von großen Mengen von Daten, wobei im allgemeinen nur einfache arithmetische Operationen benutzt werden. Hierunter fällt beispielsweise die Berechnung der monatlichen Lohn- und Gehaltszahlungen an die Mitarbeiter eines Unternehmens. In COBOL werden in hohem Maße Worte der englischen Sprache direkt benutzt, so daß das Beispiel folgende Form annimmt: MULTIPLY A BY Β GIVING Ζ. Ein anderer Aspekt, der zunehmend ins Bewußtsein rückte, war der, daß man höheren Wert auf die Unabhängigkeit von Sprachen von einem bestimmten Rechnerhersteller legte. Dies führte später zur Standardisierung von FORTRAN und COBOL durch unabhängige öffentliche Institutionen, wie etwa das American National Standard Institute (ANSI).

1.1.

Einführung

1.1.2

3

Die Entwicklung von MUMPS

Zu Beginn der sechziger Jahre setzte ein Prozeß ein, der die Struktur und Anwendungsvielfalt der damaligen Datenverarbeitung gründlich veränderte. Während bis dahin Datenverarbeitung fast ausschließlich auf großen Anlagen in noch größeren Rechenzentren im sogenannten Batchbetrieb ausgeführt wurde, konnten nun mehrere Benutzer gleichzeitig interaktiv am Rechner arbeiten. Dieses Prinzip der Benutzung einer Datenverarbeitungsanlage ist unter der Bezeichnung Timesharing-Betrieb bekannt geworden. Der Zugang zur Rechenleistung einer DV-Anlage war dadurch beträchtlich vereinfacht, sowohl für den Programmierer der unmittelbarer und direkter Programme schreiben konnte, als auch für den Nutzer einer Anwendung, der über eine schreibmaschinenähnliche Tastatur Daten von seinem Arbeitsplatz aus eingeben konnte. Im Jahre 1967 begann eine Entwicklungsgruppe am Laboratory of Computer Science am Massachusetts General Hospital unter der Leitung von G. Octo Barnett und Neil Pappalardo für eine bis dahin gänzlich neue Benutzergruppe — medizinisch orientierte Anwender — Anforderungen an eine neue Programmiersprache zu definieren. Dies geschah in Anlehnung an frühere Arbeiten der Rand Corporation, die ein für den Timesharing-Betrieb geeignetes Betriebssystem entwickelt hatte. Hauptziel war dabei weniger die mathematisch orientierte Anwendung, sondern die im Bereich der medizinischen Dokumentation notwendige Verarbeitung von textueller Information. Alle bis dahin entwickelten Sprachen taten sich außergewöhnlich schwer bei der Verarbeitung von variabel langen Zeichenketten. Als weiteres Entwicklungsziel war eine interaktive Sprache vorgegeben, die in einem Mehrbenutzerbetrieb operiert und auf den damals verfugbaren Kleinrechnern arbeiten sollte. Die dauerhafte Abspeicherung von Daten und die Wiedergewinnung von Information hieraus sollte in der Sprache integriert sein. Der Name der daraus hervorgegangenen Programmiersprache ist MUMPS 1 . Dieses Kunstwort ist die etwas unglückliche Abkürzung von „Massachusetts General Hospital's Utility Multiprogramming System". Unglücklich deshalb, weil der Name eine hauptsächlich medizinische Nutzung suggeriert und vielleicht auch deshalb, weil der Name auf eine Sprache schließen läßt, die mehr experimentellen Charakter hat, aber für „ernsthafte" Anwendungen nicht bestimmt ist. Beide Folgerungen sind jedoch in keiner Weise begründet. Tatsächlich hat MUMPS weiterhin im medizinischen Bereich eine große Verbreitung. Sehr erfolgreich operierende Krankenhausinformationssysteme sind in MUMPS geschrieben, große öffentliche Krankenhausträger in den 1

MUMPS ist eingetragener Handelsname des Massachusetts General Hospital

4

Kapitel 1. Grundlagen der Programmierung in MUMPS

USA, wie die Veterans Administration, benutzen ausschließlich auf MUMPS basierende Systeme. Der größte Einzelauftrag in der Geschichte von MUMPS wurde für die weltweit 750 Militärkrankenhäuser des amerikanischen Verteidigungsministeriums mit einem Auftragswert von über einer Milliarde Dollar Anfang 1988 vergeben. Es wird jedoch angenommen, daß weltweit deutlich mehr als die Hälfte der MUMPS Anwendungen im kommerziellen Bereich anzutreffen sind. Darunter fallen Systeme aus der Lagerwirtschaft, Reservierungs- und Buchungssysteme, Schiffahrtssysteme, große Bankenanwendungen, Systeme zur Produktionssteuerung, mehrsprachige Textdokumentationssteme und viele andere Anwendungen mit zum Teil mehr als 1000 Bildschirmen. Ein wichtiger Aspekt der Sprache ist die sehr weitgehende Standardisierung von MUMPS. Bereits 1977 wurde MUMPS als dritte Sprache nach FORTRAN und COBOL nach den Regeln des amerikanischen Standardisierungsbüros (abgekürzt ANSI, dem deutschen DIN-Institut vergleichbar) standardisiert und wird daher auch als ANS MUMPS bezeichnet. Da die Datenhaltung integraler Bestandteil der Sprache ist, gelingt es, vollkommen portable — also zwischen Rechnern verschiedener Hersteller übertragbare — Anwendungen zu schreiben. Die Prozeduren der Standardisierung und der Weiterentwicklung der Sprache sind im Anhang beschrieben. Standardrevisionen erfolgten 1984 und 1989. Der vorliegende Text entspricht dem Standard von 1989 (ANSI X.ll-1989). Einen vollständigen Überblick über die Erweiterungen des neuen Standards gegenüber dem von 1984 findet der Leser im Anhang.

1.1.3

Ein Kaleidoskop der Sprache — MUMPS im ·· Uberblick

Grundsätzlich unterscheidet sich MUMPS nicht von anderen prozeduralen Sprachen, wie etwa FORTRAN, COBOL oder C, wenngleich in der Anlage und im Detail beträchtliche Unterschiede sichtbar sind. Eine der auffälligsten Eigenheiten ist der in MUMPS verwendete Datentyp der variabel langen Zeichenkette. Die meisten anderen bekannten Sprachen benutzen mehrere Datentypen (Integer, String, Real, . . . ) , die entweder explizit durch spezielle Deklarationsanweisungen oder implizit durch die Form des Variablennamens einer Variablen zugeordnet sind. In MUMPS dagegen werden alle Informationen als Zeichenketten abgelegt. Umgekehrt kann jede Zeichenkette in MUMPS auch als Zahl oder logischer Wert interpretiert werden. Eine Deklaration ist dalier überflüssig. In konsequenter Fortentwicklung der Idee vom Wegfall aller Deklarationen ist es in MUMPS auch nicht notwendig, Felder zu deklarieren. Jede Variable kann ein Feld beliebiger Anzahl von Dimensionen und nur durch

1.1.

Einführung

5

den verfügbaren Speicher begrenzter Große daxstellen. Es belegen nur diejenigen Variablen Speicherplatz, die tatsächlich im Programm definiert wurden. Man erreicht durch diese Form der Speicherung eine optimale Ausnutzung des Speichers. Gewissermaßen in Fortführung dieses Konzeptes können in MUMPS beliebige Zeichenketten zur Indizierung von Feldern verwendet werden. Das Prinzip der beliebig tief und mit textlicher Information indizierten Variablen wird in MUMPS auf permanente Datenstrukturen ausgeweitet. Auf diese „globale Variablen" genannten baumartigen Strukturen kann systemweit zugegriffen werden. Im Gegensatz dazu stehen die „lokalen Variablen", deren Lebensdauer durch die Prozeßdauer begrenzt ist. Der Zugriff auf Variablen — gleich ob lokal oder global — wird durch mächtige Funktionen unterstützt, die MUMPS den Funktionsumfang eines Datenbanksystems geben. Die Koordinierung der Zugriffe auf globale Variablen in Mehrplatzsystemen geschieht über den LOCK-Befehl. Neben den schon erwähnten Funktionen zum Datenmanagement stehen weitere Funktionen insbesondere aus dem Bereich der Textmanipulation zur Verfügung. Außerdem kann der Programmierer eigene Funktionen definieren und wie Systemfunktionen verwenden. Weiter existieren arithmetische, logische, stringbezogene und Vergleichsoperatoren, deren Argumente automatisch von MUMPS dem Kontext entsprechend numerisch, logisch oder als Zeichenketten interpretiert werden. Der Programmfluß wird über die auch in anderen Programmiersprachen üblichen Methoden wie Unterprogrammaufruf, Verzweigung oder bedingte Ausführung gesteuert. Obwohl die Sprache grundsätzlich eine gemeinsame Menge von Variablen für Haupt- und alle Unterprogramme vorsieht, gibt es Sprachelemente wie die explizite Wertübergabe beim Unterprogrammaufruf und den NEW-Befehl, die den Sichtbarkeitsbereich von Variablen ausdrücklich begrenzen. Neben dem IF-Befehl zur bedingten Programmausführung existiert noch die „Nachbedingung", die es erlaubt, praktisch jeden einzelnen Befehl oder in einigen Fällen auch Argumente von Befehlen ohne Verzweigung des Programmflusses bedingt auszuführen. Ahnlich wie die in der Sprache integrierte Datenhaltung sind auch die Ein- und Ausgabeoperationen in der Sprachdefinition festgelegt. Durch dieses hohe Maß an Standardisierung ist es leicht möglich, vollkommen übertragbare Programme mit Bildschirmdialogen zu schreiben. Schließlich sei noch auf die betriebssystemnahen Sprachelemente und Möglichkeiten verwiesen. Als Beispiel soll der JOB-Befehl genannt werden, der den Start von Hintergrundprozessen erlaubt.

6

1.2

Kapitel 1. Grundlagen der Programmierung in MUMPS

Benutzerbereiche und das Einloggen

Um nun mit einem MUMPS-System arbeiten und die ersten Versuche mit der Sprache unternehmen zu können, ist es erforderlich, Zugriff zu dem System zu erlangen. Gerade hier unterscheiden sich die verschiedenen Systeme sehr stark. Unter anderem hängt die Vorgehensweise davon ab, ob man an einem Einbenutzer- oder an einem Mehrbenutzersystem arbeitet. Während typische MUMPS-Systeme Mehrbenutzersysteme sind, bei denen also mehrere Benutzer gleichzeitig arbeiten können, ist es zum Erlernen der Sprache völlig ausreichend, über ein Einbenutzersystem zu verfügen. Solche Systeme, die häufig sogar den parallelen Betrieb mehrerer Prozesse erlauben, werden meistens auf Basis eines PCs unter PC/MS-DOS angeboten. Ein preiswertes Einbenutzersystem kann von den Autoren vermittelt werden. Zusammen mit der Diskette eines Einbenutzersystems erhält man gewöhnlich eine Anleitung, die alle Informationen enthält, wie man MUMPS auf einen Personal Computer installiert. Hier soll nicht auf Einzelheiten eingegangen werden. Es sei nur noch darauf hinweisen, daß man nach erfolgreicher Installation das System im allgemeinen durch Eingabe des Wortes „MUMPS" startet. Bei Mehrbenutzersystemen muß man sich im allgemeinen zuerst durch Eingabe einer Benutzerkennung identifizieren (englisch: „User Code Identification", UCI). Oft verlangt das System aus Sicherheitsgründen noch die Eingabe einer zweiten Kennung, des „Programmer's Access Code" (abgekürzt: PAC). Nach erfolgreich abgeschlossener Berechtigungsprüfung erlaubt das System die Eingabe von Befehlen. Das können bei Standalone-Systemen schon MUMPS-Befehle sein (das sind Systeme, bei denen die Sprache MUMPS in das Betriebssystem integriert ist und die daher auch nur diese eine Sprache verstehen). Andere Systeme, die mehrere Sprachen verstehen, müssen erst noch durch ein Kommando angewiesen werden, den Sprachprozessor für MUMPS zu starten. Wenn schließlich das MUMPS-System bereit ist, hat es für den Benutzer einen eigenen Teil des Arbeitsspeichers reserviert, der oft auch „Partition" genannt wird. Dort können Programme des Benutzers ablaufen und ihre Daten hinterlegen. Weiterhin verwalten MUMPS-Systeme permanente Daten und Programme (solche, die auch nach dem Ende eines Programmlaufs erhalten bleiben sollen) auf dem Massenspeicher (Platte). Dabei sehen Mehrbenutzersysteme eigene Bereiche für jeden Benutzer oder für Benutzergruppen vor. Dadurch wird gewährleistet, daß sich die Benutzer nicht gegenseitig behindern. Die Zuordnung, welcher Benutzer in welchem Bereich arbeiten soll,

1.2.

Benutzerbereiche

und das Einloggen

7

geschieht über die eingegebene UCI. Ein einzelner Benutzer sieht von dem Mehrbenutzersystem nur den seiner UCI zugeordneten Bereich mit seinen Programmen und Daten, so daß das System für ihn den Anschein eines Einbenutzersystems hat. Auf Ausnahmen wird später noch hingewiesen werden.

8

1.3

Kapitel 1. Grundlagen der Programmierung

in MUMPS

Einfache syntaktische Regeln

Wie in jeder Programmiersprache gibt es auch in MUMPS eine Reihe von einfachen Absprachen, die den äußeren Aufbau einer Programmzeile festlegen. Einleuchtend und fast selbstverständlich ist die Tatsache, daß eine Zeile von links nach rechts gelesen und abgearbeitet wird. Die Zeilen selbst enthalten im allgemeinen einen oder mehrere Befehle. Sie bestehen aus Befehlsworten und Befehlsargumenten, die durch genau ein Leerzeichen getrennt werden. Es können mehrere Befehle in einer Programmzeile stehen, die dann mit einem oder mehreren Leerzeichen getrennt werden müssen. Kommentare stehen am Ende einer Programmzeile und sind von dieser durch ein Semikolon (;) getrennt. Als Sonderfall ist es auch möglich, eine Programmzeile mit dem Semikolon beginnen zu lassen, so daß alle darauf folgenden Zeichen als Kommentar interpretiert werden. Kommentare werden selbstverständlich nicht ausgeführt, sondern bei der Ubersetzung eines MUMPS-Programms ignoriert. Sie sind nur für den menschlichen Leser von Bedeutung. In der Sprachdefinition wird über die Anzahl der Befehle, die in einer Zeile höchstens stehen dürfen, nur eine indirekte Aussage gemacht: eine Programmzeile darf höchstens 255 Zeichen lang sein, natürlich einschließlich der Leerzeichen und Kommentare. Es sind also so viele Befehle erlaubt, wie in 255 Zeichen passen. Normalerweise werden Programmzeilen über eine schreibmaschinenähnliche Tastatur eingeben, an die ein Bildschirm angeschlossen ist. Viele Bildschirme gestatten es, in eine Bildschirmzeile 80 Zeichen zu schreiben. Dann springt der Cursor — das ist eine Markierung auf dem Bildschirm, die die Position des nächsten einzugebenden Zeichens anzeigt — an den Anfang der nächsten Zeile. Damit muß keineswegs die Programmzeile abgeschlossen sein. Vielmehr muß man das MUMPS ausdrücklich durch Drücken einer besonderen Taste mitteilen. Diese Taste ist bei den einzelnen Herstellern nicht einheitlich definiert. Häufig wird dafür die „carriage return" Taste benutzt. In den Beispielen wird das Betätigen dieser Taste mit symbolisiert. Die für MUMPS-Anwendungen eingesetzten Bildschirme enthalten meistens 24 Zeilen, jede Zeile enthält 80 Zeichen. Wenn also eine Bildschirmzeile 80 Zeichen, eine Programmzeile in MUMPS dagegen 255 Zeichen lang ist, dann kann man 3 Bildschirmzeilen und noch 15 Zeichen in der vierten Zeile für eine Programmzeile verwenden. Geht man darüber hinaus, kommt eine Fehlermeldung, die darauf hinweist, daß die Programmzeile zu lang ist und nicht mehr verarbeitet werden

1.3. Einfache syntaktische

Regeln

9

kann. In der Praxis sind Programmzeilen solcher Länge allerdings selten anzutreffen, vielmehr wird man versuchen, die Befehle in einer Bildschirmzeile unterzubringen. Daß man überhaupt in einer Zeile mehr als jeweils einen Befehl schreibt, ist mehr als nur Konvention. Einige MUMPS-Befehle betrachten eine Programmzeile als ein einheitliches Ganzes. Wenn im weiteren Verlauf Beispiele angegeben werden, geschieht das in Anlehnung an die übliche Arbeitsweise mit einem MUMPS-System. Hierbei gibt der Rechner immer dann, wenn er bereit ist, eine neue Eingabe zu verarbeiten, ein bestimmtes Zeichen aus, das „Prompt" genannt wird. Dieses Verhalten soll dadurch symbolisiert werden, daß in den Beispielen das Größerzeichen (>) vor jeder Anweisungszeile steht. Eine Befehlszeile wird also wie folgt dargestellt: Befehlswort Argument Befehlswort Argument Befehlswort Argum ent ; Komment eure

Dabei wurde bewußt die Befehlskette über die (Papier-) Zeile hinausgeschrieben und dabei der Beginn der Zeile durch den Prompt gekennzeichnet. Die Abwesenheit des Prompts in der zweiten Zeile zeigt, daß es ein Folgezeile ist und nicht etwa eine neue Befehlszeile.

10

Kapitel 1. Grundlagen der Programmierung

1.4 1.4.1

in MUMPS

Beispiele einfacher Sprachelemente Der WRITE-Befehl

Einer der einfachsten und zugleich häufig gebrauchten Befehle ist der WRITEBefehl.

Beispiel

>WRITE "Hallo" Hallo >

Der WRITE-Befehl wird dazu verwendet, Texte auf den Bildschirm zu schreiben. In Zusammenhang mit anderen Befehlen sei aber jetzt schon darauf hingewiesen, daß der WRITE-Befehl genauso benutzt wird, um auf Drucker oder andere Geräte zu schreiben. Man beachte, daß zwischen dem Befehlswort WRITE und dem Argument "Hallo" genau ein Leerzeichen stehen muß! Die Schreibweise des Argumentes "Hallo" zeigt noch eine weitere wichtige Festlegung von MUMPS: Texte, die als unveränderliche Zeichenfolgen im Programmtext stehen sollen, müssen von Anführungszeichen (") eingeschlossen sein. Mochte man Zahlen schreiben, kann man auf die Anführungszeichen verzichten.

Beispiel

>WRITE 2 2

>WRITE 3+5

8 >

Im letzten Beispiel wird deutlich, daß das Argument des WRITE-Befehls zunächst errechnet wird und das Ergebnis dann geschrieben wird. Im WRITEBefehl, aber auch bei (fast) allen anderen Befehlen in MUMPS, kann man mehrere durch Kommata getrennte Argumente angeben. Sie werden direkt nacheinander ohne trennende Leerzeichen oder andere Zeichen ausgegeben.

1.4. Beispiele einfacher Sprachelemente

11

Beispiel >WRITE "WRITE","mit","mehreren","Argumenten" WRITEmitmehrerenArgumenten >WRITE "WRITE ","mit "."mehreren ","Argumenten" WRITE mit mehreren Argumenten >

Der WRITE-Befehl kennt besondere Argumente zur Formatierung der Ausgabe. Hier soll zunächst der Zeilenvorschub erwähnt werden. Er wird durch ein Ausrufezeichen (!) erzeugt. Besteht ein Argument des WRITE Befehls aus einem oder mehreren Ausrufezeichen, so werden genau so viele Zeilenvorschübe erzeugt, wie Ausrufezeichen vorhanden sind. Zeilenvorschübe werden in MUMPS nie automatisch erzeugt, sondern müssen immer explizit durch Ausrufezeichen in WRITE-Befehlen angefordert werden. Beispiel >WRITE "Eine",!."Ausgabe mit",!!,"Zeilenvorschueben" Eine Ausgabe mit Zeilenvorschueben >

In ähnlicher Form wie für den Zeilenvorschub werden auch die Steuerzeichen für den Seitenvorschub und für die Positionierung innerhalb einer Zeile angegeben. Ein Vorschub auf den Beginn der nächsten Seite wird mit dem Nummernzeichen (#) erzwungen. Die Positionierung innerhalb einer Zeile wird durch das Fragezeichen (?) eingeleitet. Dem Fragezeichen folgt die Position innerhalb der laufenden Zeile, ab der geschrieben werden soll. Die zur Formatierung dienenden Argumente des WRITE-Befehls werden in der gleichen Weise wie die sonstigen Argumente angegeben. Jedoch können mehrere Formatierungszeichen ohne trennende Kommata aneinandergekettet werden. Hierbei ist jedoch die Reihenfolge zu beachten, daß zuerst Seitenvorschubszeichen, dann Zeilenvorschubszeichen und dann Tabulierungszeichen erscheinen. Eine andere Reihenfolge wäre schon von der Logik her nicht sinnvoll. Die folgenden Beispiele sollen die Anwendung verdeutlichen.

12

Kapitel 1. Grundlagen der Programmierung

in MUMPS

Beispiele

WRITE #,"Neue Seite" WRITE ?40,"Positionierung innerhalb einer Zeile" WRITE #!!!?20,"Ueberschrift",! Beim letzten Beispiel wird die Aneinanderkettung der Formatierungsoperatoren deutlich. Bei der Benutzung des Tabulierungsoperators ist es wichtig zu wissen, daß in MUMPS die erste Spalte die Nummer 0 hat. Daher bewirkt die Angabe von ?1 eine Positionierung in die zweite Spalte.

1.4.2

Variablen in MUMPS und der SET-Befehl

Jetzt soll ein weiteres wichtiges Element von (fast) allen Programmiersprachen behandelt werden: die Variablen. Variablen stellen das „Gedächtnis" der Programme dar. Es sind Speicher, die beliebige Werte, z.B. Ergebnisse von Berechnungen, aufnehmen und aufbewahren können. Jede dieser Variablen hat einen eindeutigen Namen, über den auf den Inhalt (d.h. Wert) zugegriffen werden kann. Dies geschieht einfach in der Weise, daß man den Namen der Variablen an die Stelle eines Ausdrucks oder eines Befehls schreibt, an der der Wert benötigt wird. Die Namen von Variablen in MUMPS müssen entweder mit einem Buchstaben oder einem Prozentzeichen ('/,) beginnen. Die folgenden Zeichen müssen Buchstaben oder Ziffern sein. Buchstaben dürfen in Groß- oder Kleinschreibweise eingegeben werden. Beispiele richtiger Variablennamen sind: ABC

abc

A123

Alb2c3

7,Var

V.1

'/,

Nicht erlaubt sind: 1AZ

123

Name-1

Var.X

Auch wenn die Länge von Namen nicht begrenzt ist, werden nur die ersten acht Zeichen zur eindeutigen Kennzeichnung herangezogen. Obwohl der Name von Variablen in gemischter Groß- und Kleinschreibung erlaubt ist, ist dringend zu empfehlen, eine einheitliche Schreibweise von Anfang an zu wählen. In vielen (aber nicht in allen) MUMPS-Systemen wird zwischen Variablennamen, die sonst gleich sind, aber unterschiedliche Groß- und Kleinschreibung verwenden, unterschieden.

1.4. Beispiele einfacher

13

Sprachelemente

Daher stellen in solchen Systemen beispielsweise XX, Xx, xX und xx vier unterschiedliche Namen von Variablen dar. Zur Verbesserung der Übersichtlichkeit von Programmen ist es sinnvoll, Befehle groß und die Namen von Variablen gemischt (bei langen, sprechenden Namen wie Kunde nNummer) oder klein zu schreiben. Die Zuweisung eines neuen Wertes an eine Variable wird in MUMPS durch den Befehl SET erreicht. Beispiel

>SET abc=5 >

In diesem Beispiel ist der Name der Variablen abc und ihr wird der Wert 5 zugewiesen. Das Gleichheitszeichen (=) zeigt die Zuweisung an. Es wird jeweils der Wert auf der rechten Seite des Gleichheitszeichens der Variablen auf seiner linken Seite zugewiesen. Man beachte hierbei den Unterschied zwischen einer Zuweisung, die durch das Gleichheitszeichen angezeigt wird, und der mathematischen Aussage, daß zwei Werte einander gleich sind. So ist die Aussage i = i + l im mathematischen Sinne falsch, aber die Anweisung SET i * i + l stellt einen gültigen Befehl dar und bewirkt, daß der Wert der Variablen i um eins erhöht wird. Wie das WRITE und fast alle anderen Befehle der Sprache erlaubt auch das SET die Angabe von mehreren Argumenten, die durch Kommata getrennt sind. Bei diesen Befehlen gilt: < a r g l > , < a r g 2 > , . . . ist äquivalent zu B e f e h l s w o r t >

Beispiel

>SET abc=5,x="Hallo" >WRITE " K o n t r o l l e : " , ! , a b c , ! , x < c r > Kontrolle: 5 Hallo >

...

Kapitel 1. Grundlagen der Programmierung in MUMPS

14

Im obigen Beispiel wird gleichzeitig deutlich, wie die Namen von Variablen benutzt werden, um ihren Inhalt auszugeben. Man beachte dabei, daß Textkonstanten ( " H a l l o " , " K o n t r o l l e : " ) in Anführungszeichen eingeschlossen sind, während Variablennamen (abc, x) direkt angegeben werden. Bei der Zuweisung eines Wertes an mehrere Variablen kann man die Klammerform des Befehls benutzen. Sollen den Variablen a, b, c jeweils der Wert 1 zugeordnet werden, schreibt man abkürzend:

Beispiel >SET (a,b,c)=l >

Bei der Wertzuweisung durch einen SET-Befehl wird die Seite rechts des Gleichheitszeichens zunächst ausgewertet und dann das Ergebnis den auf der linken Seite angegebenen Variablen zugewiesen. Damit ist auch das Ergebnis von Zuweisungen, bei denen die gleiche Variable rechts und links des Gleichheitszeichens auftritt, wohl definiert:

Beispiel >SET i=5 >SET i=i+l >WRITE i 6 >

Die mit dem SET-Befehl definierten Variablen werden im Arbeitsspeicher abgelegt und können innerhalb einer Sitzung beliebig benutzt und verändert werden. Für andere Benutzer des gleichen Rechners sind diese Variablen nicht sichtbar, können also weder gelesen noch geändert oder gelöscht werden. Die so definierten Variablen sind also jedem Benutzer (genauer: jedem Prozeß) lokal zugeordnet und heißen daher „lokale Variablen". Die definierten lokalen Variablen können in allen MUMPS-Systemen durch einen einfachen Befehl angezeigt oder ausgedruckt werden. In vielen Fällen wird dazu der WRITE-Befehl ohne Argument verwendet, der in dieser Form allerdings nicht standardisiert ist.

1.4. Beispiele einfacher

Sprachelemente

15

Beispiel >WRITE a abc b c i

χ

WRITE-Befehl ohne Argument

1 5 1 1 6 "Hallo"

> Hier werden die in den vorangegangenen Beispielen gesetzten Variablen angezeigt. Eine Bemerkung wert ist noch die alphabetische Sortierung der Ausgabe, die insbesondere bei großen Variablenlisten die Übersichtlichkeit wesentlich erhöht. Gerade bei der Programmentwicklung ist die Möglichkeit, jederzeit die aktuellen Werte von Variablen einfach anzeigen lassen zu können, sehr hilfreich. Fehler durch zum Beispiel falsche Zuweisung an Variablen können damit leicht entdeckt werden.

1.4.3

Löschen von Variablen mit dem KILL-Befehl

Das natürliche Gegenstück des SET-Befehls ist der KILL-Befehl. Durch ihn wird man in die Lage versetzt, aus der Gruppe der definierten lokalen Variablen einzelne (oder alle) zu entfernen. In diesem Abschnitt sollen einige Aspekte dieses Befehls behandelt werden. Weitere Details werden im Zusammenhang der indizierten Variablen beschrieben. Das Argument des Befehls ist normalerweise der Name einer Variablen, die man löschen möchte. Es gibt den Befehl in drei verschiedenen Formen. KILL a bedeutet, daß die Variable a aus der Liste der definierten Variablen entfernt wird. War diese Variable nicht definiert, hat der KILL-Befehl keine weiteren Auswirkungen. Entsprechend löscht man mit dem Befehl KILL a,b,c die Variablen a, b und c. KILL ohne Argument bedeutet, daß alle lokalen Variablen ohne Ausnahme gelöscht werden. In der dritten Form des KILL-Befehls werden Klammern benutzt, um mitzuteilen, welche Variablen nicht gelöscht werden sollen. KILL ( k , l , d e f ) heißt also: lösche alle sichtbaren Variablen (siehe dazu auch Abschnitt 2.5) bis auf k, 1, def. Anhand der argumentlosen Form des KILL-Befehls soll auf die besondere Schreibweise von argumentlosen Befehlenin MUMPS hingewiesen werden:

16

Kapitel 1. Grundlagen der Programmierung

in MUMPS

dem Befehl müssen zwei Leerzeichen folgen, bevor ein weiterer Befehl auf der gleichen Zeile geschrieben werden kann.

Beispiel KILLuuSET a=l

In diesem Beispiel sind die beiden Leerzeichen durch Uu dargestellt worden. Der Vorteil des KILL-Befehls wird besonders einsichtig, wenn man sich vor Augen hält, daß in jedem MUMPS-System der Speicherplatz, der zur Abspeicherung von Variablen zur Verfugung steht, begrenzt ist. Durch das Löschen einer Variablen wird der von ihr belegte Speicherplatz wieder frei. Es empfiehlt sich, Variablen dann zu löschen, wenn sie in der weiteren Programmausführung nicht mehr benötigt werden. Im übrigen zeugt es von einem guten Programmierstil, wenn alle Variablen, die in einem Programm oder Unterprogramm für interne Zwecke benutzt werden, an dessen Ende wieder gelöscht werden. Wie aber kann man feststellen, wieviel Speicherplatz man noch im Arbeitsspeicher zur Verfügung hat? Ehe diese Frage beantwortet werden wird, soll noch einmal auf den Aufbau einer Partition eingegangen werden. Die beiden Hauptobjekte einer Partition sind die definierten lokalen Variablen sowie das geladene MUMPS-Programm. Die Größe einer Partition kann in vielen Fällen vom SystemVerwalter eingestellt werden. In Abhängigkeit von der Größe des geladenen Programms und der Anzahl der definierten lokalen Variablen kann der aktuell zur Verfügung stehende freie Platz in der Partition mit einer sogenannten Systemvariablen angezeigt werden. Systemvariablen sind spezielle Sprachelemente in MUMPS, die Auskunft über gewisse systemnahe Parameter geben. In Anlehnung an die englische Bezeichnung werden die Systemvariablen auch „Spezielle Variablen" genannt. Jede Systemvariable (von denen es in MUMPS sieben gibt), hat einen eindeutigen Namen, der stets mit einem Dollarzeichen ($) beginnt. Man kann eine Systemvariable wie eine normale Variable verwenden, ihr aber (bis auf Ausnahmen) keinen Wert zuweisen. Als Beispiel und zur Beantwortung der oben gestellten Frage soll die Systemvariable $ST0RAGE eingeführt werden. Sie gibt die Anzahl der noch freien Zeichen in einer Partition an. Der Wert von $ST0RAGE ist natürlich in hohem Masse abhängig vom verwendeten MUMPS-System. Er ist außerdem abhängig von der aktuellen Programmgröße und von der Größe des Speicherplatzes, der für zusätzliche Verwaltungsinformation benötigt wird.

1.4. Beispiele einfacher

Sprachelemente

17

Beispiel >WRITE $STORAGE 5942 > Dieses Ergebnis zeigt, daß in der benutzten Partition noch 5942 Bytes frei sind. Dem Leser, der Zugriff zu einem MUMPS-System hat, sei an dieser Stelle empfohlen, sich den Wert von $ST0RAGE anzeigen zu lassen, danach eine Reihe von lokalen Variablen zu definieren und nunmehr den Wert von $ST0RAGE zu kontrollieren.

1.4.4

Wertzuweisung im Dialog mit dem READ-Befehl

Der SET-Befehl dient der Zuweisung von Werten innerhalb des Programms. Es muß jedoch auch möglich sein, eine Eingabe am Bildschirm als Wert einer Variablen zu übernehmen. Dazu dient der READ-Befehl. Beispiel >READ z e i l e < c r > Es erscheint kein Prompt, da jetzt eine Eingabe vom Bildschirm erwartet wird. Erst wenn ein Text eingegeben wurde, meldet sich das MUMPS-System wieder mit seinem Prompt. Fix Quark vom WDB Typ gschlejnz > Danach enthält die Variable z e i l e die Eingabe des Benutzers und kann mit dem WRITE-Befehl wieder ausgegeben werden. >WRITE "Deine Eingabe war > ",zeile Deine Eingabe war > Fix Quark vom WDB Typ g s c h l e j n z >

Das oben gezeigte Beispiel macht deutlich, daß ein Eingabebefehl alleine sehr verwirrend sein kann, da man nicht genau weiß, ob das Programm gerade noch rechnet oder ob es auf eine Eingabe wartet. Daher ist es sinnvoll, vor einer Eingabe einen Text auszugeben, der in Kurzform angibt, welche Eingabe erwartet wird.

Kapitel 1. Grundlagen der Programmierung in MUMPS

18

MUMPS unterstützt diese Vorgehensweise dadurch, daß feste Zeichenfolgen, die man dem READ-Befehl als Argument übergibt, auf den Bildschirm ausgegeben werden. Beispiel

>READ "Wie geht es Dir ? ",antwort Wie geht es Dir ? gut >WRITE "Der Dialog zeigt, dass es Dir ",antwort," geht" Der Dialog zeigt, dass es Dir gut geht" >

Die Formatierung der Eingabe wird dadurch erleichtert, daß die Drucksteuerzeichen, die für den WRITE-Befehl erlaubt sind (z.B. Ausrufezeichen für Zeilenvorschub), auch als Argumente des READ-Befehls zulässig sind. Beispiel

>READ "Gib eine Zahl ein ",a,!,"und noch eine ",b Gib eine Zahl ein 23 und noch eine 15 >WRITE ?9,"Die Summe der Zahlen ist ",a+b Die Summe der Zahlen ist 38 >

Das letzte Beispiel zeigt besonders deutlich, daß die Argumente des READBefehls von links nach rechts bearbeitet werden. Besteht ein Argument aus einer festen Zeichenfolge oder aus einem Druckformatierungszeichen, so wird eine entsprechende Ausgabe erzeugt. Wird dagegen eine Variable angegeben, so wartet der Rechner auf die Eingabe eines Wertes für diese Variable. Stehen mehrere Variablen in einem READ-Befehl, so müssen entsprechend viele Eingaben erfolgen. In manchen Fällen will man vermeiden, daß eine bestimmte Anzahl von Zeichen in der Eingabe überschritten wird. Beispielsweise soll die Länge der Eingabe des Namens einer Person auf 20 Zeichen begrenzt werden. Beispiel

>READ "Name, Vorname : ",name#20 Name, Vorname : Goethe, Johann Wolfg >

Der READ-Befehl in der angegebenen Form mit dem Nummernzeichen (#) erlaubt nur die Eingabe von einer vorgegebenen Anzahl von Zeichen.

1.4. Beispiele einfacher

Sprachelemente

19

Danach wird die Eingabeanweisung abgebrochen. Es hängt vom verwendeten Rechner ab, ob man jetzt noch geben muß oder ob dies automatisch geschieht. Der READ-Befehl ist das zentrale Sprachelement in MUMPS, um Dialoge mit dem Benutzer einer Programmanwendung zu führen. Als stark vereinfachtes Beispiel soll die Erfassung einer Adresse gezeigt werden, die aus einem Namen, einer Straße mit Hausnummer und einer Postleitzahl mit Ortsangabe besteht. Beispiel

>READ ?10,"Name, Vorname : ",nv Name, Vorname : Meier, Horst >READ ?10,"Strasse und Hausnummer : ",str Strasse und Hausnummer : Alleenweg 3a >READ ?10,"PLZ und Wohnort : ",ort PLZ und Wohnort : 6100 Darmstadt >

Die mit diesen READ-Befehlen definierten Variablen nv, str, ort können nun weiterverarbeitet werden oder permanent abgespeichert werden. Ein weiterer Aspekt des READ-Befehls soll nunmehr betrachtet werden, auf den im weiteren Verlauf des Buchs noch mehrmals Bezug genommen wird. Man hat bei dem READ-Befehl die Möglichkeit, die Eingabedauer zeitlich zu begrenzen, in dem man dem READ-Argument einen zusätzlichen Parameter — den sogenannten Timeout — nachstellt. Dieser Timeout wird durch einen Doppelpunkt von dem Variablennamen getrennt und enthält die Anzahl von Sekunden, die man maximal auf das Ende der Eingabe warten möchte. Beispiel

>READ "Bitte Eingabe, aber rasch !",eing:5 In diesem Beispiel muß innerhalb von 5 Sekunden eine Eingabe erfolgen und abgeschlossen werden, anderenfalls wird die Leseoperation automatisch beendet. Dies ist auch der Fall, wenn man bereits einen Teil der Eingabe durchgeführt hat. Die Variable eing enthält dann dieses Eingabefragment als Wert. Hat man nichts eingegeben und der Timeout ist verstrichen, enthält eing den sogenannten Leerstring (siehe hierzu auch Abschnitt 1.6.1). Die gleichzeitige Angabe einer Längenbegrenzung und einer Zeitbegrenzung erfolgt dadurch, daß man zunächst das Nummernzeichen mit der Länge und danach den Doppelpunkt mit dem Timeout angibt.

20

Kapitel 1. Grundlagen der Programmierung

in MUMPS

Beispiel >READ "Kundennummer : ",KundenNr#9:20

In diesem Beispiel wird erwartet, daß die Eingabe höchstens 9 Stellen lang ist und innerhalb von 20 Sekunden abgeschlossen wird.

1.4.5

$LENGTH als Beispiel einer einfachen Funktion

Alle bis jetzt besprochenen Befehle ermöglichen zwar das Hantieren mit Variablen und Werten, eine Verarbeitung, die vom Rechner schneller und einfacher als von Hand durchzuführen ist, hat noch nicht wirklich stattgefunden. Als ein erstes Beispiel einer solchen Verarbeitung soll die Funktion $LENGTH eingeführt werden, die es erlaubt, die Länge einer Zeichenkette zu ermitteln. Beispiel >READ "Eingabe : ",e Eingabe : MUMPS ist eine sehr interessante Sprache >WRITE "Die Eingabe ist ",$LENGTH(e)," Zeichen lang" Die Eingabe ist 40 Zeichen lang >

$LENGTH liefert also die Anzahl der Zeichen einer Variablen oder einer Zeichenkette und ist daher ein einfaches Beispiel einer Funktion, die auf Texten arbeitet. In den meisten Programmiersprachen ist der Begriff der Funktion bekannt. Kurz gesagt wird der Funktionsaufruf durch einen Wert ersetzt, der anhand des Arguments oder der Argumente berechnet wird. Im Beispiel wird an die Stelle der Funktion $LENGTH die Länge der Variablen eingesetzt. In MUMPS beginnen die Namen von allen Funktionen mit einem (bei vordefinierten Funktionen) oder zwei (bei vom Benutzer definierten Funktionen) Dollarzeichen ($). Die Argumente der Funktion werden in Klammern eingeschlossen und voneinander mit Kommata getrennt. Im allgemeinen Fall hat der Aufruf einer Funktion also folgendes Aussehen: $FUNKTIONSNAME(Argument1,Argument2,...)

oder $$FUNKTIONSNAME(Argument1,Argument2,...)

Manche Funktionen haben lediglich ein Argument, wie $LENGTH in diesem Fall, manche haben mehrere Argumente. Einige Funktionen sind durch die Sprache MUMPS vordefiniert, während andere Funktionen vom Benutzer selbst definiert werden können.

1.5.

Zahlen und numerische

1.5

Operationen

21

Zahlen und numerische Operationen

Die Darstellung von Zahlen in MUMPS ist recht einfach. MUMPS kennt sowohl ganze Zahlen (z.B. 123 oder -5), als auch Dezimalzahlen (z.B. - 1 2 . 3 4 5 ) . Um auch besonders kleine oder große Zahlen darstellen zu können, kann man sich der Exponentialdarstellung von Zahlen bedienen, bei der Zahlen durch das Produkt von Mantisse und Zehnerpotenz eingegeben werden. Dabei schreibt man zunächst die Mantisse, dann ein „E", auf das die ganzzahlige Zehnerpotenz folgt. Also wird man 1 . 3 E - 8 anstelle von0.000000013 schreiben. Alle diese Zahlen heißen numerische Literale. Literale sind konstante Werte, die fest im Prograinmtext enthalten sind. Dieser Sachverhalt soll bei der Behandlung von Stringliteralen noch einmal aufgegriffen werden. Hier seien zunächst einige Beispiele für numerische Literale angeführt: Beispiele >WRITE -0.123 -.123 >WRITE 1.2300000 1.23 >WRITE 8.9E-2 .089 >WRITE .12E7 1200000 >

; Zahl mit fuehrender Null ; Zahl mit Nullen im Dezimalteil ; Zahl in Exponentialdarstellung ; Zahl in Exponent i aldarstellung< cr>

Beim ersten Beispiel stellt man fest, daß bei der Ausgabe einer Dezimalzahl mit einem Ganzzahlanteil von Null dieser nicht ausgedruckt wird. Angehängte Nullen in einer Dezimalzahl werden ebenfalls unterdrückt, wie das zweite Beispiel zeigt. Die Ausgabe einer Dezimalzahl erfolgt — wie auch die interne Darstellung — immer in Dezimalschreibweise und nie in Exponentialform. Und schließlich ist auch die Eingabe einer Dezimalzahl ohne die führende Null erlaubt, was das letzte Beispiel vermittelt. Nicht erlaubt ist die Darstellung von Zahlen in der Form E3 (hier fehlt die Mantisse) und 1. 2 E - 1 . 6 , weil die Zehnerpotenz hier keine ganze Zahl ist. Wegen der begrenzten Darstellbarkeit auf einem digitalen Rechner müssen die Zahlen innerhalb festgelegter Grenzen liegen. In MUMPS werden mindestens die Zahlen aus dem Bereich 10~ 2 5 bis 10 2 5 und dem negativen Gegenstück einschließlich der Null unterstützt. Die Genauigkeit, mit der in MUMPS Zahlen angegeben werden können, kann bei den einzelnen Herstellern unterschiedlich sein. Der Standard

22

Kapitel

1. Grundlagen

der Programmierung

in

MUMPS

schreibt vor, daß mindestens zwölf Stellen berücksichtigt werden müssen. Man kann das leicht am eigenen Computer überprüfen. Beispiel

>SET a=4/3 ; Der Schraegstrich steht fuer Division >WRITE a,!,$LENGTH(a) 1.333333333333333333 20 >

Wie ist dieses Ergebnis zu interpretieren? Zunächst wird der Variablen a das Ergebnis der Division 4/3 zugewiesen. Bei 4/3 handelt es sich um einen unendlichen Dezimalbruch, der in der internen Darstellung irgendwann einmal abbricht. Das sieht man, wenn man a ausgibt. Die Länge der Zahl a, die dann ausgegeben wird, ist die Gesamtlänge der Darstellung (mit Punkt). Die Anzahl der signifikanten Stellen ist also in diesem Fall 20 — 1 = 19. Wie in vielen anderen Programmiersprachen kann man auch in MUMPS numerische Operationen durchführen. Einfache Beispiele hierfür stellen die vier Grundrechenarten dar, bei denen aus zwei Zahlen durch die entsprechende Operation eine dritte erzeugt wird. Man nennt diese Art der Verknüpfung zweier Zahlen eine zweistellige (man sagt auch binäre) numerische Operation. Die Rechenzeichen werden in MUMPS folgendermaßen dargestellt: (siehe dazu auch die Beispiele in Abbildung 1.1) + * /

Addition Subtraktion Multiplikation Division

>WRITE 16 >WRITE 8 >WRITE 48 >WRITE 3

12+4 12-4 12*4 12/4

>

Abbildung 1.1: Beispiele zu den arithmetischen Operationen

1.5.

Zahlen und numerische

Operationen

23

Diese Begriffe sind aus der Alltagsmathematik geläufig. Selbstverständlich können auch Variablen in der Berechnung arithmetischer Ausdrücke benutzt werden: Beispiel >SET a=10,b=5 WRITE a," : ",b," = ",a/b 10 : 5 - 2 > Arithmetische Ausdrücke wie z.B. a/b sind in MUMPS nur ein Sonderfall allgemeiner Ausdrücke, die durch Verknüpfung von Variablen oder Zeichenketten durch beliebige Operatoren entstehen. Bei der Berechnung arithmetischer Ausdrücke trifft man oft auf eine Kombination der vier Grundrechenarten, etwa 4+3*5. Bei Ausdrücken dieser Art setzt man stillschweigend eine Reihenfolge in der Berechnung voraus, indem man zunächst die Multiplikation ausführt (ergibt 15) und zu diesem Ergebnis eine vier addiert. Kurz und prägnant hat man die Regel: „Punktrechnung geht vor Strichrechnung". MUMPS verhält sich hier sehr ungewöhnlich: Beispiel >WRITE 4+3*5 35 >

MUMPS kennt nicht die Vereinbarung der Priorität der Multiplikation, sondern führt die Berechnung von links nach rechts durch. Es wird also 4 zu 3 addiert und dieses Ergebnis mit 5 multipliziert. Es sei also festgehalten, daß MUMPS eine strikte links-nach-rechts Abarbeitung vornimmt. Es gibt keine bevorzugte Operation. Diese Regel gilt für alle in MUMPS vorkommende Operatoren. Ein weiteres Beispiel: Beispiel >SET a=2,b=14,c=5 WRITE b/a+3*c 50 > Demnach wird zuerst die Division durchgeführt, dann eine 3 addiert und schließlich dieses Ergebnis mit 5 multipliziert. Möchte man hingegen das den in der Mathematik üblichen Prioritätsregeln entsprechende Ergebnis erhalten, muß man Klammern setzen. Ausdrükke in Klammern werden zuerst berechnet.

24

Kapitel 1. Grundlagen der Programmierung

in MUMPS

Beispiel >WRITE 4+(3*5) 19 >

In jedem Zweifelsfall empfiehlt sich die Benutzung von Klammern. Aus der Praxis weiß man, daß die in MUMPS übliche Reihenfolge der Berechnung eines arithmetischen Ausdrucks gerade bei Anfängern leicht zu Fehlern führt. Zum Ende dieses Abschnitts sollen noch zwei weitere numerische Operationen vorgestellt werden: \ #

Ganzzahldivision Restdivision

Die Ganzzahldivision liefert den ganzzahligen Teil einer normalen Division. Das Ergebnis der Ganzzahldivision 10\3 ist also 3. Die Restdivision liefert den Rest, der übrig bleibt, wenn man das linke Argument durch das rechte dividiert. Beispielsweise ist das Ergebnis der Restdivision 23#7 gleich 2, weil 21 durch 7 ohne Rest teilbar ist und die Differenz zu 23 gerade 2 beträgt. Die Wirkungsweise dieser beiden Operationen wird durch folgendes Beispiel klar. Angenommen man möchte eine beliebige Anzahl von Tagen in ein Wochenformat konvertieren. Ganzzahldivision durch 7 liefert dann die Anzahl der Wochen, Restdivision durch 7 liefert die noch verbleibenden Tage. Beispiel >READ "Anzahl Tage eingeben ",at Anzahl Tage eingeben 52 >WRITE at," Tage = '\at\7," Wochen und ",at#7," Tage" 52 Tage « 7 Wochen und 3 Tage

> Das sind alle numerischen Operationen, die in Standard-MUMPS zur Verfugung stehen. Dem Leser, der vielleicht schon einmal mit Basic programmiert hat, wird auffallen, daß MUMPS keine weitergehenden Funktionen, wie etwa das Potenzieren oder das Radizieren kennt. MUMPS beschränkt sich hier bewußt auf die mathematischen Grundfunktionen, weil die Stärke der Sprache im Bereich der Textbearbeitung liegt. In vielen Fällen jedoch haben die Hersteller von MUMPS-Systemen eine Erweiterung vorgenommen und erlauben die Berechnung gebräuchlicher mathematischer Funktionen durch Aufruf eigener Programme. Im weiteren Zusammenhang der numerischen Verarbeitung in MUMPS soll schließlich noch die Funktion $RAND0M eingeführt werden. $RAND0M liefert

1.5.

Zahlen und numerische

Operationen

25

eine ganzzahlige Zufallszahl in Abhängigkeit des Arguments. Stellt η eine positive ganze Zahl dar, liefert $RAND0M(n) eine Zufallszahl zwischen 0 und η — 1. Möchte man aus einem Bereich 1 bis k eine Zufallszahl auswählen, schreibt man $RANDOM(k)+l. Eine möglicherweise als Ergebnis von $RANDOM auftretende Zahl 0 führt dabei durch die Addition zu einem Gesamtergebnis von 1.

26

1.6 1.6.1

Kapitel 1. Grundlagen der Programmierung in MUMPS

Zeichenvorrat und Zeichenketten Zeichensatz und Stringliterale

Zeichenketten (im englischen „strings") wurden in einer sehr einfachen Form schon vorgestellt, nämlich als Argumente der WRITE- und READ-Befehle. Dabei wurde deutlich, daß man sie in Anführungszeichen (") einschließt. Genau genommen waren die bisherigen Zeichenketten sogenannte Textkonstanten, deren Werte sich bei der Ausführung eines Programms nicht ändern. Man nennt diese Art von Zeichenketten auch Stringliterale. Sie sollen in diesem Abschnitt genauer behandelt werden. Naheliegend ist die Frage, aus welchen Zeichen diese Literale bestehen können. Das führt uns auf den Begriff der ASCII-Zeichen, eines genormten Zeichensatzes, der 128 Zeichen umfaßt. In Anhang C findet man ein vollständiges Verzeichnis dieser Zeichen. Die ASCII-Zeichen lassen sich grob in Steuerzeichen und druckbare Zeichen einteilen. In Tabelle 1.1 findet man eine Ubersicht über die 95 druckbaren Zeichen, die den sogenannten MUMPS-Zeichensatz darstellen. Tabelle 1.1: Der MUMPS-Zeichensatz A-Z a-z 0-9 j II

* $ 7.

& >

( ) *

+ t -

.

Großbuchstaben Kleinbuchstaben Ziffern Leerzeichen Ausrufezeichen Anführungszeichen Nummernzeichen Dollar Prozent kommerzielles Und Hochkomma Klammer auf Klammer zu Stern Plus Komma Minuszeichen Dezimalpunkt

/ \ : >

< > =

?

(

Schrägstrich Rückstrich Doppelpunkt Semikolon kleiner als größer als gleich Fragezeichen Klammeraffe eckige Klammer eckige Klammer Zirkumflex Unterstreichung geschw. Klammer vert. Strich geschw. Klammer Tilde Akzent

1.6.

Zeichenvorrat und

Zeichenketten

27

Jetzt kann die eingangs gestellte Frage beantwortet werden, welche Zeichen in Stringliteralen verwendet werden dürfen. Es sind genau die 95 druckbaren Zeichen des MUMPS-Zeichensatzes. Ein Beispiel mag das verdeutlichen. Beispiel

>WRITE "ABCXYZabcxyz012789.,:~;" ABCXYZabcxyzO12789.,:~; >

In diesem Beispiel wird das Semikolon nicht als Beginn eines Kommentars interpretiert, weil es innerhalb der Zeichenkette steht. Es wird daher nicht als Programmanweisung angesehen. Eine von der angegebenen Form abweichende Darstellung wird notwendig, wenn Anführungszeichen innerhalb eines Literals enthalten sein sollen. In diesem Fall müssen sie doppelt geschrieben werden. Beispiel

>WRITE "Er sagte: ""Das i s t aber ueberraschend!""" Er sagte: "Das i s t aber ueberraschend!" >

Wenn es irgend geht, empfehlen wir, der Übersichtlichkeit halber nicht das doppelte Anführungszeichen, sondern das einfache Hochkomma zu verwenden, also Beispiel

>WRITE "Er sagte: 'Das i s t aber ueberraschend!'" Er sagte: 'Das i s t aber ueberraschend!' >

Weiterhin zählen doppelte Anführungszeichen bei der Berechnung der Länge eines Strings nicht doppelt, sondern nur einfach. Es werden also nur so viele Zeichen gezählt, wie auch ausgegeben werden. Beispiel

>SET a="ABC""D",b="",c="""",sp=" " >WRITE a,sp,$LENGTH(a),sp,$LENGTH(b),sp,c,sp,$LENGTH(c) ABC"D 5 0 " 1 >

28

Kapitel 1. Grundlagen der Programmierung

in MUMPS

In diesem Beispiel wurde der Variablen b die leere Zeichenkette ("") zugewiesen, deren Länge 0 beträgt. Im Dialog wird die leere Zeichenkette einer Variablen zugewiesen, wenn man durch bloßes Drucken der Returntaste den READ-Befehl beendet. Auch in anderer Hinsicht ist das letzte Beispiel von Interesse. Darin wurde einer Variablen eine Zeichenkette als Wert zugewiesen. In Variablen können im Unterschied zu den bisher betrachteten Stringliteralen alle 128 ASCII-Zeichen dargestellt werden. Manche Hersteller erlauben darüber hinaus noch weitere Zeichen, beispielsweise die unter MS-DOS definierten nationalen Sonderzeichen und halbgrafischen Zeichen im Codebereich von 128 bis 255. Es soll noch ein Operator vorgestellt werden, der es erlaubt, Zeichenketten zu verknüpfen. Man nennt diese Operation Verkettung oder Concatenation und benutzt für den Operator, den man Verkettungsoperator nennt, das Unterstreichungszeichen (_). Dieser Operator verbindet seine beiden Operanden zu einem einzigen String, der dann beide Operanden enthält. Beispiel >SET a="Das ist ein ",b="String" SET c=a_b WRITE c Das ist ein String >WRITE 12_34 1234 > Der zweite Teil des Beispiels erscheint merkwürdig, denn der Verkettungsoperator wird auf Zahlen angewendet und diese gelten gemeinhin nicht als Strings. Tatsächlich ist die Situation in MUMPS ganz anders, wie im nächsten Unterabschnitt deutlich werden wird.

1.6.2

Der Datentyp in M U M P S und die numerische Interpretation

Die allermeisten Programmiersprachen kennen das Konzept der Typbindung von Variablen an bestimmte Datentypen. Das bedeutet, daß eine Variable nur Werte annehmen kann, die ihrem Typ entsprechen. Gebräuchliche Datentypen in vielen Programmiersprachen sind integer (ganze Zahlen), real (alle Dezimalzahlen) oder boolean (logische Werte wahr und falsch) und noch andere. Die Zuweisung eines logischen Wertes zu einer Integervariablen würde im allgemeinen zu einem Fehler führen. Die Typbindung führt zu überschaubareren Programmen und erzwingt eine sorgfältige Programmplanung.

1.6.

Zeichenvorrat und

Zeichenketten

29

Andererseits erfordert der Zwang zur Typdefinition gerade bei kleinen Programmen größeren Schreibaufwand. Auch ist in den meisten Sprachen die mit der Typdeklaration verbundene Festlegung der Länge (oder Größe) einer Variablen äußerst lästig, weil man bei einer Änderung der Variablenlänge unter Umständen große Programmodifikationen durchführen muß. Die diesbezüglichen Verhältnisse sind in MUMPS sehr einfach. MUMPS kennt nur einen Datentyp: die variabel lange Zeichenkette. Allerdings darf die Länge 255 Zeichen nicht überschreiten. Intern arbeiten auch MUMPS Systeme mit verschiedenen Datentypen, nur merkt der Programmierer nichts davon. Es leuchtet ein, daß auch in MUMPS eine numerische Operation (z.B. α + b) verlangt, daß die Operanden numerische Werte annehmen. MUMPS führt in solchen Fällen eine automatische Typkonvertierung durch. Man spricht bei der Konvertierung einer Zeichenkette in einen numerischen Datentyp von der numerischen Interpretation der Zeichenkette. Die dabei angewandten Regeln sollen im folgenden dargestellt werden. Dabei muß man sich zunächst vor Augen halten, wie in MUMPS eine Zahl überhaupt aussehen kann. Zunächst einmal kann sie die Form einer ganzen Zahl haben (543). Auch Dezimalzahlen sind erlaubt — mit und ohne Vorkommast eilen (13.21 oder . 57). Schließlich bleibt noch die Darstellung in der Exponentialform, wobei die Mantisse eine der eben besprochenen Formen hat und der Exponent eine positive oder negative ganze Zahl sein muß (3E7 oder 0.314E+1). Allen diesen eben beschriebenen Zahlen dürfen führende Nullen voranstehen (001 oder 03E-3). Es sind beliebig viele Vorzeichen erlaubt. Jedes Minuszeichen ändert das Vorzeichen des Ergebnisses, Pluszeichen werden ignoriert. MUMPS geht bei der numerischen Interpretation von links nach rechts vor. Sie wird abgebrochen, sobald ein Zeichen erkannt wird, das nicht in das oben angegebene Muster ρ aßt. Falls keine numerische Interpretation möglich ist, hat die Zeichenkette den numerischen Wert 0. Die Rechenzeichen „+" und „-" wurden schon als zweistellige numerische Operationen eingeführt. Im Zusammenhang der numerischen Interpretation von Zeichenketten haben sie aber noch eine andere (einstellige) Bedeutung: wenn vor einem String ein „+" oder ein „-" steht, dann wird er numerisch interpretiert. Abbildung 1.2 verdeutlicht den Sachverhalt: Im Beispiel 1 wird durch die Addition die numerische Interpretation der beteiligten Operanden erzwungen. Das nächste Beispiel zeigt, daß die Zeichenkette "abcl23" numerisch zu 0 und die Zeichenkette 123abc zu 123 interpretiert wird. Die einstellige Operation + (wie auch -) erzwingt ebenfalls die numerische Interpretation, die im 3. Beispiel das Ergebnis 52 liefert, weil das Komma das erste nicht mehr numerisch interpretierbare Zeichen ist.

30

Kapitel 1. Grundlagen der Programmierung

>WRITE "12 Elefanten"+"5 Giraffen" 17 >WRITE "abcl23"+"123abc" 123 >SET a="52,43" WRITE +a 52 >WRITE +"1.2E2xyz"

in MUMPS

(Beispiel 1) (Beispiel 2) (Beispiel 3) (Beispiel 4)

120 >

Abbildung 1.2: Beispiele zur numerischen Interpretation Das letzte Beispiel schließlich zeigt, daß die numerische Interpretation auch Zahlen in Exponentialschreibweise mit einbezieht. Der Vollständigkeit halber sei noch darauf hingewiesen, daß manche Funktionen die Ganzzahlinterpretation ihrer Argumente benotigen. Dabei versteht man unter der Ganzzahlinterpretation einer Dezimalzahl den ganzzahligen Anteil dieser Zahl. Die Ganzzahlinterpretation etwa von —2.137 lautet —2. Bei der Erklärung der logischen Interpretation von Zeichenketten wird das Prinzip der numerischen Interpretation aufgegriffen. Ein einfaches Beispiel, wie man die numerische Interpretation in der Programmierpraxis verwendet, findet sich im nächsten Abschnitt.

1.7. Datum und Zeit —

1.7

$HOROLOG

31

Datum und Zeit — $HOROLOG

Es ist bei vielen Programmanwendungen sehr nützlich, über die Zeit und das Datum zu verfugen. Bei Listen, Statistiken oder Ahnlichem möchte man das Datum des Entstehens mit angeben, bei anderen Ausdrucken, die mehrmals täglich ausgegeben werden, benötigt man die Uhrzeit. MUMPS enthält die Uhrzeit in einer speziellen Variablen mit dem Namen $HOROLOG. $H0R0L0G besteht aus zwei ganzzahligen Zählern, die, durch Komma getrennt, in einer Zeichenkette enthalten sind. Beispiel

>WRITE $H0RQL0G 53741,36127 >

Merkwürdig ist zunächst das ungewöhnliche Format, das folgende Bedeutung hat: Der erste Zähler ist der Tageszähler, der zweite der Sekundenzähler. Dabei wird der Tageszähler vom 1. Januar 1841 — dem Stichtag mit dem Wert 1 — jeden Tag um 1 hochgezählt. Der 2.Februar 1988 hatte demnach den Wert 53741, das heißt, seit dem 1. Januar 1841 sind 53741 Tage vergangen. Der Sekundenzähler wird von Mitternacht an gerechnet. Das bedeutet, er liegt im Bereich 0 (0 Uhr) bis 86399 (23 Uhr 59 Minuten und 59 Sekunden). Dieses nur scheinbar komplizierte Format erfährt seine Berechtigung dadurch, daß es einfacher ist, einen Zähler hochzuzählen, als die komplexe Kalenderarithmetik zu benutzen. Hat man etwa zwei unterschiedliche Kalendertage im $H0R0L0G-Format abgespeichert, ist es einfach, daraus die Differenz zu bilden, ohne Kenntnis über Schaltmonate, unterschiedliche Tagesanzahl in den einzelnen Monaten, etc. haben zu müssen. Bei der Umwandlung der beiden Zähler in ein klar lesbares Datumsund Zeitformat bedient man sich der schon eingeführten Operatoren zur Ganzzahl- und Restdivision. Dividiert man beispielsweise eine beliebige Sekundenanzahl durch 3600, so enthält der ganzzahlige Anteil des Ergebnisses die Anzahl der Stunden. Den verbleibenden Rest erhält man durch eine entsprechende Restdivision (#). Im allgemeinen braucht man sich um solche Umwandlungen nicht zu kümmern, da (nahezu) alle MUMPS-Systeme sogenannte Dienstprogramme zur Verfügung stellen, die aus dem aktuellen Wert von $H0R0L0G lesbare Formate erzeugen. Sie können als Beipiele einer ganzen Klasse von Programmen dienen, die vom Hersteller eines MUMPS-Systems mitgeliefert werden und für verschiedene Zwecke aufgerufen werden können. Die Namen dieser Dienstprogramme fangen gewöhnlich mit einem Prozentzeichen an.

32

Kapitel

1.

Grundlagen

der Programmierung

in

MUMPS

Diese Dienstprogramme sind in dem jedem MUMPS-System mitgelieferten Systemhandbuch dokumentiert. Abschließend soll noch eine typische Anwendung der numerischen Interpretation vorgestellt werden und daraus abgeleitet die Berechnung des aktuellen Wochentags. Zunächst liefert +$H0R0L0G den Tageszähler, denn das Komma ist das erste Zeichen im Ergebnis von $H0R0L0G, das nicht mehr numerisch interpretiert werden kann. SET ntage=+$HOROLOG setzt also in die Variable ntage die Anzahl der Tage seit (einschließlich) dem 1.1.1841, einem Freitag. Die Restdivision von ntage durch 7 liefert eine Zahl zwischen 0 und 6. Sind seither genau k mal 7 Tage vergangen (also k Wochen), ist das Ergebnis der Restdivision 1. Daraus schließt man, daß wieder ein Freitag vorliegen muß. Ist das Ergebnis eine 2, schließt man entsprechend, daß k Wochen und 1 Tag vergangen sind, also Samstag ist. Die anderen möglichen Ergebnisse der Restdivision interpretiert man entsprechend. Mit ähnlichen einfachen Mitteln kann man aus einer Jahreszahl ablesen, ob ein Schaltjahr vorhegt. Dabei macht man sich die Kenntnis zunutze, daß alle durch 4 ohne Rest teilbaren Jahre Schaltjahre sind. Es gibt freilich Ausnahmen2, die eine Sonderbehandlung erforderlich machen. Die angegebene Regel ist ohne Einschränkung in diesem Jahrhundert bis 1999 gültig (ausnahmsweise sogar auch im Jahr 2000 und damit im gesamten 21. Jahrhundert).

2

Jahreszahlen, die durch 100, aber nicht durch 400 teilbar sind, sind keine Schaltjahre

1.8.

Die Syntax einer

1.8

Befehlszeile

33

Die Syntax einer Befehlszeile

Es wurde schon weiter oben erwähnt, daß in einer Befehlszeile beliebig viele Befehle stehen dürfen. Es muß nur gewährleistet sein, daß die Zeile nicht mehr als 255 Zeichen enthält. Die Befehle müssen jeweils durch mindestens ein Leerzeichen vom vorhergehenden Befehl getrennt sein. Sie werden in der Reihenfolge ausgeführt, in der sie innerhalb der Zeile stehen (von links nach rechts). Beispiel >READ "Eingabe : ",x WRITE !,"Deine Eingabe war: ",x Eingabe : Hallo Deine Eingabe war: Hallo >

Befehle selbst bestehen aus einem Befehlswort, dem ein Argument oder eine Liste von durch Komma getrennten Argumenten folgt. Liegt eine Liste vor, so hat man das Befehlswort quasi „ausgeklammert". So stellt etwa der Befehl SET a=l,b=2,c=3 eine Abkürzung der drei Befehle SET a=l SET b=2 SET c=3 dar. Freilich gibt es auch Befehle, die kein Argument benötigen. Dazu zählt der KILL-Befehl, der sowohl mit Argument als auch ohne Argument verwendet werden kann. Für einen argumentlosen Befehl ist zwingend vorgeschrieben, daß trotz fehlendem Argument der Leerschritt geschrieben wird, der eigentlich zur Trennung von Befehl und Argument vorgesehen ist. Falls dem Befehl ein weiterer Befehl in der gleichen Zeile folgt, müssen die Befehle also durch zwei Leerschritte voneinander getrennt sein. Als weiteres Beispiel für einen argumentlosen Befehl soll der HALT-Befehl eingeführt werden, der dem Rechner das Ende der Arbeit mitteilt. Dieser Befehl wird ausschließlich ohne Argumente geschrieben. Beispiel >HALT

34

Kapitel 1. Grundlagen der Programmierung in MUMPS

Es erscheint jetzt auch kein Prompt mehr, da das MUMPS-System keine Befehle mehr annehmen kann. Die weitere Vorgehensweise bei der Beendigung der Arbeit ist wieder sehr stark vom verwendeten Rechner abhängig. Kleinere Rechner wird man jetzt ausschalten können, während man bei größeren Rechnern, die mehrere Sprachen unterstützen, im allgemeinen auch noch dem Betriebssystem die Beendigung der Sitzung mitteilen muß (z.B. mit LOGOFF). An dieser Stelle soll auf ein MUMPS-Spezifikum hingewiesen werden: die Sprache kennt die Möglichkeit der verkürzten Schreibweise von Befehlen, Funktionen und Systemvariablen. Hierzu benutzt man im allgemeinen den ersten Buchstaben des Befehls- oder Funktionsnamens bzw. des Namens der Systemvariablen. Anstatt WRITE ist es erlaubt, W zu schreiben, anstatt $ LENGTH schreibt man kürzer $L, anstatt $H0R0L0G schreibt man $H. Teilweise Abkürzungen wie WRIT oder $LEN sind nicht erlaubt. Bei zwei Funktionen werden zwei Buchstaben zur Abkürzung benutzt, da sonst die Eindeutigkeit nicht mehr gewährleistet wäre. Darauf wird bei der Beschreibung dieser Funktionen explizit hingewiesen werden. Die Möglichkeit der abgekürzten Schreibweise in MUMPS ist mancher Kritik ausgesetzt, weil die Lesbarkeit von Programmen leidet. Andererseits verwenden geübte MUMPS-Programmierer ausschließlich diese abgekürzte Schreibweise und es wird glaubwürdig berichtet, daß diese Programmierer die Langform der Sprachelemente als schlecht lesbar empfinden. An dieser Stelle sei noch darauf hingewiesen, daß es möglich ist, für die Namen der verschiedenen Sprachelemente die Kleinschreibung zu verwenden. Also ist es erlaubt, write anstatt WRITE zu schreiben, auch in der abgekürzten Form w. Zur besseren Unterscheidung zwischen Namen von Befehlen und Namen von Variablen (die in diesem Buch klein geschrieben werden) empfiehlt es sich jedoch, Befehlsworte groß zu schreiben. Es sei empfohlen, einige der bisherigen Beispiele mit der abgekürzten Befehlsschreibweise nachzuvollziehen. In diesem Text werden künftig aus didaktischen Gründen beide Schreibweisen benutzt. Zum Abschluß dieses Abschnitts und des ersten Kapitels soll noch darauf hingewiesen werden, daß im weiteren Verlauf des Textes auf das nachgestellte carriage return an jedem Zeilenende (im Zeichen ) verzichtet wird.

Kapitel 2 Programme und Programmstrukturen

2.1 2.1.1

Programme in MUMPS Aufbau eines Programms

Die Beispiele, die bis jetzt betrachtet wurden, waren streng genommen noch keine vollständigen Programme, sondern nur Anweisungen an den Rechner, die dieser direkt ausgeführt hat. Daher nennt man den für die Beispiele benutzten Betriebsmodus des MUMPS-Systems Direktmodus. MUMPS kennt noch eine andere Betriebsweise, den Programmodus. In diesem Modus werden, wie der Name schon sagt, Programme ausgeführt, die zu einem früheren Zeitpunkt geschrieben und abgespeichert wurden. Programme bestehen aus einer beliebigen (aber durch die Speichergroße begrenzten) Anzahl von Anweisungszeilen. Die Anweisungen werden in der Reihenfolge ausgeführt, in der sie im Programm stehen. Das bedeutet, daß MUMPS nach der Ausführung einer Programmzeile auf die direkt darauf folgende übergeht. Ausnahmen von dieser Regel werden später noch besprochen. Eine Programmzeile besteht aus drei Teilen. Der erste Teil ist das sogenannte „Label" der Zeile. Es braucht nur bei Bedarf angegeben zu werden. Dieses Label gibt der Programmzeile einen Namen, unter dem in anderen Programmteilen auf sie Bezug genommen werden kann. Das Label ist entweder in Form einer natürlichen Zahl aufgebaut oder entsprechend den für Variablennamen geltenden Regeln. Das heißt, im ersten Fall besteht es nur aus Ziffern und im zweiten Fall fängt es mit dem Prozentzeichen oder einem Buchstaben an, die von Buchstaben oder Ziffern gefolgt werden. Labels müssen eindeutig sein, es darf also ein bestimmtes Label nur einmal in einem Programm vorkommen. Beispiele für gültige Labels sind: Al

xyz

7.1

%

123

Nicht gültig sind: 2A

Ol

a-x

A'/,B

-3

Kapitel 2. Programme und Programmstrukturen

36

Der zweite Teil einer Programmzeile muß auf jeden Fall geschrieben werden. Er dient dazu, MUMPS zu zeigen, ob ein Label vorhanden ist und, wenn ja, wo es zu Ende ist. Der Standard sieht vor, daß hierfür ein oder eine Folge von Leerzeichen benutzt wird. Der dritte Teil einer Zeile enthält die eigentlichen Anweisungen. Er ist nach den gleichen Konventionen aufgebaut, die weiter oben schon für den Direktmodus besprochen wurden. Die Gesamtlänge einer Programmzeile darf einschließlich Label und Trennzeichen nicht größer als 255 Zeichen sein. Beispiel AUSG WRITE "HALLO" ; Programmzeile mit Label 'AUSG' SET A=5,B=3 ; Programmzeile ohne Label Allen Systemen ist jedoch gemeinsam, daß jedes Programm einen eindeutigen Namen erhalten muß, unter dem es später aufgerufen werden kann. Für diesen Namen gelten die gleichen Konventionen, wie sie schon für Variablennamen dargestellt wurden. Also fängt ein Programmname mit einem Buchstaben oder einem Prozentzeichen an. Die weiteren Zeichen sind Buchstaben oder Ziffern. Beispiele gültiger Programmnamen sind: Beispiel Ζ

Plot

PI

'/.LIST

7.1

7.

Erlaubt sind große und kleine Buchstaben, wobei der Sprachstandard offen läßt, ob zwischen großen und kleinen Buchstaben unterschieden wird. Bei der Namensgebung sind einige Punkte zu beachten, in denen sich verschiedene MUMPS-Systeme unterscheiden können. Zum Beispiel ziehen viele Systeme zur Unterscheidung von Programmnamen nur die ersten acht Zeichen heran. Daher sollten sich die Namen aller Programme schon in den ersten acht Stellen unterscheiden. Die meisten MUMPS-Systeme sehen Programme, deren Name mit einem Prozentzeichen beginnt, als Systemprogramme an. Dies hat zur Folge, daß nur der SystemVerwalter dieser Systeme die sogenannten „Prozent programme" abspeichern oder verändern darf. Daher empfiehlt es sich, das Prozentzeichen zu Beginn des Namens normaler Anwendungsprogramme nicht zu benutzen. Es hat sich herausgestellt, daß die Programmbibliothek bei wachsender Anzahl von Programmen sehr schnell unübersichtlich wird. Daher ist es eine gute Vorgehensweise, alle Namen von Programmen mit dem Namenskürzel des Programmierers oder mit einer Kurzbezeichnung des Projektes, für das sie geschrieben wurden, beginnen zu lassen.

2.1.

Programme in MUMPS

37

Abbildung 2.1 zeigt die Struktur eines typischen MUMPS-Programms. Dabei wurde von einer weit verbreiteten Konvention über den Aufbau der ersten Zeile Gebrauch gemacht: sie enthält als Label den Namen des Programms und sonst nur noch einen Kommentar mit Autor, Erstellungsdatum und ähnlichen Angaben. Die meisten MUMPS-Systeme erlauben den Ausdruck der ersten Zeile aller Programme einer Programmbibliothek durch Aufruf eines besonderen Dienstprogramms.

PROG ; H/K ; 11.3.88 ; ; Struktur eines typischen MUMPS-Programms - Befehlszeilen ; zusaetzliche Kommentare LAB ; lokales Unterprogramm mit Namen LAB - Befehlszeilen

Abbildung 2.1: Die Struktur eines typischen MUMPS-Programms Zum Abschluß sei noch darauf hingewiesen, daß die Größe eines Programms 5000 Zeichen nicht übersteigen sollte. Zwar erlauben viele Hersteller größere Programme, aber aus Gründen der Übertragbarkeit auf andere Rechner sollte diese Grenze nicht überschritten werden.

2.1.2

Aufruf eines Programms — der DO-Befehl

In MUMPS gibt es keinen speziellen Befehl zum Starten eines Programms. Stattdessen wird der gleiche Befehl benutzt, der auch dem Aufruf von Unterprogrammen dient: der DO-Befehl. Formal kann daher jedes Programm als aus dem Direktmodus aufgerufenes Unterprogramm aufgefaßt werden. Beispiel >D0 "PROG

(Ausführung des Programms) > Wie man sieht, wird der Programmname als Argument des DO-Befehls angegeben. Vor dem Namen steht ein Zirkumflex das in MUMPS oft auch Globalzeichen genannt wird. Es legt fest, ob ein Name global oder lokal ist. Global bedeutet, daß der Name und damit auch das durch diesen

38

Kapitel 2. Programme und

Programmstrukturen

Namen bezeichnete Objekt (in diesem Fall: Programm), nicht im Hauptspeicher, sondern auf dem Massenspeicher, also z.B. der Platte oder der Floppy disk, zu finden ist. Im Gegensatz dazu kann man auch in einem Programm Unterprogramme aufrufen, die in diesem Programm selbst enthalten sind (siehe im Programm PROG). Diese Unterprogramme nennt man lokal. Auf die Bedeutung „lokaler Unterprogramme" wird im nächsten Abschnitt noch genauer eingegangen. Der DO-Befehl erlaubt die Angabe mehrerer durch Kommata getrennter Argumente. Diese Programme werden dann der Reihe nach ausgeführt. MUMPS kennt auch ein Gegenstück zum DO-Befehl, der die Beendigung des Programmlaufs an einer beliebigen Stelle erlaubt. Dieser Befehl heißt QUIT. Beispiel

TEST ; H/K ; 11.8.88 ; Programm testet den QUIT-Befehl WRITE "vor dem QUIT" QUIT WRITE "nach dem QUIT" Die Ausführung erbringt: >D0 "TEST vor dem QUIT >

Unterprogramme können dadurch beendet werden, daß der Programmfluß entweder das Ende des Programmtextes oder ein QUIT erreicht. Sobald ein QUIT in einem Unterprogramm erreicht wird, wird der nächste Befehl nach dem aufrufenden DO ausgeführt. QUIT ist in der hier vorgestellten Form ein argumentloser Befehl. Daher gelten auch hier die bei der Erklärung des KILL-Befehls gemachten Anmerkungen zur Schreibweise: zwischen QUIT und einem eventuell in der gleichen Zeile nachfolgenden anderen Befehl müssen zwei Leerzeichen geschrieben werden. Dies mag zunächst unsinnig erscheinen, da die Programmausführung nie den dem QUIT nachfolgenden Befehl erreichen kann, es wird jedoch später im Zusammenhang mit der Nachbedingung eine sehr sinnvolle und häufig verwendete Anwendung gezeigt.

2.2.

Lokaler und globaler

2.2

Aufruf

39

Lokaler und globaler Aufruf

Wie bereits im vorigen Abschnitt gezeigt wurde, wird ein Programm mit dem Befehl DO "Programmname gestartet. Diese Anwendung des DO-Befehls ist jedoch nur ein Sonderfall. Im allgemeinen wird der DO-Befehl zum Aufruf von Unterprogrammen benutzt. Beim Start eines Programms mit diesem Befehl wird von einer Besonderheit von MUMPS Gebrauch gemacht: Die Sprache unterscheidet nicht zwischen Programmen und Unterprogrammen. Alle Programme werden als aus dem Direktmodus aufgerufene Unterprogramme angesehen. Diese Konvention von MUMPS hat insbesondere den Vorteil, daß man beim Austesten eines Unterprogramms nicht auf ein Testprogramm, das das Unterprogramm aufruft, angewiesen ist. Der Test erfolgt einfach durch direkten Start des Unterprogramms. Um Unterprogramme aufzurufen, muß nur der DO-Befehl in einem Programm gegeben werden. Dabei wird Bezug genommen auf ein Zeilenlabel. Das Label einer Programmzeile in MUMPS beginnt immer an der ersten Stelle einer Zeile und wird durch ein Leerzeichen beendet. Fängt eine Zeile mit einem Leerzeichen an, so hat sie kein Label. Die .zulässigen Schreibweisen für Labels wurden bereits bei der Festlegung der allgemeinen Form eines Programms besprochen. Das nächste Beispiel zeigt in allgemeiner Form den Aufruf eines lokalen Unterprogramms LUP aus einem Programm PI heraus. Beispiel

PI ; H/K ; 29.4.88 ; Aufruf eines lokalen Unterprogramms DO LUP QUIT LUP ; Label LUP QUIT Deutlich wird hier die Verwendung des QUIT-Befehls, um einerseits das Programmsegment, das mit dem Label PI eingeleitet wird, vom lokalen Unterprogramm LUP abzugrenzen und anderseits um LUP zu beenden. Nach Ausführung von LUP fährt das Programm mit dem nächsten Befehl nach dem Unterprogrammaufruf DO LUP fort. Ein substantielleres Beispiel (Abbildung 2.2) zeigt jetzt schon deutlich die Struktur und Art der Verwendung lokaler Unterprogramme.

40

Kapitel 2. Programme und

URLAUB

Programmstrukturen

; H/K ; 11.3.88 ; Urlaubsplaene

ί ··· W !,"Einige Fragen zu Ihren Urlaubsplaenen" R !,"Wollen Sie dieses Jahr in Urlaub fahren? (J/N) : ",jn (falls Antwort gleich "N") QUIT R !,"Wollen Sie eine Pauschalreise buchen? (J/N) : ",jn (falls Antwort gleich "J") DO PAUSCH R !,"Wollen Sie eine Flugreise buchen? (J/N) : ",jn (falls Antwort gleich "J") DO FLUG ; Weitere Fragen zu den Urlaubsplaenen QUIT PAUSCH ; Detailfragen zu Pauschalreisen R !,"Wieviele Wochen wollen Sie verreisen? : ",azw R !,"Wieviele Personen insgesamt? : ",api ; Weitere Fragen zu Pauschalreisen... QUIT FLUG ; Detailfragen zu Flugreisen

QUIT

Abbildung 2.2: Struktur und Art der Verwendung lokaler Unterprogramme Wenn auch die Möglichkeiten des bedingten Programmaufrufs jetzt noch nicht zur Verfügung stehen, wird doch klar, wie dieses Programm nach dem Aufruf mit DO "URLAUB abläuft. Falls die generelle Frage nach einem Urlaub in diesem Jahr bereits verneint wird, wird das Programm schon hier abgebrochen. Im anderen Fall werden Fragen nach der gewünschten Urlaubsart gestellt, die mit „J" oder mit „N" beantwortet werden sollen. Falls die Frage nach der Pauschalreise bejaht wird, wird zeitweilig in das lokale Unterprogramm PAUSCH verzweigt. Dort werden entsprechende Detailfragen gestellt. Nach Abarbeiten dieses Unterprogramms wird im aufrufenden Programm die nächste Frage („Wollen Sie eine Flugreise buchen?") gestellt und entsprechend verfahren, usw. Die Einsprungpunkte von globalen Unterprogrammen müssen nicht immer am Anfang des Programmtextes liegen, sondern es können — wie auch bei lokalen Unterprogrammen — beliebige Zeilen angesprungen werden. Das geschieht in der Form, daß man zuerst das Label innerhalb des globalen Unterprogramms schreibt, gefolgt vom Globalzeichen (") und dieses gefolgt vom Programmnamen.

2.2.

Lokaler und globaler

Aufruf

41

Beispiel >D0 ΡAUSCH"URLAUB Wieviele Wochen wollen Sie verreisen? : 3 Wieviele Personen insgesamt? : 4 (..) >

Es wird also ein lokales Unterprogramm eines an sich globalen Programms aus dem Direktmodus aufgerufen. Doch damit sind immer noch nicht alle Möglichkeiten aufgeführt, die MUMPS bietet, um eine Zeile anzusprechen. Es besteht auch die Möglichkeit, auf Zeilen zu springen, die kein Label haben. Dazu muß man einfach irgend eine andere Zeile des gleichen Programms nehmen, die vor der gewünschten Zeile liegt und die ein Label hat. Jetzt zählt man, wie viele Zeilen weiter die gewünschte Zeile liegt. Dieser Abstand wird Offset genannt. Die Angabe einer Zeile mit Hilfe eines Offsets geschieht nun dadurch, daß man anstelle des Labels der gewünschten Zeile das Label einer davorliegenden Zeile, ein Pluszeichen (+) und das Offset schreibt. Beispiel >D0 PAUSCH+2~URLAUB Wieviele Personen insgesamt? : 4 (...) >

Hier wird also auf die zweite Zeile nach dem Label PAUSCH verzweigt und von dort ab das lokale Unterprogramm bis zum QUIT abgearbeitet. Die Schreibweise mit Pluszeichen und Offset ist in MUMPS zwar immer zulässig, aber sie sollte nur in Ausnahmefällen und auch dann nur mit größter Vorsicht benutzt werden. Angenommen, man würde in dem Programmsegment A DO A+2 quiT W "UNTERPROGRAMM" QUIT eine Zeile nach der Zeile mit dem Label Α einfügen, so würde das Programm nicht mehr in der erwarteten Weise laufen, da der DO-Befehl direkt auf das QUIT führen würde:

42

Kapitel 2. Programme und

Programmstrukturen

A DO A+2 ; DIESE ZEILE WURDE EINGEFUEGT quiT W "UNTERPROGRAMM" QUIT Die Erfahrung hat gezeigt, daß man bei Änderungen eines Programms nicht immer die gesamte Programmumgebung auf die Existenz einer Zeilenreferenz mit Offset hin absucht. Daher können sich hier leicht Fehler einschleichen, die nur sehr schwer zu finden sind. Trotzdem sollen noch einige Sonderfälle dieser Form des Aufrufs beleuchtet werden. Beispiel

DO ANF+B/5 DO 3+2 Wie der erste Aufruf zeigt, ist als Offset nicht nur ein Literal erlaubt, sondern ein beliebiger Ausdruck, dessen Ergebnis als ganze Zahl interpretiert wird, das heißt, Nachkommastellen werden ignoriert. Zu beachten ist ferner, daß die Zeilenreferenz ANF+B/5 nicht in ihrer Gesamtheit als Ausdruck ausgewertet wird. Es wird zuerst der Teil rechts des Pluszeichens (B/5) ausgewertet und dann, ausgehend vom Label ANF, die (B/5)-te Nachfolgezeile als Einsprungstelle des Unterprogramms benutzt. Auch die Anweisung DO 3+2 führt nicht etwa zum Label 5, sondern zur zweiten Zeile nach dem Label 3. Abschließend soll noch einmal auf den QUIT-Befehl eingegangen werden. Der QUIT-Befehl ist — wenn er zur Beendigung eines lokalen Unterprogramms verwendet wird — argumentlos. Mit dieser Aussage wird deutlich, daß der Befehl noch an anderer Stelle zum Einsatz kommt (siehe hierzu Abschnitt 2.4). Weiterhin wird ein Unterprogramm verlassen, wenn das Ende des Programmtextes erreicht wird. In diesem Fall verhält sich MUMPS so, als stände am Ende des Programmtextes ein QUIT. Jetzt wird auch deutlich, warum ein QUIT oder das Ende des Programmtextes die Ausführung eines Hauptprogramms beenden: das MUMPS-System sieht ein Hauptprogramm als aus dem Direktmodus aufgerufenes Unterprogramm an. Daher wird es in der gleichen Weise wie ein Unterprogramm beendet. Diese Beendigung sorgt für die Rückkehr in den Direktmodus.

2.3.

Wertübergabe

2.3

beim

Unterprogrammaufruf

43

Wertübergabe beim Unterprogrammaufruf

Beim Aufruf von Unterprogrammen hat man im allgemeinen den Wunsch, Parameter an dieses zu übergeben und Ergebnisse zurückzuerhalten. Dazu wird beim Aufruf mit dem DO-Befehl dem Programmnamen eine Liste von aktuellen Parametern nachgestellt, während dem aufgerufenen Label eine Liste von formalen Parametern angefügt wird. Die Liste der aktuellen Parameter wird dem Programmnamen in Klammern und durch Kommata getrennt nachgestellt. Also würde D Pl(al,a2) bedeuten, daß das lokale Unterprogramm PI aufgerufen wird und dabei die lokalen Variablen al und a2 übergeben werden. Als Programmname sind dabei die schon beim DO-Befehl ohne Wertübergabe erläuterten Formen erlaubt. Ausnahme ist die Schreibweise mit Offset. Die Liste der formalen Parameter wird wie die Liste der aktuellen Parameter in Klammern gesetzt. Die einzelnen Argumente werden durch Kommata getrennt und dem Labelnamen nachgestellt. Dabei dürfen die formalen Parameter beliebige Namen haben. Die Zuordnung zwischen aktueller und formaler Parameterliste geschieht über die Reihenfolge der Parameter. Ein einfaches Beispiel erläutert den Sachverhalt: Beispiel

s a«5,b=19 D MULT(a,b) MULTCml,m2) s m=ml*m2 U ! ,m QUIT Die Liste der aktuellen Parameter im aufrufenden Programm besteht hier aus den beiden lokalen Variablen a und b, die über diesen Aufruf an das Unterprogramm MULT übergeben werden. Dort werden ihre Werte in dieser Reihenfolge den formalen Parametern ml und m2 zugewiesen. Die in der Liste der formalen Parameter angegebenen Namen bleiben bis zum das Unterprogramm abschließenden QUIT zugreifbar. Die einzelnen aktuellen Parameter können beliebige Ausdrücke sein, also Variablen, Funktionsaufrufe oder zusammengesetzte Ausdrücke. Sie werden vor der Zuweisung an die formalen Parameter ausgewertet. Wichtig ist nur, daß die Anzahl der Werte in der Liste der aktuellen Parameter kleiner oder gleich der Anzahl der Werte in der Liste der formalen Parameter ist, denn die Werte der aktuellen Parameter werden den entsprechenden Variablen

44

Kapitel 2. Programme

und

Programmstrukturen

der Liste der formalen Parameter im Unterprogramm zugeordnet. Natürlich müssen auch die in den aktuellen Parametern vorkommenden Variablen definiert sein, da sonst die Auswertung der Ausdrücke nicht möglich ist. Im Unterprogramm stehen nicht nur die formalen Parameter mit den aktuellen Werten zur Verfügung, sondern auch alle Variablen, die im aufrufenden Programm definiert sind. Variablen, die den gleichen Namen wie formale Parameter haben, werden durch die formalen Parameter „verdeckt". Sie sind innerhalb des Unterprogramms nicht zugreifbar, bleiben jedoch unverändert erhalten. Erst bei Ausführung des das Unterprogramm beendenden QUIT werden sie wieder sichbar, da dann die formalen Parameter verschwinden. Da die Variablen des rufenden Programms im gerufenen Programm zugreifbar sind, liegt es nahe, auf die explizite Parameterübergabe zu verzichten und Variablen des rufenden Programms einfach im Unterprogramm zu verwenden. Dagegen spricht jedoch, daß es bei dieser Vorgehensweise sehr schwer wird, allgemeingültige Unterprogramme zu schreiben, da das Unterprogramm Annahmen über die Namen der Variablen im Hauptprogramm machen muß. Weiterhin sind solche Programme sehr unübersichtlich, da es nur schwer erkennbar ist, welche Variablen als Parameter des Unterprogramms verwendet werden. Diese Gründe sprechen dafür, die explizite Wertübergabe allgemein zu benutzen — insbesondere im Zusammenspiel mit dem im nächsten Abschnitt (2.5) besprochenen NEW-Befehl. Der Zweck der bisher erläuterten Wertübergabe an lokale oder globale Unterprogramme mag einleuchten, aber zwei Sachverhalte sind noch nicht ganz deutlich geworden. Wie schon weiter oben gesagt, verschwinden die formalen Parameter bei der Ausführung des das Unterprogramm beendenden QUIT-Befehls. Wenn im Laufe des Unterprogramms neue Werte an Variablen zugewiesen wurden, die in der Liste der formalen Parameter stehen, verschwinden damit auch die neuen Werte. Unterprogrammparameter in der oben dargestellten Form können also nicht zur Rückgabe von Werten benutzt werden. Dieses Verhalten der Parameterübergabe wird „call by value"1 genannt. Um auch Werte über Unterprogrammparameter zurückgeben zu können, sieht MUMPS eine zweite Form der Wertübergabe vor, die „call by reference" genannt wird. Dazu wird beim Unterprogrammaufruf vor die Namen der aktuellen Parameter, die der Rückgabe von Werten dienen sollen, ein Punkt (.) geschrieben. In diesem Fall sind als aktuelle Parameter nur Variablen und keine allgemeinen Ausdrücke erlaubt. Wenn nun im Unterprogramm der Wert des entsprechenden formalen Parameters geändert wird, ändert sich auch der Wert des aktuellen Parameters. Bemerkenswert ist dabei, daß das rufende Programm allein kontrolliert, ob ein Parameter vom Unterprogramm verändert werden darf oder nicht.

2.3.

Wert Übergabe beim

Unterprogrammaufruf

45

Beispiel

S sl»7,s2=3 D ADD(sl,s2,.res) W !»res

ADD(xl,x2,y) S y«xl+x2 QUIT Dieses Beispiel zeigt, daß als aktuelle Parameter die beiden lokalen Variablen s l und s2 übergeben werden und das Resultat in der lokalen Variablen r e s erwartet wird. Dem Namen dieser Variablen wird ein Punkt vorangestellt. In der Liste der formalen Parameter darf dieser Punkt nicht erscheinen. Bei dieser Form der Wertübergabe ist es nicht erforderlich, daß die als aktueller Parameter angegebene Variable schon im voraus definiert ist. Wichtig ist, daß im Unterprogramm alle Änderungen (einschließlich des Löschens durch den KILL-Befehl) des formellen Parameters unmittelbar mit dem Wert der als aktueller Parameter angegebenen Variablen verknüpft sind. Die Zuweisung des Additionsergebnisses an y spiegelt sich also sofort in r e s wider. Das hat interessante Konsequenzen, wenn die gleiche Variable mehrfach als Parameter in der Punktnotation angegeben wird: Beispiel

S x=4 D Κ Τ ( . χ , . χ ) KT(a,b) . . . KILL b SETT a=5 QUIT Im aufgerufenen Unterprogramm repräsentieren sowohl a als auch b die Variable χ des rufenden Programms. Dadurch hat KILL b zur Folge, daß χ und damit auch a Undefiniert werden. Die spätere Zuweisung SET a=5 wirkt sich ähnlich aus: χ und somit auch b erhalten den Wert 5. Mit diesem Mechanismus der Wertübergabe und des Rücktransfers von Ergebnissen hat man in MUMPS die Möglichkeit, allgemeingültige Programme (Bibliotheksroutinen) zu formulieren, die aus unterschiedlichen Situationen heraus aufgerufen werden können.

46

Kapitel 2. Programme und

2.4

Programmstrukturen

Benutzerdefinierte Funktionen und spezielle Variablen

Es gibt häufig Situationen, in denen von einem Unterprogramm, dem ein oder mehrere Werte übergeben und dort verarbeitet werden, nur ein einziger Wert als Ergebnis zurückgeliefert wird. Dies ist oft in mathematischen Berechnungen, aber nicht nur dort, der Fall. Man nennt solche Unterprogramme, die genau einen Wert zurückliefern, „Funktionen". Eine Funktion läßt sich prinzipiell als ein Unterprogramm realisieren, das mit einem DO-Befehl, dessen Parameterliste genau einen Rückgabeparameter enthält, aufgerufen wird. Da jedoch bekannt ist, daß Funktionen genau einen Wert zurückliefern, ist es eleganter, sie in gleicher Form wie die Systemfunktionen ($LENGTH etc.) aufzurufen, so daß der Funktionswert direkt in beliebigen Ausdrücken verwendet werden kann. MUMPS erlaubt beliebige vom Benutzer definierte Funktionen. Zur Unterscheidung von Systemfunktionen muß der Name einer solchen Funktion mit zwei Dollarzeichen ($$) beginnen. Der Aufruf einer vom Benutzer definierten Funktion hat also folgendes Aussehen: $$Funktionsname(Argl,Arg2,...,Argn)

Wichtig ist, daß mehrere Argumente zur Berechnung übergeben werden können, jedoch als Ergebnis jeweils nur ein Wert zurückgegeben wird. Im folgenden Beispiel soll eine benutzerdefinierte Funktion angesprochen werden, die jeweils die Quadratwurzel ihres Argumentes liefert. Dieses Beispiel macht die Verwendung einer solchen Funktion wie auch das dazugehörige Programm deutlich. Beispiel W !,$$SQRT(2)

SQRT(x) ; Benutzerdefinierte Funktion ; (hier Code zur Berechnung einer Wurzel) QUIT y

Man sieht unmittelbar, daß sich die Art der Verwendung einer benutzerdefinierten Funktion nicht vom Aufruf einer Systemfunktion unterscheidet. Der Programm-Code zur Berechnung der Quadratwurzel ist wie ein normales lokales Unterprogramm dem Programmkörper angehängt. Beendet wird

2.4.

Benutzerdefinierte

Funktionen

47

dieses lokale Unterprogramm durch ein QUIT mit einem Argument. Dies ist der einzige Fall, in dem QUIT ein Argument trägt. Das Argument ist ein beliebiger Ausdruck, der den Wert der Berechnung dieses Unterprogramms, den sogenannten „Rückgabewert" ergibt. Der Vorteil der Verwendung von benutzerdefinierten Funktionen gegenüber dem Aufruf von Unterprogrammen liegt in der Einfachheit der Handhabung und in der großen erreichbaren Übersichtlichkeit. Die in Programmen benötigten benutzerdefinierten Funktionen können ferner ebensogut als globales Unterprogramm geschrieben und wie ein globales Programm abgespeichert werden. In diesem Fall folgt den beiden DollarZeichen die Referenz auf eine Zeile des globalen Programms, wobei jedoch die Schreibweise mit Offset nicht erlaubt ist. Beispiel >W $$"SQRT(4)

2 >W $$DEUTSCH~DATUM($H) 12.10.1988 >

Ansonsten unterscheiden sich globale benutzerdefinierte Funktionen von lokalen in keiner Weise. Abschließend sollen noch die benutzerdefinierten speziellen Variablen vorgestellt werden. Die Schreibweise entspricht der der benutzerdefinierten Funktionen, nur wird kein Argument übergeben. Insofern entspricht die Verwendung von benutzerdefinierten speziellen Variablen dem Aufruf einer benutzerdefinierten Funktion ohne Argument. Um ein einfaches Beispiel zu konstruieren sei angenommen, daß in einem Programm oft der ausgeschriebene Name des aktuellen Monats benötigt wird. Beispiel U !,$$M0NAT

MONAT ; benutzerdefinierte spezielle Variable Monatsname (Hier Code zur Ermittlung des Monatsnamens, Resultat steht in der Variablen mon) QUIT mon

48

Kapitel 2. Programme

und

Programmstrukturen

Anstatt ein lokales Unterprogramm zu definieren, hätte man genauso gut ein globales Unterprogramm definieren können. Der Aufruf würde dann über $$*M0NAT erfolgen.

2.5.

2.5

Sichtbarkeit

von lokalen Variablen

49

Sichtbarkeit von lokalen Variablen — der NEW-Befehl

Weiter oben wurde schon erwähnt, daß die Variablen des aufrufenden Programms im aufgerufenen Programm zugreifbar sind. Dadurch besteht die Gefahr, daß im gerufenen Programm versehentlich Variablen des rufenden Programms geändert werden. Das erschwert die Erstellung von allgemein gültigen Bibliotheksprogrammen, da die dort verwendeten Variablen leicht in Namenskonflikte mit bereits definierten Variablen kommen. Daher ist es naheliegend, das schon bei der Parameterübergabe beschriebene „Verdecken" von Variablen aus dem rufenden Programm auch auf Variablen auszudehnen, die nicht in der Liste der formalen Parameter stehen. Dazu dient der NEW-Befehl, der es erlaubt, einzelne oder alle lokalen Variablen zu verbergen. Damit besteht die Möglichkeit, in einem Unterprogramm (oder einer Funktion) einen Variablennamen zu benutzen, der bereits von einem in der Aufrufhierarchie hoher stehenden Programm benutzt wurde, sofern man ihn in einem NEW-Befehl auffuhrt. So werden beispielsweise durch den Befehl NEW a , b , c die möglicherweise früher definierten Variablen a, b und c verdeckt. Danach sind innerhalb des Unterprogramms die Variablennamen a, b und c Undefiniert und können beliebig benutzt werden. Der Gültigkeitsbereich des NEW-Befehls erstreckt sich genau bis zu dem QUIT-Befehl, der das Unterprogramm beendet. Bei der Ausführung des QUIT verschwinden die neuen Werte der Variablen a, b und c und es erscheinen wieder die vor der Ausführung des NEW gültigen Werte. Neben der eben vorgestellten Form des NEW-Befehls gibt es noch zwei weitere Formen. In der argumentlosen Form wird die gesamte Liste der lokalen Variablen verdeckt. Man kann in einem so eingeleiteten Unterprogramm beliebige Variablennamen verwenden ohne in Konflikt mit Variablennamen des rufenden Programms zu kommen. Es ist allerdings dabei zu beachten, daß auch eventuell an das Unterprogramm übergebene formale Parameter verdeckt werden und daher nicht mehr zugreifbar sind. Es ist auch keine Rückgabe von Ergebnissen in lokalen Variablen möglich, da alle im Unterprogramm definierten Variablen mit Ausführung des QUIT verschwinden. Daher ist diese Form des NEW nicht zur Übergabe von Parametern und Ergebnissen geeignet. Lediglich der Parameter des QUIT bei einer benutzerdefinierten Funktion oder speziellen Variablen wird an das rufende Programm übergeben. In der dritten Form (exklusives NEW) wird eine in Klammern gesetzte Variablenliste angegeben. Dabei werden alle Variablen, bis auf die in der

50

Kapitel 2. Programme

und

Programmstrukturen

Klammer stehenden, verdeckt. Diese Form ist sehr gut geeignet, um mit der expliziten Wertübergabe kombiniert zu werden. Beispiel W !,$$P0WER(7) POWER(x) ; H/K ; 16.3.88 ; Exklusives NEW NEW (x) S y=x*x*x QUIT y In diesem Beispiel wird deutlich, wie diese Form des NEW-Befehls mit der Wertübergabe kombiniert wird. Die Variable χ wird als Parameter der Funktion "POWER benutzt, während das Ergebnis als Funktionswert zurückgegeben wird. Der NEW-Befehl ist erst seit kurzer Zeit Bestandteil des Sprachstandards. Daher wird er in älteren MUMPS-Programmen nicht benutzt. Durch seine Einfachheit und Wirksamkeit ist er jedoch von solcher Bedeutung, daß er in zukünftigen Programmen häufig zur Anwendung kommen wird. In dem rechten der folgenden beiden Beispielprogramme wird deutlich, wie der NEWBefehl den Programmierstil verändern wird. Das linke Programm zeigt den strukturellen Aufbau eines beliebigen Programms mit einem KILL auf die in diesem Programm benötigten Variablen, die im rufenden Programm keine Bedeutung mehr haben. Beispiel PI ; H/K ; 8.4.88

Κ QUIT

P2 ; H/K ; 8.4.88 NEW ()

QUIT

Das rechte Programm mit dem am Programmkopf stehenden exklusiven NEW-Befehl und der dort angegebenen Liste der importierten und zurückgegebenen Variablen kann auf das unmittelbar vor dem QUIT stehende KILL verzichten. Dadurch werden die Programme leichter lesbar, denn man erkennt sofort die in dieses Programm eingebrachten und zurückgegebenen

2.5. Sichtbarkeit von lokalen Variablen

51

Variablen. Weiter muß sich der Programmierer nicht mehr um versehentliche Doppelbenutzung von Variablennamen kümmern. Der NEW-Befehl wird im Zusammenhang mit indizierten Variablen noch einmal angesprochen werden.

52

Kapitel 2. Programme und

2.6

Programmstrukturen

Programmverzweigung mit GOTO

Die bisher behandelten Befehle hatten alle eines gemeinsam: Der Programmablauf wird nach der Abarbeitung eines Befehls direkt nach diesem Befehl fortgesetzt. Dabei wird unter Abarbeitung eines Unterprogrammaufrufs die Abarbeitung des gesamten Unterprogramms verstanden. In manchen Fällen ist es jedoch notwendig, einen Sprung innerhalb des Programmablaufs vorzunehmen, so daß die Programmausführung — ähnlich wie bei einem DO — an einer frei wählbaren Stelle fortgesetzt wird. Hierzu wird in MUMPS, wie in den meisten anderen Programmiersprachen auch, der GOTO-Befehl verwendet. Der GOTO-Befehl ist wie der DO-Befehl aufgebaut. Es können die gleichen Argumente angegeben werden und auch die Wirkung ist ähnlich. Der Unterschied zum DO besteht darin, daß die Programmausführung nicht mehr hinter den GOTO-Aufruf zurückkehrt. Beispiele

GOTO ANFANG G -MENUE G FELD+3-EINGABE Obwohl in den beiden letzten Beispielen auf ein globales Programm verzweigt wird, sollte der GOTO-Befehl aus Gründen der Übersichtlichkeit nur lokal verwendet werden. Eine typische Verwendung des Befehls besteht im Programmieren einer Iteration. Darunter versteht man die wiederholte Ausführung eines Programmteils und zwar solange, bis eine Bedingung erfüllt ist, die die Beendigung dieses Segments bewirkt. Beispiel

START ; typische Struktur einer Iteration (Programmzeile 1) (Programmzeile n) (falls Bedingung erfuellt) GOTO START WEITER

Die Programmzeilen 1 bis η stehen dabei für das wiederholt auszuführende Programmsegment, beispielsweise eine mathematische Berechnung. In Abhängigkeit einer Bedingung wird bei jedem Durchlauf geprüft, ob erneut

2.6.

Programmverzweigung mit GOTO

53

das Segment durchlaufen werden muß, oder ob zur nächsten Programmzeile mit dem Label WEITER verzweigt werden kann. Es sei an dieser Stelle bereits darauf hingewiesen, daß die Iteration in MUMPS eleganter mit dem FOR-Befehl ausgeführt werden kann, der die wiederholte Ausführung eines Programmsegmentes bewirkt. Ein weitere typische Verwendung von GOTO zeigt das nächste Beispiel. Beispiel LI R »."Kundennr: ",knr (falls falsches Eingabeformat) G LI Im Dialog wird hier ein Datenfeld erfaßt und sogleich abgeprüft, ob die Eingabe das richtige Format hatte. Die Mittel dazu werden später deutlich werden. Falls ein Eingabefehler erkannt wurde, wird mit dem Befehl GOTO LI an den Beginn der Zeile verzweigt und erneut der Dialog gestartet. Allgemein wird von der Verwendung des GOTO-Befehls abgeraten, weil sie leicht zu unübersichtlichen Programmen führen kann. In vielen Fällen zeugt die Verwendung des GOTOs von einer nicht genügend durchdachten Programmstruktur. Diese kann häufig ohne Einbuße an Funktionalität durch gewöhnliche Unterprogrammaufrufe mit dem DO-Befehl realisiert werden.

54

Kapitel 2. Programme und

2.7

Programmstrukturen

Zeichenketten als Programmzeilen — der XECUTE-Befehl

In diesem Abschnitt soll zum ersten Mal auf die besonderen Möglichkeiten von MUMPS hingewiesen werden, den Inhalt von Variablen als Programm oder als Teil eines Programms auszuführen. MUMPS kennt zwei unterschiedliche Ansätze, die in diesem Zusammenhang anzuführen sind: Die Indirektion als Möglichkeit der „indirekten" Ausführung von Teilen eines Befehls (siehe hierzu Abschnitt 3.10) und den XECUTE-Befehl.

Mit diesem Befehl kann man den Inhalt einer Variablen als einzeiliges Unterprogramm ansehen und als solches ausführen. Ein einfaches Beispiel macht das Konzept deutlich: Beispiel

>READ "Bitte geben Sie eine Befehlszeile ein : ",mbz Bitte geben Sie eine Befehlszeile ein : R χ W !,x*x >W mbz R χ W !,x*x >XECUTE mbz 12

144 >

Dieser Dialog besteht in der Eingabe einer MUMPS-Befehlszeile in die Variable mbz. Das sich anschließende W mbz schreibt diese Befehlszeile als gewohnliche Zeichenkette, während der Befehl XECUTE mbz diese Befehlszeile ausführt. Die Ausführung besteht darin, daß eine Zahl gelesen wird und das Quadrat der Zahl anschließend ausgeben wird. Die generelle Ausführung eines XECUTE-Befehls läßt sich wie folgt charakterisieren: zuerst wird der Ausdruck, der als Argument angegeben ist, ausgewertet. Die sich ergebende Zeichenkette wird als einzeiliges Unterprogramm angesehen und als solches ausgeführt. Sie besteht nur aus dem Zeilenrumpf, enthält also weder Label noch Trennzeichen, das Label und Zeilenrumpf voneinander trennt. Auch die Beendigung der Ausführung des XECUTE-Arguments folgt den bei Unterprogrammen üblichen Konventionen. Also beendet entweder ein QUIT-Befehl oder das Erreichen des Endes der Zeichenkette ihre Ausführung. Die Behandlung des XECUTE-Arguments als Unterprogramm hat interessante Folgen, wenn GOTO-Befehle in ihm enthalten sind.

2.7. Zeichenketten als Programmzeilen

55

Beispiel TEST ; H/K ; 28.9.82 ; Test fuer XECUTE X "G A" W "Nach XECUTE",! Q A W "Im Unterprogramm",! Q Die Ausführung von TEST ergibt folgendes Resultat: >D "TEST Im Unterprogramm Nach XECUTE >

Obwohl der GOTO-Befehl aus dem XECUTE-Argument herausführt, bleibt die Rücksprungadresse erhalten und sorgt für die Ausführung des dem XECUTE folgenden Befehls, wenn ein QUIT erkannt wurde. Das GOTO wird also als Sprung innerhalb des mit XECUTE eingeleiteten Unterprogramms angesehen, der die Rücksprungadresse in keiner Weise beeinflußt. Eine besonders interessante Anwendung des XECUTE-Befehls findet man in vielen MUMPS-Systemen in Form eines Editors, der mit X "'/, aufgerufen wird. Dieser Editor macht davon Gebrauch, daß der XECUTE-Befehl die Ausführung von Programmen erlaubt, die nicht wie üblich im Programmspeicher stehen, sondern in Variablen enthalten sind. Daher kann im Programmspeicher ein beliebiges anderes Programm stehen, das mit dem Editor bearbeitet wird.

Kapitel 2. Programme und Programmstrukturen

56

2.8

Uber das Editieren von Programmen ··

Programme werden in den meisten Programmiersprachen mit Texteditoren eingegeben. Das ist in MUMPS nicht anders. Wichtig ist dabei festzuhalten, daß diese Eingabemöglichkeit in keiner Form dem Sprachstandard von MUMPS unterworfen ist, daß also die verschiedenen am Markt erhältlichen MUMPS-Systeme unterschiedliche Eingabemöglichkeiten von Programmen haben (können). Man kann daher keine für alle Systeme gültigen Regeln angeben und muß daher für jeden speziellen Fall die Bedienungsanleitung des jeweiligen Editors zur Hand nehmen. Dennoch sollen zumindestens kurz die Gemeinsamkeiten beim Editieren von Programmen in MUMPS beschrieben werden. Moderne Texteditoren erlauben das seitenweise Editieren, wobei der Bildschirm als verschiebbares „Fenster" die Sicht auf Teile des Programms ermöglicht. Änderungen werden dabei direkt auf dem Bildschirm angezeigt, so daß eine schnelle, einfache Kontrolle möglich ist. Selbstverständlich gehört zu diesen Editoren eine umfangreichen Bedienungsanleitung, und zum vollständigen Beherrschen eine gewisse Erfahrung. Aber leider bieten auch heute noch nicht alle MUMPS-Systeme diese komfortablen Möglichkeiten, Programme zu schreiben. In diesem Fall müssen Programme mit einfachen Spracherweiterungen von MUMPS erstellt werden. Diese Editiermöglichkeiten, von denen jetzt die grundlegenden — wenigstens ansatzweise — erklärt werden sollen, verwenden herstellerabhängige Erweiterungen des MUMPS-Befehlssatzes. Der Sprachstandard schreibt wohldefinierte Regeln vor, wie Hersteller die Sprache nach ihren eigenen Bedürfnissen erweitern können. Dabei ist festgelegt, daß die Namen aller herstellerabhängigen Befehle mit Ζ beginnen. Daher werden sie auch Z-Befehle genannt. Als Beispiel für die Editierbefehle soll hier zunächst ZPRINT genannt werden, der bei den meisten Systemen ein in die Partition geladenes Programm auf den Bildschirm ausgibt. ZPRINT wird, wie alle Z-Sprachelemente, mit Ζ und dem nachfolgenden Buchstaben abgekürzt, also ZP. Um nun in dem beschriebenen einfachen Editiermodus ein Programm zu schreiben, wird gewöhnlich im Direktmodus Programmzeile für Programmzeile hintereinander geschrieben. Da jedoch normalerweise Eingaben im Direktmodus (wie der Name schon sagt) direkt ausgeführt werden, muß es eine Möglichkeit geben, das MUMPS-System anzuweisen, die Eingabe abzuspeichern und nicht auszuführen. Dazu wird oft vereinbart, daß für die Eingabe von Programmzeilen die Tabulatortaste (TAB-Taste) als Trennzeichen zwischen Label und Anweisungsteil dient. Es muß also für die Eingabe von Programmzeilen mit Label

2.8. Über das Editieren von Programmen

57

die Tabulatortaste nach dem Schreiben des Labels und vor dem Schreiben der ProgrammaiiWeisungen gedrückt werden. Hat die Zeile kein Label, ist zuerst die Tabulatortaste zu drücken und dann die Anweisungen einzugeben. Abbildung 2.3 zeigt ein Beispiel für die oben beschriebene Vorgehens weise, wobei das Drücken der Tabulatortaste durch symbolisiert ist. Die an sich herstellerabhängigen Befehle ZPRINT, ZSAVE, ZREMOVE, ZLOAD werden fast einheitlich von den MUMPS-Systemen DSM (DEC), Μ (InterSystems), MSM (Micronetics), CCSM (MGlobal), DTM (DataTree) und UCD MicroMUMPS verwendet. >TEST; Eingabe eines Programms >READ !,"Bitte Zahl eingeben : ",z >WRITE !,"Die dritte Potenz dieser Zahl lautet:",z*z*z >QUIT >ZPRINT ; oder abgekuerzt ZP (Ausgabe des eben geschriebenen Programms) >ZPRINT TEST+1 ; Ausgabe der 1.Zeile nach dem Label TEST READ !,"Bitte Zahl eingeben : ",z >ZSAVE TEST ; oder ZS: speichert das Programm TEST ab >ZREM0VE ; oder ZR: loescht Programm aus der Partition >ZP ; kein Programm wird angezeigt >D0 "TEST (Ausführung des Programms "TEST) >ZLOAD TEST ; oder ZL TEST (Laden des Programms TEST in die Partition) > Abbildung 2.3: Editieren mit Z-Befehlen Typisch beim Ansprechen einer einzelnen Zeile des geladenen Programms ist der Bezug auf ein Label oder ein Label plus Offset, wie im Beispiel ZP TEST+1. Auch das Löschen von einzelnen Zeilen eines Programms geschieht auf die gleiche Art, zum Beispiel würde ZR TEST+2 die zweite Zeile nach dem Label TEST löschen. Das Einfügen einer Zeile nach einer bestimmten Programmzeile geschieht ähnlich. Man bezieht sich dabei auf einen internen Zeilenzeiger, den man mit dem ZPRINT-Befehl (bei UCD MicroMUMPS mit ZMOVE) bewegen kann. Danach geschriebene Programmzeilen werden direkt hinter die so angesprochene Zeile gesetzt. Es bleibt schließlich noch das Andern von Programmtexten innerhalb einer Programmzeile. Zwar gibt es hier auch einen nicht standardisierten Be-

58

Kapitel 2. Programme und

Programmstrukturen

fehl (den ZINSERT-Befehl), den die meisten Systeme kennen. Dennoch stellen viele MUMPS-Systeme auch ein separates Programm zur Verfügung, das das Andern von Texten innerhalb einer Zeile noch weitergehend unterstützt. Dieses Programm wurde schon im vorigen Abschnitt erwähnt. Es wird mit XECUTE "'/, (in Kurzform X "'/,) aufgerufen. Danach folgt ein Dialog, der die zu ändernde Zeile und den zu ändernden Text erfragt. Die Benutzung dieses „Editors" erfordert in jedem Fall die mitgelieferte spezielle Dokumentation.

Kapitel 3 Steuerung des Programmflusses

3.1

Vergleichsoperatoren und ihre Anwendung

In vielen Programmen hat man folgende typische Situation: man vergleicht zwei Zahlen werte und verzweigt abhängig von diesem Vergleich zu verschiedenen Programmteilen. Auch MUMPS kennt diese numerischen Vergleiche, die in diesem Abschnitt behandelt werden sollen. Neben der Prüfung auf Gleichheit gibt es in MUMPS noch die Vergleichsoperationen „größer als" und „kleiner als", die alle mit dem Nicht-Symbol ( ' ) in ihr logisches Gegenteil verkehrt werden können. Sie werden insgesamt durch folgende Symbole repräsentiert: = < >

Gleichheitsoperator kleiner als größer als

'= '< '>

Ungleichheitsoperator größer oder gleich kleiner oder gleich

MUMPS kennt noch andere (stringorientierte) Vergleichsoperatoren, die im Zusammenhang mit der Textmanipulation behandeln werden. Das Ergebnis jedes der drei genannten Vergleichsoperationen (und ihrer Umkehrungen mit dem Nicht-Symbol) kann wahr oder falsch sein. Die Repräsentierung dieser beiden Möglichkeiten wird in MUMPS mit 1 (wahr) oder 0 (falsch) dargestellt. Beispiel >S a=5,b=l W a>b 1 > Der Vergleich a>b steht als Argument des WRITE-Befehls und wird daher zunächst evaluiert. Da der Vergleich logisch wahr ist, wird als Ergebnis eine 1 ausgegeben. Beispiel >S x«10,y=5 W y ' > x 1 >

Kapitel 3. Steuerung des Programmßusses

60

In diesem Beispiel ist nicht größer ('>) als kleiner oder gleich zu interpretieren. Der Größer- und der Kleineroperator sind rein numerische Operatoren. Das bedeutet, daß gegebenenfalls eine numerische Interpretation der Operanden durchgeführt wird. Beispiel >S s="abc",t="123" W s Da s und t Zeichenketten sind, müssen sie zum Vergleich numerisch interpretiert werden. Dabei wird aus der Zeichenkette "abc" numerisch eine 0 und 0 ist kleiner als 123. Im Gegensatz dazu führt der Gleichheitsoperator einen reinen Stringvergleich durch. Es wird Zeichen für Zeichen der beiden beteiligten Operanden abgeprüft, ob sie stellenweise übereinstimmen. Beispiel >S sl="Nilpferd" W sl="Nilpferd"

1 > Natürlich wird der Gleichheitsoperator auch für Prüfungen auf numerische Gleichheit verwendet. Hier muß jedoch die interne Darstellung einer Zahl berücksichtigt werden, wie das nächste Beispiel zeigt. Beispiel >S x="1.2E3",y=+x W x=y

0 > Intern ist die Darstellung von "1. 2E3" gleich 1200, so daß sich χ und y numerisch gleichen, als Zeichenketten jedoch verschieden sind. Im Zweifelsfall kann ein numerischer Vergleich erzwungen werden, indem bei beiden Argumenten des Gleichheitsoperators die numerische Interpretation zum Beispiel durch ein vorangestelltes Pluszeichen (+) erzwungen wird, also W +xe+y. Für ein kleines Beispiel zur Verwendung von Vergleichsoperatoren soll angenommen werden, daß in der Variablen j j j j eine Jahreszahl in vierstelligem Format (z.B. 1988) steht.

3.1.

Vergleichsoperatoren und Ihre Anwendung

61

Beispiel >W ?5,"Der Februar " , j j j j , " hat " , j j j j # 4 = 0 + 2 8 , " Tage" Der Februar 1988 hat 29 Tage > Im Ausdruck j j j j # 4 = 0 + 2 8 muß auf die strikte links nach rechts Abarbeitung der Operatoren geachtet werden. Die Restdivision j j j j # 4 liefert zunächst ein Ergebnis zwischen 0 und 3. Angenommen, das Ergebnis sei 0, dann ist j j j j genau durch 4 teilbar, also ein Schaltjahr. Der sich anschließende Vergleich auf 0 ist in diesem Fall wahr, liefert also eine 1. Die anschließende Addition von 28 liefert die Anzahl der Tage im Februar. Ist hingegen j j j j kein Schaltjahr, liefert die Restdivision j j j j # 4 immer einen Wert ungleich 0 und der Vergleich auf 0 ist falsch. Daher wird 0 zu 28 addiert. Abschließend sei noch darauf hingewiesen, daß das Ergebnis von Vergleichen auch über den SET-Befehl an Variablen zugewiesen werden kann. Beispiel >S lv=5>3 W l v 1 > Bei der Ausführung des SET-Befehls wird bekanntlich zunächst der rechts des Gleichheitszeichens stehende Ausdruck ausgewertet (ergibt 1) und danach an die lokale Variable lv zugewiesen. Das eigentliche Anwendungsfeld der Vergleichsoperatoren sowie der jetzt einzuführenden logischen Operatoren liegt in der bedingten Programmausführung in Verbindung mit dem IF-Befehl.

62

Kapitel 3. Steuerung des Programmßusses

3.2

Logische Operatoren und die logische Interpretation

Neben den Vergleichsoperationen spielen die logischen Operationen bei der Abprüfung von „Sachverhalten" in Programmen eine große Rolle. Unter Sachverhalten kann man sich beispielsweise folgendes vorstellen: Man hat aus einer Datei einen Personensatz ins Programm gelesen und mochte diese Daten nur dann weiterverarbeiten, wenn die Person älter als 45 Jahre alt ist und ihr Geschlecht männlich ist. Der eben geschilderte Sachverhalt wird in MUMPS durch den Und-Operator (im Zeichen &) abgeprüft. Die oben formulierte Aussage ist offensichtlich genau dann wahr, wenn beide Komponenten der Aussage (älter als 45, Geschlecht männlich) wahr sind. Damit hat man schon die formale Definition des i/nd-Operators, die besagt, daß das Ergebnis einer logischen Und-Operation genau dann wahr ist, wenn beide Operanden logisch wahr sind. Für die Übersetzung des obigen Sachverhaltes in MUMPS-Code sei angenommen, daß in den lokalen Variablen age und sex das Alter und Geschlecht einer Person steht. Dann ist der Ausdruck (age>45)&(sex="m")

die Ubersetzung dieser Abfrage in MUMPS. Da ein Operand des {Tnd-Befehls die beiden logischen Werte falsch und wahr annehmen kann, gibt es insgesamt vier Verknüpfungsmöglichkeiten, die man in einer Verknüpfungstabelle (Tabelle 3.1) zusammenfassen kann. Tabelle 3.1: Wahrheitstabelle der Verknüpfung wahr ft wahr wahr ft falsch falsch ft wahr falsch & falsch

Und-Operation

Ergebnis wahr falsch falsch falsch

Den Oder-Operator (im Zeichen !) kann man ähnlich einführen. Beispielsweise interessiert man sich für alle Personen, die weiblich sind oder für alle Personen die älter sind als 60 Jahre. Mit den gleichen Variablen sex und age kann man dann den Ausdruck formulieren:

3.2.

Logische

63

Operatoren

(sex="w")!(age>60) Diese Oder-Operation ist genau dann wahr, wenn einer der beteiligten Operanden logisch wahr ist. Tabelle 3.2 zeigt die Verknüpfungstabelle für den Oder-Operator. Tabelle 3.2: Wahrheit s t ab eile der Oder-Operation Verknüpfung

wahr wahr falsch falsch

! ! ! !

wahr falsch wahr falsch

Ergebnis

wahr wahr wahr falsch

Bei Ausdrücken der Art (sex="w")! (age>60) muß man sehr genau die Bedeutung der Klammer erkennen. Würde man das erste Klammernpaar weglassen, würde sich am Ergebnis durch die sequentielle Abarbeitimg der Operationen nichts ändern. Würde man dagegen auch noch das zweite Klammerpaar weglassen, erhielte man ein völlig anderes Ergebnis. Denn der Ausdruck sex«"w"!age>60 ist äquivalent zu dem Ausdruck (sex="w"! age)>60. Da das Ergebnis in der Klammer je nach logischer Bedeutung eine 0 oder eine 1 ist und beide Werte kleiner als 60 sind, ist der Ausdruck immer logisch falsch. MUMPS kennt auch den dritten grundlegenden Booleschen Operator, den Nicht- oder Negationsoperator (im Zeichen '). Er ist ein einstelliger Operator und seine Wirkung wird durch Tabelle 3.3 definiert. Tabelle 3.3: Wahrheitstabelle der Verknüpfung 5

'

wahr falsch

Nicht-Operation

Ergebnis

falsch wahr

Die beiden logischen Operatoren Und und Oder können mit dem NichtSymbol (im Zeichen ' ) in ihr logisches Gegenteil verkehrt werden. Das ist die gleiche Schreibweise wie bei den Vergleichsoperatoren. Die formale Definition von Nicht Und lautet: a'&b

W ' ( " a b c " ) 1 >

Im ersten Beispiel wird die Zeichenkette "MUMPS" numerisch zu 0, also logisch falsch interpretiert. Damit ist das Ergebnis der C/nd-Operation auch logisch falsch, also 0. Im zweiten Beispiel wird die Zeichenkette "abc" numerisch zu 0, also logisch falsch interpretiert. Der vorangestellte Nicht-Opera.tor verkehrt die logische Bedeutung in logisch wahr. Der Nicht-Oper&tov hat hier also eine vergleichbare Bedeutung wie die einstelligen Vorzeichen bei der numerischen Interpretation.

3.3. Bedingte

3.3

65

Programmausführung

Bedingte Programmausführung — der IFBefehl

Hervorstechendes Merkmal aller Programmiersprachen ist ihre Fähigkeit, in Abhängigkeit von gewissen Bedingungen Programmteile auszuführen oder es zu unterlassen. MUMPS kennt verschiedene Möglichkeiten der bedingten Programmausführung, von denen die naheliegenste der IF-Befehl ist. Dabei wird das Argument des Befehls getestet und wenn es in der logischen Interpretation wahr ist, wird ein genau definiertes Programmstück ausgeführt. Dieser Programmteil ist in MUMPS grundsätzlich jener Teil der Zeile, der rechts vom IF-Befehl steht. Hier wird die frühere Bemerkung verständlich, daß eine Programmzeile (oder auch Befehlszeile) in MUMPS eine Einheit darstellt. Für ein erstes einfaches Beispiel wird wieder angenommen, daß die beiden lokalen Variablen sex und age definiert seien, sex enthalte das Geschlecht und age das Alter einer Person. Beispiel >IF age>30 D "PAGE W !,"Die Auswertung ergab : "

(...)

Zunächst einmal wird das Argument des IF-Befehls (age>30) logisch ausgewertet. Falls es zu wahr evaluiert wird, wird die gesamte Programmzeile ausgeführt, ansonsten in die nächste Zeile verzweigt. Der genaue Aufbau des IF-Befehls lautet: IF Clogische Bedingung> In vielen Fällen ist das IF-Argument eine Kombination aus logischen und Vergleichsoperatoren. IF sex="m"&(age'>50) (Anweisungen) Bei längeren logischen Ausdrücken kann man diese zunächst in eine Variable setzen und diese Variable als IF-Argument verwenden: S ilv=sex="m"&(age>60)!(sex="w"&(age>65)) IF ilv (hier Code im Einflussbereich des IF-Befehls) Dabei wird der Ausdruck hinter dem ersten Gleichheitszeichen in seiner Gesamtheit logisch ausgewertet und das Ergebnis der Variablen i l v zugewiesen.

66

Kapitel 3.

Steuerung

des

Programmßusses

Eine Programmzeile kann durch mehrere IF-Befehle eingeleitet werden: IF age>30 IF sex="m" Dabei muß das erste IF-Argument bereits logisch wahr sein, damit der zweite IF-Befehl ausgeführt wird. Genauso gut hätte man durch „Ausklammern" des Befehlswortes IF kürzer schreiben können: IF age>30,sex="m" Beide Befehlsargumente müssen logisch wahr sein, damit die Programmzeile ausgeführt wird. Daher ist diese Form offensichtlich der Und-Verknüpfung (nahezu) äquivalent: IF age>30&(sex="m") Aus ökonomischen Gründen ist der Schreibweise mit dem Komma gegenüber der Schreibweise mit dem J/nd-Operator der Vorzug zu geben, weil bei der logischen Operation beide Operanden ausgewertet werden. Ist dabei der erste Operand bereits logisch falsch, ist die Auswertung des zweiten Operanden offensichtlich unnötig. Der IF-Befehl in der Form IF age>30,sex="m" bricht dagegen die Auswertung ab, wenn das erste Argument bereits logisch falsch ist. Ein weiteres Beispiel zeigt, daß der IF-Befehl mit mehreren Argumenten der an sich gleichwertigen Formulierung mit dem E/rad-Operator vorzuziehen ist. Beispiel IF x ' = 0 , l / x > . 0 1 IF x'=0&(l/x>.01) Das erste Beispiel behandelt den Fall χ = 0 problemlos, denn es bricht die Auswertung nach dem ersten Argument ab. Die zweite Variante muß aber zur Gesamtauswertung des ί/nd-Operators den rechten Operanden ausführen, was im Falle χ = 0 einen Fehler verursacht (Division durch 0).

3.3.

Bedingte

Programmausführung

67

Hier zeigt sich auch, warum oben die Äquivalenz eingeschränkt wurde: sie gilt nur für den Fall, daß die Auswertung des zweiten Arguments keine Fehlermeldung erzeugt und keine Seiteneffekte hat. Aus ökonomischen Gründen empfiehlt es sich manchmal auch, die Reihenfolge der Argumente des IF-Befehls sinnvoll zu ordnen. Angenommen man sucht nach Personen weiblichen Geschlechts, die älter als 90 Jahre sind. Folgende Abfrage ist dafür ausreichend: IF sex»"w",age>90 Wenn man nun davon ausgeht, daß männliche und weibliche Personen im Datenbestand etwa gleich häufig vorkommen, Personen älter als 90 dagegen relativ selten sind, hätte man besser geschrieben: IF age>90,sex="w" Man sieht hier, daß die unwahrscheinlichere Bedingung zunächst aufgeführt sein soll, weil in den meisten Fällen das zweite Argument des IF-Befehls bereits nicht mehr ausgewertet werden muß. Auf dieselbe Art verfahrt man zweckmäßigerweise bei Argumenten, die aus komplexen Ausdrücken bestehen: Zunächst werden die einfacher auszuwertenden Ausdrücke aufgeführt und danach erst die komplexeren Ausdrücke. Es sei jedoch hier auch betont, daß die Übersichtlichkeit der Programme Vorrang vor solchen Effizienzbetrachtungen haben muß, da die leichte Wartbarkeit eines Programms einen sehr viel höheren Stellenwert als die Einsparung der Auswertung eines IF-Arguments hat.

68

Kapitel 3. Steuerung des

3.4

Programmßusses

IF, ELSE und $TEST

Im ersten Kapitel wurden Systemvariablen (oder spezielle Variablen) ids vom MUMPS-System verwaltete Größen eingeführt, die Auskunft geben über gewisse Parameter des MUMPS-Systems. Als Beispiel wurden $HOROLOG und $ STORAGE angeführt. Im Zusammenhang mit dem IF-Befehl ist eine weitere Systemvariable von großer Bedeutung, nämlich $TEST (abgekürzt $T). Sie speichert immer den Wahrheitswert der letzten IF-Abfrage. Wenn das Argument eines IFBefehls getestet und für wahr befunden wurde, hat die Systemvariable $TEST den Wert 1, im anderen Fall den Wert 0. Ein Beispiel mag das verdeutlichen: Beispiel >IF 3

Damit kann man nun aber den Wirkungsbereich des IF-Befehls über die eigentlich betroffene Befehlszeile hinaus verlängern. Beispiel IF sex="w" IF $TEST

(Befehlszeile) (Fortsetzung der IF-Bedingung)

Für die Schreibweise IF $TEST gibt es in MUMPS eine Abkürzung, indem man einfach ein argumentloses IF schreibt. Daher sind zum letzten Beispiel folgende beiden Befehlszeilen äquivalent: IF sex="w" IFuu

(Befehlszeile) (Fortsetzung der IF-Bedingung)

Man beachte ausdrücklich die dem argumentlosen IF folgenden zwei Leerschritte, die mit dem Zeichen u symbolisiert wurden. Die Verlängerung einer logischen Bedingung auf mehrere Programmzeilen wird im folgenden Beispielprogramm besonders deutlich. Beispiel MATHE ; Beispielprogramm fuer den IF-Befehl R "Wieviel Schuljahre hast Du Mathe gelernt? ",J IF J>4 W "Dann kannst Du folgende Aufgabe loesen!",! IF R "Was ist das Ergebnis von 3+5 ?",E IF IF E=8 W !,"Das ist richtig !!" QUIT

3.4.

IF, ELSE und

$TEST

69

In diesem Programm wird zunächst gefragt, wieviel Schuljahre man Mathematik gelernt hat. In der 3. Programmzeile wird gefragt, ob die Anzahl größer als 4 ist. Ist das der Fall, wird $T gleich 1 gesetzt und mit der Ausführung der Zeile fortgefahren. Die beiden nächsten Zeilen werden nur ausgeführt, wenn $T = 1 ist. Die vorletzte Zeile zeigt die geschachtelte Anwendung eines argumentlosen und eines mit einem Argument versehenen IF-Befehls. Zu beachten ist, daß der letzte IF-Befehl (IF E=8) $TEST wieder verändert, so daß eine weitere Zeile mit argumentlosem IF von einer sehr viel komplizierteren Bedingung abhängt als die Vorläuferzeilen. Selbstverständlich kann man als Argument des IF-Befehls auch 'STEST schreiben. Beispiel

IF sex="w" (Ausführung, falls Geschlecht weiblich) IF 'STEST W !,"Person i s t maennlich" Von diesen beiden Befehlszeilen wird jeweils nur eine ausgeführt. Ist sex = "w", wird die erste Zeile ausgeführt und $TEST gleich 1 gesetzt. Die Prüfung auf ' $TEST in der zweiten Zeile ist logisch falsch und diese Zeile wird nicht ausgeführt. Genauso schließt man anders herum. Das ist nun aber ein Beispiel einer einfachen Fallunterscheidung, die beim Programmieren häufig vorkommt. In vielen Programmiersprachen wird diese Fallunterscheidung mit einer If-Then-Else-Konstruktion realisiert. Auch in MUMPS gibt es den ELSE-Befehl als Gegenstück zu IF. Er trägt grundsätzlich keine Argumente und ist in der Wirkung vergleichbar mit IF ' $TEST. Allerdings verändert er den Wert von $TEST nicht, während IF ' STEST den Wert mit jeder Ausführung umspringen läßt. IF age>30 DO "ALT(age) ELSEuuDO "JUNG(age) Die Wirkung dieser IF-ELSE-Konstruktion besteht darin, daß im Falle age > 30 das Programm "ALT ausgeführt wird, im anderen Fall das Programm "JUNG. Die beiden notwendigen Leerschritte nach dem argumentlosen ELSE sind wieder mit u u bezeichnet. Dieses Beispiel zeigt aber auch die Begrenzung der Definition von ELSE durch STEST. Falls age > 30 ist, wird das Programm "ALT ausgeführt. Nun könnte es sein, daß in diesem Programm ebenfalls IF-Befehle vorkommen, die ST verändern könnten, mit der Wirkung, daß sowohl das Programm "ALT als auch das Programm "JUNG ausgeführt werden. Eine Lösung dieses Problems zeigt folgendes Beispiel:

70

Kapitel 3. Steuerung des Programmfiusses

IF age>30 DO ~ALT(age) IF 1 ELSEuuDO "JUNG(age) Dadurch, daß in der Zeile mit dem IF-Befehl noch ein IF 1 angehängt wurde, wird in jedem Fall der $TEST-Schalter wieder auf 1 gesetzt und die darauf folgende ELSE-Zeile nicht ausgeführt. Das eben geschilderte Problem entsteht also dadurch, daß in einer Partition, das heißt für ein laufendes Programm, nur ein $TEST-Schalt er vorhanden ist. Unterprogrammaufrufe können also über im Unterprogramm vorgenommene Vergleiche den Wert von $TEST ändern. Das gilt auch für Unterprogrammaufrufe mit Wertübergabe und bei der Verwendung von NEW im Unterprogramm. Ausnahmen zu dem eben Gesagten gelten für die in Abschnitt 2.4 eingeführten benutzerdefinierten Funktionen und speziellen Variablen sowie für die in Abschnitt 3.8 dargestellten Programmblocke. Hier werden Änderungen von $TEST nicht an das rufende Programm weitergegeben, sondern es wird automatisch ein Mechanismus ähnlich NEW benutzt, um $TEST im Unterprogramm von $TEST im rufenden Programm zu entkoppeln. Daher kann man gefahrlos folgende Fallunterscheidung programmieren: IF age>30 S x=$$~Fl(age) E - U L P x=$$~F2(age) Abschließend sei noch daraufhingewiesen, daß $TEST nicht nur den Wahrheitsgehalt der letzten IF-Abfrage wiederspiegelt, sondern auch verwendet wird, um darüber Auskunft zu geben, ob ein Timeout verstrichen ist oder nicht. Der Timeout wax ja bekanntlich im Zusammenhang mit dem READBefehl ein Mittel, die Eingabedauer zu begrenzen. Beispiel READ !,"Name, Vorname : " , n v : 1 0 Wird in diesem Beispiel nicht innerhalb von 10 Sekunden der Name erfaßt, wird die READ-Operation automatisch abgebrochen. In diesem Fall würde $TEST den Wert 0 bekommen. Daher kann man das Verstreichen eines Timeouts mit dem ELSE-Befehl abprüfen. Beispiel READ !,"Name, Vorname : " , n v : 1 0 EUUW !,"Zu lange gewartet!"

3.4.

IF, ELSE und

$TEST

71

Wird innerhalb von 10 Sekunden die Eingabe beendet, ist $T gleich 1 und der dem Timeout folgende ELSE-Befehl greift nicht. Ist hingegen der Timeout verstrichen, ist $T gleich 0 und die dem ELSE folgende Meldung wird geschrieben. Die eben geschilderte Vorgehensweise wird in realen Systemen benutzt, um Dialoganwendungen automatisch zu beenden, bei denen eine gewisse Zeit keine Eingabe erfolgte. Im Vorgriff auf die Befehle OPEN, JOB und LOCK — die ebenfalls mit einem Timeout versehen werden können — sei bereits jetzt darauf hingewiesen, daß dort ähnliche Abfragen programmiert werden.

72

Kapitel 3. Steuerung des

3.5 3.5.1

Programmßusses

Bedingte Ausführung von Befehlen Nachbedingung auf Befehle

Gegenüber einem Programm ist eine Programmzeile die nächst kleinere syntaktische Einheit in MUMPS. Eine Programmzeile enthält wiederum eine Reihe von Befehlen, die ihrerseits aus Befehlsworten und Befehlsargumenten aufgebaut sind. Der IF-Befehl erlaubt es nun, die Ausführung einer gesamten Programmzeile von einer Bedingung abhängig zu machen. MUMPS bietet dieselbe Möglichkeit für einzelne Befehle, ja in einigen Fällen sogar für die Argumente eines Befehls. Sie wird „Nachbedingung" (im englischen „Postcondition") genannt. Wird die Ausführung eines Befehls von einer Bedingung abhängig gemacht, spricht man von einer „Befehlsnachbedingung", während die bedingte Auswahl eines Arguments aus einer Argumentliste „Argumentnachbedingung" genannt wird. In beiden Fällen nennt man die Bedingung „Nachbedingung" . Zunächst soll die Befehlsnachbedingung behandelt werden. Hierbei ist die Nachbedingung ein logischer Ausdruck, der einem Befehl nachgestellt wird, und der bewirkt, daß dieser Befehl nur dann ausgeführt wird, wenn der Ausdruck wahr ist. Man kann fast jeden MUMPS-Befehl mit einer Nachbedingung versehen. Ausnahmen sollen später noch behandelt werden. Die Schreibweise ist für alle Befehle einheitlich. Auf das Befehlswort folgt ohne trennende Leerzeichen ein Doppelpunkt (:) und darauf der logische Ausdruck. Ab hier wird der Befehl wie üblich fortgeführt, das heißt, es folgt ein Leerzeichen und, falls vorhanden, die Argumentliste: : u Für ein Beispiel sei angenommen, daß der Wert einer Zuweisung an eine Variable a von dem Wert einer anderen Variablen χ abhängt. Mit den Mitteln des IF-Befehls kann man schreiben: IF xCO SET a=5 Dasselbe Beispiel kann unter Verwendung einer Nachbedingung folgendermaßen formuliert werden: SET:x65 " S e i t e Das zweite Beispiel zeigt, daß alle Argumente des Befehls in gleicher Weise von der Nachbedingung betroffen sind. Also wird entweder kein Argument oder alle bearbeitet. Die Nachbedingung ist, wie schon oben erwähnt, ein beliebiger Ausdruck, dessen Ergebnis als logischer Wert interpretiert wird (0 entspricht logisch falsch, alle Zahlen ungleich 0 entsprechen logisch wahr). Die genaue Vorgehensweise bei der Interpretation wurde schon bei der Einführung des IFBefehls erklärt. Die Ausführung einer Nachbedingung verändert in keiner Weise den $TEST-Schalter. Daher sind solche Befehle auch im Wirkungsbereich einer IF-ELSE-Konstruktion sinnvoll anzuwenden. Ausnahmen von der Regel, daß die Nachbedingung bei allen Befehlen erlaubt ist, machen die Befehle, die die ihnen nachfolgende Befehlszeile steuern. Dies sind die bisher behandelten Befehle IF und ELSE sowie der im nächsten Abschnitt eingeführte FOR-Befehl. (Soll zum Beispiel bei einem IF-Befehl mit logisch falscher Nachbedingung der Rest der Zeile ausgeführt werden oder nicht?). Noch einige

Beispiele

READ:ein="" e i n WRITE:laenge>70 ! HALT:'weiter S:ein»"" ein="default" D : e r r ~FEHLER,WEITER GOTO:i

Auch der Programmaufruf mit indirekter Angabe des Programmnamens kann mit einer Argumentnachbedingung versehen werden. Beispiel >D0 Qprog:lv=l (Ausführung des in prog stehenden Programms, falls l v « l ) >

Interessant ist hier die Reihenfolge der Abarbeitung: würde die Nachbedingung zunächst ausgewertet werden und würde die Auswertung logisch falsch ergeben, dann würde möglicherweise nicht entdeckt werden, daß die Variable prog nicht definiert ist. Daher wird stets zunächst die Indirektion ausgewertet — und dadurch abgeprüft, ob die indirekt angesprochene Variable existiert — und dann erst die Nachbedingung geprüft.

90

Kapitel 3. Steuerung des

Programmßusses

MENUE ; H/K ; 25.3.88 ; Menueauswahl und Programmaufruf W ! ! , " 1 . Kundenersterfassung" W ! ! , " 2 . Datenfortschreibung" W !!,"3. Listen" W ! ! , " 4 . Auskunftsdialoge" W ! ! , " 5 . Ende" R ! ! , " B i t t e Auswahl t r e f f e n (1 b i s 5) : ",ausw ( h i e r Pruefung, ob e r l a u b t e Eingabe) D ~®("KU"_ausw) QUIT Abbildung 3.1: Fallunterscheidung durch Indirektion Abbildung 3.1 zeigt als Beispiel der Namensindirektion die in MUMPS übliche Art der Programmierung einer Fallunterscheidung anhand eines typischen, wenn auch vereinfachten Menüdialogs. Zunächst werden in diesem Programm die Menüs geschrieben und der Benutzer nach der Auswahl gefragt (Zahl zwischen 1 und 5). Nach einer Prüfung auf zulässige Eingabe kann abhängig vom eingegebenen Wert direkt eines der Unterprogramme ~KU1 bis "KU5 aufgerufen werden. Dabei wird der Ausdruck C"KU"_ausw) zu dem gewünschten Namen ausgewertet und dann zum indirekten Aufruf benutzt.

3.10.2

Die Argumentindirektion

Die zweite Form der Indirektion ist die Argumentindirektion. Hier wird ein gesamtes Befehlsargument oder sogar eine Argumentliste durch eine Indirektion dargestellt. Um mit einem einfachen Beispiel zu beginnen, soll angenommen werden, daß die Variable p l den Wert "A,B" besitzt. Die Zeichenkette "A,B" kann formal als Liste von zwei Unterprogrammen aufgefaßt werden.. Unter Verwendung der Argumentindirektion DO @pl werden beide Programme nacheinander aufgerufen. Die Schreibweise entspricht also der der Namensindirektion und in den Fällen, in denen das Argument eines Befehls aus einem Namen besteht, sind beide Formen identisch. Mit der Argumentindirektion werden nicht nur einzelne Namen, sondern ganze Argumente ersetzt. In Argumentlisten von Befehlen können direkte und indirekte Schreibweise in beliebiger Reihenfolge erscheinen. Für das Weitere muß man sich zunächst in Erinnerung rufen, welche Formen von Argumenten und Argumentlisten bei den verschiedenen Befehlen

3.10.

Die

Indirektion

91

möglich sind. Beispielsweise kann der in der Variablen a stehende String "b=5" als Argument sogar unterschiedlicher Befehle aufgefaßt werden. Bei dem SET-Befehl wird über dieses Argument der Variablen b der Wert 5 zugewiesen, bei dem WRITE- oder dem IF-Befehl wird b=5 als Vergleich aufgefaßt. Mit diesen zusätzlichen Informationen werden die Beispiele in Abbildung 3.2 zur Argumentindirektion verständlich.

>S 5 >S 5 >S 1 >S Ja >S

a="b=5" S Oa W b a="b«5'\®a W b b=5,a="b=5" W ®a b=l,x="b=l" I ®x W "Ja" b=l,x="b=l" W:@x "Ja"

(Fehlermeldung) > Abbildung 3.2: Beispiele zur Argumentindirektion Die Fehlermeldung als Reaktion auf die letzte Befehlszeile erklärt sich dadurch, daß die Nachbedingung kein Argument ist, hier also auch keine Argumentindirektion erlaubt ist. Namensindirektion wäre hier erlaubt, jedoch ist dafür der Inhalt der Variablen χ nicht zulässig. Abschließend sei noch darauf hingewiesen, daß die Argumentindirektion beim FOR-Befehl nicht zulässig ist. Man kann also nicht FOR W #!,$Y," ",$X

1 2 >F 1-1:1:5 W ?$X+3,"*M * * * * * >W # F i - l : l : 3 S x=$X+3 W !?x,x

3 7 11 >W "Zum Zeilenunterstreichen" S x-$X W ! F i » l : l : x W Zum Zeilenunterstreichen >

Abbildung 4.1: Beispiele zu $X und $Y

99

100

Kapitel 4. Kommunikation mit angeschlossenen Geräten

4.2

Zeichendarstellung

4.2.1

Der AS CH-Zeichensatz und Erweiterungen

Schon bei der Besprechung des MUMPS-Zeichen Vorrats (Abschnitt 1.6) wurde von Steuerzeichen gesprochen. Um diesen Begriff inhaltlich zu vertiefen, soll zunächst auf den ASCII-Code eingegangen werden. Da digitale Rechner in ihrem Speicher nur Zahlen darstellen können, wird bei der Abspeicherung von Buchstaben jedem Zeichen des verfugbaren Zeichensatzes eine Zahl zugeordnet, die dieses Zeichen bei der internen Verarbeitung repräsentiert. Diese Zuordnung ist im Prinzip frei wählbar. Jedoch muß man sich auf gleiche Zeichencodes einigen, wenn man Texte oder Programme von einem Rechner zu einem anderen übertragen will. Ein solcher Code, der international benutzt wird, ist der ASCII-Code, dessen vollständige Zuordnungstabelle in Anhang C zu finden ist. Die Abkürzung ASCII steht dabei für „American Standard Code for Information Interchange" . Dieser Code ist ein sogenannter 7 Bit Code und mit ihm können 27 = 128 Zeichen dargestellt werden. Uber den amerikanischen Standard hinaus ist er auch von der International Standards Organisation (ISO) als Standard definiert und wird daher gelegentlich als ISO 7-Bit-Code bezeichnet. Die Sprachdefinition von MUMPS setzt voraus, daß die druckbaren Zeichen im ASCII-Code dargestellt werden. Daher wird im weiteren stillschweigend angenommen, daß der ASCII-Code zur Zeichendarstellung benutzt wird. Die Abspeicherung von Zeichencodes ist nicht mit der Abspeicherung von numerischen Werten, also Zahlen, in MUMPS zu verwechseln. Zahlen werden in MUMPS als Zeichenketten abgespeichert. Jede Ziffer einer Zahl wird dabei durch ihren ASCII-Code repräsentiert. Die Ziffer 0 würde zum Beispiel eine interne Speicherzelle belegen, in der der Wert 48 (Code für das Zeichen "0") abgespeichert ist. Entsprechend würde die Zahl 153 in drei Speicherzellen abgelegt werden, die die Zahlen 49, 53 und 51 enthalten. Wie dargelegt, enthält der 7 Bit ASCII-Code 128 Zeichen. Für die normalen druckbaren Zeichen (siehe Abschnitt 1.6.1) werden jedoch nur 95 Positionen benotigt. Die restlichen 33 Zeichen stehen für Steuerungsfunktionen zur Verfügung. Mit ihnen können solche Funktionen wie Zeilenvorschub, akustisches Signal oder Loschen des Bildschirmes durchgeführt werden. Diese Positionen in der Tabelle werden Steuerzeichen genannt. Wenn der Code für ein solches Zeichen auf ein Terminal ausgegeben wird, bewirkt er — abhängig vom Terminaltyp — gar nichts oder eine Zustandsänderung des Terminals. Es wird jedoch im allgemeinen kein sichtbares Zeichen auf dem Bildschirm dargestellt.

4.2.

Zeichendarstellung

101

Da diesen Codes keine darstellbaren Symbole zugeordnet sind, können sie auch nicht in einem Stringliteral angegeben werden. Es werden also andere Möglichkeiten zur Erzeugung dieser Zeichen benötigt. MUMPS stellt hier besondere Funktionen zur Verfugung, die aus dem ASCII-Code eines Zeichens das Zeichen selbst erzeugen und die ein Zeichen in dessen ASCII-Code umwandeln. Die Funktionen heißen $CHAR und $ASCII. Der 7 Bit ASCII-Code enthält keine nationalen Sonderzeichen. Darunter versteht man beispielsweise im Deutschen die Umlaute und das ß. In der nach DIN standardisierten deutschen Referenzversion (siehe hierzu Anhang C) werden diese Zeichen anstelle anderer Zeichen dargestellt, das Eszet (ß) etwa steht anstelle der Tilde ("), die den ASCII-Code 126 hat. Auf die besondere Problematik dieses Ersatzes von Sonderzeichen durch normale ASCII-Zeichen wird an verschiedenen anderen Stellen dieses Buches noch hingewiesen werden. Eine andere Vorgehensweise, diese nationalen Sonderzeichen und zusätzlich andere Zeichen, wie mathematische Symbole, griechische Buchstaben, grafische Symbole (etwa Schraffuren), etc. darzustellen, besteht in einer Erweiterung des 7 Bit ASCII-Codes auf 8 Bit, womit 2 8 = 256 Zeichen dargestellt werden können. Es kommt auf das konkret verwendete MUMPSSystem an, welcher Zeichensatz zugrunde liegt. In jedem Fall können die Zeichen aus dem Code-Bereich 128 bis 255 mit der im folgenden Abschnitt beschriebenen Funktion $CHAR erzeugt werden.

4.2.2

Umwandlung eines Codes in ein Zeichen $CHAR



Die Funktion $CHAR (abgekürzt $ C ) kann ein oder mehrere Argumente besitzen. Sie erzeugt einen String, bei dem jedes Argument als ASCII-Code eines Zeichens angesehen und in dieses Zeichen transformiert wird. Die Beispiele in Abbildung 4.2 mögen das verdeutlichen. Hier wird gezeigt, daß mit der Funktion $CHAR sowohl druckbare Zeichen ids auch Steuerzeichen erzeugt werden können. Wie das dritte Beispiel zeigt, können mit dieser Funktion auch Steuerzeichen in Variablen abgelegt werden. Werte kleiner als 0 werden in den Leerst ring abgebildet, während die Reaktion auf Werte, die größer als 127 sind, von der Implementierung abhängt. Die meisten Implementierungen werden jedoch mindestens noch die Werte von 128 bis 255 unterstützen. Die Reaktion des Terminals auf Zeichen außerhalb des darstellbaren ASCII-Codes ist herstellerabhängig. Oft werden bei Codes größer als 128 spezielle Zeichen dargestellt, die nicht innerhalb, des ASCII-Zeichensatzes enthalten sind. Man kann sich leicht mit einer FOR-Schleife die Reaktion auf diese Zeichen ansehen:

102

Kapitel 4. Kommunikation

mit angeschlossenen

Geräten

>WRITE $CHAR(77,85,77),$C(80),$C(83) MUMPS >W $C(7) (Es wird ein akustisches Signal ausgegeben) >S var»"Vorschub auf"_$C(13,10)_"neue Zeile" W var Vorschub auf neue Zeile >

Abbildung 4.2: Beispiele zu SCHAR Beispiel F i=128:l:255 W $C(i),"

"

Zum Beispiel stellt auf dem IBM-PC der Code 244 die obere Hälfte und 245 die untere Hälfte des mathematischen Integralzeichen dar. Man kann daher auf diesem Rechner das gesamte Symbol durch die folgenden Befehle darstellen: Beispiel W

4.2.3

!?5,$C(244),!?5,$C(245)

Umwandlung eines Zeichens in seinen Code — $ASCII

Das Gegenstück zu der Funktion $CHAR ist die Funktion $ASCII. Sie wandelt ein Zeichen in seinen ASCII-Code um. Dazu sind einige Beispiele in Abbildung 4.3 angegeben. Es gibt zwei Schreibweisen für den Funktionsaufruf. Bei Aufruf der Funktion mit einem Argument wird das erste Zeichen des Argumentstrings in seinen ASCII-Code umgewandelt. Ist das Argument der leere String, so wird - 1 zurückgegeben. Bei der Schreibweise mit zwei Argumenten wird als zweites Argument angegeben, welches Zeichen des ersten Argumentes umgewandelt werden soll. Auch hier ist das Ergebnis -1, wenn das angegebene Zeichen nicht existiert. Das vorletzte Beispiel zeigt wieder einmal, daß MUMPS nicht zwischen Zahlen und Zeichenketten unterscheidet, während das letzte Beispiel noch einmal verdeutlicht, daß sich die Funktionen $A und $C gegenseitig aufheben, also zueinander invers sind.

4.2.

Zeichendarstellung

103

>WRITE $ASCII("K") 75 >W $A("STRING") 83 >W $A("") -1 >W $A("STRING",3) 82 >W $A("STRING",10) -1 >W $A(28,2) 56 >W $A($C(122)) 122 Abbildung 4.3: Beispiele zu SASCII

4.2.4

Die Sternsyntax

MUMPS kennt auch noch eine zweite Methode der Umwandlung von Zeichen in den zugehörigen Code und umgekehrt, die sich bei Ein- und Ausgaben verwenden läßt. Man benutzt hier einen Stern vor einem Ausdruck bzw. einem Variablennamen, um eine der Benutzung von $C und $A ähnliche Wirkung zu erzielen. Zunächst soll die Ausgabe mit WRITE anhand von Beispielen erläutert werden (Abbildung 4.4).

>WRITE *6b,»66 AB >S x»$A("A") W *x+32 a >W "Einfache",*10,"Bildschirmsteuerung" Einfache Bildschirmsteuerung > Abbildung 4.4: Beispiele zur Sternsyntax Wie die Beispiele zeigen, wird die Umwandlung dadurch erzwungen, daß ein WRITE-Argument mit einem Stern (*) beginnt. Der Wert des Ausdrucks

104

Kapitel 4. Kommunikation mit angeschlossenen Geräten

nach dem Stern wird als ASCII-Code eines Zeichens interpretiert und das Zeichen wird ausgegeben. Also ist in diesem Fall die Schreibweise WRITE *X äquivalent mit der Schreibweise WRITE $CHAR(X) Jedoch ist — insbesondere bei komplizierteren WRITE-Befehlen — die erste Scheibweise einfacher und übersichtlicher anzuwenden. Das zweite der in Abbildung 4.4 gezeigten Beispiele zeigt die Umwandlung eines Großbuchstabens in einen Kleinbuchstaben, wobei die Kenntnis ausgenutzt wurde, daß sich die entsprechenden Buchstaben im ASCII-Code gerade um 32 unterscheiden. Das dritte Beispiel erzeugt einen Zeilenvorschub (ohne Wagenrücklauf), der durch den ASCII-Code 10 erzeugt wird. Auch der READ-Befehl kennt die Schreibweise, bei der ein Argument mit einem Stern eingeleitet wird. Hier wird ein einzelnes Zeichen gelesen und sein ASCII-Code in die dem Stern folgende Variable eingetragen. Auch hier einige Beispiele: Beispiele

READ "JA (J) ODER NEIN (N) ? R *status,*x,*y R *x:5

",*jn

Wie die Beispiele zeigen, sind die „Stern"-Argumente gleichberechtigt mit anderen Argumenten des READ-Befehls. Für jedes Stern-Argument wird genau ein Zeichen eingelesen und dessen ASCII-Code in die angegebene Variable übertragen. Würde man zum Beispiel im ersten READ-Befehl ein „J" eingeben, würde die Variable j n den ASCII-Code von „J", also 74 enthalten. Das dritte Beispiel zeigt die Verwendung eines Timeouts. Wird kein Zeichen in der angegebenen Zeit eingegeben, hat die Variable den Wert — 1. Manche MUMPS-Systeme, unter anderem auch MicroMUMPS, erlauben mit dieser Sternsyntax die verdeckte Eingabe eines Zeichens, das bedeutet, die Eingabe wird nicht auf dem Bildschirm angezeigt. Diese Methode kann auf solchen Systemen daher zur verdeckten Kennworteingabe benutzt werden. Andere Systeme erlauben die verdeckte Eingabe durch spezielle Erweiterungen des OPEN-Befehls (siehe Abschnitt 4.5.3), die allerdings auch herstellerabhängig sind.

4.2.

105

Zeichendarstellung

Eine vollkommen herstellerunabhängige verdeckte Eingabe besteht darin, in einer FOR-Schleife mit der Sternsyntax jeweils ein Zeichen zu lesen, anschließend sofort den Cursor eine Stelle zurückzubewegen (mit W *8) und das gerade eingegebene Zeichen mit einem Leerzeichen zu überschreiben. Das Zeichen mit dem ASCII-Code 8 wird „Backspace" genannt. Beispiel

S kennwort="" FDR R *x Q:x«13

W * 8 , " " , * 8 S kennwort»k'ennwort_$C(x)

Abgebrochen wird dabei die FOR-Schleife durch Drücken der carriage return Taste, durch die χ den Wert 13 erhält. In diesem Zusammenhang sei darauf hingewiesen, daß die Ausgabe eines Backspace den Wert von $X um 1 vermindert, sofern $X nicht schon den Wert 0 hatte.

106

4.3

Kapitel 4. Kommunikation mit angeschlossenen Geräten

Prinzipien der Bildschirmsteuerung

Die Kommunikation mit dem Benutzer eines Programmsystems geschah bisher mit dem WRITE-Befehl, wobei die einzelnen zu erfassenden Datenfelder auf dem Bildschirm mit den elementaren Steuerfunktionen #, !, ?n ausgeführt wurden. Damit kann man den sogenannten Rollmodus des Bildschirms (englisch: scroll mode) nutzen, der weitgehend mit dem Modus der Ausgabe auf einen Fernschreiber oder einen Drucker identisch ist. Es gibt nun zwar wenige allgemeine Ratschläge, wie eine auf eine Anwendung abgestimmte Bildschirmmaske auszusehen hat. In vielen Fällen wird jedoch der Rollmodus als für den Benutzerkomfort nicht ausreichend angesehen, wenngleich er bei der Massenerfassung von Daten hinsichtlich der Schnelligkeit der Erfassung sicher von Vorteil ist. Die heutigen Bildschirme erlauben die Verwendung von Steuersequenzen, um den Cursor auf dem Bildschirm zu positionieren, Teile des Bildschirms zu löschen oder neu zu beschreiben, Zeichenattribute wie Farbe oder Blinken zu beeinflussen oder um andere Steueroperationen durchzuführen. Unterschiedliche Bildschirmhersteller verwenden dabei leider unterschiedliche Standards und auch unterschiedliche Philosophien, so daß hier nur beispielhaft ein in der MUMPS-Welt häufig gebrauchter Standard teilweise beschrieben werden kann. Diese Bildschirmsteuersequenzen, die bei der Programmierung von „normalen" Erfassungsdialogen verwendet werden, sind von der amerikanischen Normierungsbehörde (American National Standard Institute, ANSI) standardisiert und werden daher ANSI-Steuersequenzen genannt. Diejenigen Bildschirme, die diese Steuersequenzen verstehen, werden entsprechend auch ANSI-Bildschirme genannt. Aus der Vielzahl der nach diesem Standard definierten Steuersequenzen sollen zwei Gruppen näher dargestellt werden. Hier sind erstens die Cursorsteuersequenzen zu nennen, mit denen es möglich ist, den Cursor an eine bestimmte Stelle zu bewegen. Zweitens gehören dazu Sequenzen zum Steuern von Videoattributen, wie Blinken, inverse Darstellung oder Verwendung unterschiedlicher Farben (bei Farbbildschirmen). Spezielle Modi, die teilweise nur auf besonderen Bildschirmen möglich sind, sollen hier nicht näher erläutert werden. Dazu gehört beispielsweise der Grafikmodus. Der Nutzung dieser Steuersequenzen liegt eine gemeinsame Methode zugrunde: Man schreibt dazu auf den Bildschirm ein spezielles ASCII-Steuerzeichen, das von der internen Bildschirmlogik als Einleitung der Steuersequenz verstanden wird. Es wird daher auch „lead in character" genannt. Für dieses Zeichen wird häufig das ASCII-Zeichen mit dem Code 27, das sogenannte Escape-Zeichen verwendet. Escape heißt wörtlich übersetzt „Flucht" und tatsächlich „flüchtet" man aus dem normalen Darstellungsmodus des

4.3.

Prinzipien der

107

Bildschirmsteuerung

Bildschirms heraus. Diesem einleitenden Zeichen folgen weitere Zeichen, die zusammen als Steuersequenz interpretiert werden. Als einfaches Beispiel soll zunächst die Sequenz „Bewege den Cursor zu den Koodinaten χ und y" dargestellt werden. Das Koordinatenpaar x, y ist dadurch definiert, daß der Cursor in die Zeile y und in die Spalte χ bewegt wird. Um diese Wirkung auf dem Bildschirm zu erzielen, muß man zunächst das Es cape-Zeichen schreiben, danach eine eckige Klammer. Nun folgt die Angabe der y-Koordinate und — getrennt durch ein Semikolon die x-Koordinate. Abschließend folgt ein großes H. Die Gesamtheit der Folge ESC [ y ; χ Η , wobei y und χ durch ihre Werte zu ersetzen sind, führt genau die beschriebene Funktion aus. Die Erzeugung dieser Steuersequenz mit MUMPS geschieht auf folgende Art: man schreibt die Zeichen einfach mit dem WRITE-Befehl aus, wobei das Escape-Zeichen in der Sternsyntax als *27 ausgegeben wird. Die Koordinatenangaben müssen bei den ANSI-Steuersequenzen klar lesbar ausgegeben werden, was einfach durch ihre Angabe als Argument des WRITE-Befehls geschieht. Hier sei angenommen, daß die Koordinaten in den Variablen χ und y stehen. Damit wird die Positionierung wie folgt erreicht: WRITE * 2 7 , " [ " , y , " ; " , x , " H "

Tabelle 4.1: Steuersequenzen (Auswahl) und ihre Darstellung in MUMPS Cursorpositionierung in die Zeile y und Spalte χ Cursor um η Zeilen hoch Cursor um η Zeilen abwärts Bildschirm löschen Cursor home Bildschirm ab Cursor löschen

ESC[y;xH

W •27

ESC[nA ESC[nB ESC[2J ESC[H ESC [k

W • 2 7 , ' ' [" ,η,'Ά" W • 2 7 , ' ' [" ,n,"B" W • 2 7 / ' [2 J" W • 2 7 , ' '[H" W »27, '[k"

Tabelle 4.1 enthält einen Auszug aus den ANSI-Steuersequenzen. Für die Einstellung der Videoattribute wird die Sequenz ESC[nm verwendet, wobei der Parameter η die in Tabelle 4.2 dargestellte Wirkung zeigt.

108

Kapitel 4. Kommunikation

mit angeschlossenen

Geräten

Tabelle 4.2: Parameter zum Setzen der Videoattribute (Auswahl) Parameter 0 1 4 5 7

Wirkung alle Attribute zurücksetzen, weiße Zeichen auf schwarzem Grund Höhere Helligkeitsstufe Unterstreichen Blinken Invers

Eine vollständige Beschreibung der Steuersequenzen seines Bildschirms muß der Leser dem entsprechenden Benutzerhandbuch entnehmen. Ahnliche Sequenzen dienen bei der Druckersteuerung zum Beispiel zur Auswahl von Schriftart und Schriftgröße. Das direkte Hantieren mit diesen unübersichtlichen Steuercodes beim Programmieren mit MUMPS mag an dieser Stelle lästig erscheinen. Aber unter Zuhilfenahme der Indirektion vereinfacht sich die Benutzung dieser Codes erheblich und macht gleichzeitig das Programm unabhängig vom verwendeten Terminal.

4.4.

Methodik

4.4 4.4.1

der

Bildschirmsteuerung

109

Methodik der Bildschirmsteuerung Bildschirmsteuerung in der Praxis

Die prinzipielle Art und Weise der Bildschirmsteuerung ist im vorangegangenen Abschnitt erläutert worden. Die praktische Handhabung beim Programmieren soll nunmehr dargestellt werden. Im Prinzip würde es genügen, den notwendigen Code zur Erzeugung einer bestimmten Steuerfunktion direkt zu schreiben, etwa zur Cursorpositionierung W * 2 7 , " [ " , y , " ; " , x , " H " . Einfacher und bedeutend besser zu lesen ist es, die in einem Programmsystem benötigten Steuercodes einmal am Anfang und in einem separaten Unterprogramm in Variablen zu stellen und sie bei Bedarf zu benutzen. Zusätzlich ergibt sich der Vorteil, daß bei einer Anpassung des Programms an einen neuen Bildschirm nur in diesem einen Unterprogramm Änderungen vorgenommen werden müssen und der ganze Rest des Programms bildschirmunabhängig ist. Mehrere verschiedene Sprachmittel lassen sich hierzu heranziehen und sollen exemplarisch an einem einfachen Beispiel dargestellt werden. Dazu sei das ASCII-Zeichen mit dem Code 7, das Klingelzeichen, betrachtet. Um es direkt auszugeben, reicht der Befehl W *7 aus. Soll die Steuersequenz in einer Variablen abgelegt werden, ist die η alleliegendste Möglichkeit, sie einem String zuzuweisen und diesen bei Bedarf auszugeben: Beispiel

>S cpiep=$C(7) W cpiep >

Allerdings möchte man bei der Ausgabe von z.B. Cursorsteuerungssequenzen Zeile und Spalte erst kurz vor der Ausgabeanweisung festlegen, also die Ausgabeanweisung selbst parametrisieren. Das ist mit der direkten Verwendung von Variablen nicht möglich und es müssen daher die indirekten Möglichkeiten der Referenzierung betrachtet werden. Beispielsweise könnte man den gesamten Befehl in eine Variable stellen und diese mit XECUTE ausführen. Beispiel

>S xpiep="W *7" X xpiep >

Eine weitere Möglichkeit bedient sich der Argumentindirektion. Dabei wird das reine Argument des WRITE-Befehls in eine Variable gestellt und indirekt ausgeführt.

110

Kapitel 4. Kommunikation mit angeschlossenen Geräten

Beispiel >S piep="K7" W Opiep > Die beiden vorgestellten indirekten Methoden sind in ihrer Funktionalität gleichwertig, wegen der besseren Kombinationsmöglichkeiten in Argumentlisten des WRITE-Befehls soll hier jedoch der Verwendung der Indirektion der Vorzug gegeben werden. Ebenso wie in diesem einfachen Beispiel eines Klingelzeichens lassen sich nun komplexere Steuercodes in lokale Variablen stellen und indirekt einsprechen. Beispiel ; D e f i n i t i o n e i n i g e r Steuercodes am Anfang e i n e s ; Programmsystems S home»"*27,""[H , , ",*27,""[2J" , , M ; Cursor home/ ; Bildschirm loeschen M,, M S c u p = " * 2 7 , " " [ , y , " " ; " " , x , " H " " " ; P o s i t i o n i e r e Cursor S norm="*27,""[Om""" ; Normale D a r s t e l l u n g S inv="*27,""[7m""" ; inverse Darstellung

Möchte man nun innerhalb einer Bildschirmmaske auf die Position (10,10), das heißt, 10. Zeile und 10. Spalte einen Text in inverser Darstellung schreiben und anschließend wieder mit normaler Schrift weiterschreiben, genügt die folgende Codesequenz: Beispiel S x=10,y=10 W @cup,®inv,"Text",Qnorm

Im nächsten Abschnitt wird in Erweiterung dieser Methodik der Aufbau einer kompletten Erfassungsmaske gezeigt. In vielen MUMPS-Systemen werden die Steuersequenzen in Dateien abgespeichert und am Anfang eines Programmsystems in die entsprechenden Variablen geladen. Mit dieser Methodik ist es möglich, auch andere, herstellerabhängige Bildschirmsteuerungssequenzen abzuspeichern und je nach verwendetem Terminaltyp zu initialisieren. Ein so aufgebautes Programm kann also Bildschirme verschiedener Hersteller und verschiedener Bauart handhaben.

4.4.

Methodik

der

Bildschirmsteuerung

111

Da viele MUMPS-Programme die Fähigkeit der Bildschirmsteuerung benötigen, gibt es Bestrebungen, den Sprachstandard um spezielle Parameter des WRITE-Befehls zu erweitern, die benutzt werden können, um Bildschirmoperationen wie „Cursorpositionierung" oder „Löschen des Bildschirms" auszuführen. Diese Sprachelemente werden nicht Bestandteil des Standards von 1989 sein, sondern wahrscheinlich in der darauffolgenden Version berücksichtigt werden. Da es jedoch schon heute MUMPS-Systeme gibt, die diesen erwarteten Standard vorwegnehmen, soll hier kurz darauf eingegangen werden: Beginnt ein Argument des WRITE-Befehls mit einem Schrägstrich, wird es als Steuerungsanweisung angesehen. Beispiel

W /CUP(1,1) ; Positioniert den Cursor auf 1.Zeile, 1.Spalte

Mit dieser Erweiterung des WRITE-Befehls und zusätzlich anderer Sprachelemente gelingt auch die Einbeziehung anderer Standards der Datenverarbeitung, wie z.B. des grafischen Standards GKS.

4.4.2

Prinzipien tabellengesteuerter Masken

Die im vorangegangenen Abschnitt erläuterte praktische Handhabung der Bildschirmsteuerung wird vor allem zum Aufbau von Erfassungsmasken benutzt. Erfassungsmasken lassen sich in MUMPS mit großer Allgemeingültigkeit programmieren, indem man die Parameter einer Maske in Tabellen hält, sie bei der Programmausführung ausliest und mit dem XECUTE-Befehl oder der Indirekt ion ausführt. Die Technik dieser Programmgenerierung liegt außerhalb des Rahmens dieses Lehrbuchs, bildet aber gleichwohl einen Schlüssel zum Verständnis fortgeschrittener Programmierung in MUMPS. In diesem Abschnitt sollen die Prinzipien an einem einfachen Beispiel ansatzweise erläutert werden. Hierfür soll angenommen werden, daß man eine Bildschirmmaske zum Erfassen von Adressen programmieren möchte. Die zu erfassenden Daten sollen aus den folgenden Feldern bestehen: Name Straße Postleitzahl

Vorname Hausnummer Wohnort

Die Aufteilung dieser Felder auf dem Bildschirm ist sicherlich Geschmackssache, jedoch wird man generell Gruppen von logisch zusammengehörenden Feldern bilden. Das einzelne Feld ist charakterisiert durch Koordinaten und Inhalt des Erfassungsprompts (z.B. "Name : ") sowie durch Koordinaten und Länge des Eingabebereichs.

112

Kapitel 4. Kommunikation mit angeschlossenen Geräten

Zum Design einer Bildschirmmaske ist daher die Darstellung in einer Tabelle von Nutzen, die am Beispiel der Adreßerfassung das in Tabelle 4.3 dargestellte Aussehen hat. Tabelle 4.3: Parameter einer Bildschirmmaske FeldNr. 1 2 3 4 5 6

Feldname Name Vorname Strasse Hausnr. Postleitzahl Wohnort

VariablenNamen name vname str hnr

Koord. Prompt 10,5 10,45 14,5 14,45

Koord. Feld 10,12 10,55 14,15 14,58

plz ort

18,5 18,45

18,20 18,55

max. Länge 30 20 20 6 10 25

Mit diesen Angaben des Namens, der Koordinaten und der Feldlänge ist das Layout vollkommen bestimmt. In der Realität kommen noch andere Angaben dazu. Hier sind insbesondere die Feldprüfungen zu nennen, die in Kapitel 6 erläutert werden. Neben den eigentlichen Erfassungsfeldern enthält eine Bildschirmmaske einen Seitenkopf, aus dem neben anderen Informationen hervorgeht, welche Maske gerade angezeigt wird. Weiterhin ist im allgemeinen ein Informationsteil vorhanden, in dem Hilfstexte, Fehlermeldungen usw. angezeigt werden. Meistens wird hierfür eine Bildschirmzeile reserviert, bei größeren Texten wird die aktuelle Bildschirmmaske vorübergehend (auch teilweise) ausgeblendet. Das Programmieren einer Bildschirmmaske geschieht nun ganz generell in der folgenden Reihenfolge: • Bildschirm loschen, Cursor home • Aufbau Maskenkopf zu Identifikation der Anwendung • Aufbau der Maske mit den einzelnen Feldern • Positionierung auf das erste Erfassungsfeld • Erfassung und Prüfung der Daten • bei fehlerhafter Erfassung erfolgt Fehlermeldung im Informationsteil, Loschen der Daten und erneute Erfassung

4.4. Methodik der

Bildschirmsteuerung

113

Das in Abbildung 4.5 dargestellte Programm zeigt die wesentlichen Elemente einer Erfassungsmaske, wobei der Einfachheit halber nicht mit Tabellensteuerung gearbeitet, sondern die Maske explizit ausprogrammiert wurde. Für die Bildschirmsteuerung wurden dieselben Bezeichnungen wie im Abschnitt vorher gewählt.

ERFASS ; H/K ; 24.5.88 ; Beispiel einer Erfassungsmaske W Qhome D "KOPF ; Loesche Bildschirm, Cursor home, ... ; individueller Kopfaufbau S y=10,x=5 W ecup,"Name : " S x=45 W @cup,"Vorname : " S y=14,x=5 W ®cup,"Strasse : " S x=45 W Qcup,"Hausnummer : " S y«18,x=5 W ®cup,"Postleitzahl : " S x=45 W ®cup,"Wohnort : " ; Aufbau der Maske beendet, Erfassungsdialoge beginnen 1 S y=10,x=12 W Qcup R name#30 (Fehlerpruefung) 2 S x=55 W Qcup R vname#20 (Fehlerpruefung) 3 S y»14,x=15 W ®cup R str#20 (Fehlerpruefung) 4 S x=58 W Qcup R hnr#6 (Fehlerpruefung) 5 S y=18,x«20 W ®cup R plz#10 (Fehlerpruefung) 6 S x=55 W ®cup R ort#25 (Fehlerpruefung) ; Ende des Erfassungsdialogs und Beginn der Abspeicherung QUIT

Abbildung 4.5: Erfassungsprogramm Dieses Programmgerippe startet mit dem Loschen der Maske und Cursor home und es folgt der individuelle Kopfaufbau, auf den hier nicht weiter eingegangen werden soll. Danach wird die Maske komplett aufgebaut, wobei darauf verzichtet wurde, die Namen möglicherweise invers hervorzuheben und die Feldlänge mit Punkten anzuzeigen, was gelegentlich getan wird. Die einzelnen Erfassungsdialoge bestehen wieder aus der Positionierung des Cursors und dem sich anschließenden READ-Befehl mit der Angabe der maximalen Länge. Die darauf folgende Fehlerprüfung samt nachfolgender Maßnahmen im Fehlerfall wurde hier nur angedeutet. Die Bezeichnung der Labels ist identisch mit der Feldnummer und wird im Fehlerfall noch einmal angesprungen. Weiterhin kann man durch diese Labels innerhalb der Erfassungsmaske springen und nachträglich erkannte Fehler verbessern. Auch das sei hier nur angedeutet.

114

Kapitel 4. Kommunikation

mit angeschlossenen

Geräten

Bei dem klaren, systematischen Aufbau des Beispielprogramms wird es nun verständlicher, daß man die Parameter als Tabellen in MUMPS-Dateien halten kann und den Aufbau einer Maske sogar im Dialog am Bildschirm relativ einfach definieren kann. Solche Techniken dienen dazu, leistungsfähige Programmgeneratoren zu erstellen und dem Endbenutzer an die Hand zu geben.

4.5. Die Benutzung externer Geräte

4.5 4.5.1

115

Die Benutzung externer Geräte Prinzipien der Kommunikation mit externen Geräten

Den in diesem Text bisher gebrachten Beispielen war gemeinsam, daß ihre Ausgabe auf dem Benutzerterminal erfolgte. In vielen Fällen hat man jedoch die Anforderung, mit einem externen Gerät in Kommunikation zu treten. Hierzu zählt die Ausgabe auf einem dem Rechnersystem angeschlossenen Drucker, der Datenaustausch über eine Bandstation oder mit einem zweiten Rechner, aber auch das Lesen von Datensätzen aus einer Datei. Um ein externes Gerät zu benutzen, sind folgende Aktivitäten notig: 1. Reservieren des Gerätes unter eventueller Angabe von Parametern zur aktuellen Nutzung. 2. Benutzung des Gerätes. Sie kann mehrfach erfolgen und/oder abwechseln mit der Benutzung anderer Geräte. 3. Freigabe des Gerätes für andere Benutzer. Beim Grad der Unterscheidung und der Vielzahl der Charakteristika externer Geräte unterschiedlicher Hersteller fällt es schwer, gemeinsame Standards für ihre Benutzung zu definieren. Z.B. möchte man einem Drucker die Seitenlänge, Zeichenauswahl, Schriftweite o.ä. mitteilen, einem Magnetbandgerät dagegen Aufzeichnungsdichte, Blocklänge, Satzlänge u.a.. Daher ist im Gebiet der Benutzung externer Geräte eine Standardisierung recht schwer. In MUMPS sind die notwendigen Befehle zum Reservieren, zur Benutzung und zur Freigabe externer Geräte in ihrer Grundstruktur standardisiert. Die unterschiedlichen geräteabhängigen Parameter sind dagegen nur von ihrem äußeren Aufbau her festgelegt. In jedem Fall muß der Benutzer eines MUMPS-Systems in das Benutzerhandbuch seines MUMPS-Systems schauen, um Art und Möglichkeiten der Nutzung externer Geräte zu erfahren. Um hier einige Beispiele zu geben, soll auf das MicroMUMPS der University of California in Davis Bezug genommen werden. Dem Leser wird es nicht schwer fallen, die Art und Weise der Gerätebehandlung in MicroMUMPS auf sein konkretes System zu übertragen. Generell werden die externen Geräte mit einem Gerätenamen angesprochen. In MUMPS können das allgemeine Ausdrücke sein, etwa Zeichenketten. MicroMUMPS jedoch verwendet Zahlen für diesen Zweck, die in Tabelle 4.4 dargestellt sind.

116

Kapitel 4. Kommunikation

mit angeschlossenen

Geräten

Tabelle 4.4: Gerätebezeichnung in MicroMUMPS 0 1 2 3 4 5

4.5.2

Benutzerconsole Drucker Plattendatei (MSDOS-Typ) Plattendatei (MSDOS-Typ) Plattendatei (MSDOS-Typ) Kommunikationsport

Die Reservierung von Geräten mit OPEN

Vor einer Eingabe/Ausgabe-Operation auf ein externes Gerät muß dieses reserviert werden. Dieses ist vor allem aus zwei Gründen nötig: 1. Es erfolgt eine Information vom Programm an das MUMPS-System, daß das Gerät im weiteren Verlauf benutzt werden soll. 2. Es können in Mehrbenutzersystemen andere Programme bereits ihren Zugriff auf das Gerät angemeldet haben, ein gleichzeitiger Zugriff aber nicht möglich sein. In diesem Fall muß das MUMPS-System den Konflikt auflösen, z.B. dadurch, daß es ein Programm solange verzögert, bis das Gerät vom anderen Programm wieder freigegeben wurde. Dieses Reservieren eines externen Gerätes geschieht in MUMPS mit dem OPEN-Befehl, man spricht daher in deutscher Ubersetzung auch vom „Offnen" des Geräts. Mehrere Geräte können in der Argumentliste des OPEN-Befehls gleichzeitig angesprochen werden und damit reserviert werden. Jedes Argument des OPEN-Befehls besteht aus drei voneinander unabhängigen Teilen: 1. einem Ausdruck, der das zu öffnende Gerät bezeichnet, 2. System- und implementierungsabhängigen Parametern, die die genaue Art der Nutzung angeben und 3. einem Timeout. Die beiden letztgenannten Teile eines OPEN-Arguments sind optional, zum Trennen der einzelnen Teile wird der Doppelpunkt verwendet. Damit hat der OPEN-Befehl folgendes allgemeines Aussehen: OPEN DEV:(Parameter):Timeout

4.5.

Die Benutzung externer

117

Geräte

Hinsichtlich des ersten Teils eines OPEN-Arguments wurden schon Beispiele genannt, in vielen MUMPS-Systemen besteht der Name eines Gerätes aus Zahlen, die systemintern verwaltet werden. Um Beispiele für den implementierungsabhängigen zweiten Teil zu geben, stellt Tabelle 4.5 in MicroMUMPS mögliche Parameter dar. Tabelle 4.5: Beispiele für Parameter des OPEN-Befehls OPEN 1 OPEN 5

OPEN 2:("A":"DOSFILE.TXT") OPEN 1::10

; keine Parameter: Zuordnung des Druckers zum Programm. ; keine Parameter: Zuordnung des Kommunikationsports zum Programm. ; Datei DOSFILE.TXT auf Laufwerk Α wird geöffnet. ; Reservieren des Druckers mit Timeout 10 Sekunden.

Die letzte Zeile dieses Beispiels zeigt die Verwendung eines Timeout. Hierunter versteht mein die Angabe einer Anzahl von Sekunden, die man maximal auf die Reservierung des Gerätes warten möchte. Wird diese Zeit überschritten, fährt das MUMPS-Programm mit der Ausführung des nächsten Befehls fort. Möchte man ein Gerät reservieren, das bereits ein anderer Benutzer vorher geöffnet hat und verwendet keinen Timeout, muß man warten, bis der Vorgänger die Benutzung beendet hat. Wie schon bei der Einführung des Timeouts in Verbindung mit dem READBefehl erklärt wurde, kann im Programm über den Wert von $TEST abgefragt werden, ob das Offnen des Geräts erfolgreich war oder ob der Timeout verstrichen ist. War das Offnen in der angegebenen Zeit erfolgreich, dann hat $T den Wert 1, im anderen Fall den Wert 0. Daher kann man auch wieder den ELSE-Befehl zur Abfrage der $TEST-Variable verwenden, wie das nächste Beispiel zeigt: Beispiel

OPEN 1::10 ELSE W !."Geraet belegt" GOTO ABBRUCH Natürlich können auch weniger drastische Maßnahmen als der Abbruch vorgenommen werden. Beispielsweise könnte man nach einer gewissen Zeit noch einmal das Reservieren des externen Gerätes versuchen, in der Hoffnung, daß der bisherige Benutzer seine Arbeit beendet hat.

118

Kapitel 4. Kommunikation mit angeschlossenen Geräten

4.5.3

Auswahl eines Geräts und Beenden der Kommunikation — USE, $IO und CLOSE

Hat man erfolgreich das gewünschte Gerät oder die Datei reserviert, muß man angeben, welches Gerät (Datei) für Ein- oder Ausgabe benutzt werden soll. Hierzu dient der USE-Befehl. Das Argument des USE-Befehls kann wieder herstellerabhängige Parameter enthalten. Es wird dafür die gleiche Syntax wie für OPEN verwendet. Der generelle Aufbau ist wie folgt: USE DEV:(Parameter) Je nach Gerätetyp werden mit dem READ-Befehl Zeichen vom Gerät gelesen oder mit dem WRITE-Befehl Zeichen auf dieses ausgegeben. Um Mißverständnisse zu vermeiden, soll hier darauf hingewiesen werden, daß die in MUMPS definierte Datenhaltung (nämlich das Konzept der „globalen" Variablen, siehe Abschnitt 5.2) mit anderen als den hier vorgestellten Mitteln geschieht. Jedesmal, wenn durch den USE-Befehl ein Gerät spezifiziert worden ist, enthält die Systemvariable $10 den Namen des aktuell zur Benutzung vorgesehenen Gerätes. Wenn USE noch nicht benutzt wurde, enthält $10 den Namen des Benutzerbildschirms, des sogenannten Hometerminals (in manchen MUMPS-Systemen aber auch den Leerstring). Abbildung 4.6 geht davon aus, daß man in MicroMUMPS unter MSDOS eine Textdatei satzweise lesen möchte und auf einen Drucker und gleichzeitig auf den Bildschirm ausgeben mochte. Die Wirkung der Unterprogramme bedarf keiner Erklärung. Im aufrufenden Programm wird der Variablen home der Gerätenamen des Benutzerbildschirms zugewiesen. Mit dieser Variablen kann man im Programm nach der Druckausgabe das Benutzerterminal wieder zum aktuellen Ausgabegerät machen. MUMPS-Systeme sind typischerweise Mehrbenutzersysteme und man muß daher ein einmal reserviertes Gerät nach der Benutzung wieder freigeben, um es anderen Benutzern verfügbar zu machen. Diese Freigabe geschieht mit dem CLOSE-Befehl, der als Argument(e) die Namen der zu schließenden Geräte erhält. Dem einzelnen Argument können wieder systemabhängige Parameter folgen, für deren genaue Definition auch hier auf die entsprechenden Benutzerhandbücher verwiesen sei. Der allgemeine Aufbau des CL0SEBefehls ist daher wie folgt. CLOSE DEV:(Parameter)

4.5. Die Benutzung externer Geräte

119

S home»$io OPEN 1,2:("C":"TEXT.TXT") D PRINT USE home QUIT PRINT ; Unterprogramm zum Lesen aue einer MSDOS-Datei ; und Schreiben auf Drucker und Bildschirm F D CP Q:satz«"" ; Schleife, beendet bei Dateiende QUIT ; Ende des Unterprogramms PRINT CP ; Kopieren eines Satzes U 2 READ satz ; liest jeweils einen Satz aus Datei Q:satz»"" ; Rueckkehr bei Dateiende U 1 WRITE !,satz ; schreibt Satz auf den Drucker U home WRITE !,satz ; schreibt Satz auf Bildschirm QUIT ; Rueckkehr — naechster Satz Abbildung 4.6: Lesen aus Datei und Schreiben auf Drucker und Bildschirm CLOSE gibt es auch in der argumentlosen Form. Es werden dann alle geöffneten externen Geräte geschlossen und $10 erhält den Wert des Benutzerterminals oder ist leer, je nach Art des verwendeten MUMPS-Systems.

120

4.6

Kapitel 4. Kommunikation mit angeschlossenen Geräten

Der Sprachstandard und die Portabilität von Programmen

MUMPS ist eine außerordentlich weitgehend standardisierte Sprache und die Praxis zeigt, daß es möglich ist, vollständig portable — also auf andere Rechner übertragbare — Programme zu schreiben. Der Standard bezieht sogar die herstellerspezifischen Erweiterungen ein, indem er bestimmte Befehls-, Funktions- und Variablennamen dafür vorsieht. Die wichtigste Gruppe von herstellerspezifischen Befehlsnamen bilden die Z-Befehle. Von ihnen wird nur verlangt, daß ihr Name mit Ζ beginnt. Wie er fortgesetzt wird, wie die Abkürzung lautet, welche Argumente zulässig sind und welche Wirkimg sie haben, bleibt dem Hersteller überlassen. In der Gruppe der Z-Befehle können beliebig viele Befehle zur Erweiterung der Sprache vorgesehen werden. Die in den unterschiedlichen Systemen anzutreffenden Z-Befehle werden überwiegend im Bereich der Programmverwaltung (einschließlich Editieren), des Dateimanagements und der Fehlerbehandlung eingesetzt. Alle noch nicht belegten Anfangsbuchstaben von Befehlsnamen sind für spätere, standardisierte Erweiterungen der Sprache vorgesehen und sollen nicht von den Herstellern für eigene Erweiterungen benutzt werden. Entsprechend den Befehlen können auch Funktionen und spezielle Variablen um Namen erweitert werden, die mit $Z beginnen. Auch hier sind Abkürzungen und Bedeutungen vom Hersteller festzulegen. Ahnlich vage ist die Spezifikation des VIEW-Befehls. Dieser Befehl kann zur Anzeige und Manipulation maschineninterner Information benutzt werden. Auch hier sind wieder die zulässigen Argumente und deren Bedeutung dem Hersteller überlassen. In der Gruppe der Funktionen ist, wie auch bei den Befehlen, noch eine besondere Möglichkeit zum Zugriff auf maschineninterne Information vorgesehen. Dazu dient die Funktion $VIEW. Wieder sagt der Standard nichts über Anzahl und Bedeutung der Argumente aus. Alle noch nicht belegten, von Ζ abweichenden Anfangsbuchstaben für Funktionen und spezielle Variablen sind reserviert für spätere standardisierte Erweiterungen und sollen nicht herstellerspezifisch belegt werden. Wenn man portable, also übertragbare Programme schreiben will, sollte man die Benutzung der herstellerspezifischen Namen vermeiden. Das gleiche gilt für herstellerspezifische Teile von Befehlsargumenten. (z.B. OPEN, USE und CLOSE) Es gibt jedoch noch einen anderen Aspekt der Portabilität von Programmen. Er bezieht sich auf das Verhalten des MUMPS-Systems in Fällen, die im Standard nicht angegeben sind.

4.6. Der Sprachstandard und die Portabilität von Programmen

121

Ein Beispiel hierfür ist das Verhalten des Bildschirms nach Ausführung eines READ-Befehls. Bei manchen Systemen bleibt nach dem Drücken der Returntaste der Cursor an seiner ursprünglichen Position stehen. Bei anderen Systemen wird er an den Zeilenanfang positioniert. Bei wieder anderen Systemen wird der Cursor an den Anfang der folgenden Zeile positioniert. Daher sind Programme, die ein bestimmtes Verhalten des Rechners in diesen Fällen voraussetzen, nicht portabel. Bei dem oben erwähnten Beispiel des READ-Befehls könnte ein solches Programm voraussetzen, daß der Cursor an seiner Position stehen bleibt und automatisch eine unvollständige Eingabe ergänzen. Es lassen sich noch viele weitere Beispiele finden, bei denen nicht allgemein gültige Annahmen die Übertragbarkeit von Programmen erschweren. Daher sollte man sich beim Schreiben eines portablen Programms auf den Sprachstandard und auf die Portabilitätsanforderungen stützen und nur die dort gemachten Aussagen voraussetzen. In manchen Fällen läßt es sich nicht vermeiden, implementierungsspezifische Befehle zu verwenden. Hier empfiehlt es sich, diese Befehle in einem oder einigen wenigen Unterprogrammen zusammenzufassen. Diese Unterprogramme sollten in der Dokumentation besonders hervorgehoben werden. Bei der Übertragung auf einen anderen Rechner müssen dann nur die wenigen, genau definierten Unterprogramme geändert werden, um das gesamte Programmsystem auf dem neuen Rechner funktionsfähig zu machen.

Kapitel 5 Datenhaltung und Datenmanagement

5.1 5.1.1

Indizierte Variablen Zahlen als Indizes

Es ist gelegentlich sinnvoll, eine Ansammlung von verwandten Variablen in einer einzigen Variablen zusammenzufassen, deren einzelne Inhalte durch einen Index spezifiziert werden. Man nennt eine solche Variable eine „indizierte" Variable und die so definierten Strukturen Felder. Ein einfaches Beispiel für ein Feld ist die Zuordnung einer Postleitzahl zum Ort. Als Namen des Feldes konnte man o r t nehmen, als Index würde man die Postleitzahl nehmen und als Wert die zugehörige Stadt. Ein Auszug aus diesem Feld konnte folgendes Aussehen haben: ort(6000)»"Frankfurt" ort(6100)="Darmstadt" ort(6200)»"Wiesbaden" ort(6300)»"Mainz" Hier wird die formale Schreibweise einer indizierten Variablen deutlich. Hinter dem Variablennamen schreibt man den Index in Klammern. Die Wertzuweisung wird wie bei nicht indizierten Variablen über den SET-Befehl vorgenommen. Beispiel

S ort(6000)»"Frankfurt" Der Vorteil von indizierten Variablen wird sofort mit dem Beispiel in Abbildung 5.1 deutlich. Es stellt einen vereinfachten Ausschnitt aus einem Erfassungsprogramm für Postleitzahlen und zugehörigen Orten dar. In diesem Beispiel wird der argumentlose FOR-Befehl benutzt, um eine Schleife aufzubauen, in der nacheinander verschiedene Paare von Postleitzahlen und Orten gelesen und mit dem SET-Befehl in die indizierte Variable o r t gespeichert werden. Abgebrochen wird die FOR-Schleife, falls durch bloßes

124

Kapitel 5. Datenhaltung und

Datenmanagement

F DERF Q:post="" ERF R ! , " P o s t l e i t z a h l : ",post Qrpost«"" R !,"0rt : ",ort S o r t ( p o s t ) - o r t quiT

Abbildung 5.1: Ausschnitt aus einem Erfassungsprogramm für die Zuordnung Postleitzahl — Ort

Drücken der Returntaste der Leerstring eingegeben wurde, wodurch zuerst das Unterprogramm und dann die FOR-Schleife beendet werden. In den wichtigsten höheren Programmiersprachen findet sich das Konzept der indizierten Variablen. MUMPS unterscheidet sich jedoch bei der Art der Definition und der Nutzung von Feldern in mehrfacher Hinsicht. Von besonderer Bedeutung ist die Tatsache, daß man in vielen anderen Sprachen Felder dimensionieren muß. Dahinter verbirgt sich die Reservierung von Speicherplatz für ein Feld. Dieser Speicherplatz muß oft von vornherein reserviert werden, auch wenn er aktuell nicht voll ausgeschöpft wird, wenn also Elemente eines Feldes nicht definiert sind. Für das Feld, dessen Indizes Postleitzahlen darstellen, müßte man je nach Programmiersprache 9000 oder 10000 Eintrage vorher reservieren, obwohl es zwischen den Postleitzahlen Lücken gibt und die erste Postleitzahl erst bei 1000 beginnt. MUMPS verfolgt hier ein anderes Konzept. Das Dimensionieren eines Feldes entfallt komplett, weil nur diejenigen Elemente Speicherplatz belegen, denen ein Wert zugewiesen wurde. Man nennt diese in MUMPS verwendete Speichermethode gestreute Speicherung (sparse array). Diese Speichermethode steht im Gegensatz zur dichten Speicherung (dense array), bei der der

gesamte mögliche Speicherplatz von Beginn an belegt wird. Die gestreute Speicherung ist für die in MUMPS sehr häufig vorkommenden dünn besiedelten Felder sehr ökonomisch. Insbesondere mehrdimensionale Felder, also Felder mit mehreren Indizes (MUMPS setzt keine direkte Grenze für die Anzahl der Indizes), sind bei der in MUMPS üblichen Verwendung nur sehr dünn besiedelt. In Anlehnung an das vorangegangene Beispiel soll für die indizierte Variable ort noch ein zweiter Index eingeführt werden, der innerhalb einer Stadt nach Stadtteilen oder Ortsteilen unterscheidet. Ein Auszug aus diesem zweidimensionalen Feld für Frankfurt könnte dann folgendes Aussehen haben:

5.1.

Indizierte

125

Variablen

ort(6000,l)="Frankfurt Innenstadt" ort(6000,70)="Frankfurt Niederrad" ort(6000, 75 )="Frankfurt Flughafen" ort(6000,80)="Frankfurt Hoechst" Hier wird sofort sichtbar, daß man mit der dichten Speicherung rasch an die Grenze des zur Verfügung stehenden Speicherplatzes gelangen würde, weil mehrere hundertausend Speicherzellen reserviert werden müßten. Dieses Beispiel erläutert zusätzlich die Schreibweise von mehrdimensionalen Feldern. Die einzelnen Indizes werden voneinander mit Kommata getrennt. In MUMPS ist die Anzahl nur insoweit begrenzt, als die Länge aller ausgewerteten Indizes plus die Anzahl der Indizes multipliziert mit 2 plus die Länge des Namens der Variablen 127 Zeichen nicht übersteigen darf. Jeder einzelne numerische Index muß außerdem im in MUMPS erlaubten Zahlenintervall liegen. Anstatt nun eine solche Struktur als ein- oder mehrdimensionales Feld, also als Vektor oder Matrix zu interpretieren, ist es in MUMPS üblich, solche Felder als hierarchische Struktur zu betrachten. ort

. . . ort(6000) ort(6000,1)

ort(6100)

ort(6200)

ort(6300)

ort(6000,70)

Abbildung 5.2: Die Interpretation von Feldern als hierarchische Datenstrukturen In Abbildung 5.2 bezeichnet die (nicht indizierte) Variable ort den Beginn der hierarchischen Struktur. Da man eine solche Struktur auch als Baum bezeichnet, stellt ort die Wurzel des Baumes dar. Die Aste des Baumes verbinden die als Knoten bezeichneten Teile der indizierten Variablen wie ort, ort(6000) oder ort(6000,70). Von der Variablen ort sagt man, sie stehe auf der nullten (Index-) Stufe, während ort (6000) auf der ersten Stufe und entsprechend die zweifach indizierten Variablen (z.B. ort (6000,70)) auf der zweiten Stufe stehen, ort (6000) bezeichnet man als Vorgänger der zweifach indizierten Variablen ort(6000,70), usw. Entsprechend sagt man von den zweifach indizierten Variablen, sie seien Nachfolger der einfach indizierten Variablen.

126

5.1.2

Kapitel 5. Datenhaltung

und

Datenmanagement

Löschen und Wertübergabe von (Teil-) Bäumen

Genauso wie bei den nichtindizierten lokalen Variablen lassen sich indizierte Variablen ganz oder teilweise mit dem KILL-Befehl (siehe dazu Abschnitt 1.4.3) löschen. Zur Erklärung dient noch einmal die gerade entwickelte hierarchische Struktur der indizierten Variablen ort. Generell gilt, daß ein KILL auf einen Knoten dieser Struktur den Knoten selbst und alle Nachfolger bis in die unterste Hierarchiestufe löscht. Daher hätte beispielsweise der Befehl KILL ort (6000) zur Folge, daß dieser Knoten und die darunterliegenden Nachfolger (also ort (6000,1), usw.) gelöscht werden. Die anderen Teile dieser Datenhierarchie, also ort (6100) und folgende, sind von diesem KILL-Befehl nicht betroffen. Um die gesamte Struktur ort zu löschen, genügt der Befehl KILL ort. Beim KILL-Befehl in der Ausschlußform (Löschen aller Variablen bis auf die in der Klammer angegebenen) sind indizierte Variablen als Argumente nicht erlaubt. Ahnlich verhält es sich mit dem NEW-Befehl, der in Abschnitt 2.5 eingeführt wurde. Hier gilt, daß mit dem Befehl NEW a die lokale Variable a und alle Nachfolger von a verborgen werden. Indizierte Variablen als Argumente eines NEW-Befehls sind nicht gestattet und,auch (meistens) nicht sinnvoll. Schließlich soll noch auf die beiden Formen der Wertübergabe beim Parameterpassing eingegangen und die Möglichkeiten des Transfers von Feldern erläutert werden. Bei der ersten Form wird prinzipiell nur ein Wert übergeben und der Aufruf DO "PI (a) würde den Wert der lokalen Variablen a weitergeben. Eventuell vorhandene indizierte Nachfolger von a, also z.B. a ( 2 , 1 ) sind hiervon nicht betroffen. Völlig anders ist es bei der zweiten Form der Wertübergabe, in der die Parameter mit einem Punkt versehen übergeben werden (z.B. DO PR(.a), siehe Abschnitt 2.3). Hier gilt, daß die gesamte indizierte Struktur, also alle Nachfolger von a in das aufgerufene Programm transferiert werden. Aus Gründen der Einfachheit und der Übersichtlichkeit des Aufrufs ist es jedoch nicht möglich, in dieser Form der Wertübergabe Teilfelder zu transferieren. Der Aufruf DO P R ( . a ( 2 , l ) ) ist nicht erlaubt.

5.1.3

Zeichenketten als Indizes

MUMPS benutzt indizierte Variablen in einem viel allgemeineren Sinne als andere Programmiersprachen: sie werden nicht nur als mehrdimensionale Felder gesehen, sondern als allgemeine Baumstrukturen, womit Datenbanksystemen vergleichbare Datenstrukturen aufgebaut werden können.

5.1.

Indizierte

127

Variablen

Daher erscheint es nur natürlich, nicht nur numerische Indizes zu verwenden, sondern den Geltungsbereich von Indizes auf beliebige Zeichenketten zu erweitern. Das führt zu dem Begriff der nichtnumerischen Indizes. Als erstes Beispiel diene die indizierte Variable plz, die in gewisser Weise zum Feld ort im vorigen Abschnitt assoziiert ist. Der Index für die Variable plz ist der Ortsname, während der Inhalt aus der dazugehörigen Postleitzahl besteht. plz("Frankfurt")=6000 plz("Darmstadt")=6100 plz("Wiesbaden")=6200 plz("Mainz")=6300 In diesem Beispiel sind die Indizes Zeichenketten und die Zuweisung erfolgt wie bei numerischen Indizes über den Befehl S plz("Frankfurt")=6000 Als Indizes sind in MUMPS alle druckbaren Zeichen erlaubt. Darunter versteht man eine beliebige Kombination aus Ziffern, kleinen und großen Buchstaben und Interpunktionszeichen. Die Verwendung von Steuerzeichen als Indizes ist nicht gestattet. Ebenso führt die Verwendung des Leerstrings ("") als Index zu einer Fehlermeldung. Die Länge eines einzelnen Index ist auf 63 Zeichen begrenzt. Ein Beispiel soll die Abbildung eines Flugplans in eine hierarchische Datenstruktur beschreiben. Gewöhnlich haben Flugpläne auszugsweise das in Tabelle 5.1 gezeigte Aussehen. Tabelle 5.1: Auszug aus einem Flugplan Von/From/De

Frankfurt

Nach/To/A

Oslo

1234567 12345-7 1234567

10.10-12.10 19.35-21.25 16.40-20.20

+2:00 Fornebu +2.00 LH1336 SK1630 LH1342/SK474

737

Nonstop Nonstop via CPH

Diese Tabelle beschreibt die Flugverbindungen zwischen Frankfurt und Oslo. Aus diesem Flugplan sind die Abflugszeiten, Ankunftszeiten, Flugnummern, Flugzeugtyp sowie mögliche Zwischenlandungen ersichtlich.

128

Kapitel

5. Datenhaltung

und

Datenmanagement

Die Abbildung einer solchen Tabelle in eine oder mehrere indizierte Variable(n) hängt sehr stark von der Problemstellung ab, die man lösen möchte. An dieser Stelle soll nicht die Problematik des Designs von Datenstrukturen besprochen werden (hierzu sei auf Abschnitt 5.6 verwiesen). Es wird vielmehr auf die Möglichkeiten des Aufbaus der Indizes eingegangen. Eine einfache Fragestellung besteht darin, Auskunft über Abflugsort, Zielort und Abflugszeit zu bekommen. Hierzu genügt eine dreifach indizierte Variable, die als Wert die übrige Information enthält. Beispiel

flug("Frankfurt","Oslo","10:10")="1234567~12:10~LH 1336~737~ Nonstop" Wenn auch an dieser Stelle die Sprachmittel, die MUMPS zur Analyse solcher komplex aufgebauter Variablen zur Verfügung stellt, noch nicht eingeführt wurden, stellt dies ein typisches Beispiel der Informationsspeicherung dar. Diejenigen Parameter, die als Schlüssel, also als Zugriffsinformation verwendet werden sollen, werden als Indizes benutzt. Die weiteren Größen stehen der Reihe nach im Werteteil der Variablen, jeweils separiert durch ein Trennzeichen. Ein Ausdruck der indizierten Variablen f l u g als Abbild der Tabelle 5.1 würde daher folgendes Bild ergeben: Beispiel

flug("Frankfurt","Oslo","10:10")*"1234567~12:10~LH 1336~737~ Nonstop" flug("Frankfurt","0slo","16:40")*"1234567~20:20~LH 1342/SK 4 74~~via CPH" flug("Frankfurt","Oslo","19:35")="12345-7"21:25~SK 1630~Non stop" Es bleibt noch anzumerken, daß man bei der Erfassung dieser Informationen an keinerlei Reihenfolge gebunden ist. Ein weiteres Merkmal von MUMPS besteht darin, daß die Sortierung der beliebig eingegebenen Indizes nach bestimmten Regeln intern vorgenommen wird. Daher entspricht die Reihenfolge der Ausgabe auch nicht der in Tabelle 5.1. Aus dem gleichen Grund wird anstelle des in der Tabelle verwendeten Punktes in der Zeitangabe der Doppelpunkt benutzt. Darauf wird in Abschnitt 5.3.1 noch eingegangen.

5.1.

Indizierte

Variablen

129

Die Möglichkeit der Verwendung von Zeichenketten als Indizes erlaubt nun die Definition komplexer Datenstrukturen, auf die im weiteren Verlauf noch zurückgekommen wird. Wesentlich ist dafür jedoch, daß es eine Möglichkeit gibt, Informationen, d.h. Variablen bzw. Datenstrukturen auch über das Ende einer Computersitzung hinaus speichern zu können. Sie soll im nächsten Abschnitt behandelt werden.

130

5.2 5.2.1

Kapitel 5. Datenhaltung

und

Datenmanagement

Datenhaltung in MUMPS — Globale Variablen Grundprinzipien globaler Variablen

Die bisher betrachteten Variablen (indiziert oder nicht indiziert) werden als lokal bezeichnet, da sie lokal zu einer Computersitzung an einem MUMPSSystem sind. Mit dem Ende der Sitzung gehen die Werte lokaler Variablen verloren. Weiterhin hat in Mehrbenutzersystemen jeder Benutzer (genauer: jeder Prozeß) einen eigenen Satz lokaler Variablen. Sie sind untereinander nicht sichtbar und nicht veränderbar. Im Gegensatz dazu kennt MUMPS den Begriff der globalen Variablen, die die gleiche Struktur wie die lokalen Variablen aufweisen, also gar nicht, einfach oder mehrfach indiziert sein können. In verkürzter Sprechweise wird in MUMPS die globale Variable oft auch einfach Global (sprich „Globel'") genannt. Globale Variablen dienen der permanenten Speicherung von Information. Auf sie ist über die Grenzen einzelner Sitzungen hinaus Zugriff möglich. Globais erlauben sowohl den gleichzeitigen Zugriff mehrerer auf einem Mehrbenutzersystem nebeneinander laufender Programme als auch den Zugriff von Programmen, die zeitlich nacheinander laufen. Globale Variablen sind mit Dateien vergleichbar und werden auch wie diese benutzt. Dabei wird ein Satz einer Datei eindeutig durch den verwendeten Index identifiziert, er entspricht also einem Knoten des durch eine globale Variable realisierten Baums. MUMPS erlaubt es, globale Variablen in (nahezu) gleicher Weise zu verwenden wie lokale Variablen. Eine Variable wird einfach dadurch als global gekennzeichnet, daß ihrem Namen ein Zirkumflex (") vorangestellt wird, das im Sprachgebrauch von MUMPS auch als „Globalzeichen" bezeichnet wird. Also wird durch Umstellen der auf die lokale Variable o r t bezogenen Anweisungen wie S ort(6000)="Frankfurt" auf die globale Variable "ort S "ort(6000)="Frankfurt" erreicht, daß die Datenstruktur global abgelegt wird, also nicht bei Ende der Sitzung verlorengeht.

131

5.2. Globale Variablen

Wie schon gesagt, entspricht eine globale Variable einer Datei, und ein Element einer indizierten globalen Variablen entspricht einem Datensatz dieser Datei, der über den Index (oder die Indizes) qualifiziert wird, der also wie bei den lokalen Variablen den Charakter eines Schlüssels annimmt. Die Namensgebung von globalen Variablen ist in jeder Beziehung der von lokalen Variablen gleich. Es können also auch kleine und große Buchstaben verwendet werden. Zur leichteren Unterscheidung von lokalen Variablen werden die globalen Variablen im folgenden mit Großbuchstaben geschrieben. Der Zugriff auf globale Variablen unterliegt bei Mehrbenutzersystemen im allgemeinen besonderen Berechtigungsprüfungen, die dafür sorgen sollen, daß Daten immer nur von solchen Benutzern bzw. Programmen gelesen oder verändert werden dürfen, die dazu autorisiert sind. Solche Prüfungen sind jedoch bei der Handhabung globaler Variablen nicht sichtbar und daher auch nicht Gegenstand dieses Buches. Globale Variablen müssen in keiner Weise vorher initialisiert werden. Man kann sie fast in jeder Situation wie lokale Variablen verwenden. Beispiel WRITE "PLZ(postlz),! In diesem Beispiel ist das Argument des WRITE-Befehls eine globale Variable. Der Inhalt dieses Datensatzes wird auf den Bildschirm geschrieben. Weiter dürfen globale Variablen auch in zusammengesetzten Ausdrücken direkt vorkommen: Beispiel S 1»$L(-G1(11))+$LCG1(12)) Hier wurde die Länge, also die Anzahl der Zeichen, der globalen Variablen ' G l (11) und "Gl (12) addiert. Weiterhin ist es möglich, als Index eines Globals eine andere globale Variable zu verwenden, also z.B. S x«~Gl(~RES). Die einzigen Unterschiede bei der Verwendung von lokalen und globalen Variablen sind im folgenden summarisch aufgeführt. Globale Variablen dürfen nicht benutzt werden: • als Argument eines READ-Befehls, d.h. R ~GV ist nicht gestattet • als Laufvariablen eines FOR-Befehls, d.h. FOR stattet

: 1 :n ist nicht ge-

• als Argument des KILL-Befehls in der Klammerform, d.h. KILL (~GV) ist nicht gestattet

132

Kapitel 5. Datenhaltung und

Datenmanagement

• als Argument des NEW-Befehls, d.h. NEW "A ist nicht gestattet • als Ubergabewert bei der Parameterübergabe in der Punktform (call by reference), d.h. DO PROG(."A) ist nicht gestattet Nur dieser letzte Punkt bedarf einer besonderen Erklärung. Während beim einseitigen Transfer etwa mit DO " P I ( " G l ( 2 1 ) ) eine globale Variable als Parameter erlaubt ist, kann bei der Punktform nur mit lokalen Variablen gearbeitet werden. Im ersten Fall wird der Wert des Globais übergeben, während im zweiten Fall eine Beziehung zwischen den Parametern der aktuellen und formalen Liste hergestellt wird, die sich jedoch auf Variablen im Hauptspeicher, also lokale Variablen beschränkt.

5.2.2

Lesen, Schreiben und Löschen von globalen Variablen

Aus dem vorangegangenen Abschnitt wurde deutlich, daß eine indizierte globale Variable als ein Datensatz einer MUMPS-Datei interpretiert werden kann. Wie in anderen Datenhaltungssystemen gibt es eine Reihe von Elementaroperationen bei der Verwendung von Globais, die in diesem Abschnitt vertieft werden sollen. Diese Grundoperationen bestehen aus: 1. Lesen eines Datensatzes 2. Schreiben eines Datensatzes 3. Andern eines Datensatzes 4. Loschen eines Datensatzes Für die nachfolgenden Beispiele sei angenommen, daß in einem Global mit dem Namen "PERS Informationen über Personen gespeichert sind. Der Index enthalte den Namen dieser Personen und der Wert eines Eintrags enthalte Informationen zu einer Person. Ein Auszug aus diesem Global könnte das in Abbildung 5.3 dargestellte Aussehen haben. Zum Lesen eines Datensatzes dieses Globais ist es notwendig, einen existierenden Index (den Schlüssel) zu kennen. Wenn dieser Index beispielsweise in der lokalen Variablen name steht, kann auf den Datensatz zugegriffen werden. Beispiel:

Lesen eines Datensatzes

W name,!,"PERS(name) S 1v="PERS(name)

5.2.

Globale Variablen

133

"PERS("Hasenstab,Robert")="" ~PERS("Heuke,Michael,,)="" "PERS("Joeckel,WolfgangM)»"" "PERSO'Kupny,Anton")="" ~PERS("Oehlenschlaeger,Wilma")ss"" "PERS("Reddert-Bree,Herma,,)slI," "PERSC'ReichenbaecherjGuenter'O^'^Informationen zur Person>" Abbildung 5.3: Auszug aus einem Global mit Personeninformationen In der ersten Zeile wird der Name und die zugehörige Information einer Person auf den Bildschirm geschrieben, im zweiten Fall wird der Satz in eine lokale Variable gestellt. In beiden Fällen muß der Datensatz aus der Datei gelesen werden. Mochte man umgekehrt einen neuen Datensatz in die Datei schreiben, geht das nur über den SET-Befehl. Wenn der neue Name wieder in der Variablen name und die zugehörige Information in der Variablen i n f steht, zeigt folgendes Beispiel einen Schreibvorgang: Beispiel:

Schreiben eines Satzes

S "PERS(name)=inf Das Ändern eines Datensatzes geschieht einfach dadurch, daß man den alten Datensatz überschreibt. Beispiel:

Andern eines Datensatzes

S "PERS(name)=infneu Hier wurde angenommen, daß die Variable infneu die zu ändernde Information für den Index name enthält. Schließlich wird das Löschen eines Datensatzes mit dem Index name mit dem KILL-Befehl ausgeführt. Beispiel:

Löschen eines Datensatzes

KILL "PERS(name) Mit dieser Form des Befehls wird genau ein Datensatz aus dem Global "PERS gelöscht. Der Befehl KILL "PERS hätte zur Folge, daß der gesamte Global "PERS gelöscht würde.

134 5.2.3

Kapitel 5. Datenhaltung

und

Datenmanagement

Dienstprogramme für Globais

Dienstprogramme sind Programme, die entweder Auskünfte über systemnahe Parameter geben oder über die man selbst aktiv das MUMPS-System beeinflussen kann. Im Zusammenhang mit Globals sollen in diesem Abschnitt einige nützliche Dienstprogramme beschrieben werden. Diese Dienstprogramme sind teilweise nur Personen mit besonderen Systemvollmachten zugänglich. Wenn auch die Benutzung der Globals vollkommen standardisiert ist, so hängen Art der Benutzung und die Namen der zur Verfügung stehenden Dienstprogramme sehr stark vom verwendeten MUMPS-System ab. Die an dieser Stelle beschriebenen Programme finden sich in der einen oder anderen Form in den meisten MUMPS-Systemen und man muß in seinem Benutzerhandbuch den Namen des betreffenden Programms nachschlagen. Für die Verwaltung der globalen Variablen ist in erster Linie ein Programm notwendig, das eine vollständige Liste aller definierten Globals liefert. In manchen Fällen gibt es noch eine erweiterte Liste, die zusätzliche Informationen über Schutzcodes, Speicherform und belegten Speicherbereich liefert. Programme zum allgemeinen Management von Globals darf meist nur der Systemverwalter bedienen. Mit diesen Programmen ist man in der Lage, einen Zugriffsschutz auf Globals zu definieren, die Speicherungsform festzulegen und noch weiteres mehr. Besonders wichtig und vom Programmierer häufig gebraucht sind Programme, die eine Liste des Inhalts eines Globals produzieren. In vielen Fällen sind diese Programme sehr komfortabel angelegt und erlauben auch das teilweise Listen eines Globals nach bestimmten Kriterien. Unter einem Globaleditor versteht man ein Werkzeug, mit dem man in einem Global gezielt nach Zeichen suchen oder Teile eines Globals ändern kann. Auch Kopierprogramme, die Teile eines Globals in einen anderen Global kopieren, sind von großem Nutzen. Und schließlich gibt es meistens noch eine ganze Gruppe von Programmen, mit denen man Globals (z.B. auf Magnetband) sichern und ebenso von einem Magnetband wieder zurückspielen kann. Es soll abschließend noch bemerkt werden, daß die Qualität eines MUMPS-Systems in hohem Maße durch die Güte dieser Dienstprogramme bestimmt ist.

135

5.3. Sortierung und $ORDER

5.3 5.3.1

Sortierung und $ ORDER Die Sortierreihenfolge

Bei der Einführung der indizierten lokalen und globalen Variablen ist die Verwendung von beliebigen Zeichenketten als Indizes deutlich geworden. Bereits dort wurde von einer Sortierreihenfolge in MUMPS gesprochen. Um das „Navigieren" in hierarchischen Datenstrukturen vorzubereiten, soll zunächst der Begriff der Sortierung in MUMPS vertieft werden. Gäbe es in MUMPS nur alphabetische Zeichenketten als Indizes, könnte man nach der normalen lexikalischen Ordnung sortieren. Auch die Einbeziehung der übrigen ASCII-Zeichen als Indizes kann mit einer Erweiterung der lexikalischen Sortierordnung gewährleistet werden. Dabei werden zwei Zeichenketten von links nach rechts verglichen, bis das erste Zeichen gefunden wird, in dem sie sich unterscheiden oder eine endet. Die Zeichenkette, die an dieser Stelle das Zeichen mit dem kleineren ASCII-Code hat oder die beendet ist, kommt in der Sortierreihenfolge früher. Diese Reihenfolge wird in MUMPS zur Sortierung von Zeichenketten verwendet. Bei der Ordnung von numerischen Indizes versagt jedoch diese Sortierung in dem Sinne, daß sie die Indizes nicht immer in einer numerisch aufsteigenden Reihenfolge sortiert. Das liegt unter anderem daran, daß Zahlen eine unterschiedliche Anzahl von Stellen haben. So wird zum Beispiel die Zahl 2, die numerisch kleiner als 10 ist, lexikalisch nach der 10 einsortiert, da bei der lexikalischen Sortierordnung zuerst die ersten Zeichen der beiden Zeichenketten verglichen werden, also 1 mit 2. Da sich die Zeichen unterscheiden, steht fest, daß die 2 hinter der mit 1 beginnenden Zeichenkette einsortiert werden muß. Aus diesem Grund gibt es in MUMPS zwei Sortierreihenfolgen für Indizes: 1. für numerische Werte die Sortierung nach der mathematischen Großerbeziehung, nach der negative Zahlen vor der Null und vor den positiven Zahlen eingeordnet werden und 2. danach alle anderen Zeichenketten nach der lexikalischen Sortierreihenfolge. Die folgende (nichtgeordnete) Liste von indizierten Variablen x(0)

x("a")

x(lOO)

x(-12)

x(-3.1)

x("AM)

x(" a b c")

würde also in MUMPS die folgende Sortierung erfahren: x(-12)

x(-3.1)

x(0)

x(100)

x(" a b c")

x("A")

x("a")

136

Kapitel 5. Datenhaltung

und

Datenmanagement

Hierzu vergleiche man die ASCII-Tabelle und beachte den fuhrenden Leerschritt im Index " a b c". Nun stellt sich jedoch noch ein weiteres Problem, nämlich die Anwesenheit von führenden oder dem Dezimalpunkt folgenden Nullen in einer Zahl. Hierzu gehören die Zahlen 12.3000, bei der drei für den Wert der Zahl bedeutungslose Nullen angehängt sind, sowie 006, bei der die beiden führenden Nullen den Wert der Zahl ebenfalls nicht verändern. Die Regel der Sortierreihenfolge in MUMPS erkennt solche Zahlen nicht als numerische Werte, sondern als Zeichenketten. Daher werden diese Zeichenketten innerhalb der ASCII-Reihenfolge sortiert. Dies birgt Gefahr, wenn man z.B. Meßwerte als Indizes einer Variablen abspeichern möchte, die auszugsweise folgendes Aussehen hätten: 1.12

1.10

1.07

1.23

1.30

Die Zahlen 1.07,1.12,1.23 werden dabei, den Regeln folgend, als Zahlen erkannt und entsprechend sortiert, die Zahlen 1.10, 1.30 jedoch werden als Zeichenketten angesehen und danach einsortiert. Die Lösung dieses Problems besteht darin, daß man bei der Zuweisung die numerische Interpretation der Indizes durch ein vorangestelltes Pluszeichen (+) erzwingt. Beispiel

F R !,"Messwert : ",mw Q:mw-""

S 'MESSC+mw)-""

In diesem Beispiel wird in einer Schleife wiederholt ein Meßwert erfaßt und global in der Variablen "MESS als Index abgespeichert. Durch die numerische Interpretation wird eine möglicherweise eingegebene Zahl 1.10 in die Zahl 1.1 transformiert. Die geschilderte Problematik war auch der Grund, warum die in Tabelle 5.1 angegebene Uhrzeit (z.B. 10.10, 19.35) nicht in dieser Form als Index in der Variablen f l u g verwendet wurde: 10.10 wird wegen der Endziffer 0 als Zeichenkette, 19.35 als Dezimalzahl interpretiert, so daß 19.35 vor 10.10 einsortiert würde. Durch die Schreibweise mit Doppelpunkt werden alle Indizes als Zeichenkette aufgefaßt und richtig einsortiert. Aus der Sicht eines Benutzers, der mit nationalen Zeichensätzen arbeitet, muß im Hinblick auf die Sortierung von Zeichenketten auf einen weiteren Sachverhalt hingewiesen werden. Im Deutschen werden beispielsweise die Umlaute durch besondere, nichtalphabetische Zeichen dargestellt, die eine

5.3. Sortierung und $ORDER

137

völlig andere Einordnung in den 7 Bit ASCü-Zeichensatz bedingen. So wird das kleine „ä" durch das ASCII-Zeichen „{" (Code 123) repräsentiert. Zeichenketten, in denen das „ä" vorkommt, werden entsprechend nach hinten einsortiert. Entsprechendes gilt für herstellerspezifische Erweiterungen des Zeichensatzes wie beim IBM-PC, bei dem das „ä" durch den ASCH-Wert 132 repräsentiert wird. Eine einfache Losung kann im Rahmen dieses Textes nicht vorgeschlagen werden. Es sei jedoch darauf hingewiesen, daß die Problematik der nationalen Zeichensätze bei der Weiterentwicklung von MUMPS eine bedeutende Rolle spielt.

5.3.2

Das Navigieren in Datenstrukturen — $ORDER

Im Gegensatz zur dichten Speicherung von Feldern mit fortlaufendem numerischem Index kann bei der Indizierung mit beliebigen Zeichenketten auf einer gegebenen Indexstufe der nächste vorkommende Index nie vorausgesagt werden. In dieselbe Kategorie fällt die Frage nach dem ersten Index auf einer beliebigen Indexstufe. Beide Fragen können für lokale oder globale Variablen gestellt werden und die Lösung mit Hilfe der Funktion $ORDER ist für beide Variablentypen gleich. Die generelle Wirkung von $0RDER ist folgende. $0RDER gibt der Reihe nach auf einer Indexstufe alle vorkommenden Indizes, wobei von der im vorhergehenden Abschnitt definierten Sortierordnung ausgegangen wird. Als Argument von $0RDER dient eine lokale oder globale Variable, die mit dem vorhergehenden Index indiziert ist. Als Beispiel dient wieder der Global "PERS aus Abschnitt 5.2.2, der hier noch einmal auszugsweise dargestellt werden soll: "PERS("Hasenstab,Robert")=" I n f o r m a t i o n e n zur Person>" "PERS("Heuke,Michae1")="" "PERS("Joeckel,Wolfgang")«'^Informationen zur Person>" ~PERS("Kupny,Anton")«"" Dann ist das Ergebnis des Funktionsaufrufs $0RDER("PERS("Heuke.Michael")) der nächste Index im Global "PERS, im Beispiel also "Joe e k e l , Wolf gang". Mit diesem Index kann man durch erneutes Anwenden der Funktion $0RDER den folgenden Index erhalten, usw. Damit ist man in der Lage, sequentiell

138

Kapitel 5. Datenhaltung

und

Datenmanagement

in einer Variablen auf einer Indexebene alle Indizes und damit alle Werte der Indexknoten zu bekommen. Wie bekommt man aber den ersten Index auf einer Indexebene und wie erfährt man das Ende einer Indexstufe, das heißt, wenn keine weiteren Indizes mehr definiert sind? In beiden Fällen ist die Antwort gleich. Um den ersten Index innerhalb einer Indexebene zu bekommen, indiziert man die Variable symbolisch mit dem Leerstring ("") Beispiel

W $0RDER("PERS("")) und erhält als Ergebnis den ersten Namen in diesem Global. Wendet man $ ORDER auf den letzten definierten Namen an, erhält man wieder einen Leerstring, der das Ende der Daten auf dieser Index-Stufe signalisiert. Entsprechend kann man zu einem gegebenen Knoten den ersten Index eine Stufe tiefer finden. Für ein einfaches Beispiel soll eine Erweiterung des Flugplans, der in Abschnitt 5.1 eingeführt wurde, betrachtet werden. Anstatt in einer lokalen Variablen seien die Flugangaben in der globalen Variablen "FLUG abgespeichert (siehe Abbildung 5.4).

"FLUGO'Frankfurt","Abu Dhabi","10:05")=" 7~18:25"LH 762 "AB3~Nonstop" "FLUG("Frankfurt"."Addis Ababa'V'lO:15")«"-2 ~19:55"LH 5 90~AB3~1 Stop" "FLUG("Frankfurt","Adelaide", "21:30") =" 4—"08:05"LH 792" "via MEL" Abbildung 5.4: Auszug aus einem Flugplan Um nun den jeweils nächsten Index auf der zweiten Indexstufe zu bekommen, muß man sich bereits auf den ersten Index beziehen ("Frankfurt"). Beispiel

>W $0RDER("FLUG("Frankfurt","")) Abu Dhabi > Abbildung 5.5 faßt die Wirkung von $0RDER noch einmal summarisch zusammen.

139

5.3. Sortierung und $ORDER

Abbildung 5.5: Übersicht über SORDER Das sequentielle Durchlaufen einer Variablen auf einer Indexebene ist ein typischer iterativer Prozeß, der in MUMPS am besten mit einer FOR-Schleife programmiert wird. Dazu besetzt man eine Variable mit dem Leerstring vor und benutzt eine offene FOR-Schleife. Betspiel S x« M "

F U J S

x=$ORDER(~PERS(x)) q:x«""UL]W ! ,x,?30,~PERS(x)

Dies ist eine für MUMPS sehr typische Befehlszeile. Da χ mit dem Leerstring vorbesetzt ist, bekommt man beim ersten Durchlauf den ersten definierten Index und danach der Reihe nach alle anderen. Irgendwann ist der letzte Eintrag erreicht und χ hat wieder die leere Zeichenkette als Wert. Dies führt über den QUIT-Befehl mit der Nachbedingung zum Abbruch der FOR-Schleife. Dabei wurden die beiden Leerstellen, die dem QUIT hier folgen müssen, mit Uu symbolisiert. Wenn man wieder von dem vorher definierten Global "PERS ausgeht, ist daher das Ergebnis der Reihe nach: Hasenstab,Robert Heuke.Michael Joe ekel, Wolf gang Kupny,Anton

Informationen

Person> Person> Person>

Genausogut kann man auf einer tieferliegenden Indexstufe alle definierten Indizes bekommen. Als Beispiel sei der Global 'FLUG auf der zweiten Stufe angeführt. Dort sind die Zielorte von Frankfurt aus angegeben.

140

Kapitel 5. Datenhaltung und

Datenmanagement

Beispiel

>S x="" F S x=$0("FLUG("Frankfurt",x)) Q:x=""UuW x , ! Abu Dhabi Addis Ababa Adelaide >

Die Verwendung einer indizierten Variablen als Argument von $0RDER bedeutet nicht, daß dieser Knoten auch wirklich existieren muß. Würde man beispielsweise im Global "PERS mit dem sicher nicht existierenden Index "Gzzzzz" auf den nächsten Eintrag abfragen, würde man als Ergebnis den nächsten existierenden Index bekommen. >W $0("PERS("Gzzzzz")) Hasenstab,Robert > Unter Verwendung dieser Methode ist es möglich, eine Teilliste von Indizes zu erzeugen, die beispielsweise alle Namenseinträge liefert, die mit einer bestimmten Buchstabenkombination beginnen. Abschließend soll noch darauf hingewiesen werden, daß $ ORDER nicht nur den nächsten aktuell definierten Knoten liefert, sondern auch „virtuelle" Knoten. Um diesen Begriff zu erklären, sei angenommen, daß a ( l ) und a(3) definiert seien, a(2) jedoch nicht, sondern nur Nachfolger von a(2), etwa a ( 2 , l ) . a(2) wird ein virtueller Knoten genannt, weil er selbst keinen Wert besitzt, aber Nachfolger existieren. $0RDER(a(l)) liefert dann 2 als Ergebnis, obwohl a(2) nicht existiert. Mit diesem Ergebnis ist man jedoch in der Lage, die Nachfolger von a(2) vollständig zu bestimmen. Im Zusammenhang mit $0RDER soll noch auf die Funktion $NEXT eingegangen werden. Sie spielte in früheren Jahren und in zurückliegenden Versionen des Standards die Rolle von $0RDER. Aus heutiger Sicht ist nur wichtig, daß die Wirkung von $NEXT in allen Fällen der Wirkung von $0RDER gleicht. Der einzige Unterschied besteht darin, daß der Endwert gleich —1 ist, im Gegensatz zum Leerstring bei $0RDER.

5.3.3

Analyse der Baumstruktur — $ QUERY

Mit $0RDER und in Verbindung mit der noch einzuführenden Funktion $DATA ist es prinzipiell möglich, einen beliebigen Datenbaum in MUMPS komplett zu analysieren. Das ist deshalb keine ganz einfache Aufgabe, weil $0RDER

5.3.

Sortierung und BORDER

141

in erster Linie die Indizes auf einer gegebenen Indexstufe liefert und die Entscheidung, ob auf einer tieferen Indexebene Datenknoten existieren, im Programm selbst verwaltet werden muß. Für manche Anwendungen ist es jedoch notig, eine indizierte lokale oder globale Variable beliebiger Tiefe mit allen Knoten von oben nach unten und von links nach rechts zu durchlaufen. Hierzu gibt es die Funktion $QUERY, die die gerade gestellte Aufgabe sehr einfach lost. $ QUERY liefert Informationen über den Nachfolger eines Knotens, falls dieser existiert oder, falls kein Nachfolger existiert, über den benachbarten Knoten in der schon diskutierten Sortierreihenfolge. Dabei liefert $QUERY nur die Namen von Knoten, die auch Daten enthalten. Virtuelle Knoten werden übersprungen. Für ein erstes Beispiel sei angenommen, daß die in Abbildung 5.6 erzeugte und in Abbildung 5.7 dargestellte globale Datenstruktur definiert sei. In dieser Abbildung werden die virtuellen Knoten mit dem Symbol φ dargestellt.

~A(1)=1 ~A(2)=2 ~A(3)=3 -A(l,l)=ll ~A(2,1)=21 ~A(2,2,3)=223 ~1(3,2,3,4)=3234 "1("Beethoven",9)="Neunte Symphonie" Abbildung 5.6: Zuweisungen zur Erläuterung von $QUERY Der Befehl W $QUERY(~A(1)) erbringt nun nicht wie $ ORDER den nächsten Index auf der ersten Indexstufe, sondern die nächste volle Globalreferenz (also Name des Globais plus Indizes), die ausgehend von dieser Stufe existiert. Beispiel >W $QUERY(~A(1))

-1(1,1) >

Ausgehend von der nichtindizierten Wurzel von ~A findet man nacheinander die auf tieferen Ebenen definierten vollen Globalreferenzen (von oben nach unten). Existiert irgendwann kein tieferer Eintrag, ist das Ergebnis des Aufrufs die in der Sortiereihenfolge nächste volle Globalreferenz auf dieser oder auf einer höheren Stufe (von links nach rechts).

142

A

Kapitel δ. Datenhaltung und

Datenmanagement

A(l)

A(l,l)

A

A (2, 2,3)

A

A{3,2,3,4)

Abbildung 5.7: Globale Datenstruktur zur Erläuterung von $QUERY Der Wert der nächsten Globalreferenz kann unter Benutzung der Indirektion angezeigt werden. Beispiel

>W ®$QUERY(~A(3)) 3234 >

Denn $QUERY(~A(3)) liefert die Referenz "A(3,2,3,4) und die vorgestellte Indirektion liefert den Wert dieser Referenz. Um den Global "A im Beispiel komplett zu durchlaufen, wird man eine FOR-Schleife mit "A als Anfangswert eröffnen (Abbildung 5.8). S x=$Q(@x) liefert jeweils den nächsten Knoten und falls kein weiterer mehr existiert, greift wie bei $0RDER die Abbruchbedingung Q:x="".

5.3.4

Indirektion auf Indizes

Im Abschnitt 3.10 waren die indirekten Sprachmittel in MUMPS als wirksames, aber für den Anfänger nicht leicht zu durchschauendes Werkzeug erkannt worden. In dieser Hinsicht wurden bereits die Namensindirektion und die Argumentindirektion beschrieben. In diesem Abschnitt soll nun eine weitere Form der Indirektion, die auf Indizes arbeitet, eingeführt werden.

5.3. Sortierung und $ORDER

143

>S x="~A" F S x=$Q(®x) Q:x="" W x,"=",®x,! ~Α(1)«1 "A(l,1)=11 ~A(2)=2 ~A(2,1)"21 ~A(2,2,3)=223 "A(3)*3 ~A(3,2,3,4)=3234 ~A("Beethoven",9)=Neunte Symphonie >

Abbildung 5.8: Globalausdruck mit $QUERY Bei der Entwicklung von Programmen, die auf beliebigen Teilbäumen von Globals arbeiten können, steht man oft vor dem Problem, daß in einer Variablen der Name des Teilbaumes steht, also z.B. n="x(2,5)", man aber einen untergeordneten Knoten ansprechen will. Die Aufgabenstellung würde in diesem Fall also lauten: Sprich den Knoten x ( 2 , 5 , d ) an. Bei Verwendung der bisher schon eingeführten Form der Indirektion würde die Losung so aussehen, daß man mit Textbearbeitungsfunktionen den Inhalt von η so modifiziert, daß der Name des anzusprechenden Knotens erzeugt wird. Dieser könnte dann mit der Namensindirektion indirekt angesprochen werden. Diese Vorgehensweise ist recht aufwendig und unübersichtlich, kann aber durch eine besondere Form der Indirektion ersetzt werden, die Indexindirektion. Damit würde die oben gestellte Aufgabe durch den Ausdruck QnQ(d) gelöst. Die Klammern um die Variable d sind zwingend. Hätte beispielsweise d den Wert "3,1", so würde mit ®n®(d) der Knoten x ( 2 , 5 , 3 , l ) angesprochen werden. Einige weitere Beispiele sind in Abbildung 5.9 zu finden. Es soll abschließend noch bemerkt werden, daß mit dieser Form der Indirektion in Verbindung mit $0RDER beliebig aufgebaute Globals komplett analysiert werden können.

144

Kapitel 5. Datenhaltung

und

Datenmanagement

>S b="xyz",@b®(l)=l W xyz(l) 1 >S c="abc(2,3)",®c®(4,5)«2345 W abc(2,3,4,5) 2345 >S d="A",®c®(d)«"X" W abc(2,3,"A") X > Abbildung 5.9: Beispiele zur Indexindirektion

5.4.

Existenz von Datensätzen

5.4

145

Existenz von Datensätzen — $DATA und $GET

In MUMPS wird grundsätzlich eine Fehlermeldung erzeugt, wenn man sich in einem Ausdruck auf eine nichtexistierende lokale oder globale (indizierte) Variable bezieht. Gerade bei indizierten Variablen steht man im Grunde vor demselben Problem, wie es bei der Einführung von $0RDER geschildert wurde: man kann in vielen Fällen nicht mit Sicherheit sagen, ob ein Knoten, das heißt ein Index, auf einer gewissen Indexebene existiert oder nicht. Für diese Art der Analyse hierarchischer Strukturen auf Existenz eines Knotens steht in MUMPS die Funktion $DATA zur Verfügung. Das Argument der Funktion $DATA ist eine lokale oder globale (indizierte) Variable. Wollte man zum Beispiel die Existenz der globalen Variablen "PERS(name) prüfen, müßte man schreiben: $DATA("PERS(name)) Vor der Darstellung der möglichen Werte dieses Funktionsaufrufs soll eine kleine hierarchische Datenstruktur für ein Beispiel definiert werden. Beispiel >S a = 0 , a ( l ) = l , a ( 3 ) = 3 , a ( l , l ) = l l , a ( l , 2 ) = 1 2 , a ( l , 3 ) « 1 3 , a ( 2 , l ) » 2 1 >S a ( 2 , 2 ) = 2 2 , a ( 3 , 1 ) = 3 1 >

Als Datenbaum hat diese indizierte Variable das in Abbildung 5.10 dargestellte Aussehen.

Abbildung 5.10: Grafische Darstellung der Struktur der Variablen a

146

Kapitel 5. Datenhaltung

und

Datenmanagement

Dem Leser wird vielleicht auffallen, daß der Knoten a ( 2 ) nicht existiert, jedoch Nachfolger auf der nächsten Indexstufe, nämlich a ( 2 , 1 ) und a ( 2 , 2 ) . Das Ergebnis einer Existenzprüfung mit $DATA ist nun recht einfach. Existiert ein Knoten, hat jedoch keine Nachfolger auf einer tieferen Stufe, so ist das Ergebnis 1. In der lokalen Variablen a im obigen Beispiel trifft das auf die zweifach indizierten Variablen a ( l , l ) , a ( l , 2 ) , a ( l , 3 ) , a ( 2 , l ) , a ( 2 , 2 ) und a ( 3 , l ) zu. Beispiel

>W $DATA(a(2,l)) 1 > Eine weitere Möglichkeit in einer hierarchischen Struktur besteht darin, daß sowohl ein Knoten existiert als auch Nachfolger dazu existieren. Im Beispiel ist das für a, a ( l ) und a(3) der Fall. Das Ergebnis von $DATA ist hier 11. Beispiel

>W $DATA(a(l)) 11 > Wenn schließlich weder der Knoten selbst noch Nachfolger zu diesem existieren, ist das Ergebnis eines Aufrufs von $DATA gleich 0. Beispiel

>W $DATA(a(4)) 0 > In der oben dargestellten Datenstruktur macht der Knoten a ( 2 ) insofern eine Ausnahme, als er nicht existiert, jedoch Nachfolger von ihm, nämlich a ( 2 , l ) und a ( 2 , 2 ) . Er ist also ein virtueller Knoten. In diesem Fall ist das Ergebnis von $DATA gleich 10. Beispiel

>W $DATA(a(2)) 10 >

5.4.

Existenz von Datensätzen

147

Summarisch lassen sich diese vier Werte von $DATA in einer Tabelle darstellen: Wert einer Variable existiert existiert nicht

hat Nachfolger $D ist 11 $D ist 10

hat keine Nachfolger $D ist 1 $D ist 0

Man kann sich die Funktionsweise von $DATA auch anders merken: die Einerstelle gibt an, ob die Variable Daten enthält (Einerstelle gleich 1), oder nicht (Einerstelle gleich 0). Entsprechendes gilt für die Zehnerstelle. Sie gibt über die Existenz eines Nachfolgers Auskunft, wobei man noch beachten muß, daß die Zahldarstellung in MUMPS führende Nullen unterdrückt und daher die Null in der Zehnerstelle nicht dargestellt wird. $DATA ist in MUMPS eine vergleichsweise häufig gebrauchte Funktion. Die Existenz einer lokalen oder globalen Variablen wird in vielen Fällen in Verbindung mit dem IF-Befehl abgefragt. Wollte man zum Beispiel prüfen, ob im Global "PERS ein Eintrag "Mueller,Hans" existiert und, wenn ja, die Daten schreiben, so genügen folgende Zeilen: Beispiel

>S x="Mueller,Hans" >IF $DATA(~PERS(x ) ) W !,x,?30,-PERS(x) >

Das Ergebnis des Aufrufs $DATA(~PERS(x)) wird für den IF-Befehl direkt logisch interpretiert. Die Werte 1 und 11 spiegeln die Existenz eines Datensatzes wieder und in beiden Fällen ist die logische Interpretation 1, also wahr. Man vergleiche dazu den Abschnitt 3.2. Im allgemeinen ist auch der Wert 10 möglich, der den degenerierten Fall darstellt, daß der Wert einer Variablen nicht existiert, jedoch Nachfolger dieses Knotens. Im Falle des Globals "PERS war dieser Fall ausgeschlossen und die IF-Abfrage im letzten Beispiel spiegelt immer die Existenz wieder. Kann man diesen Fall jedoch nicht ausschließen, so prüft man die Existenz eines Knotens, indem man eine Restdivision durch 10 ausführt. Das soll anhand der oben eingeführten Variablen a dargestellt werden. Beispiel

>IF $DATA(a(2))#10 W ! , a ( 2 ) >

Existierte a ( 2 ) , so sind die beiden möglichen Ergebnisse des Aufrufs von $DATA 1 oder 11. Die Restdivision durch 10 ergibt immer 1, also logisch wahr

148

Kapitel 5. Datenhaltung

und

Datenmanagement

und der Wert von a(2) kann ausgedruckt werden. Existiert a(2) nicht, sind die beiden möglichen Ergebnisse Ο oder 10 und die Restdivision durch 10 ergibt 0, also logisch falsch. Ahnlich kann man verfahren, wenn man wissen mochte, ob ein Knoten einen Nachfolger hat. Beispiel

>IF $DATA(a(2))\10 W !,"a(2) hat Nachfolger" a(2) hat Nachfolger >

Nachfolger existieren genau dann, wenn der Wert von $DATA 10 oder 11 ist. Eine Ganzzahldivision durch 10 liefert dann eine 1 als Ergebnis. Sind die Werte von $DATA gleich 0 oder 1, so liefert die entsprechende Division 0 als Ergebnis. In vielen Fällen bietet es sich schließlich an, $DATA direkt zu negieren. Beispiel

>IF '$DATA("PERS(x)) W !,"Person unbekannt." >

Wenn "PERS(x) nicht existiert und keine Nachfolger hat, dann liefert der Aufruf eine 0, die negiert logisch wahr ergibt. In manchen Fällen prüft man auf die Existenz einer lokalen oder globalen Variablen und weist dieser Variablen einen Leerstring ("") zu, falls sie nicht existiert. Beispiel

>S:'$DATA(x) x="" > Das bedeutet umgekehrt, daß die Variable ihren Wert behält, falls sie bereits existierte. Oft soll auf Variablen zugegriffen werden, von denen nicht mit Sicherheit bekannt ist, ob sie existieren. Dieses Problem läßt sich grundsätzlich durch vorherige Abfrage von $DATA losen. So liefert $SELECT($DATA(var)#10:var,1:"") den Wert der Variablen var, falls diese existiert und sonst den Leerstring. Da diese Anwendung sehr häufig in MUMPS-Programmen vorkommt, wird

5.4. Existenz von Datensätzen

149

sie einfacher und schneller durch die funktional äquivalente Funktion $GET realisiert. Beispiel

>S var=$GET(var) >

Im Anschluß an diesen Funktionsaufruf ist die Variable var auf jeden Fall definiert, var hat entweder den ursprünglichen Wert oder den Leerstring, falls sie vorher nicht definiert war.

150

5.5 5.5.1

Kapitel 5. Datenhaltung

und

Datenmanagement

Der implizite Bezug auf einen Global Die Naked Reference

MUMPS kennt für den Zugriff auf globale Variablen nicht nur die oben angegebene, sondern auch noch eine andere, abgekürzte Schreibweise, die sogenannte „naked reference". Hier wird nicht der Name einer Variablen mit allen Indizes angegeben, sondern man bezieht sich auf den letzten angesprochenen Knoten einer globalen Variablen und gibt relativ zu diesem Knoten Indizes an. Diese etwas abstrakte Beschreibung soll zunächst am folgenden Beispiel erläutert werden. Beispiel >S ~A(1)=22 WRITE "(1) 22

>S ~(1,"A")="HALLO" W ~("A") HALLO >S ~("B")="DATEN VON B" W "A(1, M B") DATEN VON Β > Die Wirkung der „naked reference" läßt sich am besten verstehen, wenn man sich vorstellt, daß jeder Befehl (bis auf eine Ausnahme, auf die später noch eingegangen wird), der sich auf einen Knoten einer globalen Variablen bezieht, einen als „naked indicator" bezeichneten Zeiger verändert. Dieser „naked indicator" zeigt jeweils auf den Vorgänger in der Baumstruktur der letzten angesprochenen Variablen. Wenn man Nachfolger (mittelbare und unmittelbare) des Knotens, auf den verwiesen wird, ansprechen will, kann man sich der „naked reference" bedienen. Bei dieser Schreibweise gibt man das Globalzeichen ohne folgenden Namen an. Dem Globalzeichen folgen ein oder mehrere Indizes, die in Klammern eingeschlossen sind. Die Indizes werden jetzt nur noch für die Indexstufen angegeben, die durch den „naked indicator" noch nicht festgelegt sind. Bei dem oben angegebenen Beispiel setzt der erste SET-Befehl den „naked indicator" auf "Α. Der nachfolgende WRITE-Befehl gibt einen Index an und führt damit auf die erste dem nicht indizierten Namen folgende Indexebene. Er läßt den „naked indicator" unverändert. Im folgenden SET-Befehl sind zwei Indizes angegeben. Daher führt dieser Befehl auf die zweite Indexebene und es wird das Element "A(l,"A") verändert. Der „naked indicator" zeigt jetzt auf den Vorgänger dieses Knotens, also auf ~A(1). Daher darf die 1 nicht mehr in dem folgenden WRITEBefehl erscheinen.

5.5. Der implizite Bezug auf einen Global

151

In der folgenden Zuweisung wird der Nachfolger des Knotens ~A(1) angesprochen, der den Index "B" hat. Das ist, wie der nachfolgende WRITE-Befehl bestätigt, das Element ~A(1,MB") Nach der Erklärung der Wirkungsweise der „naked reference" soll noch auf einige Besonderheiten bei ihrer Benutzung eingegangen werden. Zuerst sei noch einmal erwähnt, daß es nur einen „naked indicator" für jedes MUMPS-Programm gibt. Daher kann zu einer Zeit natürlich auch nur ein Global mit „naked reference" angesprochen werden. Die Benutzung der „naked reference" ergibt einen Fehler, wenn der „naked indicator" nicht definiert ist. Das ist in drei verschiedenen Situationen der Fall: 1. Nach dem Einloggen, wenn noch kein Global angesprochen wurde. 2. Nach dem Bezug auf eine nichtindizierte globale Variable 3. Nach der Benutzung von $DATA oder $0RDER mit einer nicht existierenden indizierten globalen Variable als Argument. Nun kann man sich bei der Betrachtung dieser doch recht unübersichtlichen Zugriffsmoglichkeit auf Variablen nach dem Sinn und der Berechtigung einer solchen Konstruktion fragen. Sie erklärt sich zum Teil aus der Geschichte der Sprache, da in früheren Implementierungen die Laufzeit mancher Programme durch die Verwendung der „naked reference" stark verringert werden konnte. Ein anderer Grund ist, daß sich allgemeingültige Operationen auf Globais entwickeln lassen, die unabhängig vom Namen des Globais und von seiner Baumstruktur sind. Hier sei noch erwähnt, daß natürlich auch die Funktion $ ORDER mit „naked reference" verwendet werden kann. Hier entspricht, für den Fall, daß χ die Laufvariable auf der laufenden Indexstufe ist, der Funktionsaufruf $0RDER(~ (x) ) dem Fortschreiten auf der laufenden Indexstufe, während $0RDER(~(x,"")) einen Abstieg um eine Indexstufe bedeutet. Zum Abschluß der Betrachtungen über die „naked reference" soll noch einmal auf die Gefahr hingewiesen werden, die in ihrer Verwendung liegt. Lesbarkeit und Wartungsfreundlichkeit werden sehr stark eingeschränkt, da man schon bei kleinen Programmen nach kurzer Zeit den Überblick verliert, an welcher Stelle des Programms der „naked indicator" auf welchen Globalknoten zeigt. Daher geht man bei jeder Einfügung einer Globalreferenz das Risiko ein, den „naked indicator" zu verändern, obwohl vielleicht in einem später aufgerufenen Programmteil Annahmen über seine Stellung gemacht werden, die durch die Änderung nicht mehr zutreffen. Das gilt um so mehr, je freizügiger man GOTOs verwendet.

152

Kapitel 5. Datenhaltung

und

Datenmanagement

Daher sollte die „naked reference" immer nur in engem Zusammenhang (sowohl räumlich als auch logisch) mit einer voll indizierten Referenz auf einen Globalknoten benutzt werden. Diese Empfehlung ist um so leichter zu befolgen, da in modernen Systemen die Verwendung der „naked reference" keine Laufzeitvorteile mehr bringt.

5.5.2

Die Tücken des SET-Befehls

Der SET-B efehl war in Abschnitt 1.4.2 in seinen beiden üblichen Formen recht informell eingeführt worden. Beispiel

>S x = 2 * $ L ( s t r ) , ( i , j , k ) * 3 Hierbei wird unter einer „üblichen" Form entweder die einfache Zuweisung an eine Variable verstanden (im Beispiel S x«2*$L(str)) oder die mehrfache Zuweisung mit der Klammerform (im Beispiel S ( i , j,k)=3). Bereits bei der Einführung war darauf hingewiesen worden, daß zunächst der Ausdruck auf der rechten Seite des Gleichheitszeichens ausgewertet wird, ehe die Zuweisung an die Variable erfolgt. Im Hinblick auf die Indizierung von Variablen und die Schreibweise der „naked reference" muß die Reihenfolge der Abarbeitung der einzelnen Teile des SET-Befehls präzisiert werden: Bei der Zuweisung eines Wertes an eine indizierte Variable wird der Index grundsätzlich vor dem Ausdruck auf der rechten Seite des Gleichheitszeichens ausgewertet. Beispiel

>S ( i , j ) - 5 , x ( i + j ) - 1 0 0 In diesem Beispiel wird den Variablen i und j zunächst der Wert 5 zugewiesen. Dann wird der Index der Variablen χ errechnet, und daher der Variablen χ (10) der Wert 100 zugewiesen. Diese Regel bleibt auch bei der mehrfachen Form des SET-Befehls gültig. Beispiel

>S i = 3 , ( i , x ( i ) ) » 5 Nach der Zuweisung von 3 an die Variable i werden die einzelnen Variablen in der Klammer darauf untersucht, ob sie indiziert sind. Das ist bei

5.5.

Der implizite Bezug auf einen Global

153

χ der Fall und der Index hat aktuell den Wert 3. Anschließend wird die eigentliche Zuweisung von 5 an die Variablen i und χ (3) vorgenommen. Wenn bei dem SET-Befehl von der Schreibweise der „naked reference" Gebrauch gemacht wird, kann die Auswertung sehr unübersichtlich werden. Beispiel

S -(l)»~G(i,l)+l Auch hier gilt die Regel, daß nach Auswertung des Index auf der linken Seite des Gleichheitszeichens die rechte Seite der Anweisung ausgewertet wird. Hier wird daher der „naked indicator" auf ' G ( i ) gesetzt. Die Form ~ (1) auf der linken Seite der Zuweisung steht somit für ~G(i, 1) und das Beispiel ist mit dem Befehl S ~G(i,l)=~G(i, 1)+1 identisch. Noch komplexer ist das folgende Beipiel zu interpretieren. Beispiel

>S ~X(3)=5,~(~(3))=~C(4) Wie wird hier die „naked reference" aufgelöst? Die Reihenfolge wird durch die Regel bestimmt, daß Indizes zunächst ausgewertet werden. Durch den ersten SET-Befehl steht der „naked indicator" auf "X und der Index im zweiten SET-Argument wird daher zu ~X(3) evaluiert. Jetzt wird der Wert von ~C(4) ermittelt und der „naked indicator" steht auf ~C. Das hat zur Folge, daß die Variable, an die zugewiesen werden soll, -C(-X(3)) ist. Daher ist das letzte Beispiel äquivalent zu: S ~X(3)*5,~C(-X(3))=~C(4) Es versteht sich fast von selbst, daß man der leichteren Lesbarkeit wegen auf solche geschachtelten Anweisungen —wann immer es geht — verzichten sollte.

154

Kapitel 5. Datenhaltung ytnd Datenmanagement ··

5.6 5.6.1

Design von Datenstrukturen — Überlegungen und Beispiele Einführung und Beschreibung des Modells

Datenhaltung geschieht in MUMPS über globale Variablen. Die dadurch erreichte Einfachheit der Handhabung der Datenhaltung zeichnet MUMPS gegenüber anderen Programmiersprachen aus. Die Art der Verwendung von Globale soll in diesem Abschnitt am Beispiel einer einfachen Anwendung dargelegt werden. Es soll im Rahmen dieses Buches ein einfaches Hotelinformationssystem konzeptionell vorgestellt, das entsprechende Datenbankdesign schrittweise eingeführt, die Probleme aufgezeigt, Lösungsmöglichkeiten angeboten und das Design weiterentwickelt werden. Theoretische Aspekte und die Diskussion über hierarchische oder relationale Modelle sollen dabei in den Hintergrund treten, Wert wird auf pragmatisches Vorgehen gelegt. Der Leser, der bereits Erfahrung in der Benutzung anderer Datenbanksysteme hat, kann leicht die MUMPS zugrunde liegenden Prinzipien begreifen. Derjenige Leser, dem das Neuland ist, lernt beispielhaft einige Grundgedanken des Aufbaus von Datenbanken, die er auch an anderer Stelle nutzbringend verwenden kann. Wesentlich für die folgenden Betrachtungen ist, daß in MUMPS globale Variablen die Aufgabe der Datenspeicherung übernehmen, also Dateien oder Relationen anderer Systeme ersetzen. Das Design der Struktur von globalen Variablen entspricht dem Entwurf von Datenbanken oder Dateistrukturen. Ausgangspunkt ist ein stark vereinfachtes Informationssystem für ein Hotel. Hotelgäste sollen über ein Erfassungssystem registriert werden und es sollen die täglich in Anspruch genommenen Leistungen, wie Übernachtung, Mahlzeiten und sonstiger Service gespeichert werden. Dem Leser sollte es nicht schwer fallen, die hier konkret beschriebene Anwendung auf andere Informationssysteme zu übertragen. Im Grunde werden zwei Hauptglobals benotigt: • einen Personenglobal, der die Namen und die Adressen der Gäste enthält. • einen Leistungsglobal, in dem die Leistungen und ihre Preise verzeichnet sind. Folgende Datenelemente sollen gespeichert werden:

5.6.

Design von

155

Datenstrukturen

Personenglobal Feldname Name, Vorname Straße und Hausnummer Postleitzahl und Wohnort

Feldtyp Freitext Freitext Freitext

Länge 50 50 50

Leistungsglobal Feldname Leist ungsbezeichnung Leistungscode Leist ungspreis

Feldtyp Freitext Zahl Zahl

Länge 50 5 7

Die Elemente des Personenglobals würden in einer echten Anwendung um zusätzliche personenbezogene Daten erweitert werden, wie Staatsangehörigkeit, Kreditkartennummer usw. Der Leistungsglobal würde in Wirklichkeit alle zur Rechnungsschreibung nötigen Daten enthalten und soll im Beispiel auszugsweise das in Tabelle 5.2 gezeigte Aussehen haben: Tabelle 5.2: Auszug aus dem Leistungsglobal Leistungsbezeichnung Übernachtung in Zimmer 1 usw. bis letztes Zimmer 136 Frühstück Kategorie I Frühstück Kategorie Π

Leistungscode 1 136 200 205

Leistungspreis 8950 15900 750 950

Mittagessen

300

2290

Abendessen

400

1850

Hotelbar: Mineralwasser

501

275

Der Preis ist hier in Pfennigen angegeben, was gewisse Vorteile bei der Berechnung von Preisen mit sich bringt. Die Diskussion des Designs der personenbezogenen globalen Variablen wird ausführlich im nächsten Abschnitt vorgenommen. Für die Struktur des Globals, der die leistungsbezogenen Daten enthält, genügt es im Augenblick, den Leist ungscode als Index und den Klartext sowie den Preis als Wert des Globals zu nehmen.

156

Kapitel 5. Datenhaltung und

Datenmanagement

Beispiel

~LEIST(300) »"Mittagessen"* 2290" Das folgende Beispiel zeigt einen typischen Fall: Frau Ute Immergast, wohnhaft in 6203 Hochheim, Birnenallee 2a hat am 10. und 11.Juni 1988 im Hotel übernachtet, am ersten Tag im Zimmer 89, am zweiten Tag im Zimmer 14. Für den ersten Tag ist ein Abendessen abzurechnen, für den zweiten Tag Frühstück der Kategorie I, Mittagessen, sowie Abendessen. Aus der Zimmerbar hat sie zwei Mineralwasser entnommen. Die zu konzipierenden Datenstrukturen und ihre Verbindung untereinander sollen diese Leistungen widerspiegeln, Personen- und Leistungsdaten sollen im Dialog angezeigt werden können.

5.6.2

Abbildung in Globalstrukturen

Üblicherweise sind in MUMPS die Schlüssel eines Datensatzes, auf die direkt zugegriffen werden können soll, Indizes einer globalen Variablen. Die einzelnen Datenfelder, die keinen Schlüsselcharakter besitzen, werden im Zuweisungsteil des SET-Befehls aneinandergekettet. Sie werden dabei durch Trennzeichen voneinander separiert. Das bietet den großen Vorteil der Abspeicherung von variabel langen Feldern, denen man nicht im voraus eine maximale Länge zuordnen muß. Dies hat günstige Auswirkungen auf den benotigten Plattenspeicherplatz. Das Kernstück eines Hotelinformationssystems sollte die Personendaten umfassen. Da in MUMPS eine Indizierung mit Zeichenketten möglich ist, bietet sich als Schlüssel sofort der Name der Person an. ~'PERS(Name)=" Straße* Ort"

also zum Beispiel für die Person "Haier,Hans": *"PERSC"Maier,Hans")s"Somienweg 23*6000 F r a n k f u r t " Hier bedeutet "PERS der Name des Globais, der Index ist der Name der Person, die Felder Straße und Ort auf der Zuweisungsseite sind mit dem Trennsymbol voneinander getrennt, von dem zwingend angenommen wird, daß es in der zu erfassenden Information nicht vorkommt. Auf die einzelnen Felder dieses Datensatzes kann mit effizienten Funktionen (siehe z.B. $PIECE im Abschnitt 6.3.1) einfach zugegriffen werden. Nach der Erfassung der entsprechenden lokalen Variablen name, s t r und o r t kann ein solcher Global im Programm wie folgt gesetzt werden:

5.6.

Design von

Datenstrukturen

157

S ~PERS(name)=str_"~"_ort Aus wenigstens zwei voneinander unabhängigen Gründen ist der gewählte Aufbau jedoch noch nicht optimal: 1. Es ist nicht ökonomisch, den Namen der Gäste als Index für alle globalen Variablen des Hotelinformationssystems zu wählen. Der Name ist einfach zu lang. Es genügt im Grunde, ihn in einem zentralen Global einmal abzuspeichern. 2. Dieses Design berücksichtigt noch nicht, daß es unterschiedliche Personen (Gäste) mit gleichen Namen geben kann. Beim zweiten Problem würde durch eine Erweiterung des Schlüssels (z.B. durch die Hinzunahme des Geburtstag) die Wahrscheinlichkeit reduziert, der Fall aber nicht gänzlich ausgeschlossen, daß zwei Personen mit dem gleichen Schlüssel existieren. Aber es ließe sich sofort durch die Hinzunahme einer zweiten Indexebene lösen, in dem man die erste Person mit gleichen Namen mit dem laufenden Index 1, die zweite Person mit dem Index 2 usw. abspeichert. Wenn beispielsweise zwei Personen mit dem Namen "Heins Maier" erfaßt wurden, existieren zwei Datensätze, die sich im zweiten Index unterscheiden. Beispiel

~PERS("Maier,Hans",l)="Sonnenweg 23~6000 Frankfurt" ~PERS("Maier,Hans",2)="Alleenring 5~6100 Darmstadt" Im Programm muß dann aber natürlich bei der Erfassung eines Namens abgefragt werden, ob und wieviele Personen gleichen Namens bereits existieren. Aber vor allem aus dem ersten Grund wählt man einen anderen Weg. Man weist jeder erfaßten Person eine fortlaufende Nummer zu, die man Personenidentifikationsnummer (PID) nennt. Der jeweils letzte Wert steht in einer globalen Variablen. Die PID ist im folgenden Sinne eindeutig: Unterschiedliche Personen besitzen unterschiedliche PIDs und unterschiedliche PIDs stehen für unterschiedliche Personen. Diese Eigenschaft nennt man eine eineindeutige Zuordnung. Es soll im folgenden angenommen werden, daß es für das Informationssystem wesentlich ist, eine eineindeutige Zuordnung von PIDs zu Personen zu erreichen. Dies gilt wahrscheinlich nicht in vollem Maße für ein Hotelinformationssystem, aber sehr wohl für ein Krankenhausinformationssystem, bei dem das Krankenblatt über die PID identifiziert wird und es notwendig ist,

158

Kapitel δ. Datenhaltung und

Datenmanagement

bei einem Besuch eines Patienten vollständige Daten über frühere Besuche und Krankheiten zu erhalten. Ehe die damit verbundenen Probleme betrachtet werden, soll die Struktur des Globale mit der PID ids Schlüssel allgemein und auszugsweise in einem Beispiel dargestellt werden (siehe Abbildung 5.11). Der Name des Globale soll "HOT sein.

Allgemeine Struktur: ~H0T(0)«"/e£rte vergebene PID" ~ HOT (PID ) • "Name und Adresse der Person" Beispiel: ~H0T(0)=87112 ~HOT(l)="Immergast,Ute~Birnenallee 2a~6203 Hochheim" ~H0T(31635)«"Maier,Hans"Sonnenweg 23~6000 Frankfurt" "H0T(65297)="Maier,Hans~Alleenring 12*6100 Darmstadt" "H0T(87112)«"Becker,Elke"Im Wingert 12"6200 Wiesbaden" Abbildung 5.11: Struktur von 'HOT mit PID als Index Da der Wert von "HOT(O) gerade 87112 beträgt, sind in diesem Global 87112 unterschiedliche Personen mit Namen und ihren Adressen gespeichert. Im Beispiel wurde der erste und letzte Eintrag angegeben, sowie die beiden Datensätze für die beiden Personen mit dem Namen "Hans Maier" mit den PIDs 31635 und 65297. Die PID ist ihrem Wesen nach eine interne Kennzeichnung, niemand müßte sie wissen, um an die Daten dieser Person zu gelangen, obwohl eine (z.B.) Kundenkarte sie zur schnelleren Identifikation enthalten könnte. Ist nur der Name und nicht auch die PID bekannt, kann auf die Daten der Person im momentanen Design nicht direkt zugegriffen werden. Hier wird die Notwendigkeit eines zweiten Globale sichtbar, der als Referenzvariable angesehen werden kann und alle PIDs zu einem Namen liefert. Dieser Global erhält (vorübergehend) den Namen "XHOT und er weist die in Abbildung 5.12 dargestellte Struktur auf. Man sieht deutlich die Rolle dieser Variablen als „inversen" Global zu "HOT. Daher spricht man auch von einem invertierten Global. Die Variable "XHOT ist über den ersten Index (den Namen) automatisch alphabetisch

5.6. Design von Datenstrukturen

159

Allgemeine Struktur: ~XROT(Name, PID)™""

Beispiel: -XHOTC'Becker,Elke",87112)-"" ~XH0T("Immergast,Ute",1)="" •

· ·

~XHOT("Maier,Hans",31635)«"" -XHOT("Maier,Hans",65297)-""

Abbildung 5.12: Struktur von "XHOT sortiert, so daß Personen mit gleichem Namen in der Variablen logisch hintereinander stehen, sortiert auf der zweiten Indexstufe nach aufsteigender PID. Die Aufnahme der PID in den Index und nicht in den zunächst näher liegenden Datenteil ist ein Trick, der gewährleistet, daß für verschiedene Personen mit gleichem Namen ("Maier,Hans") mehrere Einträge erzeugt werden (die PIDs würden sich sonst überschreiben). Mit dieser Struktur und $ORDER als Sprachelement ihrer Analyse bekommt man zu einem gegebenen Namen alle gespeicherten PIDs und kann sie und die dazugehörenden Datenfelder am Bildschirm anzeigen. Daß der Name einer Person überhaupt schon erfaßt wurde, kann im Programm mit $DATA abgefragt werden: Beispiel

IF $D("XHOT(name)) W »."Person ",name," i s t schon bekannt!"

5.6.3

Aspekte eindeutiger Personenidentifizierung

Nun ergibt sich bei Namen, die für unterschiedliche Personen stehen, sofort eine Zuordnungsproblematik. Wie soll man bei der Erfassung von Daten zur Person "Maier,Hans", der mehrfach im Global gespeichert ist, wissen, welche PID er hat? Die Lösung dieses Problems besteht darin, daß der Benutzer alle Personen gleichen Namens zusammen mit der PID und den Adressen angezeigt bekommt und selbst entscheidet, um welche Person es sich handelt.

160

Kapitel 5. Datenhaltung und

Datenmanagement

Beispiel s name-="Maier,Hans" W !,name,! s x»"" F S x«$0(~XH0T(name,x)) Q:x«""

W »,x,?20,~H0T(x)

Diese beiden Zeilen zeigen beispielhaft einen Identifizierungsdialog für den Namen "Hans Maier". Am Bildschirm wird der Name angezeigt und darunter, jeweils in einer Zeile, die PID und die Daten dazu. Es sei noch darauf hingewiesen, daß mit der feldextrahierenden Funktion $PIECE (siehe Abschnitt 6.3.1) eine optisch ansprechendere Ausgabe erzielt werden kann. Die Anzeige zum letzten Beispiel ergibt folgendes Bild: Haier,Hans 31635 65297

Maier,Hans"Sonnenweg 23~6000 F r a n k f u r t Maier,Hans~Alleenring 12~6000 Darmstadt

Im Dialog kann der Bearbeiter nun anhand der Adresse entscheiden, welche PID zur konkret gemeinten Person gehört. An dieser Stelle der Entscheidung gibt es zwei Möglichkeiten. Einmal kann der Bearbeiter die zu erfassende Person als neue Person identifizieren, obgleich der Namen bereits abgespeichert wurde. Das bedeutet, eine weitere Person mit gleichem Namen soll gespeichert werden. Natürlich liegt eine Ersterfassung auch dann vor, wenn der Name der Person überhaupt noch nicht gespeichert wurde. In beiden Fällen muß eine neue PID vergeben werden. Das geschieht mit folgendem Code: S pid=~H0T(0)+l,~H0T(0)»pid Es wird also der Variablen pid der um eins vermehrte letzte Wert von "HüT(O) als neue PID zugewiesen und anschließend dieser Wert in ~H0T(0) selbst eingetragen. An dieser Stelle soll bereits auf die Problematik des Mehrbenutzerbetriebs hingewiesen werden, die in Kapitel 7 mit dem LOCK-Befehl erneut diskutiert wird. Aus prinzipiellen Gründen muß diese Befehlszeile dann um eine vorübergehende Sperre ergänzt werden. Hierzu sei auf Abschnitt 7.2 verwiesen. Der zweite Fall liegt vor, wenn der Benutzer des Systems die zu registrierende Person als bereits abgespeichert erkennt. Die Vergabe einer neuen PID

5.6. Design von Datenstrukturen

161

ist dann nicht erforderlich (und wäre sogar fehlerhaft) und die Leistungen werden unter dieser bereits früher vergebenen PID eingetragen. Nun können bei der eindeutigen Zuordnung einer PID zu einer Person Probleme und Fehler auftreten, die dazu führen, daß die eineindeutige Zuordnung von Personen zu PIDs nicht mehr gewährleistet ist. Beispielhaft sollen zwei davon genannt werden: 1. Bei dem Identifizierungsdialog wird der Name einer Person fehlerhaft eingegeben. Dadurch wird die Person nicht als schon gespeichert erkannt und eine neue PID vergeben, mit der Folge, daß für eine Person zwei verschiedene Namensschreibungen und damit zwei PIDs existieren. 2. Es kann vorkommen, daß die Auswahl bei der Identifikation fehlerhaft vorgenommen wird. Durch einen Umzug und eine dadurch geänderte Adresse wird der Name wie im ersten Fall nicht als schon gespeichert erkannt und zu der bereits existierenden Namensliste unter einer neuen PID hinzugefugt. Zur Vermeidung solcher Fehler gibt es keine generelle Lösung. Man muß sehr sorgfältig die einleitende Identifikation einer Person vornehmen. Abschließend soll auf eine häufig verwendete Designüberlegung hingewiesen werden. Der invertierte Global "XHOT(name ,pid) war für den Zweck der eineindeutigen Zuordnung der Person und der PID kreiert worden. Er ist jedoch logisch sehr eng mit dem Global "HOT verwandt. Weiterhin ist auch zu beachten, daß der Index auf der ersten Stufe des Globais "HOT numerisch und der Index auf der ersten Stufe des Globals "XHOT alphabetisch ist. Durch die getrennte Sortierung von Zahlen und Zeichenketten in MUMPS ist es möglich und schont die oft sehr belegte Liste aller im System definierter Globals, "XHOT in "HOT zu integrieren. Anstatt "XHOT(name ,pid) würde man dann die globale Variable "HOT(name,pid) wählen. In ihr würden dann zunächst auf der ersten Indexstufe alle PIDs mit den entsprechenden Adressen kommen, danach die Einträge mit dem Namen auf der ersten Stufe und der PID auf der zweiten. Der Global hat damit das folgende Aussehen: ~H0T(0)-" letzte vergebene PID" " H O T ( P I D ) ="Name und Adresse der Person" "HOT (Name, PID)'""

162

Kapitel 5. Datenhaltung

5.6.4

und

Datenmanagement

Die Einbeziehung leistungsbezogener Daten

Während der vorangegangene Abschnitt der eineindeutigen Zuordnung von PID und Person gewidmet war, sollen in diesem Abschnitt die leistungsbezogenen Daten in das Design einfließen. Es ist sicher ein vernünftiger Gedanke, daß die einzelnen Leistungen tageweise eingetragen werden. Jedoch soll im Moment im Hinblick auf generelle Fragestellungen noch darauf verzichtet werden, die Datenstruktur so anzulegen, daß mehrere Leistungen an einem Tag gespeichert werden können. Für das datumsbezogene Erfassen von Leistungen wird auf der zweiten Indexstufe des Globals "HOT zu einer PID und zu einem Tagesdatum (in der Variable datum) der Leistungscode und die Leistungsanzahl (in den Variablen l z i f f und lanz) eingetragen. Das (vorläufige) Design lautet also: "HOT( PID, Datum)="Leistungscode

~Leistungsanzahl"

Beispiel: ~H0T(31635,"25.07.88")-"501~2" Dieser konkrete Globaleintrag zeigt für den Gast "Hans Maier" (PID 31635) die Entnahme von zwei Mineralwasser (Leistungscode 501) am 25.07.88. Es sei an dieser Stelle angemerkt, aber nicht weiter vertieft, daß man das Datum aus Gründen der Sortierung, aber auch der einfacheren Handhabung, nicht in der oben angegebenen klar lesbaren Form, sondern als Zähler in der $H0R0L0G-Form verwendet. Der Globaleintrag bekommt dann die Form: -H0TC31635,53897)="501~2" Mit dem so konzipierten Design der Globals soll eine kleine Fallstudie unternommen werden. Angenommen ein Gast kommt zur Registrierung und verlangt Auskunft über die von ihm in Anspruch genommenen Leistungen. Weiter wird angenommen, daß der Gast nicht das erste Mal in diesem Hotel ist und seine bereits früher vergebene PID weder ihm noch dem Personal momentan geläufig ist. Der normale Weg ist nun der, daß in einem Dialog am Bildschirm über den Namen die PID ermittelt wird und danach Zugriff auf die Leistungsdaten besteht. Zugriff auf die Leistungsdaten bedeutet hier Zugriff auf alle Leistungsdaten, auch auf die früherer Aufenthalte! Das ist kein prinzipieller Nachteil, jedoch wird sich der Gast in den allermeisten Fällen nur für die Leistungsdaten des aktuellen Hotelaufenthaltes interessieren. Mit dem bisher gewählten

5.6.

Design von

163

Datenstrukturen

Design ist das auch grundsätzlich möglich, aber es bietet sich eine einfachere Lösung an, indem man die unterschiedlichen Aufenthalte eines Gastes als verschiedene Fälle betrachtet und jedem Fall eine weitere eindeutige Nummer als Kennzeichen gibt. Das geschieht aus rein pragmatischen Gründen und kann in anderen Informationssystemen wie folgt interpretiert werden: Krankenhausinformationssystem Kundeninformationssystem Bibliotheksinformationssystem usw.

Aufhahmenummer Auftragsnummer Ausleihnummer

Es soll hier ganz allgemein von einer Fallnummer gesprochen werden, die der lokalen Variablen f a l l zugeordnet ist. Das bringt folgende Änderung im Design des zentralen Globale. Zu jeder PID gibt es nun eine oder mehrere Fallnummern. Diese folgen im Global "HOT der PID auf der zweiten Indexstufe. Auf der dritten Indexstufe folgt das Datum der Leistung. "HOT(.PID,Fall,

Datum)*"Leistungscode"Leistungsanzahl"

Die Fallnummer selbst wird zweckmäßig auch im Global "HOT verwaltet. Es bietet sich an, sie — ähnlich wie die PID — auf der zweiten Indexstufe zu verwalten: "H0T(0,0)»"/eS jz«"Fruehling/Sommer/Herbst/Winter" >W $L(jz,"/") 4 >

Das zusätzliche zweite Argument in $L ist also das Trennsymbol, das die Felder voneinander separiert. Das Ergebnis des Funktionsaufrufs ist die Anzahl der Felder zu diesem Trennsymbol (und nicht die Anzahl der Trennsymbole). Beispiel

>W ~H0T(65297) ,! ,$L(~H0T(65297) ,·'**") Maier,Hans"Alleenring 12"6100 Darmstadt 3 >

6.3. Spezielle Feldveraxbeitung

177

Das Trennsymbol muß nicht notwendigerweise aus genau einem Zeichen bestehen. Es ist hier eine beliebige Zeichenkette erlaubt, wobei überlappendes Auftreten dieser Zeichenkette nur einfach gezählt wird. Beispiel >W $ L ( " M i s s i s s i p p i " . " s s " ) 3 >V $L("Mis8issippiV'issi") 2 >

Abschließend soll noch auf zwei Sonderfälle eingegangen werden. Falls das Trennsymbol in der Zeichenkette nicht vorkommt, liefert $L als Wert eine 1. Wird als Trennsymbol der Leerstring angegeben, liefert $LENGTH immer 0. Die Extraktion der einzelnen Felder geschieht mit $PIECE. In der überwiegend gebrauchten Form mit drei Argumenten ist das erste Argument der Funktion die Zeichenkette mit Feldstruktur, das zweite Argument ist das Trennsymbol und das dritte Argument ist die Angabe der Feldnummer. Ein einfaches Beispiel macht die Verwendung deutlich. Beispiel >S jz«"Fmehling/Sommer/Herbst/Winter" >W $PIECE(jz,"/",3) Herbst > Um aus dem schon mehrfach verwendetem Datensatz ~H0T(65297) das dritte Feld (Postleitzahl und Ort) zu extrahieren, ist folgende Anweisung erforderlich: Beispiel >W $P("HOT(65297),"*",3) 6100 Darmstadt > Bemerkenswert ist, daß man als Argument von $PIECE direkt die globale Variable ~H0T(65297) verwenden kann. In den Fällen, in denen man sich auf das erste Feld eines Satzes beziehen mochte, kann man auf die 1 als drittes Argument verzichten. $ P ( ~ H 0 T ( 6 5 2 9 7 ) , l i e f e r t das erste Feld des Datensatzes, also den Namen.

178

Kapitel 6. Analyse und Synthese von Zeichenketten

Weiterhin ist es möglich, auch mehrere, hintereinander liegende Felder durch Angabe eines Bereiches zu extrahieren. Beispiel >W jz Fruehling/Sommer/Herbst/Winter >W $P(jz,'-",2,3) Sommer/Herbst >

Durch die Einführung eines vierten Argumentes werden in diesem Beispiel die Felder 2 und 3 extrahiert. Das Trennsymbol zwischen Feld 2 und Feld 3 bleibt dabei erhalten. Wie bei $LENGTH ist auch bei $PIECE das Trennsymbol nicht auf nur ein Zeichen beschränkt: Beispiel >W $P("Mississippi","issi",2) ssippi >W """",$P("Mississippi","",2),""""

• I II >

In der zweiten Anweisung wurde als Trennsymbol der Leerstring gewählt und $PIECE liefert als Ergebnis ebenfalls den Leerstring. Auf zwei Sonderfalle soll schließlich noch eingegangen werden. In beiden nachfolgenden Beispielen ist das als zweites Argument aufgeführte Trennsymbol in der Zeichenkette nicht vorhanden. Im ersten Fall bezieht man sich auf das erste Feld bezüglich dieses Trennsymbols und als Ergebnis bekommt man die gesamte Zeichenkette. Im zweiten Fall bezieht man sich auf ein Feld großer 1 und das Ergebnis ist die leere Zeichenkette. Beispiel >W jz Fruehling/Sommer/Herbst/Winter >W $P(jz,":",l) Fruehling/Sommer/Herbst/Winter >W $L($P(jz,":",3))

0 >

6.3. Spezielle

179

Feldverarbeitung

Ist ein Feld selbst aus Unterfeldern zusammengesetzt, und möchte man eines der Unterfelder extrahieren, führt eine geschachtelte Anwendung von $PIECE zum Ziel. Um beispielsweise in dem Datensatz ~H0T(65297)«"Maier,Hans~Alleenring 12*6100 Darmstadt" den Ortsnamen zu extrahieren, wird man sich zunächst auf das dritte Feld bezüglich des Trennsymbols """ beziehen und in diesem Feld auf den Leerschritt als Trennsymbol zwischen Postleitzahl und Ort. Beispiel

>W $P($P(~H0T(65297),""",3)," ",2) Darmstadt > Abschließend soll noch ein Beispiel angegeben werden, wie man alle Felder einer Zeichenkette mit im voraus nicht bekannter Feldanzahl mittels einer FOR-Schleife extrahiert. Beispiel

>S t « " / " F i » l : l : $ L ( j z , t ) W ! ? 5 , i , " . 1. 2. 3. 4.

",?10,$P(jz,t,i)

Fruehling Sommer Herbst Winter

> Diese FOR-Schleife läuft über alle Felder des Satzes j z , weil $ L ( j z , " / " ) gerade die -Feldanzahl liefert.

6.3.2

Zuweisung von Feldern — SET $PIECE

Einmal abgespeicherte Daten können Veränderungen unterliegen. Ein Feldinhalt in einem Datensatz kann sich ändern, sei es wegen fehlerhafter Eingaben (ein Name war falsch geschrieben, eine Zahl weist einen „Dreher" auf), oder sei es wegen sich ändernder Daten. Hierzu zählt das Andern einer Adresse, hervorgerufen durch einen Umzug, aber auch das Ergänzen von Feldern um neue Informationen oder die Hinzunahme weiterer Felder in einen Datensatz.

180

Kapitel 6. Analyse und Synthese von Zeichenketten

Wieder gilt, daß das Überschreiben eines Feldes innerhalb einer Variablen durch bereits eingeführte Sprachelemente durchgeführt werden kann. Bezeichne: satz t ninf

Datensatz mit variabel vielen Feldern Trennsymbol neue Informationen

Um das n-te Feld durch die neue Information ninf zu ersetzen, bricht man den Datensatz hinter dem Feld η — 1 auf, verkettet mit den erforderlichen Trennsymbolen den neuen Feldinhalt und fügt schließlich den Rest des Satzes an. Wegen der Bedeutung dieser Operation und der Häufigkeit, mit der man Feldänderungen durchführt, stellt MUMPS eine Erweiterung des SET-Befehls zur Verfügung, nämlich SET $PIECE. Beispiel >SET $PIECE(satz,t,n)»ninf

Diese Anweisung wird durch das Befehlswort SET eingeleitet. Das nachfolgende $PIECE enthält als erstes Argument die zu ändernde Variable, als zweites Argument das Trennsymbol in dieser Variablen und als drittes Argument die zu ändernde Feldnummer. Rechts vom Gleichheitszeichen steht der neue Feldinhalt. Wesentlich ist, daß das erste Argument von $PIECE bei dieser Zuweisung der Name einer Variablen sein muß. Es ist daher keine geschachtelte Anwendung möglich, um Teilfelder zu ersetzen. Beispiele >s jz«"Fruehling/Sominer/Sommer/Winter" >S $PIECE(jz,"/",3)e"Herbst" W !, jz Fruehling/Sommer/Herbst/Winter >S $P(-H0T(65297),M-",2)«"Victoriastr. 52" W !,~H0T(65297) Maier,Hans"Victoriastr. 52~6100 Darmstadt >

Im letzten Beispiel ist wieder das direkte Andern eines Feldes einer globalen Variablen bemerkenswert. Der Reihe nach geschieht hier folgendes: 1. Der Satz ~HOT(65297) wird gelesen. 2. Das zweite Feld wird mit der neuen Information ersetzt.

6.3.

Spezielle

Feldverarbeitung

181

3. Der geänderte Satz wird wieder zurückgeschrieben. Im Grunde ist die Konstruktion SET $PIECE eine Erweiterung des SETBefehls. Das wird deutlich, wenn — anders als bei den vorherigen Beispielen — SET $PIECE auf eine bisher nicht definierte Variable angewendet wird. Beispiel

>KILL nd >S $PIECE(nd,,,-",5)="5.tes Feld" W nd 5.tea Feld >

Das Beispiel zeigt, daß die Variable nd nach der Zuweisung existiert und aus der notwendigen Anzahl von Trennsymbolen besteht, damit fünf Felder aufgespannt werden. Im fünften Feld selbst steht die Information. Das eben demonstrierte Verhalten ist außerordentlich nützlich und zeigt, wie SET $PIECE auf die Bedürfnisse der Feldverarbeitung in MUMPS abgestimmt ist. Diese Möglichkeit der automatischen Erzeugung von Trennsymbolen wird gelegentlich zweckentfremdet, um eine Zeichenkette, in der sich ein Zeichen ständig wiederholt, zu erzeugen. Angenommen, man wollte in eine Variable bind 80 Bindestriche stellen. Dann genügt die Anweisung S $ P ( b i n d , , 8 1 ) = " " . Hiermit werden 81 Felder bezüglich des Bindestrichs als Trennsymbol erzeugt. Das wird mit genau 80 Bindestrichen erreicht. In das 81. Feld wird der Leerstring eingetragen. Das automatische Ergänzen der erforderlichen Anzahl von Trennsymbolen kann natürlich auch bei bereits existierenden Sätzen verwendet werden. Dazu sei angenommen, daß man in dem Global "HOT(65297) neben dem Namen, der Straße und dem Wohnort als viertes Feld die Telefonnummer ergänzen möchte, die in der Variablen tel stehe. Beispiel

>S $P("HOT(65297),"-",4)»tel SET $PIECE sorgt dafür, daß hinter dem dritten Feld ein neues Trennsymbol angefügt wird, das es vom vierten Feld — der Telefonnummer — trennt. Zum Abschluß dieses Abschnitts sollen noch ein paar kurze Überlegungen zur Wahl und zum Gebrauch von Trennsymbolen in Datensätzen vorgenommen werden.

182

Kapitel 6. Analyse und Synthese von Zeichenketten

Die Trennzeichen in Datensätzen sind prinzipiell frei wählbar, aber es ist zwingend notwendig, daß das gewählte Trennzeichen in den einzelnen Datenfeldern nicht vorkommen darf. Hat man beispielsweise einen Datensatz, der aus 5 Feldern besteht (hierzu sind 4 Trennzeichen erforderlich), so würde ein Trennzeichen in einem Feld bedeuten, daß nunmehr 5 Trennsymbole in dem Datensatz vorhanden sind und damit 6 Felder aufgespannt werden. Der Bezug auf ein Datenfeld mit $PIECE würde ein falsches Ergebnis liefern, wenn man ein Feld nach diesem zusätzlichen Trennsymbol extrahiert. Schließlich muß noch auf ein Problem bei der Benutzung nationaler Sonderzeichen hingewiesen werden. Arbeitet man mit dem 7 Bit ASCII-Zeichensatz, dann wird beispielsweise in der deutschen Referenzversion das „0" durch den Rückstrich (\) dargestellt. Verwendet man diesen Rückstrich als Trennzeichen, dann wird jedes abgespeicherte „O" als zusätzliches Trennzeichen angesehen. Entsprechendes gilt für die ASCII-Zeichen, die Umlaute und das „ß" darstellen.

6.4.

Datenprüfung

6.4 6.4.1

mit

Textoperatoren

183

Datenprüfung mit Textoperat oren Fehlerarten bei der Eingabe

In guten Programmen werden die im Dialog erfaßten Daten einer intensiven Gültigkeitsprüfung unterzogen. Diese Prüfungen helfen schon bei der Erfassung fehlerhafte Eingaben zu erkennen, so daß sie zurückgewiesen und ein zweites Mal korrekt eingegeben werden können. In diesem Abschnitt soll die Problematik der Datenprüfung an einem Beispiel vertieft werden. Hierzu sei angenommen, daß ein Datum in der Form tt.mm.jj erfaßt werden soll (also beispielsweise 31.03.75). Welche Fehler können bei der Erfassung eines Datums auftreten? Die häufigste Fehlerart ist sicher die Eingabe eines falschen Datumsformates. Der formale Aufbau eines Datums besteht in diesem Beispiel aus zwei Ziffern gefolgt von einem Punkt, wieder zwei Ziffern und einem Punkt und schließlich noch einmal zwei Ziffern. Dieses Format stellt ein Muster dar und der Fehler kann daxin liegen, daß die konkrete Eingabe diesem Muster nicht entspricht. Das ist etwa der Fall, wenn anstelle des Datums versehentlich eine ganz andere Information erfaßt wird (z.B. Telefonnummer) oder wenn man bei der Eingabe den Punkt versehentlich falsch setzt, also zum Beispiel 310.3.75 schreibt. Da diese Fehlerart den formalen Aufbau eines Datums verletzt, spricht man von syntaktischen Fehlern. Sie werden in MUMPS außerordentlich leicht durch einen Mustervergleich erkannt. Nicht ganz so einfach zu erkennen sind im allgemeinen semantische Fehler in einer Eingabe. Bei dieser Fehlerart wird zwar das vorgeschriebene Format eingehalten, aber die Eingabe stimmt mit der Realität nicht überein, sie ist also logisch falsch. Im Beispiel der Datumsüberprüfung könnte ein semantischer Fehler darin bestehen, daß die Bereiche der einzelnen Komponenten des Datums, also Tage, Monate (und auch Jahre) nicht stimmen. Das Datum 41.03.57 ist zwar syntaktisch richtig, aber semantisch falsch. Um diese Art von Fehlern im Programm abzufangen, muß man die einzelnen Komponenten des Datums extrahieren (mit $EXTRACT oder $PIECE) und prüfen, ob sie in den gültigen Bereichen liegen. Eine weitere Art sematischer Fehler stellen die kontextabhängigen Fehler dar. Darunter versteht man Eingaben, die syntaktisch und logisch für sich allein genommen richtig sind, aber im Kontext mit zusätzlichen anderen Informationen nicht stimmen. Beispielsweise ist der 29. Februar ein an sich zulässiges Datum, aber nur dann, wenn es sich um ein Schaltjahr handelt.

184

Kapitel 6. Analyse und Synthese von Zeichenketten

Generell ist das Erkennen von kontextabhängigen Fehlern schwierig und es müssen zum Erkennen eines solchen Fehlers häufig spezielle globale Variablen gehalten werden, in denen die zulässigen Eingaben verzeichnet sind. Das ist zum Beispiel der Fall, wenn man sicher gehen will, ob zu einer eingegebenen Postleitzahl der ebenfalls erfaßte Ort stimmt. Der Leser findet Elemente eines Datumsprüfprogramms im Abschnitt 6.4.3.

6.4.2

Der Mustervergleich

Außerordentlich nützlich zum Erkennen von syntaktischen Fehlern ist der Mustervergleich. Mit ihm ist es sehr einfach möglich, eine Zeichenkette daraufhin zu untersuchen, ob sie einem vorgegebenen Muster entspricht. Das Symbol für den Mustervergleich ist das Fragezeichen (?). Der linke Operand ist die Zeichenkette, die untersucht werden soll. Der rechte Operand ist ein sogenanntes „Muster" (englisch: „pattern", daher wird der Mustervergleich im englischen Sprachgebrauch auch „pattern match" genannt). Grundlage des Mustervergleichs ist der ASCII-Zeichensatz, der für den Mustervergleich in einzelne Zeichenklassen unterteilt wird. Dabei denkt man sich die Steuerzeichen als eine Zeichenklasse, die aus den ASCII-Zeichen mit den Codes 0-31, sowie 127 besteht. Ebenso verfährt man mit den Interpunktionszeichen, den Ziffern und den alphabetischen Zeichen. Jede dieser Klassen bekommt eine Kurzbezeichnung, auf die man sich beim Mustervergleich bezieht. Tabelle 6.1 gibt eine vollständige Klasseneinteilung des ASCII-Zeichensatzes für den Mustervergleich. Tabelle 6.1: Klasseneinteilung für den Mustervergleich Zeichenklasse C Ρ Ν υ L A Ε

Bezeichnung Steuerzeichen Interpunktionszeichen Ziffern Großbuchstaben Kleinbuchstaben Alphabetische Zeichen Alle ASCnZeichen

ASCIICodes 0-31,127 32-47,58-64, 91-96,123-126 48-57 65-90 97-122 65-90, 97-122 0-127

Beispiel(e) Klingelzeichen $,u 0-9 Α- Ζ a - ζ A - Ζ und a- ζ

6.4.

Datenprüfung

mit

185

Textoperatoren

Dabei sind die beiden letzten Gruppen genaugenommen keine eigenen Zeichenklassen, sondern nur Zusammenfassungen anderer Klassen, die häufig benötigt werden. Dabei gilt: A= U+ L Ε = C+ P+ N+U + L Die Abkürzungen für die Zeichenklassen haben folgende englische Bedeutung, die man auch als mnemotechnische Hilfe benutzen kann: C Ρ L Ε

Control characters Punctuation Lower case Every sign

Ν A U

Numeric Alphabetic Upper case

Beim Mustervergleich wird jedes Zeichen einer Zeichenkette daraufhin untersucht, in welche Zeichenklasse es fällt. Ein Muster ist nach einem einfachen Prinzip aufgebaut: Es besteht aus einer ohne Trennzeichen aneinandergereihten Folge von Musterelementen. Jedes dieser Musterelemente besteht aus einem Teil, der einen Wiederholungsfaktor angibt und einem Teil, der angibt, auf welche Zeichenklasse verglichen wird. Als ein Beispiel soll wieder ein Datum in der Form t t . m m . j j (etwa 31.03.75) herangezogen werden, das in der Variablen datum stehe. Folgende Befehlszeile kann der Prüfung einer Datumseingabe dienen: Beispiel IF datum?2NlP2NlP2N W "Datumseingabe richtig !" Es wird der Inhalt der Variablen datum geprüft, ob sie dem Muster 2N1P2N1P2N entspricht. Dieses besteht aus 5 Musterelementen, nämlich 2N, IP, 2N, IP, 2N. Jedes der Musterelemente besteht aus einem Wiederholungsfaktor und der Angabe einer Zeichenklasse, also erkennt das Musterelement 2N genau zwei aufeinanderfolgende numerische Zeichen, während 1P genau ein Interpunktionszeichen erkennt. Das Ergebnis des Vergleichs ist ein logischer Wert, es ist also stets wahr (1, d.h. Variable entspricht dem Muster) oder falsch (0, d.h. Variable entspricht nicht dem Muster). Eingaben der Form 3 1 0 . 3 . 7 5 werden als falsch erkannt, weil diese Zeichenkette durch 3 Ziffern eingeleitet wird und damit das dritte Zeichen kein Interpunktionszeichen ist, wie durch 2N1P gefordert. Das im Beispiel genannte Muster 2N1P2N1P2N hat allerdings noch einen kleinen Schönheitsfehler. Es wird ja lediglich verlangt, daß das Trennsymbol

186

Kapitel 6. Analyse und Synthese von

Zeichenketten

zwischen t t und mm (und ebenso zwischen mm und j j) ein Zeichen aus der Klasse der Interpunktionszeichen sein muß. Eingaben der Form 31/03/75, aber auch 31?03?75 wurden ebenfalls den Mustervergleich passieren. Daher kann in MUMPS auch eine Zeichenkette (oder ein einzelnes Zeichen) direkt in der Musterangabe aufgeführt werden. Auch dieses Literal muß von einem Wiederholungsfaktor eingeleitet werden. Um beispielsweise an einer Stelle genau einen Punkt vorzuschreiben, genügt also das Musterelement 1"." und man kann das Muster für ,ein Datum genauer formulieren: 2N1"."2N1"."2N. Jedoch sind noch allgemeinere Musterangaben möglich, um bei einer Datumseingabe auch einstellige Tages- und Monatsangaben (etwa 31.3.75) zu erlauben. Man kann nämlich den Wiederholungsfaktor zu einer Zeichenklasse oder zu einer Zeichenkette als Bereich formulieren. 1. 2N bedeutet beispielsweise, daß mindestens eine und höchstens zwei Ziffern vorkommen dürfen. Daher kann man das oben angegebene Beispiel reformulieren: Beispiel

IF datum?l.2N1"."1.2N1"."2N W !,"Datumseingabe r i c h t i g " Ein vollständiges Verzeichnis aller erlaubten Bereichsformate enthält die Tabelle 6.2. Tabelle 6.2: Formate der Wiederholungsfaktoren WiederholungsFaktor

. . . •

Bedeutung Das Muster muß genau so oft erscheinen, wie durch die Zahl festgelegt. Das Muster muß mindestens und höchstens mal erscheinen. Das Muster muß mindestens mal und kann beliebig oft erscheinen. Das Muster darf höchstens mal erscheinen. Das Muster darf beliebig oft erscheinen.

Anstatt nun abzuprüfen, ob eine Zeichenkette einem Muster entspricht, macht man oft das Umgekehrte: man prüft, ob eine Zeichenkette einem vorgegebenen Muster nicht entspricht. Dann kann man einen Fehlertext anzeigen und erneut die Eingabe dieser Zeichenkette verlangen.

6.4.

Datenprüfung

mit

Textoperatoren

187

Dazu gibt es zu dem Mustervergleichsoperator — wie auch zu jedem anderen Vergleichsoperator — auch die negierte Form mit vorangestelltem Nicht-Symbol ('?). Ein Auszug aus einem Erfassungsprogramm könnte das folgende Aussehen haben: Beispiel 3 IF datum'?1.2Nl"."1.2Nl"."2N W !,"Falsche Eingabe" GOTO 3 Falls also das Datum ein falsches Format aufweist, wird ein Fehlertext geschrieben und über den Befehl GOTO 3 an den Beginn der Zeile verzweigt, wo erneut die Eingabe verlangt wird. Der Mustervergleich wird in MUMPS häufig zur syntaktischen Kontrolle eingegebener Daten verwendet. Zusätzlich zur Angabe einer einzelnen Zeichenklasse kann auch eine Kombination aus verschiedenen Zeichenklassen angegeben werden. Eine Prüfung eines Namens kann verlangen, daß dieser nur aus (kleinen und großen) Buchstaben, sowie aus (gewissen) Interpunktionszeichen aufgebaut sein darf. Das letztere ist etwa bei Meier-Hornbach oder O'Hara der Fall. Dazu faßt man Α und Ρ zusammen und schreibt: name?1.AP Hier soll ein Name aus mindestens einem Buchstaben oder Interpunktionszeichen bestehen, in der Praxis eine sicher nicht ausreichende Prüfung, denn nicht alle Interpunktionszeichen sollen erlaubt sein. Als besseres Muster bietet sich an, einen Namen mit einem Großbuchstaben beginnen zu lassen, gefolgt von einer beliebigen Kombination von großen und kleinen Buchstaben, sowie einem einzelnen Interpunktionszeichen, wie "-" oder " , n , wieder gefolgt von einer beliebigen Anzahl von alphabetischen Zeichen. Beispiel IF name , ?lU.A.l"-".A&(name , ?lU.A.l"'".A) W "Formatfehler" Andere Daten können aus noch komplexeren Mustern aufgebaut sein, so daß der Gedanke erwächst, solche Muster in Zeichenketten zu speichern und diese indirekt anzusprechen. Das würde eine dynamische Definition von Musterelementen für einen Mustervergleich erlauben. MUMPS stellt hier eine weitere Form der Indirektion zur Verfügung. Hier wird das gesamte Muster, also der Teil, der rechts des Operators „?" steht, durch eine Indirektion in der gewohnten Schreibweise ersetzt.

188

Kapitel 6. Analyse und Synthese von

Zeichenketten

Diese Form der Indirektion soll zunächst durch ein Beispiel erläutert werden, wobei angenommen werden soll, daß eine bis zu dreistellige Zahl geprüft werden soll. Beispiel

>S m="1.3N" IF zahl'TGm W "Fehler i n der Eingabe" Hier wird also auf das in der Variablen m stehende Muster Bezug genommen. Um auf das oben angegebene Beispiel einer Namensprüfung zurückzukommen, kann man nun die einzelnen Muster zusammenbauen: Beispiel

S m="lU.A",ml=m_".1""-"".A",m2=m_".1""»"".A" IF name'? 3 1 ) W !."Tagesangabe falsch" QUIT m12) W !."Monatsangabe falsch" QUIT

Abt ldung 6.6: 2. Teil des Datumprüfprogamms: Bereichskontrolle

Der dritte Teil (Abbildung 6.7) ist der anspruchsvollste und überprüft, ob die Tagesangabe mit dem Monat harmoniert, wobei auch Schaltjahre berücksichtigt werden. Zunähst muß man erkennen, daß in der Variablen mat die maximale Anzahl der Tage zu einem Monat steht. Diese Zahl wird über $PIECE ermittelt, deren erstes Argument (mon) diese Angabe in Form einer Liste enthält. Die aktuelle Anzahl der Tage im Februar wird über einen einfachen Algorithmus ermittelt. Dabei wird das Jahr j j einer Restdivision durch 4 unterzogen. Ist j j genau durch 4 teilbar, liegt ein Schaltjahr vor. Dieser Algorithmus hat für dieses Jahrhundert ab 1901 Gültigkeit und für das nächste auch, weil das Jahr 2000 ausnahmsweise ein Schaltjahr ist. Im A usdruck j j#4=0+28 ergibt j j#4 gerade dann Null, wenn j j ein Schaltjahr ist. Der sich anschließende Vergleich auf 0 ergibt nun eine 1, falls ein Schaltjahr vorliegt (da er in diesem Fall logisch wahr ist) und die anschließende Addition von 28 spiegelt die korrekte Anzahl der Tage des Februars in einem Schaltjahr wieder. Falls j j kein Schaltjahr darstellt, ist das Ergebnis von j j#4=0 eine 0 und die Anzahl der Tage im Februar wird ebenfalls richtig berechnet. Dieser Ausdruck ist mit dem Verkettungsoperator in die Zeichenkette der übrigen maximalen Tagesangaben integriert und $PIECE liefert für alle Monate die richtige Tagesangabe.

190

Kapitel 6. Analyse und Synthese von

Zeichenketten

FORMAT ... BEREICH ... KONTEXT ; Kontrolle auf kontextabhaengige Fehler S mon e "31/"_(jj#4=0+28)_"/31/30/31/30/31/31/30/31/30/31" S mat-$P(mon,"/",mm) IF tt>mat S nml e "Januar/Febmar/Maerz/April/Mai/Juni/" IF S mn2·"Juli/August/September/Oktober/November/Dezember" IF S mlist=mnl_mn2 IF W ! , "Der ",$P(mlist,"/",nun) ," hat nur ",mat," Tage " QUIT

Abbildung 6-7: 3. Teil des Datumprüfprogamms: Kontextabhängige Fehler

Falls nun die aktuell eingegebene Tagesangabe die maximal zulässige übersteigt, liegt ein Fehler vor, der mit einer aussagekräftigen Meldung angezeigt wird. Dafür baut man sich in der Variablen m l i s t die Namen der Monate — getrennt mit dem Zeichen "/" — auf und greift mittels $PIECE auf den durch mm spezifizierten Monat zu. Das letztere stellt eine klassische Anwendung der Tabellenverarbeitung mittels $PIECE in MUMPS dar. Auch der oben dargestellte Algorithmus zur Bestimmung der Anzahl der Tage im Februar ist ein Beispiel einer Kombination von unterschiedlichen Operationen, der man häufig in MUMPS begegnet.

6.4.4

Der Folgt- und Enthält-Operator

Der numerische Vergleichsoperator < prüft bekanntlich, ob der linke Operand kleiner als der rechte ist. Eine entsprechende Operation läßt sich auch auf Zeichenketten definieren, wenn man von der lexikalischen Reihenfolge ausgeht. Bei diesem Vergleich werden zwei Zeichenketten von links nach rechts verglichen bis das erste Zeichen gefunden wird, in dem sie sich unterscheiden. Die Zeichenkette, die an dieser Stelle das Zeichen hat, das in alphabetischer Reihenfolge zuerst steht, wird als kleiner bezeichnet. Wird bei dem Vergleich das Ende einer Zeichenkette erreicht, bevor ein Zeichen gefunden wird, in dem sie sich unterscheiden, so wird die kürzere Zeichenkette als kleiner bezeichnet. MUMPS stellt einen Operator zur Verfugung, der Zeichenketten nach dem angegebenen Schema vergleicht. Es ist der Folgt-Operator, der durch das Zeichen ] dargestellt wird. Diese Operation wird zu wahr evaluiert, wenn der linke Operand dem rechten in der lexikalischen Reihenfolge folgt,

6.4.

Da tenprüfung mit

Textoperatoren

191

also großer ist. Auch der Folgt-Operator läßt sich durch die Voranstellung des Nicht-Zeichens (') negieren. Beüpiele >W 1 >W 0 >W Ο >S 1 >

"KLEINER"]"GROESSER" "ENDE"']"ANFANG" "!"]"+" a-123 W a]""

Das vorletzte Beispiel soll verdeutlichen, daß beliebige ASCII-Zeichen auf die lexikalische Sortierung hin überprüft werden. Hierbei kommt das Ausrufezeichen (ASCII-Code 33) vor dem Pluszeichen (ASCII-Code 43). Im letzten Beispiel wurde geprüft, ob der Inhalt einer Variablen dem Leerstring folgt. Das ist genau dann der Fall, wenn die Variable einen Wert ungleich dem Leerstring hat. Daher ist diese Abfrage dem Ausdruck a ' · " " äquivalent. Vorsicht ist geboten, wenn man Zeichenketten miteinander vergleicht, die nationale Sonderzeichen enthalten. In der deutschen Sprache sind das die Umlaute und das ß. Da diese Zeichen in der ASCII-Sortierordnung hinter den alphabetischen Zeichen stehen, kann es beim Vergleich zu unerwarteten Ergebnissen kommen. Der Enthält-Oper&tOT (im Zeichen [) überprüft eine Zeichenkette darauf, ob sie eine andere enthält. Dieser Operator ist der $FIND-Funktion sehr ähnlich. Wenn man das Resultat der $FIND-Funktion logisch interpretiert, liefert der Ausdruck $FIND(A,B) das gleiche Ergebnis wie der Ausdruck A[B. Die zweite Möglichkeit ist natürlich kürzer zu schreiben und übersichtlicher. Auch für den Enthah-OperatoT gibt es die negierte Schreibweise, bei der das Nicht-Zeichen dem Operator vorangestellt wird. Beispiele >W 1 >R Ja >W 1 >

"ABCDEFGHIJKLMN"["DEF" "Ja oder Nein (j/n) : ",jn G:jn="j" WEITER Q:jn["n" oder Nein (j/n) : j 5*5[2

192

Kapitel 6. Analyse und Synthese von Zeichenketten

Im zweiten Beispiel findet man einen typischen Antwortdialog, bei dem man j oder η eingeben darf. Die entsprechende Antwort wird über einen Enthält-OperaAoT geprüft. Man beachte, daß zum Programm WEITER verzweigt wird, falls genau ein j eingegeben wurde, jedoch das Programm beendet wird, falls in der Variablen j n ein η enthalten ist. Die Eingabe von j a j a j a n beendet also das Programm. Das letzte Beispiel verdeutlicht wieder einmal, daß in MUMPS Zahlen gleichwertig mit Zeichenketten sind und daß Ausdrücke, selbst wenn sie verschiedenartige Operatoren enthalten, immer von links nach rechts ausgewertet werden.

6.5. Formatierung der Ausgabe

6.5 6.5.1

193

Formatierung der Ausgabe Rechtsbündige Ausgabe — $ J U S T I F Y

Um Datenfelder unterschiedlicher Länge beispielsweise in Listen rechtsbündig anzuordnen, wird in MUMPS die Funktion $JUSTIFY verwendet. Die Wirkungsweise dieser Funktion wird von der Anzahl ihrer Parameter gesteuert. Zunächst soll der Aufruf mit zwei Argumenten dargestellt werden. In diesem Fall wird eine beliebige Zeichenfolge rechtsbündig in ein Feld fester Liinge eingetragen. Das Prinzip soll zunächst anhand von zwei Beispielen erläutert werden. Beispiele

>W $JUSTIFY("Fernsehantenne",20) UUL)U_HJF emsehantenne >W $J(3.14,7) uuu3·14 > Der erste Parameter enthält das darzustellende Datenelement. Der zweite Parameter gibt die Länge des Feldes an, in dem die Daxstellung rechtsbündig erfolgen soll. $ JUSTIFY erzeugt so viele Leerzeichen auf der linken Seite der Zeichenkette (hier durch u dargestellt), bis die angegebene Feldlänge erreicht ist. Also steht im ersten Beispiel die Zeichenkette in einem Feld der Länge 20, wählend im zweiten Beispiel das zu formatierende Datenelement eine Dezimak;ahl ist, die in einem Feld der Länge 7 steht. Wenn die Zeichenkette länger ist als die angegebene Feldlänge, wird sie nicht abgeschnitten, sondern in ihrer vollen Länge übernommen. Beispiel

>S s1;r="Zeichenkette" >W $J(str,5) Zeichenkette > Manchmal wird $ JUSTIFY in der Form $J("",1) zur Erzeugung einer Zeichenkette zweckentfremdet, die nur Leerschritte enthält. Hierbei wird der Leerstring rechtsbündig in eine Zeichenkette der Länge 1 eingetragen. Damit gelingt es auch, Datenfelder linksbündig in einem Feld der Länge 1 zu schreiben.

194

Kapitel 6. Analyse und Synthese von Zeichenketten Beispiel

W $E(df_$J("",1),1,1) Hierbei wird das zu formatierende Datenfeld df mit einem String, der nur Leerschritte enthält, verkettet und anschließend mit $EXTRACT auf die Länge 1 verkürzt. Die Form von $JUSTIFY mit drei Argumenten kann als Erweiterung der Form mit zwei Argumenten angesehen werden. Sie ist für numerische Datenelemente vorgesehen und sorgt für deren Formatierung. Der dritte Parameter gibt die Anzahl der zu erzeugenden Nachkommastellen an. Falls es erforderlich ist, wird der numerische Wert des ersten Arguments gerundet oder mit Dezimalpunkt und Nachkommastellen versehen. Das Ergebnis der Umwandlung wird wie bei der zweiargumentigen Form rechtsbündig in das Feld eingesetzt. Auch hier sollen einige Beispiele (Abbildung 6.8) die Benutzung der Funktion verdeutlichen. >W $ J ( 3 . 1 4 , 7 , 4 ) u3.1400 >W $ J ( 3 . 1 4 , 7 , 0 ) UUUUULJ3

>W $ J ( 2 0 . 8 7 , 5 , 1 ) 20.9 >W $ J ( 0 . 5 , 6 , 3 ) IJO . 500 >S z a h l = 3 0 8 . 2 9 7 , r n d = $ J ( z a h l , 0 , 2 ) W rnd 308.30 > u

Abbildung 6.8: Die dreiargumentige Form von $JUSTIFY Die Beispiele zeigen noch einige wichtige Verhaltensweisen der $ JUSTIFYFunktion in Sonderfallen. Wenn der dritte Parameter gleich 0 ist, werden keine Nachkommastellen und auch kein Dezimalpunkt erzeugt. Führende Nullen werden unterdrückt. Jedoch haben Zahlen, deren Betrag kleiner als eins ist, eine Null vor dem Dezimalpunkt. Im letzten Beispiel wurde die Funktion nur zum Runden — nicht zum rechtsbündigen Ausrichten — benutzt. Von dieser einfachen Rundungsmoglichkeit kann natürlich auch im Laufe von Berechnungen Gebrauch gemacht werden.

6.5.

Formatierung der Ausgabe

6.5.2

195

Spezielle Formatierung mit $FNUMBER

Gerade bei der Programmierung von Berichten kommt es häufig vor, daß man die dort enthaltenen Zahlen — sei es der besseren Übersicht, sei es der Gewohnheit des Lesers wegen — besonders hervorheben mochte. Dazu zählt zum Beispiel das explizite Schreiben eines Pluszeichens vor einer positiven Zahl, odsr das Schreiben der Vorzeichen (+ oder - ) hinter diese Zahl, und noch anderes mehr. Einige dieser Anforderungen werden mit der Funktion $FNUMBER ausgeführt. Sie kann zwei oder drei Argumente besitzen, wobei das erste Argument generell die zu formatierende Zahl darstellt. Das zweite Argument von $FNUMBEi besteht aus einem Code, der in Tabelle 6.3 zusammengefaßt ist. Tabelle 6.3: Übersicht über das zweite Argument von $FNUMBER Code + , Τ Ρ

Bedeutung Schreibt ein Pluszeichen vor eine positive Zahl unterdrückt das Minuszeichen bei negativen Zahlen jede 3. Vorkommastelle wird mit einem Komma getrennt schreibt das Vorzeichen an das Ende der Zahl schreibt negative Zahlen in Klammern, bei positiven Zahlen Leerschritte anstatt der Klammern

Das dritte Argument in SFNUMBER kann wahlweise verwendet werden und gibt die Anzahl der Nachkommast eilen der Zahl an. Es ist äquivalent zu dem dritten Argument der Funktion $JUSTIFY. Das i:a Abbildung 6.9 dargestellte kleine Programm behandelt die wichtigsten Formatierungen einer Zahl mit $FNUMBER. Die Codes können da, wo es Sinn ergibt, kombiniert werden. So wird beispielsweise der Code "Ρ," eine negative Zahl in Klammern setzen und nach jeder dritten Vorkommastelle ein Trennkomma einfügen. Die Kombination des Codes "P" mit den Codes "+", und "T" ist allerdings nicht sinnvoll und ergibt eine Fehlermeldung.

196

Kapitel 6. Analyse und Synthese von Zeichenketten

FN ; H/K ; 11.6.88; Beispiele fuer $FNUMBER S x=-12345678,y=4321 W !,"Beispiele fuer $FNUMBER",! W !,"Pluszeichen bei positiven Zahlen",?40,$FN(y,"+") W !."Ünterdrueckt Minuszeichen",?40,$FN(x,"-") W !,"An jeder 3.Stelle ein Komma",?40,$FN(x,",") W !."Vorzeichen an das Ende der Zahl",?40,$FN(x,"T M ) W !,"Negative Zahlen in Klammern",?40,$FN(x,"P") W !,"2 Nachkommastellen",?40,$FN(y,"+",2) QUIT

Der Aufruf dieses Programms bringt folgendes Ergebnis: Beispiele fuer $FNUMBER Pluszeichen bei positiven Zahlen Ünterdrueckt Minuszeichen An jeder 3.Steile ein Komma Vorzeichen an das Ende der Zahl Negative Zahlen in Klammern 2 Nachkommastellen

+4321 12345678 -12,345,678 12345678(12345678) +4321.00

Abbildung 6.9: Formatieren mit SFNUMBER

6.6.

Ein einfacher Menütreiber

6.6

wit

$TEXT

197

Ein einfacher Menütreiber mit $ T E X T

Im weiteren Zusammenhang mit der Textmanipulation ist noch die Funktion $TEXT zu nennen. Sie dient dazu, einzelne Zeilen einer beliebigen Routine im Programmablauf verfügbar zu machen. Es kommt auf das Argument von $TEXT an, welche Zeile als Ergebnis des Aufrufs geliefert wird. Es sei daran erinnert, daß $T, die Abkürzung von $TEXT, gleichzeitig auch die Abkürzung der Systemvariable $TEST ist. Verwechslungen können nicht auftreten, da $TEXT immer ein Argument hat, während $TEST als spezielle Variable kein Argument hat. Im Grunde gibt es zwei Argumenttypen von $TEXT. Bei dem ersten Typ bezieht man sich auf die n-te Programmzeile des geladenen Programms, indem man ein Pluszeichen und diese Zeilennummer schreibt. Beispiele $TEXT(+5) $TEXT(+i) $T(+0) Die erste Zeile des Beispiels liefert die fünfte Zeile, das zweite Beispiel ergibt allgemein die i-te Zeile des aktuell geladenen Programms, während das letzte den Namen des aktuell geladenen Programms liefert. Der Aufruf ergibt einen Leerstring, wenn der Wert des Ausdrucks zu groß ist oder wenn sich keine Routine im Speicher befindet. Die zweite Schreibweise des Arguments bezieht sich auf ein Label eines beliebigen Programms. Hat das Argument die Form eines Zeilenlabels (mit oder ohne Offset), so wird die bezeichnete Zeile übergeben. Ist das Label im Programm nicht vorhanden oder das Offset zu groß, liefert die Funktion wieder den Leerstring. Beispiele $TEXT(PRINT~DB) $T(GET+3~PR) $T(PUT) In der ersten Form liefert $TEXT die Zeile mit dem Label PRINT im Programm DB. Das zweite Beispiel bezieht sich auf das Programm "PR, dort auf das Label GET und mittels eines Offsets auf die 3. Zeile hinter diesem Label. Da im dritten Beispiel kein globaler Programmname angegeben wurde, bezieht man sich hier auf das Label PUT des aktuell geladenen Programms. In MU MPS gibt es mehrere typische Verwendungsweisen von $TEXT, von denen zw«d beispielhaft vorgestellt werden.

198

Kapitel 6. Analyse und Synthese von Zeichenketten

Eine erste Anwendung bietet sich im Zusammenhang mit dem XECUTEBefehl an. Dieser Befehl erlaubt die Ausführung von Programmen, die nicht wie üblich im Programmspeicher stehen, sondern die in Variablen enthalten sind. Da der Programmspeicher nicht benotigt wird, wird er auch nicht verändert. Daher kann man seinen Inhalt, also z.B. ein mit dem BREAK-Befehl unterbrochenes Programm, mit einer einfachen Routine ausgeben. Man speichert dazu die Befehlsfolge F 7.i=0:l q:$T(+%i)-""

W $T(+%i) , !

in einer lokalen oder globalen Variablen ab. Angenommen, man verwendet dazu die Variable "'/.LIST, so kann man die Ausgabe der Programmliste mit dem Befehl X -/.LIST starten. Manche MUMPS-Installationen benutzen ähnliche Mechanismen in Verbindung mit implementierungsabhängigen Erweiterungen des Befehlssatzes zum Abspeichern und Korrigieren von Programmen. In der zweiten Anwendungsform läßt sich die Funktion $TEXT elegant nutzen, wenn man größere Mengen von unveränderlichen Daten, zum Beispiel Tabellen oder Bildschirmmasken, im Programm benotigt. Dann schreibt man die Daten in Form von Kommentaren in das Programm und greift darauf mit der Funktion $TEXT zu. Ein einfaches Beispiel soll diese Anwendung näher erläutern. Dazu soll angenommen werden, daß eine Liste von möglichen Menüs eines fiktiven Hotelinformationssystems auf dem Bildschirm angezeigt werden und — in Abhängigkeit der Auswahl des Benutzers — in das entsprechende Unterprogramm verzweigt werden soll. Die Menünamen und die zugehörigen Programme werden an den Schluß des Programms in Form von Kommentaren in Programmzeilen gestellt. Das auf das Wesentliche verkürzte Programm hat das in Abbildung 6.10 dargestellte Aussehen. Nach dem Schreiben der Überschrift werden in einer offenen FOR-Schleife über den Aufruf $T(TAB+i) den indizierten Variablen l ( i ) die entsprechenden Kommentarzeilen zugewiesen. 1 ( 1 ) hat zum Beispiel den Wert ; Gaesteerfassung

;"HGA"

6.6.

Ein einfacher Menütreiber

mit

$TEXT

199

MENUE ; H/K ; 1 8 . 6 . 8 8 ; Menuetreiber » ··· W !?10,"Hotelinformationssystem",!! F i » l : l S 1(i)»$TEXT(TAB+i) Q : l ( i ) « " " DO AUSG(i,l(i>) R ! , " B i t t e Auswahl t r e f f e n ( l - " , i - l , " ) : ",aus IF i i u s , ? l N ! ( a u s < l ) ! ( a u s > i - l ) W !,"Nicht erlaubte Eingabe ! " IF GOTO MENUE D esfP(l(aus),";",3) QUIT AUSG(i,t) ; Ausgabe einer Menuezeile W !?5,i,". ",$P(t,";",2) QUIT TAB ; Menueliste ; Gaesteerfassung ;"HGA ; Leistungsdaten ;"HLE ; Reservierung ;~HRE ; Rechnungsschreibung ;"HRS ; Hotelbelegung ;~HHB Abbildung 6.10: Verwendung von $TEXT für einen Menütreiber Das zweite Feld zum Trennsymbol " ; " stellt die Menübezeichnung und das dritte den zugehörigen Programmnamen dar. Zunächst werden die Menüs angezeigt und der Benutzer soll seine Auswahl eingeben. Abgebrochen wird die FOR-Schleife, wenn keine weitere Programmzeile gefunden wird und daher $TEXT gleich dem Leerstring ist. Weiterhin wird die Eingabe der Auswahl des Benutzers einer Prüfung unterzogen. Wenn er eine nicht erlaubte Auswahl trifft, wird die Menüliste noch einmal komplett aufgebaut. In $P(l(aus) f " ; " , 3 ) steht der Programmname zum Menü mit der in aus enthaltenen Nummer und der Programmaufruf erfolgt indirekt. Die hier beispielhaft vorgestellte Anwendung von $TEXT weist auf die Möglichkeit tabellengesteuerter Programmierung in MUMPS hin, die es erlaubt, allgemein verwendbare Programmgeneratoren zu entwickeln. Bei grösseren Tabellen ist es allerdings zweckmäßig , diese als Globals zu realisieren. Es bleibt abschließend noch anzumerken, daß bei kompilierenden MUMPS-Systemen, die zwischen dem ursprünglich eingegebenen Programm (dem Quellcode) und dem ausführbaren Programm (dem Objektcode) unterscheiden, zusätzliche Überlegungen bei der Benutzung von $TEXT eine Rolle spielen.

200

Kapitel 6. Analyse und Synthese von Zeichenketten

Beispielsweise werden bei manchen MUMPS-Compilern die mit einem Semikolon eingeleiteten Kommentarzeilen bei der Ubersetzung entfernt, die mit zwei Semikolons eingeleiteten jedoch beibehalten.

Kapitel 7 Mehrbenutzersysteme

7.1

Begriffsklärung

MUMPS wurde für den Einsatz in sogenannten Mehrbenutzersystemen entwickelt. Wie die Bezeichnung schon aussagt, ermöglichen solche Systeme die gleichzeitige Ausführung von Programmen mehrerer Benutzer. Oft bestehen die Rechner aus nur einer Verarbeitungseinheit (CPU). Wie läßt sich aber die Forderung nach gleichzeitiger Ausführung mehrerer Programme, also nach gleichzeitiger Verarbeitung mehrerer Anweisungsstrome, mit dem Vorhandensein nur einer Verarbeitungseinheit vereinbaren? Die Lösung für dieses Problem bietet die „ Quasi-Parallel Verarbeitung", bei der die Verarbeitungseinheit für kurze Zeit ein Programm bearbeitet und dann die Ausführung unterbricht, um ein anderes Programm zu bearbeiten. Auch hier wird die Ausführung nach kurzer Zeit unterbrochen, um ein drittes Progriimm auszuführen. Nachdem das letzte Programm für kurze Zeit bearbeitet wurde, erhält wieder das erste Programm etwas Rechenzeit, usw. Da hier gleichsam die gesamte zur Verfügung stehende Rechenzeit in Scheiben geschnitten wird, nennt man das beschriebene Verfahren auch Zeitscheibenverfahren. Da die Rechenzeit nach gewissen Regeln unter allen Programmen aufgeteilt wird, hat der einzelne Benutzer den Eindruck, alleine an einem Rechner mit geringerer Rechenleistung zu arbeiten. Die einzelnen Programme, die von der Maschine scheinbar parallel verarbeitet werden, nennt man Prozesse. Innerhalb jedes einzelnen Prozesses erfolgt die Verarbeitung streng sequentiell, das heißt, in einem Programm werden die Befehle immer in einer fest vorgegebenen Reihenfolge nacheinander ausgeführt. Die Reihenfolge selbst kann jedoch in MUMPS durch DO, GOTO, XECUTE und QUIT beeinflußt werden. Ein Prozeß wird normalerweise gestartet, wenn ein Benutzer anfangt, an der Maschine zu arbeiten. Er wird im allgemeinen durch die Ausführung des HALT-Befehls beendet. Es gibt jedoch auch Anwendungsfalle, in denen ein Prozeß nicht von einem Terminal aus gestartet werden soll, sondern wo ein Prozeß unabhängig von einem menschlichen Benutzer im Hintergrund arbeiten seil. Der Start von Hintergrundprozessen ist dann besonders hilfreich, wenn man im Dialog Daten erfaßt hat und diese Daten ganz oder teilweise nach

202

Kapitel 7.

Mehrbenutzersysteme

der Erfassung ausdrucken will (z.B. als Etiketten oder Formulare). Normalerweise wäre für die Zeit des Druckens der aktive Bildschirm blockiert, weil jetzt das Druckprograinm läuft. Wenn aber das Druckprogramm im Hintergrund läuft, können im Vordergrund parallel weitere Daten erfaßt werden. Jedem Prozeß angegliedert ist eine Partition, die neben anderen Informationen die lokalen Variablen enthält. Ein Zugriff auf die lokalen Daten einer anderen Partition ist nicht möglich. Jedoch kann es geschehen, daß mehrere Benutzer (genauer: mehrere Prozesse) gleichzeitig auf die gleiche globale Variable zugreifen wollen. Dabei muß gewährleistet sein, daß keine Inkonsistenzen auftreten, die Zugriffe müssen synchronisiert werden. Die Sprachmittel, die in MUMPS für den Mehrbenutzerbetrieb zur Verfügung stehen, werden in den nachfolgenden Abschnitten besprochen.

7.2. Sperren im

7.2 7.2.1

Mehrbenutzerbetrieb

203

Sperren im Mehrbenutzerbetrieb — der LOCK-Befehl Einführung in die Problematik

Durch den Mehrbenutzerbetrieb und den dadurch möglichen Zugriff mehrerer Benutzer auf eine globale Variable können ernsthafte Synchronisationsprobleme entstehen, für deren Vermeidung in MUMPS ein eigener Befehl existiert Ein solcher Sachverhalt liegt im folgenden Beispiel vor. Angenommen eine gewisse Anzahl von MUMPS-Programmen benutzt eine globale Variable als eindeutigen Zähler, das heißt, jedes Anwendungsprogramm liest den letzten 2lählerstand, erhöht ihn um eins und benutzt ihn als identifizierendes Merkmal. Man findet diese Situation oft bei der Vergabe von eindeutigen Kundennummern oder von Aufnahmenummern in Krankenhäusern und anderswo. Im Beispiel eines Hotelinformationssystem, das im Abschnitt 5.6 entwickelt wurde, stellte die Personenidentifikationsnummer (PID) ein solches Merkmal dar. In der dortigen Notation war die letzte vergebene PID im Global ~HOT(0) gespeichert und die Befehlszeile, die die Vergabe der neuen PID steuert und die Abspeicherung steuert könnte folgendes Aussehen haben: S pid«~H0T(O)+l,~H0T((»-pid S ~HOT(pid)="Datenfelder" In einer Mehrbenutzer-Umgebung kann es durch überlappenden Zugriff mehrerei Prozesse zu Inkonsistenzen kommen. Von den möglichen Problemen soll der sogenannte „Lost Update" näher erläutert werden. Dieser Begriff bezeichnet den Verlust von Daten infolge unzureichender Synchronisation der Programme. Angenommen, der Benutzer Α liest "H0T(0) ( wird aber nach dem ersten Argument des ersten SET in der Ausführung der Programmzeile unterbrochen, weil das Zeitscheibenverfahren dem nächsten Prozeß Rechenzeit zuteilt. Konkret bedeutet das, daß in der lokalen Variablen pid schon der um eins erhöhte Wert vorhanden ist, er jedoch noch nicht nach "HOT(O) abgespeichert werden konnte. Ein Benutzer Β liest jetzt den aktuellen Wert von "HOT(O) und führt die ganze Befehlszeile aus, ehe Benutzer Α den Rest der Zeile durchführen kann. Damit benutzen Β und A den gleichen Wert für pid, so daß die von Β abgespeicherte Änderung durch Α überschrieben wird und damit verloren geht. Zur Vermeidung dieser Situation gibt es in Mehrbenutzersystemen SperrMechanismen, die vorübergehend eine Datei oder Teile von ihr für den Zugriff eines weiteren Benutzers sperren.

204

7.2.2

Kapitel 7.

Mehrbenutzersysteme

Der LOCK-Befehl

Die eben dargestellten Synchronisationsprobleme werden in MUMPS durch den Befehl LOCK vermieden. LOCK sperrt die in seinem Argument angegebene lokale oder globale Variable für andere Benutzer so lange, bis die Angabe eines expliziten Entsperrens erfolgt. Das Problem des 'Lost Updates' würde vermieden, wenn die kritische Programmzeile folgendes Aussehen hätte: LOCK ~H0T(0) S pid=~H0T(0)+l,~H0T(0)=pid L0CKuuS 'HOT(pid)» "Datenfelder" In dieser Befehlszeile kommt zweifach der Befehl LOCK vor. Beim ersten Mal trägt er als Argument die globale Variable ~H0T(O). Diese Variable ist für andere Benutzer nach der Ausführung des LOCK gesperrt. Ein weiterer Benutzer kann daher nicht diese Befehlszeile ausführen und daher auch nicht dieselbe PID erhalten. Das zweite LOCK gibt die Sperre wieder frei. Es wird nach der Erhöhung des Globale ~Ή0Τ(0) um eins ausgeführt. Ein zweiter Benutzer kann danach ohne Gefahr eines „Lost Updates" auf ~H0T(0) zugreifen. Dieses LOCK ist argumentlos und die dabei notwendigen zwei Leerschritte wurden mit Uu hervorgehoben. Alle Sperren auf lokale oder globale Variablen werden in einer sogenannten LOCK-Tabelle eingetragen, auf die man in den meisten Systemen Zugriff mittels Dienstprogrammen hat. Versucht ein Prozeß eine Variable zu sperren, so kontrolliert das MUMPS-System zunächst diese LOCK-Tabelle, ob ein Eintrag diese Variable als schon gesperrt ausweist. Ist das nicht der Fall, wird ein Eintrag in die Tabelle vorgenommen, andernfalls kann keine Sperre ausgesprochen werden. Wenn eine Variable freigegeben wird, wird umgekehrt der Eintrag aus dieser Tabelle entfernt. Das Sperren einer Variablen mit dem LOCK-Befehl ist im folgenden Sinne als Konvention zu verstehen. Wenn ein Benutzer eine Variable gesperrt hat, so kann ein weiterer Benutzer dieselbe Variable nicht noch einmal sperren, wohl aber beliebig auf sie zugreifen, also sie verändern oder sogar löschen. Nur wenn jeder Teilnehmer seine kritischen Programmteile mit LOCK sichert, ist es also möglich, Synchronisationsprobleme zu vermeiden. Es gibt den LOCK-Befehl in verschiedenen Formen, die in Tabelle 7.1 dargestellt sind. Dabei kann das ein Variablenname oder eine in Klammern eingeschlossene Liste von Variablennamen sein. Beim Anfordern von Sperren wird die Programmausführung so lange unterbrochen, bis alle geforderten (in der geklammerten Liste angegebenen) Sperren verfügbar sind. Das kann unter Umständen lange dauern. Deshalb kann ein Timeout nach dem Argument des Befehls angegeben werden.

7.2. Sperren im

Mehrbenutzerbetrieb

205

Tabelle 7.1: Die verschiedenen Formen des LOCK-Befehls Schreibweise LOCK LOCK LOCK H- LOCK -

Erläuterung Hebt alle Sperren auf Hebt alle Sperren auf und fordert dann neue Sperre(n) an, wie durch spezifiziert. Fordert zusätzliche Sperre(n) an, wie durch spezifiziert. Gibt selektiv Sperre(n) frei, wie durch spezifiziert.

Beispiel

LOCK ~A:5 ELSE GOTO TIMEOUT In diesem Beispiel wird die globale Variable "A mit einem Timeout von 5 Sekunden gesperrt. Ist die angegebene Zeit verstrichen, ohne daß eine Sperre möglich war, wird der Systemvariablen $TEST der Wert 0 zugewiesen und mit der Ausführung des Programms fortgefahren. Wie es in diesen Fällen üblich ist, kann $T explizit abgefragt werden oder aber implizit mit ELSE. Das ist die gleich e Technik wie bei den anderen Befehlen, bei denen die Angabe eines Timeout» möglich ist. Ist hingegen das Sperren erfolgreich, wird $TEST gleich 1 gesetzt. Der Wert vor. $T wird nicht geändert, wenn die Angabe eines Timeouts fehlt. Die beiden ersten in Tabelle 7.1 angegebenen Fälle führen zum automatischen Freigeben aller bereits angeforderter Sperren. Das hat eine interessante Konsequenz, wenn man mit diesen Sprachmitteln mehrfache Sperren anfordern mochte. Möchte man beispielsweise ~A und ~B gleichzeitig sperren, scheint auf den ersten Blick L * Α, ~B ausreichend. Nun ist jedoch L ~A, ~B äquivalent mit L *A L ~B. Der zweite LOCKBefehl hebt aber den ersten auf, so daß abschließend nur "B gesperrt ist, nicht aber zusätzlich auch "A. Möcht e man dagegen beide Variablen sperren, muß man die zu sperrenden Variablen klammern, also L (~A,~B) schreiben. Dann werden beide Variablen gesperrt. Es ist mit dieser Form des Befehls nicht möglich, zusätzliche Sperren anzufordern, ohne bereits vorhandene Sperren Zumindestens zeitweise wieder freizugeben. Das sukzessive Anfordern und Freigeben von Sperren wird mit den beiden letzten in Tabelle 7.1 angegebenen Formen des LOCK durchgeführt. Sie werden im nächsten Abschnitt noch ausführlicher besprochen.

Kapitel 7.

206

Mehrbenutzersysteme

In der durch Klammern zusammengefaßten Liste von Variablen könnten einige durch andere Benutzer bereits reserviert sein, andere seien es nicht. Die Situation ist eindeutig geregelt: Nur wenn alle Variablen „frei" sind, wird ein LOCK auf die Liste durchgeführt. Ist nur eine Variable von einem anderen Benutzer gesperrt, wird der komplette LOCK verzögert, bis alle Variablen frei sind oder bis der hinter der Klammer angegebene Timeout verstrichen ist. Die Angabe eines Timeouts auf einzelne Elemente der Liste ist nicht möglich. Am Schluß soll das Sperren von Teilen von indizierten Variablen behandelt werden. Ebenso wie es möglich war, Teilbäume einer hierarchischen Datenstruktur mit dem Befehl KILL zu löschen, ist es möglich, Teilbäume zu sperren. Dazu sei angenommen, eine globale Variable ~A habe die Werte ~A(1)=1, ~A(2)=2, -A(l,l)«3. Die Wirkung von LOCK ~A(1) besteht dann darin, daß neben "A(l) auch ~A(1,1) gesperrt würde. ~A(2) bleibt von dem LOCK unberührt und kann von anderen Benutzern reserviert werden. Generell werden bei einem LOCK auf einen Knoten einer Variablen alle Nachfolger des Knotens mitgesperrt, während Nachbarknoten dem LOCK nicht unterliegen. Es sei schließlich noch angemerkt, daß die Angabe einer „naked reference" als Argument des LOCK-Befehls nicht erlaubt ist und daß der LOCK-Befehl umgekehrt den „naked indicator" nicht verändert. Das gilt natürlich nur dann, wenn der Index einer Variablen nicht selbst eine globale Variable ist. In LOCK ~A(~B) würde sich der „naked indicator" durch den Zugriff auf ~B ändern.

7.2.3

Zusätzliche Sperren

Der im letzten Abschnitt erläuterte Mechanismus der automatischen Freigabe bereits gewährter Sperren vor dem Anfordern neuer Sperren hat einen sinnvollen Hintergrund: es wird eine Konkurrenzsituation zweier Prozesse vermieden, die sich wie folgt charakterisieren läßt. Angenommen Benutzer 1 hat ~A gesperrt, Benutzer 2 hat ~B gesperrt. Benutzer 1 möchte zusätzlich ~B sperren (den schon Benutzer 2 gesperrt hat), Benutzer 2 möchte zusätzlich ~A sperren (den schon Benutzer 1 gesperrt hat). Die Folge dieser beiden Anforderungen ist, daß sich beide Prozesse gegenseitig blockieren. Diese Situation wird deadlock genannt. Mit dem Mechanismus der grundsätzlichen Freigabe aller früheren Sperren beim LOCK-Befehl ist die deadlock-Situation völlig ausgeschlossen. Man handelt sich aber einen anderen Nachteil ein, der darin besteht, daß man nicht eine zusätzliche Sperre anfordern kann, ohne die schon ausgesprochenen Sperren zu verlieren. Man ist auch nicht in der Lage, fremdgeschriebene Unterprogramme, die LOCK-Befehle verwenden, in die eigene Anwendung einzubinden, weil diese die eigenen Sperren aufheben.

7.2.

Sperren im

Mehrbenutzerbetrieb

207

Daher gibt es spezielle Formen des LOCK-Befehls, die der zusätzlichen Anforderung und der selektiven Freigabe von Sperren dienen. Bei der Verwendung dieser Formen des LOCK-Befehls kann ein deadlock auftreten, der jedoch durch Verwendung des Timeouts behandelt werden kann. Wie schon in Tabelle 7.1 in den beiden letzten Eintragen dargestellt wurde, sperrt ein vor das Argument gestelltes Pluszeichen die Variable zusätzlich und ein vor das Argument gestelltes Minuszeichen gibt sie wieder frei, ohne daß andere Sperren betroffen wären. Beispiel LOCK +~A,+~B L -~B Im Beispiel sperrt der erste LOCK-Befehl die Variablen ~A und danach während der zweite die Variable *B wieder freigibt. Wie bei einem gewöhnlichen LOCK wird der zusätzliche LOCK in die L0CKTabelle eingetragen und dort verwaltet. FürL +~A,+~B hätte man auch L +(~A, "B) schreiben können. Allerdings unterscheiden sich beide Varianten dadurch, daß im ersten Fall ~A gesperrt werden leann, wenn ~B von einem anderen Benutzer bereits gesperrt ist. Im zweiten Fall werden entweder beide Sperren oder keine gewährt. Damit ist im ersten Fall die Wahrscheinlichkeit eines deadlock etwas hoher als im zweiten Fall. Deswegen sollte die zweite Schreibweise vorgezogen werden. Mit cler Technik der inkrementellen Sperren können in Unterprogrammen zusätzliche LOCKs angefordert werden, ohne vorherige Sperren zu verlieren. Zu beachten ist, daß die Anzahl der zusätzlichen LOCKs mit der Anzahl der entsprechenden Freigaben übereinstimmen muß. Wenn in verschiedenen Unterprogrammen z.B. dreimal L +~A angefordert wurde, jedoch mit L - ~ A nur zweimal entsperrt wurde, ist ~A immer noch einmal gesperrt und kann von anderen Benutzern nicht gesperrt werden. Eines; Hinweises bedarf auch noch die Tatsache, daß ein gewohnliches LOCK wie bisher zunächst alle Sperren freigibt, auch die mit dem Pluszeichen angeforderten zusätzlichen Sperren. Nach der Befehlszeile: L +CA.-B.-C) L ~B ist nur noch "B gesperrt.

208

Kapitel 7.

7.3

Hintergrundprozesse

7.3.1

Der JOB-Befehl

Mehrbenutzersysteme

In der Einleitung zu diesem Kapitel war von der Nützlichkeit von Hintergrundprozessen gesprochen worden, die jeweils von anderen Prozessen gestartet werden. Dazu wird ein spezielles Kommando benotigt, das den Start eines neuen Prozesses zur Folge hat. In MUMPS wird hierzu der JOB-Befehl benutzt. Als Argument wird der Einsprungpunkt einer Routine angegeben. Diese Angabe bewirkt, daß in dem neuen Prozeß implizit ein DO-Befehl auf den Einsprungpunkt gegeben wird. Beispiel

JOB "PRINT In diesem Beispiel wird das Programm "PRINT als Hintergrundprozeß gestartet. Wird der Einsprungpunkt in der lokalen Form angegeben, so beginnt die Ausführung des neuen Prozesses an dem angegebenen Label in der gerade benutzten Routine. Auch beim JOB-Befehl ist eine Argumentliste erlaubt. Für jedes Argument wird ein eigener Prozeß erzeugt, dessen Ausführung an der angegebenen Stelle beginnt. Es ist möglich, den JOB-Befehl mit implementierungsspezifischen Parametern zu versehen. Diese Parameter werden durch einen Doppelpunkt (:) von der Angabe des Einsprungpunktes getrennt. Sollen mehrere Parameter angegeben werden, so müssen sie in Klammern eingeschlossen und untereinander durch Doppelpunkte getrennt werden. Wichtigstes Beispiel für diese Parameter ist in vielen Systemen die Angabe der Partitionsgröße, mit der der Hintergrundprozeß gestartet werden soll. Denn mit der Initiierung eines neuen Prozesses ist die Eröffnung einer eigenen Partition verbunden. Ist der so beanspruchte Arbeitsspeicherplatz nicht verfügbar, wird die Initüerung so lange verzögert, bis ein anderer Prozeß beendet wird und den benotigten Platz freigibt. Da diese Zeitspanne nicht vorhersehbar ist, kann man den JOB-Befehl mit einem Timeout versehen und damit eine Zeit angeben, die man maximal warten mochte. Die Angabe des Timeouts erfolgt nach den implementierungsabhängigen Parametern und wird von diesen mit einem weiteren Doppelpunkt getrennt.

7.3.

Hintergrundprozesse

209

Beispiel

JOB LB1~PRNT::15 ELSE G "JOBERR Wenn die Prozeßerzeugung innerhalb der angegebenen Zeit nicht möglich war, wird $TEST auf 0 gesetzt und mit der Ausführung des Programms fortgefahreri. Mit ELSE kann das Verstreichen des Timeouts abgefragt werden. Wurde kein Timeout angegeben, wartet das Programm so lange, bis es möglich ist, den neuen Prozeß zu starten. $TEST wird in diesem Fall nicht verändert. In manchen Anwendungen ist es erforderlich, eine eindeutige Identifikation des gerade laufenden Prozesses zu erhalten. Dazu dient die spezielle Variable $ JOB. Sie hat einen Wert, der den laufenden Prozeß eindeutig identifiziert. Beispiel

S *SCRATCH($JOB,χ)*a W t.datum," ",$J Der Inhalt von $J0B kann zum Beispiel dazu dienen, einen systemweit eindeutigen Teilbaum eines Globals zu erzeugen, um große Datenmengen temporal: abzuspeichern. Eine andere Anwendungsmöglichkeit wäre die Identifizierung der Herkunft einer Druckerausgabe durch den Inhalt von $J0B.

7.3.2

Wertübergabe an Hintergrundprozesse

Die Wirkung des JOB-Befehls besteht im implizitem Aufruf eines Programms in einer eigenen Partition. Es besteht dabei die Möglichkeit, ähnlich der Wertübergabe beim Programmaufruf mit dem DO-Befehl, eine Liste von Parametern an den Hintergrundprozeß zu übergeben. Allerdings ist nur die direkte Wertübergabe (call by value) möglich, bei der die Ausdrücke in der Liste der aktuellen Parameter mit den Variablennamen der Liste der formalen Parameter korrespondieren. Beispiel

JOB ~FORM(abs,$L(len)+l,vl*v2) In diesem Beispiel wird angenommen, daß in dem Programm, das den Hintergrundprozeß erzeugt, die lokalen Variablen abs, len sowie vi und v2 existieren. Diese bzw. die hiervon abgeleiteten Ausdrücke $L(len)+l und vl*v2 werden an den Hintergrundprozeß übergeben.

210

Kapitel 7.

Mehrbenutzersysteme

Die Ausdrücke werden den Variablen der Liste der formalen Parameter der Reihe nach zugeordnet. Hat der Einsprungpunkt von "FORM das Aussehen FORMCxl ,x2,x3,x4), so erhält im Hintergrundprozeß die Variable x l den Wert von abs, x2 den Wert von $L(len)+l und x3 den Wert vl*v2. x4 bleibt Undefiniert. Es gilt allgemein, daß die Anzahl der aktuellen Parameter nicht großer sein darf, als die Anzahl der Elemente in der Liste der formalen Parameter. Eine Rückführung von Ergebnissen des Hintergrundprozesses an den initiierenden Vordergrundprozeß ist nicht möglich, weil der neue Prozeß — einmal gestartet — völlig selbständig ist. Daher gibt es auch keinen mit dem D0-Befehl vergleichbaren Mechanismus, bei dem durch die Punktsyntax in der Liste der aktuellen Parameter ein Rücktransfer möglich wäre. Möglicherweise auftretende Fehlermeldungen werden jedoch auf dem dem Vordergrundprozeß zugeordneten Bildschirm ausgegeben.

7.3.3

Verzögerung der Ausführung mit HANG

Oft hat man den Wunsch, Hintergrundprozesse nur zu solchen Zeiten laufen zu lassen, in denen wenig Dialogarbeit stattfindet, damit sie die Antwortzeiten des Systems nicht unnötig verschlechtern. In solchen Fällen, wie auch in anderen Fällen, in denen die Programmausführung eine gegebene Zeit suspendiert werden soll, ist der HANG-Befehl von Nutzen. Als Argument hat der Befehl einen numerischen Ausdruck, der angibt, wieviele Sekunden die Programmausführung verzögert werden soll. Beispiel

HANG 10 Η I+J HANG kann mit Η abgekürzt werden. Eine Verwechslung mit dem Befehl HALT, dessen Abkürzung ebenfalls Η ist, ist nicht zu befürchten, weil HALT stets argumentlos geschrieben wird, HANG dagegen immer mit Argument. Die Angabe von negativen Argumenten (einschließlich 0) hat keine Wirkung. Zu beachten ist ferner, daß Nachkommastellen des Arguments (z.B. bei Η 1.65) in einigen MUMPS-Installationen unter Umständen ignoriert werden. In dem oben geschilderten Fall, in dem ein Hintergrundprozeß nur zu verkehrsschwachen Zeiten, beispielsweise erst ab 20 Uhr laufen soll, kann der HANG-Befehl eingesetzt werden, um den Hintergrundprozeß direkt nach seinem Start bis zur gegebenen Zeit zu suspendieren.

7.3. Hin t ergrun dprozesse

211

Beispiel PRINT ; H/K; 8.9.88; Druck am Abend S jetzt«$P($H,",",2),acht=20*60*60 Η acht-jetzt

Dieses Beispiel zeigt die ersten Zeilen des Hintergrundprozesses. Dort wird zuerst die aktuelle Zeit (ausgedrückt in „Sekunden seit Mitternacht") in die Vejiable jetzt und die Zielzeit, nämlich 20 Uhr, in die Variable acht gespeichert. Die Differenz acht —jetzt ergibt die Anzahl von Sekunden, die noch bis 20 Uhr gewartet werden muß. Daher suspendiert der HANG-Befehl den Prozeß genau bis 20 Uhr. Interessant ist, daß das gezeigte Programmfragment auch funktioniert, wenn de:: Prozeß nach 20 Uhr gestartet wird. Dann ist jetzt größer als acht, die Subtraktion ergibt also eine negative Zahl. Ist das Argument des HANG negativ oder 0, hat der Befehl keine Wirkung, so daß der Prozeß sofort anlaufen kann.

Anhang A Übersicht über die Sprache

A.l

Darstellungsweise

In diesem Anhang sollen alle Sprachelemente von MUMPS noch einmal zusammenfassend dargestellt werden. Dabei wurde Wert auf eine verdichtete Darstellung gelegt, so daß der Anhang als Nachschlagewerk verwendet werden kann. Er ist in vier Teile gegliedert: Übersicht über Befehle, Funktionen, Systemvariablen und über Operatoren. Grundsätzlich wird jedes Sprachelement mit einem kurzen erläuternden Text und einigen Beispielen aufgeführt. In den ersten drei Teilen wird zusätzlich noch die formale Festlegung der Schreibweise (Syntax) in erweiterter Backus-Naur-Form, (EBNF) angegeben. Diese Notation soll im folgenden erläutert werden. Mit dieser Notation ist es möglich, formal die Regeln, nach denen eine Sprache aufgebaut ist, darzustellen. Elemente einer EBNF sind terminale Symbole, nicht-terminale Symbole und eine kleine Anzahl von Meta-Operatoren, die der Verknüpfung der Symbole dienen. Unter terminalen Symbolen versteht versteht man Zeichenfolgen, die in genau dieser Form in einem MUMPS-Programm erscheinen müssen. Terminale Symbole werden in der hier verwendeten EBNF ohne besondere Kennzeichnung geschrieben. Beispielsweise könnte DO ein terminales Symbol sein, das fordert, daß an dieser Stelle im Programtext das Wort DO erscheinen soll. Das terminale Symbol „u" wird benutzt, um einen Leerschritt daxzustellen. Nicht-terminale Symbole werden verwendet, um auf weitere Definitionen zu verweisen. Sie werden in eingeschlossen. Beispielsweise könnte das nicht-terminale Symbol anzeigen, daß an dieser Stelle im Programmtext etwas stehen muß, was durch die mit eingeleitete Definition festgelegt wird. Diese Festlegung, nämlich die Regel, wie ein Ausdruck zu schreiben ist, kann an beliebiger anderer Stelle der EBNF erfolgen. Einige häufig benötigte nicht-terminale Symbole werden im nächsten Abschnitt aufgeführt. Eine Definition eines nicht-terminalen Symbols besteht aus dem Symbol selbst, dem Definitions operator „: :=" und dem definierenden Ausdruck. Der definierende Ausdruck ist eine Aneinanderreihung von terminalen und nicht-

214

Anhang Α.

Übersicht über die Sprache

terminalen Symbolen, die noch durch Meta-Operatoren gruppiert werden können. Die folgenden Meta-Operatoren werden benutzt: Meta-Operator 1 Neue Zeile [...]

Bedeutimg Definitionssymbol Trennt Alternativen Trennt Alternativen Optional Kennzeichnet Wiederholungen

Damit läßt sich beispielsweise die Definition F[OR] u = [,] . . .

wie folgt deuten: schreibe den Befehl F oder FOR gefolgt von genau einem Leerschritt, dann den Namen einer lokalen Variablen wie durch spezifiziert, gefolgt von einem Gleichheitszeichen und einem . Optional können weitere durch Komma getrennte folgen. Um die Darstellung zu vereinfachen, werden bei der Syntaxbeschreibung von Befehlen, Funktionen und speziellen Variablen die, Namen groß geschrieben. Es ist jedoch grundsätzüch erlaubt, sie in einer beliebigen Mischung von Groß- und Kleinschreibung daxzustellen.

A.2

Struktur eines Programms

Die formale Beschreibung einer Programmiersprache mit einer EBNF beginnt im allgemeinen mit der Festlegung, wie ein Programm aufgebaut ist, um dann schrittweise immer detailliertere Information zu einzelnen Sprachelementen zu liefern. Entsprechend wird auch hier vorgegangen. Das nicht-terminale Symbol, das ein komplettes Programm repräsentiert ist .





  • ::= < l i n e > . . . ::= [ < l a b e l > ] u . . . [ < l i > ] . . . < l i n e b o d y > i j . . . ::= . [ J . . . u ...

    : [ u . . . ] . . . : : * ;

    Dabei wird durch das Ende einer Zeile und durch < p r i n t a b l e char a c t e r s> eine beliebige Folge von druckbaren Zeichen symbolisiert.

    A.3.

    Häufig benötigte nicht-terminale

    Symbole

    215

    repräsentiert die Syntax eines beliebigen Kommandos wie weiter unten dargestellt. Auch hier soll noch einmal daraufhingewiesen werden, daß zum Zeitpunkt der Entstehung dieses Buches noch nicht endgültig geklärt war, ob die durch
  • dargestellte Punktsyntax in den Standard aufgenommen wird. Daher ist eventuell dieses syntaktische Element zu streichen.

    A.3

    Häufig benötigte nicht-terminale Symbole

    Im folgenden sollen die bei der Beschreibung der einzelnen Sprachelemente häufig benötigten nicht-terminalen Symbole definiert werden. Das geschieht aus Gründen der Einfachheit und Übersichtlichkeit teilweise informal durch verbale Beschreibung. Der Leser, der eine komplette formale Beschreibung benötigt, sei auf den Standard verwiesen. A.3.1

    Namen

    Einzelne Neimen von Variablen werden mit

    bezeichnet. Da in MUMPS Indizierung, Namensindirektion und Indexindirextion fast überall dort erlaubt ist, wo Namen erlaubt sind, werden

    als Name einer lokalen bzw. globalen Variablen definiert. Hinter diesen Symbolen können sich beliebige Indizierungen und Indirektionen verbergen. Zusammenfassend wird ::* I als Name einer lokalen oder globalen Variablen definiert. Soll bei lokalen Variablen zwar Namensindirektion, aber weder Indizierung noch Indexindirektion erlaubt sein, wird die Definition

    benutzt.

    : : = | [ , < a c t u a l > ] . . . )

    ::= | . | .® von Bedeutung.

    218

    A.3.4

    Anhang Α. Übersicht über die Sprache

    Ein / Ausgabe

    Bei OPEN, USE und CLOSE können herstellerabhängige Geräteparameter angegeben werden. Sie werden als : : = | ([[]:]...) referenziert. Bei READ und WRITE gibt es besondere Parameter zur Formatierung der Ausgabe. Sie werden mit : : = []...[?] ? : : = ! I * spezifiziert.

    A.4.

    Übersicht

    über

    219

    Befehle

    •·

    A.4 A.4.1

    Ubersicht über Befehle BREAK

    B[REAK] [:] expr>] uArgumentsyntax nicht spezifiziert B[REAK][:[,< if argument > ] . . . 30,sex="m" Form führt den Rest der Zeile nur aus, Iu wenn $TEST gleich 1 ist. In der Form mit I «c!«d Argument wird der Rest der Zeile genau dann ausgeführt, wenn die logische Interpretation des IF-Arguments wahr (also 1) ist. $T bekommt dann den Wert 1. Wird das IF-Argument logisch zu falsch interpretiert, wird mit der Ausführung der nächsten Zeile fortgefahren. Eine Argumentliste wird elementweise logisch evaluiert, Beispiel 2 und 3 sind äquivalent. Die Argumentindirektion kann benutzt werden. Eine Nachbedingung ist nicht erlaubt. Der Einflußbereich von IF kann durch Verwendung des argumentlosen DO-Befehls auf einen gesamten Programmblock erweitert werden. Siehe auch Abschnitte 3.3 und 3.4

    Anhang Α. Übersicht über die Sprache

    224

    A.4.10

    JOB

    J [ 0 B ] [ : < l o g expr>] u [,] . . .

    [:] Clabelref>[:] Q

    ([,]...)

    [:] :

    ([[]:]...) Beispiele: JOB bewirkt, daß einer oder mehrere parallele MUMPS-Prozesse vom aktuellen JOB Ά J B.Al'PROG Programm gestartet werden. · Als ArguJ :g="Walir" J l : : 1 0 ment wird der Einsprungpunkt einer loJ Avar:(JOB-Parameter) kalen oder globalen Routine angegeben. J ~Pl(4,xl,$E(name)) Parametertransfer in den Hintergrundprozeß ist über eine Liste von in Klammern gesetzten Ausdrücken möglich. Durch Doppelpunkt getrennt kann die Angabe von herstellerabhängigen Prozeßparametern erfolgen. Das kann entweder ein einzelner Ausdruck oder eine in Klammern gesetzte Liste von Ausdrücken sein. Durch einen weiteren Doppelpunkt getrennt kann ein Timeout angegeben werden. Siehe auch Abschnitte 7.3.1 und 7.3.2

    A.4.11

    KILL

    Κ[ILL][:] u K[ILL][:] U [,] : : = ([, . . . ] ) ®

    ...

    Beispiele: KILL löscht die in der Argumentliste angegebenen lokalen oder globalen VariaKILLu Κ a,b,"C blen. In der argumentlosen Form werden Κ a(l,3) alle lokalen Variablen gelöscht, während Κ (vi,v2,v3) bei der Klammerschreibweise, die nur für Κ:bed pi,p2,Ovar«(ind) nichtindizierte lokale Variablen verwendet werden kann, alle lokalen Variablen, die nicht in der Klammer aufgeführt sind, gelöscht werden. Ein KILL auf einen Knoten einer indizierten Variablen bewirkt das Löschen des Knotens und aller Nachfolger. Globale Variablen und Teilbäume

    A.4.

    Übersicht über

    Befehle

    225

    können nur mit der selektiven Form gelöscht werden. Siehe auch Abschnitte 1.4.3 und 5.1.2

    A.4.12

    LOCK

    L[OCK][:]u L[0CK][:]u[,].·. ::= [+|-][:] l:0) getrennt durch einen Doppelpunkt. Die 5 V $S(a=2:5,i:0) Interpretation der Argumente wird von 0 links nach rechts ausgeführt bis ein logi- S m i n = $ S ( s < t : s , l : t ) scher Ausdruck wahr ist. $S liefert dann den Wert des Ausdrucks rechts des Doppelpunkts. Mindestens ein logischer Ausdruck muß wahr sein, sonst erfolgt eine Fehlermeldung. Siehe auch Abschnitt 3.9

    A.5.

    Übersicht über

    A.5.16

    Funktionen

    237

    STEXT

    $T[EXT](+) $T[EXT]() liefert den Namen des aktuel- Beispiele: len Programms. Ist das Argument eine V $TEXT(+0) positive ganze Zahl mit einem führen- ROUT den Pluszeichen, wird die entsprechende W $T(+5) READ χ WRITE x+1 Programmzeile des aktuellen Programms V IT(LAB) ausgegeben. Im zweiten Fall ist das Ar- LAB READ b gument eine Zeilenreferenz (Label oder V $T(LAB+1"PR1) SET a=3 Label plus Offset) plus Programmname eines beliebigen Programms des Directories, wobei $TEXT den Inhalt dieser Zeile übergibt. Existiert die Zeilenreferenz nicht, ist das Ergebnis ein Leerstring. Wird kein Programmname angegeben, wird die entsprechende Zeile des geladenen Programms übergeben. Gesonderte Überlegungen beim Gebrauch von $TEXT sind dann angebracht, wenn das MUMPS-System ObjektCode erzeugt, d.h. compilierend ist. Siehe auch Abschnitt 6.6 $TEXT(+0)

    A.5.17

    $TRANSLATE

    $TR[ANSLATE](,) $TR[ANSLATE](,,) wandelt die im ersten Argu- Beispiele: ment angegebene Zeichenkette zeichen- S g*"GORS",k="gors" weise nach einer durch das zweite und V STRAISLATE("GROSS",g,k) das dritte Argument spezifizierten Erset- gross V |TR("Arbeitsordnung","aeiou") zungsvorschrift um und übergibt das Er- rbtsrdnng gebnis als Funktionswert. In der Form mit drei Argumenten wird für jedes Zeichen des ersten Arguments festgestellt, ob und an welcher Stelle es zum ersten Mal im zweiten Argument vorkommt. Ist das der Fall, wird es durch das Zeichen ersetzt, das an der gleichen Stelle im dritten Argument erscheint. Überschüssige Zeichen des zweiten Arguments, die keine Übersetzung im dritten Argument mehr haben, werden im ersten Argument entfernt. Das gleiche passiert, wenn nur zwei Argumente angegeben werden. Siehe auch Abschnitt 6.2.3 $ TRAN SLATE

    238

    Anhang Α.

    A.5.18

    Übersicht

    über die

    Sprache

    $VIEW

    $V[IEW](nicht spezifiziert)

    $VIEU ist eine herstellerabhängige Funktion, die einem Programm systemabhängige Daten übergibt.

    Beispiele:

    $VIEH(...) Siehe auch Abschnitt 4.6

    A.5.19

    $Z - F u n k t ionen

    $Z[nicht spezifiziert](nicht spezifiziert)

    Jede zusätzliche, nicht im Standard enthaltene Funktion, die ein Hersteller definiert, muß mit $ Ζ beginnen.

    Beispiele: $zpreviousCI(io)) SZVERlFY(a)

    Siehe auch Abschnitt 4.6

    A.6

    Ubersicht über Systemvariablen

    A.6.1

    $HOROLOG

    $H [0R0L0G]

    SHOROLOG enthält das Datum und die Uhrzeit in Form von 2 Zählern, die durch ein Komma getrennt sind. Der erste Zähler gibt die Anzahl der Tage seit Freitag, dem 1. Januar 1841 (Stichtag mit dem Wert 1), der zweite Zähler die Anzahl der Sekunden seit Mitternacht an.

    Beispiele: V $H0R0L0G 51797,57754 W $P(SH,",",2) 57800 V +$H 51797

    Siehe auch Abschnitt 1.7

    Α.6.

    Übersicht über System

    A.6.2

    239

    variablen

    $IO

    $i[o] $10 gibt den Namen des aktuell benutzten Ein- oder Ausgabegerätes an. Siehe auch Abschnitt 4.5.3

    A.6.3

    Beispiele:

    U "TERM" S χ=$Ι0 V χ TERM

    $JOB

    $ J [OB] $ JOB enthält eine positive ganze Zahl, die jeden laufenden MUMPS-Prozeß eindeutig identifiziert. $J verändert sich nicht, solange dieser Prozeß aktiv ist.

    Beispiele:

    V $J0B 1024 S "TEMP($ J)="String" Siehe auch Abschnitt

    A.6.4

    7.3.1

    SSTORAGE

    $S [TORAGE] $ STORAGE liefert die Anzahl der Zeichen des noch zur freien Verfugung stehenden Speicherplatzes für den aktuellen Prozeß.

    A.6.5

    Beispiele:

    V SSTORAGE 769 Siehe auch Abschnitt

    $TEST

    $T[EST] $TEST enthält den Wert des zuletzt berechneten Testschalters. $T wird gesetzt bei IF mit Argument, sowie bei den Befehlen OPEN, LOCK, READ und JOB mit Timeout. Argumentloses IF und ELSE (wie auch eine Nachbedingung) verändern den Wert von $T nicht.

    Beispiele:

    IF a=5 S x=l V $TEST 1 (falls a=5) 0 "FLOPPY"::10 V $T 1 (falls OPEI innerhalb von 10 Sek. erfolgreich)

    1.4.3

    240

    Anhang Α.

    Übersicht über die Sprache

    $TEST wird beim Aufruf von benutzerdefinierten Funktionen und speziellen Variablen sowie beim Aufruf eines Programmblocks mit dem argumentlosen DO gesichert und nach dem Ende des Aufrufs wieder auf den alten Wert gesetzt. Siehe auch Abschnitt 3.4 A.6.6

    $X

    $x $X gibt die horizontale Position des Zei- Beispiele: chens an, das auf dem aktuellen Gerät als w ?5,$x nächstes ausgegeben wird. Am Zeilenbe6 ginn hat $X den Wert 0. Formfeed und Linefeed setzen den Wert von $X auf 0. Backspace ($C(8)) erniedrigt den Wert, wenn er nicht schon 0 war. Siehe auch Abschnitt 4.1 A.6.7

    $Y

    $Y

    $Y enthält die vertikale Position des ak- Beispiele: tuellen Ein-/ Ausgabegerätes an. Die erw t ! ! ! ,$Y ste Zeile ist als 0 definiert. Ein Formfeed 3 setzt $Y auf 0. Jedes nachfolgende Linefeed erhöht den Wert von $Y um 1. Siehe auch Abschnitt 4.1 A.6.8

    $Z-Variablen

    $Z[nicht spezifiziert]

    Jede

    herstellerspezifische

    Erweiterung

    der Systemvariablen muß mit $Z beginnen. Programme, die herstellerunabhän-

    Beispiele: $zerror $ztrap

    gig sein müssen, sollten $Z-Systemvariablen nicht benutzen. Siehe auch Abschnitt

    4.6

    Α. 7. Übersicht über

    Operatoren

    241

    ··

    Α.7

    Ubersicht über Operatoren

    A.7.1 Allgemeines Ausdrücke in MUMPS sind entweder atomar oder zusammengesetzt. Ein atomarer Ausdruck ist der Name einer lokalen, globalen oder Systemvariablen, ein Funktionsaufruf, der Aufruf einer benutzerdefinierten speziellen Variablen oder Funktion oder ein numerisches oder Stringliteral. Auch ein beliebiger in Klammern gesetzter Ausdruck wird als atomar betrachtet. Weiterhin kann ein atomarer Ausdruck durch eine beliebige Zahl von einstelligen Operatoren eingeleitet werden. Formal ergibt sich damit: ::= $ $([,]...) $$ $$

    ()

    ::= ' | + | -

    wobei , , Cuser var name> und die Nainen von Systemvariablen, Funktionen, benutzerdefinierten speziellen Variablen und Funktionen bezeichnen. Ein zusammengesetzter Ausdruck besteht aus atomaren Ausdrucken, die durch zweistellige Operatoren miteinander verknüpft sind. Die zweistelligen Operatoren gruppieren sich in String-, arithmetische, logische und Vergleichsoperatoren, wobei der Operator für den Mustervergleich noch eine Sonderstellung einnimmt. Formal wird damit ein Ausdruck wie folgt dargestellt:

    :[]... ::=

    ['] [ ' ] [']? : : * _

    : : = + | - | * l / | \ l # ::= ft I !

    : : = < | > | [ | ] | =

    242

    Anhang Α. Übersicht über die Sprache

    Die Reihenfolge der Abarbeitung eines Ausdrucks unterliegt keiner Präferenz einzelner Operatoren und wird strikt von links nach rechts vorgenommen (Beispiel: j j#4=0+28_31). Klammern können jedoch eingesetzt werden, um die Reihenfolge der Auswertung von Teilausdrucken zu beeinflussen. Der einzige Datentyp in MUMPS ist die Zeichenkette. Arithmetische Operationen werden auf Zeichenketten durchgeführt und liefern numerische Werte, die als Spezialfälle von Zeichenketten angesehen werden können. Es existieren eindeutige Regeln zur Interpretation von Zeichenketten als Zahlen und von Zahlen als Boolesche Wahrheitswerte (in Verbindung mit den logischen Operatoren). Es gibt drei einstellige Operatoren: ' (nicht), + (plus) und - (minus). Die Reihenfolge der Anwendung ist strikt von links nach rechts. Jeder logische und Vergleichsoperator kann mit dem Nicht-Symbol ( * ) in seinem Ergebnis invertiert werden. Dabei gilt folgende allgemeine Regel: A'B

    ist äquivalent zu

    >

    (AB).

    Hierbei steht für einen der Operatoren =, [ , < , ] , >, &, ! sowie ? (Mustervergleich).

    A.T.2

    Ubersicht über die einzelnen Operatoren

    Einstellige a r i t h m e t i s c h e O p e r a t o r e n ( + , —) Diese Operatoren forcieren die numerische Interpretation des Operanden. Im Falle des Mmui-Operators wird das Vorzeichen konvertiert. Ausdruck

    Wert

    +"abc" +"123abc"

    0 123 -6

    +$H

    53937

    D e r einstellige logische O p e r a t o r (') Der Nicht-OperaXor invertiert den logischen Wert des folgenden Ausdrucksatoms. Die zweifache Anwendung forciert die logische Interpretation seines Operanden.

    Α.7.

    Übersicht über

    243

    Operatoren

    Ausdruck

    Wert

    >1 »0 >'"33xyz" "$F(a,x)

    0 1 1 =a[x

    Zweistellige arithmetische Operatoren (+, —, *, / ,

    #)

    Neben den vier Grundrechenarten existieren noch die Ganzzahldivision \ , die die Ganzzahlinterpretation einer normalen Division liefert, sowie die Restdivision #, die formal definiert ist als: a#b

    entspricht

    α — (£>*|α/δ|),

    wobei die größte Zahl kleiner oder gleich χ darstellt. Bei / , \ und # ist eine Division durch 0 nicht erlaubt. Sei a=5, b=7, c=10 Ausdruck a+b a*b-c c/a 2+a*b 2+(a*b) b#a c#a 9.123\1 c\3

    Wert 12 25 2 49 37 2 0 9 3

    Arithmetische Vergleichsoperatoren () Diese beiden Operatoren vergleichen zwei numerische Werte auf größer oder kleiner. Bei diesen Vergleichen werden die Operanden zunächst in numerische Werte konvertiert, ehe der Vergleich ausgeführt wird.

    244

    Anhang Α. Übersicht über die Sprache

    Sei y=12, z=15 Ausdruck y"16abc" z»>y_z y=z

    Umlaut A 0 Ü

    ASCII 91 92 93

    gleich

    [ \ ]

    Literaturverzeichnis [1] Für Anfänger in MUMPS bietet sich insbesondere das Standardlehrbuch von R.F. Walters in englischer Sprache an. Walters, R.F. (1988) — The ABC's of MUMPS: An Introduction For Novice and Intermediate Programmers. MUMPS Users' Group Europe. [2] Ebenfalls für den Anfänger gedacht ist das folgende mit vielen Beispielen versehene Lehrbuch. Krieg, A.F., Miller, D.H., Bressler, G.L. (1984) — Computer Programming in Standard MUMPS. Second Edition. MUMPS Users' Group Europe. [3] Das „Kochbuch" von Brown und Glaeser ist eine lesenswerte Sammlung von Programmen und Programmfragmenten in MUMPS, die den etwas fortgeschritteneren Programmierer in die Lage versetzen, größere Anwendungen zu schreiben. Brown, D.B., Glaeser, D.H. (1985) — A Cookbook of MUMPS. Programmer's Techniques and Routines. MUMPS Users' Group Europe. [4] An den Leser, der betriebsystemnahe Grundlagen über MUMPS der Systeme DSM, MSM, Μ und daraus abgeleitet sehr wertvolle Designvorschriften für MUMPS-Anwendungen kennenlernen möchte, findet in dem für Fortgeschrittene gedachten Buch von Volkstorf sehr viele Detailinformationen. Volkstorf, C.S. (1985) — The MUMPS Handbook of Efficiency Techniques. MUMPS Users' Group Europe. [5] Für den Anwendungsprogrammierer, der mit dem Datenbankmanagementsystem FileMan arbeitet, ist ganz besonders das einführende Werk von Richard G. Davis zu empfehlen. Es ist als Benutzerhandbuch gedacht und enthält auch Beispiele der komplexeren Anwendungsmöglichkeiten des FileMan. Davis, R.G. (1987) — FileMan, A Database Manager. MUMPS Users' Group Europe. [6] Als Grundlage für eine Einordnung von MUMPS zu anderen Programmiersprachen wird ein Artikel aus dem Jahre 1977 wärmstens empfohlen. Munnecke, T.H., Walters, R.F., Bowie, J., Lazarus, C.B., Bridger,D. A. (1977) — MUMPS: Characteristics and Comparison with Other Programming Systems. Medical Informatics, Vol.2, No.3, pp.173-196.

    254

    Literaturverzeichnis

    [7] Als Referenz für den neuen Standard wurden die beiden folgenden Dokumente des MUMPS Development Committees verwendet. MDC X11/SC1/88-50 (Oktober 1988) — Part I: Language Specification und Part Π: MUMPS Portability Requirements. Alle mit MUMPS Users' Group gekennzeichneten Bücher können über die Autoren dieses Lehrbuchs bezogen werden. Adresse: MUMPS Users' Group Europe c/o Klinikum der J.W.Goethe Universität Zentrum der Medizinischen Informatik Theodor Stern Kai 7 6000 Frankfurt 70

    Sachverzeichnis

    Sachverzeichnis ! 11, 62, 97, 245 " 10, 14, 26 "" siehe Leerstring * 11, 18, 24, 97, 243 $ 16, 20 $$ 46 * 12, 31, 35, 36 ft 62, 66, 245 ' 59, 63, 242 ' ! 64, 245 'ft 63, 245 '< 59, 64, 243 ' = 59, 64, 244 »> 59, 64, 243 ' ? 187, 244 '[191,244 >] 191, 244 ( 23 ) 23 * 22, 103, 243 + 22, 29, 41, 195, 205, 242, 243 , 10, 195 - 22, 29, 195, 205, 242, 243 . 21, 44, 83 / 22, 111, 243 : 19, 72, 86 ; 8 < 59, 243 = 13, 59, 244 > 9, 59, 243 ? 11, 97, 184, 244 « 88 [ 172, 191, 244 \ 24, 243 ] 190, 244 ' 37, 40, 47, 130 _ 28, 246 A 184 abgekürzte Schreibweise 34 217

    < a c t u a l l i s t > 217 Addition 22 Andern von Zeilen 57 aktuelle Parameter 43 alphabetische Zeichen 184 Anführungszeichen 10, 14, 26 ANSI 2 Anweisungen 36 Anweisungsteil 56 Anzahl von Feldern 175 Arbeitsspeicher 14 Argument einer Funktion 20 eines Befehls 10 Argumentindirektion 90-91, 109 argumentlose Befehle 15 $ASCII 102, 230 ASCII-Zeichen 26 ASCII-Zeichensatz 100-105. 184 Assembler 1 Ast 125 Aufruf 37, 39 Ausdruck 23 Ausführung eines Programms 37 Ausrufezeichen Oder 62, 245 Zeilen Vorschub 11, 97 Auswahlfunktion 86 Backspace 105 Baum 125, 130 bedingte Ausführung 65 bedingte Ausführung 72 Befehl 13 Argument 8, 33 argumentlos 15 Kurzform 34 Befehlsnachbedingung 72 Befehlswort 8, 10, 33, 72 Befehlszeile 33 Benutzer 14

    257

    Sach Verzeichnis Benutzerbereiche 6 benutzerdefiniert Funktion 46-47, 70, 81 spezielle Variable 47-48, 70 Benutzerkennung 6 Benutzung eines Geräts 118 Bibliotheksroutinen 45 Bildschirmattribute 106 Bildschirmsteuerung 97, 106-114 Block 83 BREAK 92-95. 219 breakpoint 94 C 184 call by reference 44 call by value 44, 209 carriage return 8, 34 $CHAR 101-102. 230 CLOSE 118-119. 219 Concatenation stehe Verkettung 8 Cursor 8 Cursorpositionierung 106 $DATA 145-149. 159, 231 Datei 130 Datenbanksystem 154 Datenhaltung 154 Datenprüfung 183-192 Datensatz 131, 132 Datenstruktur 154 Datentyp 28, 29 Datum 31 deadlock 206 dense array 124 Design von Datenstrukturen 154-165 218 Dezimalzahlen 21 Dialog 19, 71 dichte Speicherung 124 Dienstprogramme 31, 134 Direktmodus 35, 42, 56 Division 22, 243 217 DO 37-38. 39, 43, 73, 83, 126, 132, 208, 220

    Dollarzeichen 16, 20, 46 Doppelpunkt bei $SELECT 86 Nachbedingung 72 Timeout 19 druckbare Zeichen 127 druckbarer Zeichensatz 26 Ε 184 Ε (Exponent) 21 Eckige Klammer auf 191, 244 Eckige Klammer zu 190, 244 Editieren von Programmen 56-58 Einbenutzersystem 6 Einfügen von Zeilen 57 Eingabefehler 183 Einloggen 6 einstellige Operation 29 einstellige Operatoren 29, 63 ELSE 68-71. 73, 83, 117, 2 0 4 , 2 0 8 , 2 2 1 Enthält 172, 191, 244 < e n t r y r e f > 217 Erfassung 19 Erfassungsmasken 111 Ersetzen von Zeichen 172 von Zeichenketten 174 erste Zeile 37 Escape 106 exklusiv 49 Exponentialdarstellung 21 216, 241 216, 241 $EXTRACT 169-170. 231 Extraktion von Feldern 175 von Zeichen 169 Fakultät 81 Fallunterscheidung 90 Fehler 92 Feld 123 Feldstruktur 177 Feldverarbeitung 175-182 $FIND 170-172. 191, 232 Floppy disk 38

    258 $FNUMBER 1^5, 232 Folgt 190, 244 FOR 76-80. 83, 91, 123, 131, 139, 221 Ausdrucksformat 76 Bereichsformat 77 offenes Format 78 formale Parameter 43 < f o r m a l l i s t > 217 218 Formatierung 11, 18, 193-195 Formatierungsoperatoren 97 Fragezeichen Mustervergleich 184, 244 Tabulierung 11, 97 freier Platz 16 Freigabe einer Sperre 204 eines Geräts 118 Funktion 20, 49 benutzerdefiniert 46-47, 70, 81 Kurzform 34 Funktionsargument 20 Ganzzahldivision 24, 31, 148, 243 Ganzzahlinterpretation 30 Genauigkeit der Zahldarstellung 21 Gerät Benutzung 118-119 Freigabe 118-119 Reservierung 116-117 Gerätename 115 gestreute Speicherung 124 $GET 149, 233 Gleichheitszeichen Operator 59, 244 Zuweisung 13 Global 130 global 37 Programm 39 Variable 130-134, 150, 154, 203 Globalzeichen 37, 40, 47 GOTO 52-53. 54, 73, 79, 85, 222 Größerzeichen Operator 59, 243 Prompt 9 Großbuchstaben 184

    Sach Verzeichnis Grundrechenarten 22 215 HALT 33, 222 HANG 210-211. 223 Hauptspeicher 38 herstellerspezifisch 56, 120 hierarchische Struktur 125, 145 Hintergrundprozeß 201, 208-211 Hochkomma 59, 63, 242 $H0R0L0G 31-32. 87, 162, 238 IF 65-67. 68, 73, 83, 223 argumentlos 68 If-Then-Else-Konstruktion 69 implementierungsabhängig 56, 120 Index 123, 130 nichtnumerisch 127 numerisch 123 Indirektion 88-91. 142 Argument- 90-91 Index- 142-143 Muster- 187 Namens- 88-90 indizierte Variablen 123-129 216 Interpretation logische 64 numerische 28, 32 Interpunktionszeichen 184 < i n t l i t > 216 invertierter Global 158 $10 118-119. 239 Iteration 52, 76 fußgesteuert 79 kopfgesteuert 78 $J0B 209, 239 JOB 71, 208-210. 224 Jobidentifikation 209 $ JUSTIFY 193-194. 233 KILL 15-16. 45, 126, 131, 133, 224 Klammeraffe 88 Klammern 20, 23, 43 Kleinbuchstaben 184 Kleinerzeichen 59, 243

    259

    Sachverzeichnis Knoten 125, 126, 130, 145 Komma 10, 20, 195 Kommentar 8, 37 Konkatenation siehe Verkettung Konventionen beim Programmieren 37 L 184 Label 35, 39, 40, 56 217 217 Lange 169 Längenbegrenzung 18 Laufvariable 76 lead in character 106 leere Zeichenkette 28 Leerstring 19, 138, 148, 149 Leerzeichen 10, 16, 36 $LENGTH 234 mit einem Argument 20, 169-170 mit zwei Argumenten 175-177 lexikalische Reihenfolge 190 Line start indicator 36 links-nach-rechts Abarbeitung 23 Literal numerisches 21 String- 26 215 LOCK 71, 160, 204-207. 225 Löschen 126 von Variablen 15 von Zeilen 57 216 logische Interpretation 64» 65 logische Operatoren 62-64, 65 logischer Ausdruck 72, 86 Logoff 33 Logon 6 lokal 37 Programm 39 Variable 14-16, 43 Lost Update 203 215 Mantisse 21

    Massenspeicher 38 Mehrbenutzersystem 6,

    116,

    130,

    201-211

    Menü 90, 198 Minus 22, 29, 195, 205, 242, 243 Multiplikation 22, 243 Musterindirektion 187 Mustervergleich 184-188. 244 Ν 184 Nachbedingung auf Argumente 73-75 auf Befehle 72-73 auf den ELSE Befehl 73 auf den FOR Befehl 73, 80 auf den IF Befehl 73 Nachfolger 125, 126, 146 nächster Index in der Baumstruktur 140 in einer Hierarchiestufe 135, 140 Naked Reference 150-153. 206 215 Namen Groß- und Kleinschreibung 12 von Programmen 36 von Variablen 12 Namensindirektion 88-90 nationale Sonderzeichen 182, 191 nationale Zeichensätze 136 natürliche Zahl 35 Negationsoperator 63 NEW 49-51. 70, 126, 132, 226 $NEXT 140; 234 Nicht 59, 63, 242 Nicht Oder 64, 245 Nicht Und 63, 245 216 numerische Gleichheit 60 numerische Interpretation 28-30, 32, 60 numerische Operationen 22-25 numerische Operatoren 60 216 Nummernzeichen Längenbegrenzung 18 Restdivision 24, 243

    Sachverzeichnis

    260 Sei ten Vorschub 11, 97 Oder 62, 245 Offset 41, 47, 57 OPEN 71, 104, 116-117. 226 &0RDER 135-140. 151, 159, 234 Ρ 184, 195 PAC 6 Parallelverarbeitung 201 Parameter 43 Parameter passing 43 Partition 6, 16, 56, 70, 208 244 Personal Computer 6 Personenidentifizierung 159 $PIECE 177-179. 189, 235 Platte 38 Plus 22, 29, 41, 195, 205, 242, 243 Portabilität 120 Positionierung 11, 98 Postcondition 72 Programmblock 70, 83 Programme 35 Programmer's Access Code 6 Programmname 36 Programmodus 35 Programmstruktur 37 Programmunterbrechung 92 Programmverzweigung 52-53 Programmzeile Aufbau ^ Ausführung 35 Eingabe einer 56 mit $TEXT gelesen 197 nach ELSE 69 nach FOR 76 nach IF 65 Prompt 9, 17 Prozentzeichen 12, 31, 35, 36 Prozeß 14, 201 Punkt Blöcke 83 Call by reference 44 Dezimal- 21 Punktsyntax 83

    $QUERY 140-142. 235 QUIT 38, 39, 42-44, 49, 54, 78, 83, 227 mit Argument 47 $RAND0M 24-25. 236 READ 17-20. 70, 104, 121, 131, 227 Längenbegrenzung 18 Rechenzeichen 22 rechtsbündig 193 Reihenfolge von Operationen 23 Rekursion 81 Reservierung 116 Restdivision 24, 31, 61, 147, 243 217 < r o u t i n e r e f > 217 Rückgabe von Werten 44 Rückgabewert 47 Ruckstrich 24, 243 Rundung 194, 195 Schleifen 76 Schlüssel 131, 156 Schrägstrich Division 22, 243 Gerätesteuerung 111 Sei ten Vorschub 11, 98 $SELECT 86-87. 236 Semikolon 8 sequentieller Zugriff 139 SET 12-15. 123, 152-153. 228 Klammerform 14 SET SPIECE 179-181 sichtbare Variablen 15 Sichtbarkeit 49 Sortierung 15, 135-137 sparse array 124 Speicherplatz 16 Sperre 160, 203-207 spezielle Variablen 16, 68, 97 benutzerdefiniert 47-48, 70 Kurzform 34 Sprung 52 Standalone 6 Standard 50, 56 Stern

    Sachverzeichnis ASCII Codierung/Decodierung 103 Multiplikation 22, 243 Sternsyntax 103-105 Steuersequenz 106 Steuerzeichen 26, 184 $ST0RAGE 16-17. 239 Stringliteral 26-28 Stringvergleich 60 < s t r l i t > 216 Substrings 175 Subtraktion 22 Suchen 170 Synchronisation 203 Syntax 8-9, 33-34 Systemprogramm 36 Systemvariablen 16, 34, 68, 97 Τ 195 Tabellensteuerung 111 Tabellenverarbeitung 87, 190 Tabulatortaste 56 Tabulierung 11, 97 Teilzeichenketten 175 $TEST 68-71. 73, 83, 117, 204, 208, 239 $TEXT 197-199. 237 Textkonstanten 14, 26 Textmanipulation 167 Textoperatoren 183-192 Timeout 19, 70, 104, 116, 204, 208 Timesharing 3 $ TRANSLATE 172-174. 237 Trennsymbol 175, 181 Typkonvertierung 29

    261 User Code Identification 6 Variablen 12-15. 28 als Programm 54 globale 130-134 Löschen von 15 lokale 14-16, 43 sichtbare 15 spezielle 16 Typbindung 28 verdecken 44, 49 verdeckte Eingabe 104 Vergleichsoperator 59-61. 65 Verkettung 28, 246 Verknüpfung 22 Verzögerung 210 $VIEW 120, 238 VIEW 120, 229 virtueller Knoten 140, 146 215 Vorgänger 125 Vorzeichen 29 Wertübergabe 43, 70, 126, 209-210 Wertzuweisung 12, 17 Wiederholungsfaktor 185 WRITE 10-12. 14, 103, 229 argumentlos 14 Wurzel 125

    132,

    $X 97-98. 105, 240 XECUTE 54-55. 58, 73, 88, 109, 229 $Y 97-98. 240 Ζ

    ü 184 UCI 6 Umlaute 136, 182 Und 62, 66, 245 Ungleichheitsoperator 59, 244 Unterbrechungspunkt 94 Unterfelder 179 Unterprogramm 38, 39, 46, 49, 54, 70 Unterstreichungszeichen 28, 246 USE 118-119. 228

    -Befehle 56, 120, 230 -Funktionen 120, 238 -Spezielle Variablen 120, 240 Zahlen 10, 21-22, 29 Zahlenbereich 21 Zeichendarstellung 100 Zeichenkette 26=28, 167 Zeichenklassen 184 Zeichensatz 26 Zeichenvorrat 26

    262 Zeile 65 Zeilenvorschub 11, 98 Zeit 31 ZGO 94 Ziffern 184 ZIHSERT 58 Zirkumflex 37, 40, 47, 130 ZLOAD 57 ZMOVE 57 ZPRINT 56-57 ZREMOVE 57 ZSAVE 57 ZTRAP 95 Zufallszahl 25 zusätzliche Sperre 204-207 Zuweisung 12

    Sachverzeichnis

    de Gruyter Lehrbücher (Auswahl) Gerhard Niemeyer

    Einführung in das Programmieren in ASSEMBLER Systeme IBM, Siemens, Univac, Interdata, IBM-PC/370 5., bearbeitete und erweiterte Auflage. 15,5 χ 23 cm. 332 Seiten. 1984. Kartoniert DM 44,ISBN 311010128 9 de Gruyter Lehrbuch Ein Lehr- und Nachschlagewerk für Programmierer in Ausbildung und Praxis.

    Wolfgang £. Spieß/Friedrich G. Rheingans

    Einführung in das Programmieren in FORTRAN auf der Grundlage von FORTRAN 77 7., neubearbeitete und erweiterte Auflage. 15,5 χ 23 cm. 250 Seiten. 1985. Mit 20 Abbildungen und Tafeln, 15 Tabellen. Kartoniert DM 28,- ISBN 311010433 4 de Gruyter Lehrbuch Ein an den Bedürfnissen der Praxis orientiertes Lehrbuch für den Anfanger im Programmieren mit Beispielen und Übungen.

    Gerhard Niemeyer

    Einführung in das Programmieren in PASCAL Mit Sonderteil UCSD-PASCAL-System 2., verbesserte Auflage. 15,5 χ 23 cm. 167 Seiten. 1983. Kartoniert DM 24,ISBN 3 110095327 de Gruyter Lehrbuch Bewußt einfach gehaltene Programmierbeispiele zu allen wichtigen Sprachelementen; besondere Betonung hierarchischer Programmstrukturen; Einführung einer neuartigen Struktogrammtechnik. Darstellung aller wesentlichen Optionen des UCSD-PASCAL-Systems in Form einer ausführlichen Benutzeranleitung.

    Arno Schulz

    Einführung in das Programmieren in PL/1 2., neubearbeitete und erweiterte Auflage. 15,5 χ 23 cm. 332 Seiten. 1984. Kartoniert DM 49,50 ISBN 3 11009988 8 de Gruyter Lehrbuch Eine Darstellung der elementaren Teile von PL/1 einschließlich der Dateiverarbeitung mit Programmierbeispielen und Aufgaben zur Lernkontrolle. Vermittlung von Regeln, wie die Syntax von PL/1 in einem gut strukturierten Programm anzuwenden ist. Preisänderungen vorbehalten

    Walter de Gruyter · Berlin · New York

    Helmut Balzert · Heinz U. Hoppe · Reinhard Oppermann Helmut Peschke · Gabriele Rohr · Norbert A. Streitz (Hrsg.)

    Einführung in die Software-Ergonomie 15,5 χ 23 cm. XVIII, 396 Seiten. Mit 114 Abbildungen und 29 Tabellen. 1988. Kartoniert DM 58,- ISBN 311011939 0 (Mensch Computer Kommunikation · Grundwissen 1 herausgegeben von Helmut Balzert) Die Grundlagen der Mensch-Computer-Kommunikation gehören in Zukunft zum Basiswissen eines jeden Informatikers, Arbeitswissenschaftlers und Psychologen. Die Reihe MCK-Grundwissen bietet eine systematische Einführung in das Gebiet der Mensch-Computer-Kommunikation. Der 1. Band enthält folgende Beiträge in didaktisch-methodisch aufbereiteter Form: Einführung

    Fragestellungen und Forschungsstrategien der Software-Ergonomie (Ν. A. Streitz) Grundlagen

    Grundlagen menschlicher Informationsverarbeitung (G. Rohr)/Arbeitsund organisationspsychologische Aspekte (E. Ulich)/E/A-Geräte für die Mensch-Computer-Interaktion (H. Balzert) Dialogfonnen

    Klassifikation von Dialogformen (E. Eberleh)/Menüauswahl (E. Eberleh)/ Interaktionssprachen (M. Zoeppritz, G. Rohr)/Masken und Formulare (H. Zwerina)/Direkte Manipulation (R. Ilg, J. Ziegler) Hilfesysteme

    Anforderungen an Hilfesysteme (J. Bauer, T. Schwab)/Probleme bei der Konstruktion und beim Einsatz von Hilfesystemen (T. Hermann) Entwurf und Gestaltung

    Aufgabenanalyse unf Funktionsentwurf (J. Ziegler)/Empirie des Softwareentwurfs: Richtlinien und Methoden (D. Ackermann)/Werkzeuge für die Prototypenentwicklung von Benutzerschnittstellen (H. U. Hoppe)/Partizipative Entwicklung und Einführung von Informationssystemen (H. Peschke)/Software-ergonomische Evaluationsverfahren (R. Oppermann) Ausblick

    Trends und Perspektiven der Software-Ergonomie (H. Balzert) Glossar Preisänderung vorbehalten

    Walter de Grayter · Berlin · New York