summaryrefslogtreecommitdiff
path: root/doc/programmer-manual/1.8.7/doc/programmierhandbuch.2b
blob: c2103ba1fd0cb4965d9737039855e185e98fda07 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
#headandbottom("52","EUMEL-Benutzerhandbuch","TEIL 2 : ELAN","2")# 
#pagenr ("%", 52)##setcount(1)##block##pageblock# 
#headeven# 
#center#EUMEL-Benutzerhandbuch 
#center#____________________________________________________________ 
 
#end# 
#headodd# 
#center#TEIL 2 : ELAN 
#center#____________________________________________________________ 
 
#end# 
#bottomeven# 
#center#____________________________________________________________ 
2 - % #right#GMD 
#end# 
#bottomodd# 
#center#____________________________________________________________ 
GMD #right#2 - % 
#end# 
 
 
2.5  Programmstruktur 
 
Ein ELAN-Programm kann aus mehreren Moduln (Bausteinen) aufgebaut sein, die in
ELAN PACKETs genannt werden. Das letzte PACKET wird "main packet" genannt,
weil in diesem das eigentliche Benutzerprogramm (Hauptprogramm) enthalten ist. 
Dies soll eine Empfehlung sein, in welcher Reihenfolge die Elemente eines PACKETs
geschrieben werden sollen: 
 
Ein "main packet" kann aus folgenden Elementen bestehen: 
 
a) Deklarationen und Anweisungen. Diese müssen nicht in einer bestimmten Reihen­
   folge im Programm erscheinen, sondern es ist möglich, erst in dem Augenblick zu
   deklarieren, wenn z.B. eine neue Variable benötigt wird. Es ist jedoch gute Pro­
   grammierpraxis, die meisten Deklarationen an den Anfang eines Programms oder
   Programmteils (Refinement, Prozedur) zu plazieren. 
 
   <Deklarationen> ; 
   <Anweisungen> 
 
____________________________________________________________________________ 
 ........................... Beispiel: ......................... 
 INT VAR erste zahl, zweite zahl; 
 
 page; 
 put ("erste Zahl = "); get (erste zahl); 
 put ("zweite Zahl ="); get (zweite zahl) 
 
____________________________________________________________________________ 
 
 
b) Deklarationen, Refinements und Anweisungen. In diesem Fall ist es notwendig, die 
   Refinements hintereinander zu plazieren. Refinement-Aufrufe und/oder
   Anweisungen sollten textuell vorher erscheinen. 
 
   <Deklarationen> ; 
   <Refinement-Aufrufe und/oder Anweisungen> . 
   <Refinements> 
 
   Innerhalb der Refinements sind Anweisungen und/oder Deklarationen möglich. 
 
____________________________________________________________________________ 
 ........................... Beispiel: ......................... 
 INT VAR erste zahl, zweite zahl; 
 
 loesche bildschirm; 
 lies zwei zahlen ein. 
 
 loesche bildschirm: 
   page. 
 
 lies zwei zahlen ein: 
   put ("erste Zahl = "); get (erste zahl); 
   put ("zweite Zahl ="); get (zweite zahl). 
 
____________________________________________________________________________ 
 
 
c) Deklarationen, Prozeduren und Anweisungen. Werden Prozeduren vereinbart,
   sollte man sie nach den Deklarationen plazieren. Danach sollten die Anweisungen
   folgen: 
 
   <Deklarationen> ; 
   <Prozeduren> ; 
   <Anweisungen> 
 
   Mehrere Prozeduren werden durch ";" voneinander getrennt. In diesem Fall sind
   die Datenobjekte aus den Deklarationen außerhalb von Prozeduren statisch, d.h.
   während der gesamten Laufzeit des Programm vorhanden. Solche Datenobjekte
   werden auch PACKET-Daten genannt. Im Gegensatz dazu sind die Datenobjekte
   aus Deklarationen in Prozeduren dynamische Datenobjekte, die nur während der
   Bearbeitungszeit der Prozedur existieren. Innerhalb einer Prozedur dürfen wieder­
   um Refinements verwendet werden. Ein Prozedur-Rumpf hat also den formalen
   Aufbau wie unter a) oder b) geschildert. 
 
   Die Refinements und Datenobjekte, die innerhalb einer Prozedur deklariert wurden,
   sind lokal zu dieser Prozedur, d.h. können von außerhalb nicht angesprochen
   werden. 
 
____________________________________________________________________________ 
 ........................... Beispiel: ......................... 
 INT VAR erste zahl, zweite zahl; 
 
 PROC vertausche (INT VAR a, b): 
   INT VAR x; 
 
   x := a; 
   a := b; 
   b := x 
 END PROC vertausche; 
 
 put ("erste Zahl = "); get (erste zahl); 
 put ("zweite Zahl ="); get (zweite zahl); 
 IF erste zahl > zweite zahl 
    THEN vertausche (erste zahl, zweite zahl) 
 FI 
 
____________________________________________________________________________ 
 
 
d) Deklarationen, Prozeduren, Anweisungen und PACKET-Refinements. Zusätzlich
   zu der Möglichkeit c) ist es erlaubt, neben den Anweisungen außerhalb einer
   Prozedur auch Refinements zu verwenden: 
 
   <Deklarationen> ; 
   <Prozeduren> ; 
   <Anweisungen> . 
   <Refinements> 
 
   Diese Refinements können nun in Anweisungen außerhalb der Prozeduren benutzt
   werden oder auch durch die Prozeduren (im letzteren Fall spricht man analog zu
   globalen PACKET-Daten auch von PACKET-Refinements oder globalen Refine­
   ments). In PACKET-Refinements dürfen natürlich keine Datenobjekte verwandt
   werden, die lokal zu einer Prozedur sind. 
 
____________________________________________________________________________ 
 ........................... Beispiel: ......................... 
 INT VAR erste zahl, zweite zahl; 
 
 PROC vertausche (INT VAR a, b): 
   INT VAR x; 
 
   x := a; 
   a := b; 
   b := x 
 END PROC vertausche; 
 
 loesche bildschirm; 
 lies zwei zahlen ein; 
 ordne die zahlen. 
 
 loesche bildschirm: 
   page. 
 
 lies zwei zahlen ein: 
   put ("erste Zahl = "); get (erste zahl); 
   put ("zweite Zahl ="); get (zweite zahl). 
 
 ordne die zahlen: 
   IF erste zahl > zweite zahl 
      THEN vertausche (erste zahl, zweite zahl) 
   FI 
 
____________________________________________________________________________ 
#page# 
 
2.6  Zusammengesetzte Datentypen 
 
In ELAN gibt es die Möglichkeit, gleichartige oder ungleichartige Datenobjekte zu
einem Objekt zusammenzufassen. 
 
 
2.6.1  Reihung 
 
Die Zusammenfassung gleichartiger Datenobjekte, wird in ELAN eine Reihung (ROW)
genannt. Die einzelnen Objekte einer Reihung werden Elemente genannt. 
 
Eine Reihung wird folgendermaßen deklariert: 
  
-  Schlüsselwort #on("i")##on("b")#ROW#off("i")##off("b")# 
-  Anzahl der zusammengefaßten Elemente 
   (INT-Denoter oder durch LET definierter Name) 
-  Datentyp der Elemente 
-  Zugriffsrecht ( #on("i")##on("b")#VAR#off("i")##off("b")# oder #on("i")##on("b")#CONST#off("i")##off("b")# ) 
-  Name der Reihung. 
 
____________________________________________________________________________ 
 ........................... Beispiel: ......................... 
 ROW 10 INT VAR feld 
 
____________________________________________________________________________ 
 
 
Im obigen Beispiel wird eine Reihung von 10 INT-Elementen deklariert. ROW 10 INT
ist ein (neuer, von den elementaren unterschiedlicher) Datentyp, für den keine Opera­
tionen definiert sind, außer der Zuweisung. Das Accessrecht (VAR im obigen Bei­
spiel) und der Name ('feld') gilt - wie bei den elementaren Datentypen - für diesen
neuen Datentyp, also für alle 10 Elemente. 
 
Warum gibt es keine Operationen außer der Zuweisung? Das wird sehr schnell
einsichtig, wenn man bedenkt, daß es ja sehr viele Datentypen (zusätzlich zu den
elementaren) gibt, weil Reihungen von jedem Datentyp gebildet werden können: 
 
 
ROW 1 INT             ROW 1 REAL 
ROW 2 INT             ROW 2 REAL 
 :                     : 
ROW maxint INT        ROW maxint REAL 
 
ROW 1 TEXT            ROW 1 BOOL 
ROW 2 TEXT            ROW 2 BOOL 
 :                     : 
ROW maxint TEXT       ROW maxint BOOL 
 
 
Für die elementaren INT-, REAL-, BOOL- und TEXT-Datentypen sind unter­
schiedliche Operationen definiert. Man müßte nun für jeden dieser zusammengesetz­
ten Datentypen z.B. auch 'get'- und 'put'-Prozeduren schreiben, was allein vom
Schreibaufwand sehr aufwendig wäre. Das ist der Grund dafür, daß es keine vorgege­
bene Operationen auf zusammengesetzte Datentypen gibt. 
 
Zugegebenermaßen könnte man mit solchen Datentypen, die nur über eine Operation
verfügen (Zuweisung), nicht sehr viel anfangen, wenn es nicht eine weitere vorgege­
bene Operation gäbe, die Subskription. Sie erlaubt es, auf die Elemente einer Reih­
ung zuzugreifen und den Datentyp der Elemente "aufzudecken". 
 
Form: 
 
Rowname #on("i")##on("b")#[#off("i")##off("b")# Indexwert #on("i")##on("b")#]#off("i")##off("b")# 
 
Beispiel: 
 
 
feld [3] 
 
 
bezieht sich auf das dritte Element der Reihung 'feld' und hat den Datentyp INT. Für
INT-Objekte haben wir aber einige Operationen, mit denen wir arbeiten können. 
 
____________________________________________________________________________ 
 ........................... Beispiele: ........................ 
 feld [3] := 7; 
 feld [4] := feld [3] + 4; 
 
____________________________________________________________________________ 
 
 
Eine Subskription "schält" also vom Datentyp ein ROW ab und liefert ein Element der
Reihung. Die Angabe der Nummer des Elements in der Reihung nennt man Subskript
oder Index (in obigem Beispiel '3'). Der Subskript wird in ELAN in eckigen Klammern
angegeben, um eine bessere Unterscheidung zu den runden Klammern in Ausdrücken
zu erreichen. Ein subskribiertes ROW-Datenobjekt kann also überall da verwendet
werden, wo ein entsprechender Datentyp benötigt wird (Ausnahme: nicht als Schlei­
fenvariable). 
 
____________________________________________________________________________ 
 ........................... Beispiel: ......................... 
 PROC get (ROW 10 INT VAR feld): 
    INT VAR i; 
    FOR i FROM 1 UPTO 10 REP 
      put (i); put ("tes Element bitte:"); 
      get (feld [i]); 
      line 
    END REP 
 END PROC get; 
 
 PROC put (ROW 10 INT CONST feld): 
    INT VAR i; 
    FOR i FROM 1 UPTO 10 REP 
      put (i); put ("tes Element ist:"); 
      put (feld [i]); 
      line 
    END REP 
 END PROC put 
 
____________________________________________________________________________ 
 
 
In diesen Beispielen werden Reihungen als Parameter benutzt. 
 
Diese beiden Prozeduren werden im folgenden Beispiel benutzt um 10 Werte einzu­
lesen und die Summe zu berechnen: 
 
____________________________________________________________________________ 
 ........................... Beispiel: ......................... 
 ROW 10 INT VAR werte; 
 lies werte ein; 
 summiere sie; 
 drucke die summe und einzelwerte. 
 
 lies werte ein: 
    get (werte). 
 
 summiere sie: 
    INT VAR summe :: 0, i; 
    FOR i FROM 1 UPTO 10 REP 
      summe INCR werte [i] 
    END REP. 
 
 drucke die summe und einzelwerte: 
    put (werte); 
    line; 
    put ("Summe:"); put (summe). 
 
____________________________________________________________________________ 
 
 
Da es möglich ist, von jedem Datentyp eine Reihung zu bilden, kann man natürlich
auch von einer Reihung eine Reihung bilden: 
 
____________________________________________________________________________ 
 ........................... Beispiel: ......................... 
 ROW 5 ROW 10 INT VAR matrix 
 
____________________________________________________________________________ 
 
 
Für eine "doppelte" Reihung gilt das für "einfache" Reihungen gesagte. Wiederum
existieren keine Operationen für dieses Datenobjekt (außer der Zuweisung), jedoch ist
es durch Subskription möglich, auf die Elemente zuzugreifen: 
 
 
matrix [3] 
 
 
liefert ein Datenobjekt mit dem Datentyp ROW 10 INT. 
 
Subskribiert man jedoch 'matrix' nochmals, so erhält man ein INT: 
 
 
matrix [2] [8] 
 
 
(jede Subskription "schält" von Außen ein ROW vom Datentyp ab). 
#page# 
 
2.6.2  Struktur 
 
Strukturen sind Datenverbunde wie Reihungen, aber die Komponenten können unglei­
chartige Datentypen haben. Die Komponenten von Strukturen heißen Felder (Reihun­
gen: Elemente) und der Zugriff auf ein Feld Selektion (Reihungen: Subskription). Eine
Struktur ist - genauso wie bei Reihungen - ein eigener Datentyp, der in einer
Deklaration angegeben werden muß. 
 
Die Deklaration einer Struktur sieht folgendermaßen aus: 
 
-  Schlüsselwort #schl ("STRUCT#off("i")##off("b")# 
-  unterschiedliche Datenobjekte in Klammern. Die Datenobjekte werden mit Datentyp und Namen angegeben 
-  Zugriffsrecht ( #on("i")##on("b")#VAR#off("i")##off("b")# oder #on("i")##on("b")#CONST#off("i")##off("b")# ) 
-  Name der Struktur. 
 
____________________________________________________________________________ 
 ........................... Beispiel: ......................... 
 STRUCT (TEXT name, INT alter) VAR ich 
 
____________________________________________________________________________ 
 
 
Wiederum existieren keine Operationen auf Strukturen außer der Zuweisung und der
Selektion, die es erlaubt, Komponenten aus einer Struktur herauszulösen. 
 
Die Selektion hat folgende Form: 
 
Objektname #on("i")##on("b")#.#off("i")##off("b")# Feldname 
 
Beispiele: 
 
 
ich . name 
ich . alter 
 
 
Die erste Selektion liefert einen TEXT-, die zweite ein INT-Datenobjekt. Mit diesen
(selektierten) Datenobjekten kann - wie gewohnt - gearbeitet werden (Ausnahme:
nicht als Schleifenvariable). 
 
Zum Datentyp einer Struktur gehören auch die Feldnamen: 
 
____________________________________________________________________________ 
 ........................... Beispiel: ......................... 
 STRUCT (TEXT produkt name, INT artikel nr) VAR erzeugnis 
 
____________________________________________________________________________ 
 
 
Die obige Struktur ist ein anderer Datentyp als im ersten Beispiel dieses Abschnitts,
da die Namen der Felder zur Unterscheidung hinzugezogen werden. Für Strukturen -
genauso wie bei Reihungen - kann man sich neue Operationen definieren. 
 
Im folgenden Programm werden eine Struktur, die Personen beschreibt, die Prozedu­
ren 'put', 'get' und der dyadische Operator HEIRATET definiert. Anschließend werden
drei Paare verHEIRATET. 
 
____________________________________________________________________________ 
 ........................... Beispiel: ......................... 
 PROC get (STRUCT (TEXT name, vorname, INT alter) VAR p): 
    put ("bitte Nachname:"); get ( p.name); 
    put ("bitte Vorname:");  get ( p.vorname); 
    put ("bitte Alter:");    get ( p.alter); 
    line 
 END PROC get; 
 
 PROC put (STRUCT (TEXT name, vorname, INT alter) CONST p): 
    put (p.vorname); put (p.name); 
    put ("ist"); 
    put (p.alter); 
    put ("Jahre alt"); 
    line 
 END PROC put; 
 
 OP HEIRATET 
    (STRUCT (TEXT name, vorname, INT alter) VAR w, 
     STRUCT (TEXT name, vorname, INT alter) CONST m): 
    w.name := m.name 
 END OP HEIRATET; 
 
____________________________________________________________________________ 
 
 
____________________________________________________________________________ 
 ........................... Beispiel: ......................... 
 ROW 3 STRUCT (TEXT name, vorname, INT alter) VAR frau, 
                                                  mann; 
 
 personendaten einlesen; 
 heiraten lassen; 
 paardaten ausgeben. 
 
 personendaten einlesen: 
    INT VAR i; 
    FOR i FROM 1 UPTO 3 REP 
      get (frau [i]); 
      get (mann [i]) 
    END REP. 
 
 heiraten lassen: 
    FOR i FROM 1 UPTO 3 REP 
      frau [i] HEIRATET mann [i] 
    END REP. 
 
 paardaten ausgeben: 
    FOR i FROM 1 UPTO 3 REP 
      put (frau [i]); 
      put ("hat geheiratet:"); line; 
      put (mann [i]); line 
    END REP. 
  
____________________________________________________________________________ 
 
 
Reihungen und Strukturen dürfen miteinander kombiniert werden, d.h. es darf eine
Reihung in einer Struktur erscheinen oder es darf eine Reihung von einer Struktur
vorgenommen werden. Selektion und Subskription sind in diesen Fällen in der Reihen­
folge vorzunehmen, wie die Datentypen aufgebaut wurden (von außen nach innen). 
#page# 
 
2.6.3  LET-Konstrukt für zusammengesetzte Datentypen 
       
 
Die Verwendung von Strukturen oder auch Reihungen kann manchmal schreibauf­
wendig sein. Mit dem LET-Konstrukt darf man Datentypen einen Namen geben.
Dieser Name steht als Abkürzung und verringert so die Schreibarbeit. Zusätzlich wird
durch die Namensgebung die Lesbarkeit des Programms erhöht. 
 
Form: 
 
#on("i")##on("b")#LET#off("i")##off("b")# Name #on("i")##on("b")#=#off("i")##off("b")# Datentyp 
 
Der Name darf nur aus Großbuchstaben (ohne Blanks) bestehen. 
 
____________________________________________________________________________ 
 ........................... Beispiel: ......................... 
 LET PERSON = STRUCT (TEXT name, vorname, INT alter); 
 
 PROC get (PERSON VAR p): 
    put ("bitte Nachname:"); get ( p.name); 
    put ("bitte Vorname:");  get ( p.vorname); 
    put ("bitte Alter:");    get ( p.alter); 
    line 
 END PROC get; 
 
 PROC put (PERSON CONST p): 
    put (p.vorname); put (p.name); put ("ist"); 
    put (p.alter);   put ("Jahre alt"); line 
 END PROC put; 
 
 OP HEIRATET (PERSON VAR f, PERSON CONST m): 
    f.name := m.name 
 END OP HEIRATET; 
 
 ROW 3 PERSON VAR mann, frau; 
  
____________________________________________________________________________ 
 
 
Überall, wo der abzukürzende Datentyp verwandt wird, kann stattdessen der Name
PERSON benutzt werden. Wohlgemerkt: PERSON ist kein neuer Datentyp, sondern
nur ein Name, der für STRUCT (....) steht. Der Zugriff auf die Komponenten des
abgekürzten Datentyps bleibt erhalten (was bei abstrakten Datentypen, die später
erklärt werden, nicht mehr der Fall ist). 
 
Neben der Funktion der Abkürzung von Datentypen kann das LET-Konstrukt auch
zur Namensgebung für Denoter verwandt werden (siehe 2.3.1.2). 
 
 
 
2.6.4  Denoter für zusammengesetzte 
       Datentypen (Konstruktor)
       
 
Oft ist es notwendig, Datenverbunden Werte zuzuweisen (z.B.: bei der Initialisierung).
Dies kann durch normale Zuweisungen erfolgen: 
 
____________________________________________________________________________ 
 ........................... Beispiel: ......................... 
 LET PERSON = STRUCT (TEXT name, vorname, INT alter); 
 
 PERSON VAR mann; 
 
 mann.name := "meier"; 
 mann.vorname := "egon"; 
 mann.alter := 27 
  
____________________________________________________________________________ 
 
 
Eine andere Möglichkeit für die Wertbesetzung von Datenverbunden ist der Konstruk­
tor: 
 
Form: 
 
Datentyp #on("i")##on("b")#:#off("i")##off("b")# #on("i")##on("b")#(#off("i")##off("b")# Wertliste #on("i")##on("b")#)#off("i")##off("b")# 
 
In der Wertliste wird für jede Komponente des Datentyps, durch Kommata getrennt,
ein Wert aufgeführt. Besteht eine der Komponenten wiederum aus einem Datenver­
bund, muß innerhalb des Konstruktors wiederum ein Konstruktor eingesetzt werden. 
 
____________________________________________________________________________ 
 ........................... Beispiel: ......................... 
 LET PERSON = STRUCT (TEXT name, vorname, INT alter); 
 
 PERSON VAR mann, frau; 
 
 frau := PERSON : ( "niemeyer", "einfalt", 65); 
 frau HEIRATET PERSON : ( "meier", "egon", 27) 
  
____________________________________________________________________________ 
 
 
Ein Konstruktor ist also ein Mechanismus, um ein Datenobjekt eines Datenverbundes
in einem Programm zu notieren. 
 
Konstruktoren sind natürlich für Reihungen auch möglich: 
 
____________________________________________________________________________ 
 ........................... Beispiel: ......................... 
 ROW 7 INT VAR feld; 
 feld := ROW 7 INT : ( 1, 2, 3, 4, 5, 6, 7); 
____________________________________________________________________________ 
#page# 
 
2.7  Abstrakte Datentypen 
 
 
2.7.1  Definition neuer Datentypen 
 
Im Gegensatz zur LET-Vereinbarung für Datentypen, bei der lediglich ein neuer
Name für einen bereits vorhandenen Datentyp eingeführt wird und bei der somit auch
keine neuen Operationen definiert werden müssen (weil die Operationen für den
abzukürzenden Datentyp verwandt werden können), wird durch eine TYPE-Verein­
barung ein gänzlich neuer Datentyp eingeführt. 
 
Form: 
 
#on("i")##on("b")#TYPE#off("i")##off("b")# Name #on("i")##on("b")#=#off("i")##off("b")# Feinstruktur 
 
Der Name darf nur aus Großbuchstaben (ohne Blanks) bestehen. Die Feinstruktur
(konkreter Typ, Realisierung des Datentyps) kann jeder bereits definierte Datentyp
sein. 
 
____________________________________________________________________________ 
 ........................... Beispiel: ......................... 
 TYPE PERSON = STRUCT (TEXT name, vorname, INT alter) 
 
____________________________________________________________________________ 
 
 
Der neudefinierte Datentyp wird abstrakter Datentyp genannt. Im Gegensatz zu
Strukturen und Reihungen stehen für solche Datentypen noch nicht einmal die Zuwei­
sung zur Verfügung. Ein solcher Datentyp kann genau wie alle anderen Datentypen
verwendet werden (Deklarationen, Parameter, wertliefernde Prozeduren, als Kompo­
nenten in Reihungen und Strukturen usw.). 
 
Wird der Datentyp über die Schnittstelle des PACKETs anderen Programmteilen zur
Verfügung gestellt, so müssen Operatoren und/oder Prozeduren für den Datentyp
ebenfalls "herausgereicht" werden. Da dann der neudefinierte Datentyp genauso wie
alle anderen Datentypen verwandt werden kann, aber die Komponenten (Feinstruktur)
nicht zugänglich sind, spricht man von abstrakten Datentypen. 
 
Welche Operationen sollten für einen abstrakten Datentyp zur Verfügung stehen?
Obwohl das vom Einzelfall abhängt, werden meistens folgende Operationen und
Prozeduren definiert: 
 
-  'get'- und 'put'-Prozeduren. 
-  Zuweisung (auch für die Initialisierung notwendig). 
-  Denotierungs-Prozedur (weil kein Konstruktor für den abstrakten Datentyp außer­
   halb des definierenden PACKETs zur Verfügung steht) 
 
 
 
2.7.2  Konkretisierung 
 
Um neue Operatoren und/oder Prozeduren für einen abstrakten Datentyp zu schrei­
ben, ist es möglich, auf die Komponenten des Datentyps (also auf die Feinstruktur)
mit Hilfe des Konkretisierers zuzugreifen. Der Konkretisierer arbeitet ähnlich wie die
Subskription oder Selektion: er ermöglicht eine typmäßige Umbetrachtung vom ab­
strakten Typ zum Datentyp der Feinstruktur. 
 
Form: 
 
#on("i")##on("b")#CONCR#off("i")##off("b")# #on("i")##on("b")#(#off("i")##off("b")# Ausdruck #on("i")##on("b")#)#off("i")##off("b")# 
 
____________________________________________________________________________ 
 ........................... Beispiel: ......................... 
 TYPE MONAT = INT; 
 
 PROC put (MONAT CONST m): 
    put ( CONCR (m)) 
 END PROC put; 
  
____________________________________________________________________________ 
 
 
Der Konkretisierer ist bei Feinstrukturen notwendig, die von elementarem Datentyp
sind. Besteht dagegen die Feinstruktur aus Reihungen oder Strukturen, dann wird
durch eine Selektion oder Subskription eine implizite Konkretisierung vorgenommen. 
 
____________________________________________________________________________ 
 ........................... Beispiel: ......................... 
 TYPE LISTE = ROW 100 INT; 
 
 LISTE VAR personal nummer; 
 ... 
 personal nummer [3] := ... 
 (* das gleiche wie *) 
 CONCR (personal nummer) [3] := ... 
 
____________________________________________________________________________ 
 
 
2.7.3  Denoter für abstrakte 
       Datentypen (Konstruktor)
       
 
Denoter für neudefinierte Datentypen werden mit Hilfe des Konstruktors gebildet: 
 
Form: 
 
Datentyp #on("i")##on("b")#:#off("i")##off("b")# #on("i")##on("b")#(#off("i")##off("b")# Wertliste #on("i")##on("b")#)#off("i")##off("b")# 
 
In der Wertliste wird für jede Komponente des Datentyps, durch Kommata getrennt,
ein Wert aufgeführt. Besteht eine der Komponenten wiederum aus einem Datenver­
bund, muß innerhalb des Konstruktors wiederum ein Konstruktor eingesetzt werden. 
 
____________________________________________________________________________ 
 ........................... Beispiel: ......................... 
 TYPE GEHALT = INT; 
 
 GEHALT VAR meins :: GEHALT : (10000); 
 
____________________________________________________________________________ 
 
 
Besteht die Feinstruktur aus einem Datenverbund, muß der Konstruktor u.U. mehrfach
geschachtelt angewandt werden: 
 
____________________________________________________________________________ 
 ........................... Beispiel: ......................... 
 TYPE KOMPLEX = ROW 2 REAL; 
 
 KOMPLEX CONST x :: KOMPLEX : ( ROW 2 REAL : ( 1.0, 2.0)); 
 
____________________________________________________________________________ 
 
 
Auf die Feinstruktur über den Konkretisierer eines neudefinierten Datentyps darf nur in
dem PACKET zugegriffen werden, in dem der Datentyp definiert wurde. Der Konstruk­
tor kann ebenfalls nur in dem typdefinierenden PACKET verwandt werden. 
 
____________________________________________________________________________ 
 ........................... Beispiel: ......................... 
 PACKET widerstaende DEFINES WIDERSTAND, REIHE, PARALLEL, 
                             :=, get, put: 
 
 TYPE WIDERSTAND = INT; 
 
 OP := (WIDERSTAND VAR l, WIDERSTAND CONST r): 
    CONCR (l) := CONCR (r) 
 END OP :=; 
 
 PROC get (WIDERSTAND VAR w): 
    INT VAR i; 
    get (i); 
    w := WIDERSTAND : (i) 
 END PROC get; 
 
 PROC put (WIDERSTAND CONST w): 
    put (CONCR (w)) 
 END PROC put; 
 
 WIDERSTAND OP REIHE (WIDERSTAND CONST l, r): 
    WIDERSTAND : ( CONCR (l) + CONCR (r)) 
 END OP REIHE; 
 
 WIDERSTAND OP PARALLEL (WIDERSTAND CONST l, r): 
   WIDERSTAND : 
     ((CONCR (l) * CONCR (r)) DIV (CONCR (l) + CONCR (r))) 
 END OP PARALLEL 
 
 END PACKET widerstaende 
  
____________________________________________________________________________ 
 
 
Dieses Programm realisiert den Datentyp WIDERSTAND und mit den Operationen
eine Fachsprache, mit dem man nun leicht WIDERSTANDs-Netzwerke berechnen
kann, wie z.B. folgendes: 
 
 
 
                          +---R4---+ 
                          |        | 
        +---R1---+        +---R5---+ 
        |        |        |        | 
     ---+        +---R3---+        +--- 
        |        |        |        | 
        +---R2---+        +---R6---+ 
                          |        | 
                          +---R7---+ 
 
 
Zur Berechnung des Gesamtwiderstandes kann nun folgendes Programm geschrieben
werden: 
 
____________________________________________________________________________ 
 ........................... Beispiel: ......................... 
 ROW 7 WIDERSTAND VAR r; 
 widerstaende einlesen; 
 gesamtwiderstand berechnen; 
 ergebnis ausgeben. 
 
 widerstaende einlesen: 
    INT VAR i; 
    FOR i FROM 1 UPTO 7 REP 
       put ("bitte widerstand R"); put (i); put (":"); 
       get (r [i]); 
    END REP. 
 
 gesamtwiderstand berechnen: 
    WIDERSTAND CONST rgesamt :: (r [1] PARALLEL r [2]) REIHE 
      r [3] REIHE (r [4] PARALLEL r [5] PARALLEL r [6] 
      PARALLEL r [7]). 
 
 ergebnis ausgeben: 
    line; 
    put (rgesamt). 
____________________________________________________________________________ 
#page# 
 
2.8  Dateien 
 
Dateien werden benötigt, wenn 
 
-  Daten über die Abarbeitungszeit eines Programms aufbewahrt werden sollen; 
-  der Zeitpunkt oder Ort der Datenerfassung nicht mit dem Zeitpunkt oder Ort der
   Datenverarbeitung übereinstimmt; 
-  die gesamte Datenmenge nicht auf einmal in den Zentralspeicher eines Rechners
   paßt; 
-  die Anzahl und/oder Art der Daten nicht von vornherein bekannt sind. 
 
Eine Datei ("file") ist eine Zusammenfassung von Daten, die auf Massenspeichern
aufbewahrt wird. Dateien sind in bestimmten Informationsmengen, den Sätzen ("re­
cords") organisiert. 
 
 
 
2.8.1  Datentypen FILE und DIRFILE 
 
In ELAN gibt es zwei Arten von Dateien. Sie werden durch die Datentypen FILE 
und DIRFILE realisiert: 
 
 
 
FILE: 
sequentielle Dateien. Die Sätze können nur sequentiell gelesen bzw. geschrieben
werden. Eine Positionierung ist nur zum nächsten Satz möglich. 
 
 
DIRFILE: 
indexsequentielle Dateien. Die Positionierung erfolgt direkt mit Hilfe eines Schlüssels
("key") oder Index, kann aber auch sequentiell vorgenommen werden. 
 
#on("b")#Wichtig: #off("b")# 
DIRFILEs sind auf dem EUMEL-System standardmäßig nicht implementiert! Deswe­
gen wird auf diesen Dateityp hier nicht weiter eingegangen. 
#page# 
 
2.8.2  Deklaration und Assoziierung 
 
Dateien müssen in einem ELAN-Programm - wie alle anderen Objekte auch -
deklariert werden. 
 
Form: 
 
#on("i")##on("b")#FILE#off("i")##off("b")# #on("i")##on("b")#VAR#off("i")##off("b")# interner Dateibezeichner 
 
____________________________________________________________________________ 
 ........................... Beispiel: ......................... 
 FILE VAR f 
 
____________________________________________________________________________ 
 
 
Dabei ist zu beachten, daß im EUMEL-System alle FILEs als VAR deklariert werden
müssen, denn jede Lese/Schreib-Operation verändert einen FILE. 
 
Dateien werden normalerweise vom Betriebsystem eines Rechners aufbewahrt und
verwaltet. Somit ist eine Verbindung von einem ELAN-Programm, in dem eine Datei
unter einem Namen - wie jedes andere Datenobjekt auch - angesprochen werden
soll, und dem Betriebssystem notwendig. Dies erfolgt durch die sogenannte Assozi­
ierungsprozedur. Die Assoziierungsprozedur 'sequential file' hat die Aufgabe, eine in
einem Programm deklarierte FILE VAR mit einer bereits vorhandenen oder noch
einzurichtenden Datei des EUMEL-Systems zu koppeln. 
 
Form: 
 
#on("i")##on("b")#sequential file#off("i")##off("b")# #on("i")##on("b")#(#off("i")##off("b")# Betriebsrichtung, Dateiname #on("i")##on("b")#)#off("i")##off("b")# 
 
Es gibt folgende Betriebsrichtungen (TRANSPUTDIRECTIONs): 
 
 
input:  
Die Datei kann vom Programm nur gelesen werden. Durch 'input' wird bei der Asso­
ziierung automatisch auf den ersten Satz der Datei positioniert. Ist die zu lesende
Datei nicht vorhanden, wird ein Fehler gemeldet. 
 
 
output:  
Die Datei kann vom Programm nur beschrieben werden. Durch 'output' wird bei der
Assoziierung automatisch hinter den letzten Satz der Datei positioniert (bei einer
leeren Datei also auf den ersten Satz). Ist die Datei vor der Assoziierung nicht vor­
handen, wird sie automatisch eingerichtet. 
 
 
modify:  
Im EUMEL-System gibt es noch die Betriebsrichtung 'modify'. 
Die Datei kann vom Programm in beliebiger Weise gelesen und beschrieben werden.
Im Gegensatz zu den Betriebsrichtungen 'input' und 'output', bei denen ausschließlich
ein rein sequentielles Lesen oder Schreiben erlaubt ist, kann bei 'modify' beliebig
positioniert, gelöscht, eingefügt und neu geschrieben werden. 
 
Nach erfolgter Assoziiierung ist auf den zuletzt bearbeiteten Satz positioniert. Die
Datei wird automatisch eingerichtet, wenn sie vor der Assoziierung nicht vorhanden
war. 
 
Der zweite Parameter der Assoziierungsprozedur gibt an, unter welchem Namen die
Datei in der Task existiert oder eingerichtet werden soll. 
 
____________________________________________________________________________ 
 ........................... Beispiel: ......................... 
 FILE VAR meine datei :: sequential file (output, "xyz"); 
 
____________________________________________________________________________ 
 
 
Folgendes Beispiel zeigt ein Programm, welches eine Datei liest und auf dem Ausga­
bemedium ausgibt: 
 
____________________________________________________________________________ 
 ........................... Beispiel: ......................... 
 FILE VAR f :: sequential file (input, "datei1"); 
 TEXT VAR satz; 
 WHILE NOT eof (f) REP 
    getline (f, satz); 
    putline (satz); 
 END REP. 
  
____________________________________________________________________________ 
 
 
Eine genau Übersicht der für Dateien existierende Operatoren und Prozeduren finden
Sie im Teil 5.3. 
#page# 

2.9  Abstrakte Datentypen 
     im EUMEL-System
     
 
 
2.9.1  Datentyp TASK 
 
Tasks müssen im Rechnersystem eindeutig identifiziert werden; sogar im EUMEL-
Rechner-Netz sind Tasks eindeutig identifizierbar. Dazu wird der spezielle Datentyp
'TASK' benutzt, denn die Identifizierung einer Task über den Namen ist nicht eindeu­
tig. Der Benutzer kann ja einen Tasknamen ändern, eine Task löschen und eine
neue Task mit gleichem Namen einrichten, die jedoch nicht gleich reagiert. Somit
werden Tasks eindeutig über Variablen vom  Datentyp TASK identifiziert. 
 
____________________________________________________________________________ 
 ........................... Beispiel: ......................... 
 TASK VAR plotter := task ("PLOTTER 1") 
  
____________________________________________________________________________ 
 
 
Die Taskvariable 'plotter' bezeichnet jetzt die Task im System, die augenblicklich den
Namen "PLOTTER 1" hat. Die Prozedur 'task' liefert den systeminternen Taskbe­
zeichner. 
 
Nun sind Taskvariablen auch unter Berücksichtigung der Zeit und nicht nur im aktuel­
len Systemzustand eindeutig. Der Programmierer braucht sich also keine Sorgen
darüber zu machen, daß seine Taskvariable irgendwann einmal eine "falsche" Task
(nach Löschen von "PLOTTER 1" neu eingerichtete gleichen oder anderen Namens)
identifiziert. Wenn die Task "PLOTTER 1" gelöscht worden ist, bezeichnet 'plotter'
keine gültige Task mehr. 
 
Unbenannte Tasks haben alle den Pseudonamen "-". Sie können nur über Taskvari­
ablen angesprochen werden. 
 
 
____________________________________________________________________________ 
 ........................... Beispiel: ......................... 
 PROC generate shutup manager: 
    TASK VAR son; 
    begin ("shutup", PROC shutup manager, son) 
 END PROC generate shutup manager; 
 
 PROC shutup manager: 
    disable stop; 
    command dialogue (TRUE); 
    REP 
       break; 
       line; 
       IF yes ("shutup") 
          THEN clear error; 
               shutup 
       FI 
    PER 
 END PROC shutup manager 
  
____________________________________________________________________________ 
 
 
Ein Taskvariable wird zum Beispiel als Parameter für die Prozedur 'begin' benötigt. 
 
begin 
   #on("b")#PROC begin (TEXT CONST son name, PROC start, 
               TASK VAR new task)#off("b")# 
   Die Prozedur richtet eine Sohntask mit Namen 'son name' (im Beispiel: shutup)
   ein, die mit der Prozedur 'start' (im Beispiel: shutup manager) gestartet wird. 'new
   task' (im Beispiel: son) identifiziert den Sohn, falls die Sohntask korrekt eingerich­
   tet wurde. 
#page# 
 
2.9.2   Datentyp THESAURUS 
 
Ein Thesaurus ist ein Namensverzeichnis, das bis zu 200 Namen beinhalten kann.
Dabei muß jeder Name mindestens ein Zeichen und höchstens 100 Zeichen lang sein.
Steuerzeichen (code < 32) werden im Namen folgendermaßen umgesetzt: 
 
#on("i")##on("b")#steuerzeichen#off("b")##off("i")# wird umgesetzt in #on("i")##on("b")#"""" + code(steuerzeichen) + """"#off("b")##off("i")# 
 
Ein Thesaurus ordnet jedem eingetragenen Namen einen Index zwischen 1 und 200
(einschließlich) zu. Diese Indizes bieten dem Anwender die Möglichkeit, Thesauri zur
Verwaltung benannter Objekte zu verwenden. (Der Zugriff erfolgt dann über den Index
eines Namens in einem Thesaurus). So werden Thesauri u.a. von der Dateiverwaltung
benutzt. Sie bilden die Grundlage der ALL- und SOME-Operatoren. 
 
____________________________________________________________________________ 
 ........................... Beispiel: ......................... 
 initialisiere; 
 arbeite thesaurus ab. 
 
 initialisiere: 
    THESAURUS VAR eine auswahl :: SOME (myself); 
    TEXT VAR thesaurus element; 
    INT VAR index :: 0. 
 
 arbeite thesaurus ab: 
    REPEAT 
       get (eine auswahl, thesaurus element, index); 
       IF thesaurus element = "" 
          THEN LEAVE arbeite thesaurus ab 
       FI; 
       fuehre aktionen durch 
    PER. 
 
    fuehre aktionen durch: 
       edit (thesaurus element); 
       lineform (thesaurus element); 
       pageform (thesaurus element); 
       print (thesaurus element). 
  
____________________________________________________________________________ 
 
 
Dieses Beispiel führt für eine Auswahl der in der Task befindlichen Dateien nachein­
ander die Kommandos 'edit', 'lineform', 'pageform' und 'print' aus. 
 
Die benutzten Operatoren und Prozeduren leisten folgendes: 
 
#ix("SOME")# 
   #on("b")#THESAURUS OP SOME (TASK CONST task) #off("b")# 
   Der Operator bietet das Verzeichnis der in der angegeben Task befindlichen
   Dateien zum Editieren an. Namen, die nicht gewünscht sind, müssen aus dem
   Verzeichnis gelöscht werden. 
 
 
#ix("get")# 
   #on("b")#PROC get (THESAURUS CONST t, TEXT VAR name, INT VAR index) 
   #off("b")# Die Prozedur liefert den nächsten Eintrag aus dem angegebenen Thesaurus 't'.
   'Nächster' heißt hier, der kleinste vorhandene mit einem Index größer als 'index'.
   Dabei wird in 'name'der Name und in 'index'der Index des Eintrags geliefert. 
#page# 
 
2.9.3  Datenräume 
 
Datenräume sind die Grundlage von Dateien im EUMEL-System. Einen Datenraum
kann man sich als eine Sammlung von Daten vorstellen (u.U. leer). Man kann einem
Datenraum durch ein Programm einen Datentyp "aufprägen". Nach einem solchen
"Aufpräge"-Vorgang kann der Datenraum wie ein "normaler" Datentyp behandelt
werden. 
 
Standarddateien (FILEs) sind eine besondere Form von Datenräumen. Sie können nur
Texte aufnehmen, da sie ja hauptsächlich für die Kommunikation mit dem Menschen
(vorwiegend mit Hilfe des Editors bzw. Ein-/ Ausgabe) gedacht sind. Will man Zahlen
in einen FILE ausgeben, so müssen diese zuvor in Texte umgewandelt werden. Hier­
für stehen Standardprozeduren zur Verfügung (z.B. 'put (f, 17)'). 
 
Will man aber Dateien zur Kommunikation zwischen Programmen verwenden, die
große Zahlenmengen austauschen, verursachen die Umwandlungen von Zahlen in
TEXTe und umgekehrt unnötigen Rechenaufwand. Zu diesem Zweck werden im
EUMEL-System Datenräume eingesetzt, die es gestatten, beliebige Strukturen
(Typen) in Dateien zu speichern. Solche Datenräume kann man weder mit dem Editor
noch mit dem Standarddruckprogramm (print) bearbeiten, da diese ja den Typ des in
dem Datenraum gespeicherten Objektes nicht kennen. 
 
 
 
2.9.3.1  Datentyp DATASPACE 
 
Datenräume können als eigener Datentyp (DATASPACE) in einem Programm behan­
delt werden. Somit können Datenräume (als Ganzes) ohne Kenntnis eines eventuell
(vorher oder später) aufgeprägten Typs benutzt werden. 
 
Als Operationen auf DATASPACE-Objekte sind nur Transporte, Löschen und Zuwei­
sung zugelassen. 
 
____________________________________________________________________________ 
 ........................... Beispiel: ......................... 
 DATASPACE VAR ds 
 
____________________________________________________________________________ 
 
 
Für Datenräume ist die Zuweisung definiert. Der Zuweisungsoperator (':=') bewirkt
eine Kopie des Datenraums vom rechten auf den linken Operanden. 
 
____________________________________________________________________________ 
 ........................... Beispiel: ......................... 
 DATASPACE VAR datenraum :: nilspace; 
 
____________________________________________________________________________ 
 
 
Die Prozedur 'nilspace' liefert einen leeren Datenraum. Der Datenraum 'datenraum' ist
also eine Kopie des leeren Datenraums. 
 
Die Prozeduren und Operatoren für Datenräume werden im Teil 5.4.7 beschrieben. 
#page# 
 
2.9.3.2  BOUND-Objekte 
 
Wie bereits erwähnt, kann man einem Datenraum einen Datentyp aufprägen. Dazu
werden #ib#BOUND#ie#-Objekte benutzt. Mit dem Schlüsselwort #on("i")##on("b")#BOUND#off("i")##off("b")#, welches in der
Deklaration vor den Datentyp gestellt wird, teilt man dem ELAN-Compiler mit, daß
die Werte eines Datentyps in einem Datenraum gespeichert sind bzw. gespeichert
werden sollen. 
 
____________________________________________________________________________ 
 ........................... Beispiel: ......................... 
 BOUND ROW 1000 REAL VAR liste 
 
____________________________________________________________________________ 
 
 
Die Ankopplung des BOUND-Objekts an eine Datei erfolgt mit dem Operator #on("i")##on("b")#:=#off("i")##off("b")#. 
 
Form: 
 
BOUND-Objekt #on("i")##on("b")#:=#off("i")##off("b")# Datenraum 
 
____________________________________________________________________________ 
 ........................... Beispiel: ......................... 
 BOUND ROW 1000 REAL VAR gehaltsliste := new ("Gehälter") 
 
____________________________________________________________________________ 
 
 
Die Prozedur 'new' kreiert dabei einen leeren Datenraum (hier mit dem Namen 'Ge­
hälter'), der mit Hilfe der Zuweisung (hier: Initialisierung) an die Variable 'gehaltsliste'
gekoppelt wird. 
 
Nun kann man mit der 'gehaltsliste' arbeiten wie mit allen anderen Feldern auch. Die
Daten, die in 'gehaltsliste' gespeichert, werden eigentlich im Datenraum 'Gehälter'
abgelegt. 
 
____________________________________________________________________________ 
 ........................... Beispiel: ......................... 
 gehaltsliste [5] := 10 000.0;           (* Traumgehalt *) 
 gehaltsliste [index] INCR 200.0;        (* usw.        *) 
  
____________________________________________________________________________ 
 
 
Man kann auch Prozeduren schreiben, die auf der Gehaltsliste arbeiten. 
 
____________________________________________________________________________ 
 ........................... Beispiel: ......................... 
 PROC sort (ROW 1000 REAL VAR liste): 
 ... 
 END PROC sort; 
 ... 
 sort (CONCR (gehaltsliste)); 
 ... 
  
____________________________________________________________________________ 
 
 
Man beachte, daß der formale Parameter der Prozedur 'sort' nicht mit BOUND spezi­
fiziert werden darf (BOUND wird nur bei der Deklaration des Objekts angegeben). Das
ist übrigens ein weiterer wichtiger Vorteil von BOUND-Objekten: man kann alle
Prozeduren des EUMEL-Systems auch für BOUND-Objekte verwenden, nur die
Datentypen müssen natürlich übereinstimmen. 
 
 
Häufige Fehler bei der Benutzung von Datenräumen 
 
-  Wenn man an ein DATASPACE-Objekt zuweist (z.B.: DATASPACE VAR ds :=
   new ("mein datenraum")), so erhält man, wie bereits erwähnt, eine Kopie des
   Datenraums in 'ds'. Koppelt man jetzt 'ds' an ein BOUND-Objekt an und führt
   Änderungen durch, so wirken diese nur auf die Kopie und nicht auf die Quelle.
   Für Änderungen in der Quelle, also in der vom Datei-Manager verwalteten Datei,
   ist stets direkt anzukoppeln. 
 
____________________________________________________________________________ 
 ........................... Beispiel: ......................... 
 BOUND ROW 10 INT VAR reihe; 
 INT VAR i; 
 
 PROC zeige dsinhalt (TEXT CONST datenraum): 
    BOUND ROW 10 INT VAR inhalt := old (datenraum); 
    INT VAR j; 
    line; 
    putline ("Inhalt:" + datenraum); 
    FOR j FROM 1 UPTO 10 REP 
       put (inhalt (j)) 
    PER 
 END PROC zeige dsinhalt; 
 
 (*  falsch: es wird auf der Kopie gearbeitet:           *) 
 DATASPACE VAR ds := new ("Gegenbeispiel: Zahlen 1 bis 10"); 
 reihe := ds; 
 besetze reihe; 
 zeige dsinhalt ("Gegenbeispiel: Zahlen 1 bis 10"); 
 
 (*  richtig: es wird auf dem Datenraum gearbeitet:      *) 
 reihe := new ("Beispiel: Zahlen 1 bis 10"); 
 besetze reihe; 
 zeige dsinhalt ("Beispiel: Zahlen 1 bis 10"). 
 
 besetze reihe: 
    FOR i FROM 1 UPTO 10 REP 
       reihe (i) := i 
    PER. 
  
____________________________________________________________________________ 
 
 
   Der Datenraum 'Gegenbeispiel: Zahlen 1 bis 10' wird nicht mit Werten besetzt,
   sondern die Kopie dieses Datenraums, der unbenannte Datenraum 'ds'. Auf dem
   direkt angekoppelten Datenraum 'Beispiel: Zahlen 1 bis 10' werden die Werte
   gespeichert. 
 
 
-  Wenn man ein DATASPACE-Objekt benutzt, ohne den Datei-Manager zu
   verwenden, so muß man selbst dafür sorgen, daß dieses Objekt nach seiner
   Benutzung wieder gelöscht wird. Das Löschen geschieht durch die Prozedur
   'forget'. Ein automatisches Löschen von DATASPACE-Objekten erfolgt nicht bei
   Programmende (sonst könnten sie ihre Funktion als Datei nicht erfüllen). Nur
   durch 'forget' oder beim Löschen einer Task werden alle ihr gehörenden
   DATASPACE-Objekte gelöscht und der belegte Speicherplatz freigegeben. 
 
 
-  Ferner ist zu beachten, daß vor der Ankopplung an ein BOUND-Objekt das
   DATASPACE-Objekt initialisiert wird (im Normalfall mit 'nilspace'). 
 
____________________________________________________________________________ 
 ........................... Beispiel: ......................... 
 DATASPACE VAR ds :=  nilspace; 
 BOUND ROW 1000 REAL VAR real feld := ds; 
 .... 
 real feld [index] := wert; 
 .... 
 forget (ds) (* Datenraum löschen, 
                damit der Platz wieder verwendet wird *) 
  
____________________________________________________________________________ 
 
 
-  Will man auf die Feinstruktur eines BOUND-Objekts zugreifen, so muß man
   strenggenommen den Konkretisierer benutzen: 
 
   Form: 
 
   #on("i")##on("b")#CONCR#off("i")##off("b")# #on("i")##on("b")#(#off("i")##off("b")# Ausdruck #on("i")##on("b")#)#off("i")##off("b")# 
 
   Der Konkretisierer ermöglicht eine typmäßige Umbetrachtung vom BOUND-Objekt
   zum Datentyp der Feinstruktur. Ist der Zugriff jedoch eindeutig, so wird 'CONCR'
   automatisch vom Compiler ergänzt. 
 
____________________________________________________________________________ 
 ........................... Beispiel: ......................... 
 BOUND INT VAR i := old ("i-Wert"); 
 INT VAR x; 
 x := wert. 
 
 wert: 
    IF x < 0 
      THEN 0 
      ELSE CONCR (i) 
    FI. 
  
____________________________________________________________________________ 
 
 
In diesem Beispiel muß der Konkretisierer benutzt werden, da sonst der Resultattyp
des Refinements nicht eindeutig ist (BOUND oder INT?). 
 
 
 
2.9.3.3  Definition neuer Dateitypen 
 
Durch die Datenräume und die Datentyp-Definition von ELAN ist es für Programmie­
rer relativ einfach, neue Datei-Datentypen zu definieren. In der Regel reicht der
Datentyp FILE für "normale" Anwendungen aus, jedoch kann es manchmal sinnvoll
und notwendig sein, neue Datei-Typen für spezielle Aufgaben zu definieren. 
 
In diesem Abschnitt soll an dem Beispiel DIRFILE (welcher zwar im ELAN-Standard
definiert, aber nicht im EUMEL-System realisiert ist) gezeigt werden, wie ein neuer
Datei-Datentyp definiert wird: 
 
____________________________________________________________________________ 
 ........................... Beispiel: ......................... 
 PACKET dirfiles DEFINES DIRFILE, :=, dirfile, getline, ...: 
 
 LET maxsize = 1000; 
 
 TYPE DIRFILE = BOUND ROW maxsize TEXT; 
 (* DIRFILE besteht aus TEXTen; Zugriff erfolgt ueber einen
    Schluessel, der den Index auf die Reihung darstellt *) 
 
 OP := (DIRFILE VAR dest, DATASPACE CONST space): 
    CONCR (dest) := space 
 END OP :=; 
 
 DATASPACE PROC dirfile (TEXT CONST name): 
    IF exists (name) 
       THEN old (name) 
       ELSE new (name) 
    FI 
 END PROC dirfile; 
 
 PROC getline (DIRFILE CONST df, INT CONST index, 
               TEXT VAR record): 
    IF index <= 0 
       THEN errorstop ("access before first record") 
    ELIF index > maxsize 
       THEN errorstop ("access after last record") 
       ELSE record := df [index] 
    FI 
 END PROC getline; 
 
 PROC putline (DIRFILE CONST df, INT CONST index, 
               TEXT VAR record): 
    ... 
 END PROC putline; 
 
 ... 
 END PACKET dirfiles; 
  
____________________________________________________________________________ 
 
 
Die Prozedur 'dirfile' ist die Assoziierungsprozedur für DIRFILEs (analog 'sequential
file' bei FILEs). 'dirfile' liefert entweder einen bereits vorhandenen Datenraum oder
richtet einen neuen ein. Um eine Initialisierung mit der 'dirfile'-Prozedur vorneh­
men zu können, braucht man auch einen Zuweisungsoperator, der den Datenraum an
den DIRFILE-Datentyp koppelt. 
 
Zugriffe auf einen DIRFILE sind nun relativ einfach zu schreiben. Im obigen Beispiel
wird nur die Prozedur 'getline' gezeigt. 
 
Nun ist es möglich, Programme zu schreiben, die den DIRFILE-Datentyp benut­
zen. 
 
____________________________________________________________________________ 
 ........................... Beispiel: ......................... 
 DIRFILE VAR laeufer :: dirfile ("Nacht von Borgholzhausen"); 
 INT VAR nummer; 
 TEXT VAR name; 
 
 REP 
    put ("Startnummer bitte:"); 
    get (nummer); 
    line; 
    put ("Name des Laeufers:"); 
    get (name); 
    putline (laeufer, nummer, name); 
    line 
 UNTIL no ("weiter") END REP; 
 ... 
  
____________________________________________________________________________ 
#page# 
 
2.9.4  Datentyp INITFLAG 
 
Im Multi-User-System ist es oft notwendig, Pakete beim Einrichten einer neuen
Task in dieser neu zu initialisieren. Das muß z.B. bei der Dateiverwaltung gemacht
werden, da die neue Task ja nicht die Dateien des Vaters erbt. Mit Hilfe von
INITFLAG-Objekten kann man zu diesem Zweck feststellen, ob ein Paket in dieser
Task schon initialisiert wurde. 
 
 
INITFLAG 
   #on("b")#TYPE INITFLAG #off("b")# 
   Erlaubt die Deklaration entsprechender Flaggen. 
 
:= 
   #on("b")#OP := (INITFLAG VAR flag, BOOL CONST flagtrue) #off("b")# 
   Erlaubt die Initialisierung von INITFLAGs 
 
initialized 
   #on("b")#BOOL PROC initialized (INITFLAG VAR flag) #off("b")# 
   Wenn die Flagge in der Task A auf TRUE oder FALSE gesetzt wurde, dann liefert
   sie beim ersten Aufruf den entsprechenden Wert, danach immer TRUE (in der
   Task A!). 
 
   Beim Einrichten von Söhnen wird die Flagge in den Sohntasks automatisch auf
   FALSE gesetzt. So wird erreicht, daß diese Prozedur in den neu eingerichteten
   Söhnen und Enkeltasks genau beim ersten Aufruf FALSE liefert. 
 
____________________________________________________________________________ 
 ........................... Beispiel: ......................... 
 PACKET stack DEFINES push, pop: 
 
 INITFLAG VAR in this task := FALSE ; 
 INT VAR stack pointer ; 
 ROW 1000 INT VAR stack ; 
 
 PROC push (INT CONST value) : 
 
    initialize stack if necessary ; 
    .... 
 
 END PROC push ; 
 
 PROC pop (INT VAR value) : 
 
    initialize stack if necessary ; 
    .... 
 
 END PROC pop ;. 
 
 initialize stack if necessary : 
 IF NOT initialized (in this task) 
    THEN stack pointer := 0 
 
 FI . 
 
 END PACKET stack 
____________________________________________________________________________